diff --git a/base/applications/calc/lang/en-US.rc b/base/applications/calc/lang/en-US.rc index 402991ab06d..e025692c360 100644 --- a/base/applications/calc/lang/en-US.rc +++ b/base/applications/calc/lang/en-US.rc @@ -343,8 +343,8 @@ IDR_MENU_SCIENTIFIC_1 MENU DISCARDABLE BEGIN POPUP "Edit" BEGIN - MENUITEM "Copy\tCTRL-C", IDM_EDIT_COPY - MENUITEM "Paste\tCTRL-V", IDM_EDIT_PASTE + MENUITEM "Copy\tCtrl+C", IDM_EDIT_COPY + MENUITEM "Paste\tCtrl+V", IDM_EDIT_PASTE END POPUP "View" BEGIN @@ -375,8 +375,8 @@ IDR_MENU_SCIENTIFIC_2 MENU DISCARDABLE BEGIN POPUP "Edit" BEGIN - MENUITEM "Copy\tCTRL-C", IDM_EDIT_COPY - MENUITEM "Paste\tCTRL-V", IDM_EDIT_PASTE + MENUITEM "Copy\tCtrl+C", IDM_EDIT_COPY + MENUITEM "Paste\tCtrl+V", IDM_EDIT_PASTE END POPUP "View" BEGIN @@ -408,8 +408,8 @@ IDR_MENU_STANDARD MENU DISCARDABLE BEGIN POPUP "Edit" BEGIN - MENUITEM "Copy\tCTRL-C", IDM_EDIT_COPY - MENUITEM "Paste\tCTRL-V", IDM_EDIT_PASTE + MENUITEM "Copy\tCtrl+C", IDM_EDIT_COPY + MENUITEM "Paste\tCtrl+V", IDM_EDIT_PASTE END POPUP "View" BEGIN diff --git a/base/applications/calc/lang/ja-JP.rc b/base/applications/calc/lang/ja-JP.rc new file mode 100644 index 00000000000..5a7a52e05f9 --- /dev/null +++ b/base/applications/calc/lang/ja-JP.rc @@ -0,0 +1,732 @@ +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT + +// Dialog + +IDD_DIALOG_SCIENTIFIC DIALOGEX 0, 0, 316, 161 +STYLE DS_SHELLFONT | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "ReactOS 電卓" +MENU IDR_MENU_SCIENTIFIC_1 +FONT 9, "MS UI Gothic", 0, 0, 0x1 +BEGIN + GROUPBOX "",IDC_STATIC,4,16,132,21 + CONTROL "16進",IDC_RADIO_HEX,"Button",BS_AUTORADIOBUTTON,8,24,29,10 + CONTROL "10進",IDC_RADIO_DEC,"Button",BS_AUTORADIOBUTTON,40,24,29,10 + CONTROL "8進",IDC_RADIO_OCT,"Button",BS_AUTORADIOBUTTON,72,24,27,10 + CONTROL "2進",IDC_RADIO_BIN,"Button",BS_AUTORADIOBUTTON,100,24,26,10 + GROUPBOX "",IDC_STATIC,4,36,76,21 + CONTROL "Inv",IDC_CHECK_INV,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,8,44,26,10 + CONTROL "Hyp",IDC_CHECK_HYP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,40,44,29,10 + CTEXT "",IDC_TEXT_PARENT,84,40,24,17,SS_CENTERIMAGE, + WS_EX_CLIENTEDGE + CTEXT "",IDC_TEXT_MEMORY,112,40,24,17,SS_CENTERIMAGE, + WS_EX_CLIENTEDGE + PUSHBUTTON "Sta",IDC_BUTTON_STA,4,64,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Ave",IDC_BUTTON_AVE,4,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | WS_DISABLED | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Sum",IDC_BUTTON_SUM,4,102,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | WS_DISABLED | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "s",IDC_BUTTON_S,4,121,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | WS_DISABLED | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Dat",IDC_BUTTON_DAT,4,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | WS_DISABLED | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "F-E",IDC_BUTTON_FE,38,64,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "dms",IDC_BUTTON_DMS,38,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "sin",IDC_BUTTON_SIN,38,102,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "cos",IDC_BUTTON_COS,38,121,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "tan",IDC_BUTTON_TAN,38,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "(",IDC_BUTTON_LEFTPAR,64,64,24,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Exp",IDC_BUTTON_EXP,64,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "x^y",IDC_BUTTON_XeY,64,102,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "x^2",IDC_BUTTON_Xe2,64,121,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "x^3",IDC_BUTTON_Xe3,64,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON ")",IDC_BUTTON_RIGHTPAR,90,64,24,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "ln",IDC_BUTTON_LN,90,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "log",IDC_BUTTON_LOG,90,102,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "n!",IDC_BUTTON_NF,90,121,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "1/x",IDC_BUTTON_RX,90,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "MC",IDC_BUTTON_MC,124,64,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "MR",IDC_BUTTON_MR,124,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "MS",IDC_BUTTON_MS,124,102,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "M+",IDC_BUTTON_MP,124,121,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "pi",IDC_BUTTON_PI,124,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "7",IDC_BUTTON_7,158,64,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "4",IDC_BUTTON_4,158,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "1",IDC_BUTTON_1,158,102,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "0",IDC_BUTTON_0,158,121,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "A",IDC_BUTTON_A,158,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "8",IDC_BUTTON_8,184,64,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "5",IDC_BUTTON_5,184,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "2",IDC_BUTTON_2,184,102,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "+/-",IDC_BUTTON_SIGN,184,121,24,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "B",IDC_BUTTON_B,184,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "9",IDC_BUTTON_9,210,64,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "6",IDC_BUTTON_6,210,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "3",IDC_BUTTON_3,210,102,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON ",",IDC_BUTTON_DOT,210,121,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "C",IDC_BUTTON_C,210,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "/",IDC_BUTTON_DIV,236,64,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "*",IDC_BUTTON_MULT,236,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "-",IDC_BUTTON_SUB,236,102,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "+",IDC_BUTTON_ADD,236,121,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "D",IDC_BUTTON_D,236,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Mod",IDC_BUTTON_MOD,262,64,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Or",IDC_BUTTON_OR,262,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Lsh",IDC_BUTTON_LSH,262,102,24,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "=",IDC_BUTTON_EQU,262,121,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "E",IDC_BUTTON_E,262,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "And",IDC_BUTTON_AND,288,64,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Xor",IDC_BUTTON_XOR,288,83,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Not",IDC_BUTTON_NOT,288,102,24,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Int",IDC_BUTTON_INT,288,121,24,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "F",IDC_BUTTON_F,288,140,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + GROUPBOX "",IDC_STATIC,140,16,172,21 + CONTROL "Qword",IDC_RADIO_QWORD,"Button",BS_AUTORADIOBUTTON | + NOT WS_VISIBLE,148,24,37,10 + CONTROL "Dword",IDC_RADIO_DWORD,"Button",BS_AUTORADIOBUTTON | + NOT WS_VISIBLE,188,24,37,10 + CONTROL "Word",IDC_RADIO_WORD,"Button",BS_AUTORADIOBUTTON | NOT + WS_VISIBLE,232,24,33,10 + CONTROL "Byte",IDC_RADIO_BYTE,"Button",BS_AUTORADIOBUTTON | NOT + WS_VISIBLE,272,24,30,10 + CONTROL "Degrees",IDC_RADIO_DEG,"Button",BS_AUTORADIOBUTTON,148,24, + 42,10 + CONTROL "Radians",IDC_RADIO_RAD,"Button",BS_AUTORADIOBUTTON,192, + 24,42,10 + CONTROL "Gradians",IDC_RADIO_GRAD,"Button",BS_AUTORADIOBUTTON, + 236,24,44,10 + PUSHBUTTON "C",IDC_BUTTON_CANC,272,40,40,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "CE",IDC_BUTTON_CE,228,40,40,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Back",IDC_BUTTON_BACK,184,40,40,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + RTEXT "",IDC_TEXT_OUTPUT,4,4,308,12,SS_CENTERIMAGE, + WS_EX_CLIENTEDGE + DEFPUSHBUTTON "",IDC_BUTTON_FOCUS,0,0,5,5, NOT WS_VISIBLE +END + +IDD_DIALOG_STANDARD DIALOGEX 0, 0, 172, 127 +STYLE DS_SHELLFONT | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "ReactOS 電卓" +MENU IDR_MENU_STANDARD +FONT 9, "MS UI Gothic", 0, 0, 0x1 +BEGIN + PUSHBUTTON "C",IDC_BUTTON_CANC,128,24,40,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "CE",IDC_BUTTON_CE,84,24,40,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Back",IDC_BUTTON_BACK,40,24,40,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + RTEXT "",IDC_TEXT_OUTPUT,4,4,164,12,SS_CENTERIMAGE, + WS_EX_CLIENTEDGE + PUSHBUTTON "7",IDC_BUTTON_7,40,48,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "4",IDC_BUTTON_4,40,67,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "1",IDC_BUTTON_1,40,86,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "0",IDC_BUTTON_0,40,105,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "8",IDC_BUTTON_8,66,48,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "5",IDC_BUTTON_5,66,67,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "2",IDC_BUTTON_2,66,86,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "+/-",IDC_BUTTON_SIGN,66,105,24,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "9",IDC_BUTTON_9,92,48,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "6",IDC_BUTTON_6,92,67,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "3",IDC_BUTTON_3,92,86,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON ",",IDC_BUTTON_DOT,92,105,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "/",IDC_BUTTON_DIV,118,48,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "*",IDC_BUTTON_MULT,118,67,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "-",IDC_BUTTON_SUB,118,86,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "+",IDC_BUTTON_ADD,118,105,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "Sqrt",IDC_BUTTON_SQRT,144,48,24,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "%",IDC_BUTTON_PERCENT,144,67,24,17,BS_CENTER | + BS_VCENTER | BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "1/x",IDC_BUTTON_RX,144,86,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "=",IDC_BUTTON_EQU,144,105,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + CTEXT "",IDC_TEXT_MEMORY,4,24,24,17,SS_CENTERIMAGE, + WS_EX_CLIENTEDGE + PUSHBUTTON "MC",IDC_BUTTON_MC,4,48,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "MR",IDC_BUTTON_MR,4,67,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "MS",IDC_BUTTON_MS,4,86,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + PUSHBUTTON "M+",IDC_BUTTON_MP,4,105,24,17,BS_CENTER | BS_VCENTER | + BS_NOTIFY | BS_OWNERDRAW | WS_TABSTOP + DEFPUSHBUTTON "",IDC_BUTTON_FOCUS,0,0,5,5, NOT WS_VISIBLE +END + +IDD_DIALOG_CONVERSION DIALOGEX 0, 0, 320, 130 +STYLE DS_SHELLFONT | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "ReactOS 電卓" +MENU IDR_MENU_STANDARD +FONT 9, "MS UI Gothic" +BEGIN + PUSHBUTTON "変換",IDC_BUTTON_CONVERT,35,105,76,17 + COMBOBOX IDC_COMBO_CATEGORY,4,31,140,168,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP | CBS_SORT + LTEXT "分類:",IDC_STATIC,4,20,56,8 + COMBOBOX IDC_COMBO_FROM,4,60,140,168,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP | CBS_SORT + LTEXT "変換元:",IDC_STATIC,4,49,56,8 + COMBOBOX IDC_COMBO_TO,4,87,140,168,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP | CBS_SORT + LTEXT "変換先:",IDC_STATIC,4,76,56,8 + CONTROL "C",IDC_BUTTON_CANC,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,276,24,40,17 + CONTROL "CE",IDC_BUTTON_CE,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,232,24,40,17 + CONTROL "Back",IDC_BUTTON_BACK,"Button",BS_OWNERDRAW | + BS_CENTER | BS_VCENTER | BS_NOTIFY | WS_TABSTOP,188,24, + 40,17 + RTEXT "",IDC_TEXT_OUTPUT,4,4,312,12,SS_CENTERIMAGE, + WS_EX_CLIENTEDGE + CONTROL "7",IDC_BUTTON_7,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,188,48,24,17 + CONTROL "4",IDC_BUTTON_4,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,188,67,24,17 + CONTROL "1",IDC_BUTTON_1,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,188,86,24,17 + CONTROL "0",IDC_BUTTON_0,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,188,105,24,17 + CONTROL "8",IDC_BUTTON_8,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,214,48,24,17 + CONTROL "5",IDC_BUTTON_5,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,214,67,24,17 + CONTROL "2",IDC_BUTTON_2,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,214,86,24,17 + CONTROL "+/-",IDC_BUTTON_SIGN,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,214,105,24,17 + CONTROL "9",IDC_BUTTON_9,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,240,48,24,17 + CONTROL "6",IDC_BUTTON_6,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,240,67,24,17 + CONTROL "3",IDC_BUTTON_3,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,240,86,24,17 + CONTROL ",",IDC_BUTTON_DOT,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,240,105,24,17 + CONTROL "/",IDC_BUTTON_DIV,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,266,48,24,17 + CONTROL "*",IDC_BUTTON_MULT,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,266,67,24,17 + CONTROL "-",IDC_BUTTON_SUB,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,266,86,24,17 + CONTROL "+",IDC_BUTTON_ADD,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,266,105,24,17 + CONTROL "Sqrt",IDC_BUTTON_SQRT,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,292,48,24,17 + CONTROL "%",IDC_BUTTON_PERCENT,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,292,67,24,17 + CONTROL "1/x",IDC_BUTTON_RX,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,292,86,24,17 + CONTROL "=",IDC_BUTTON_EQU,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,292,105,24,17 + CTEXT "",IDC_TEXT_MEMORY,152,24,24,17,SS_CENTERIMAGE, + WS_EX_CLIENTEDGE + CONTROL "MC",IDC_BUTTON_MC,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,152,48,24,17 + CONTROL "MR",IDC_BUTTON_MR,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,152,67,24,17 + CONTROL "MS",IDC_BUTTON_MS,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,152,86,24,17 + CONTROL "M+",IDC_BUTTON_MP,"Button",BS_OWNERDRAW | BS_CENTER | + BS_VCENTER | BS_NOTIFY | WS_TABSTOP,152,105,24,17 + DEFPUSHBUTTON "",IDC_BUTTON_FOCUS,0,0,5,5,NOT WS_VISIBLE +END + +IDD_DIALOG_ABOUT DIALOGEX DISCARDABLE 0, 0, 264, 169 +STYLE DS_SHELLFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "ReactOS 電卓について" +FONT 9, "MS UI Gothic" +BEGIN + DEFPUSHBUTTON "OK",IDOK,105,148,52,16 + CONTROL IDB_BITMAP_ROS,IDC_STATIC,"Static",SS_BITMAP | SS_CENTERIMAGE | + SS_REALSIZEIMAGE | WS_BORDER,4,4,104,48 + LTEXT "ReactOS 電卓",IDC_STATIC,120,12,132,8, + SS_CENTERIMAGE + LTEXT "Version ",IDC_TEXT_VERSION,120,20,132,8,SS_CENTERIMAGE + LTEXT "Written by Carlo Bramini",IDC_STATIC,120,32,132,8, + SS_CENTERIMAGE + GROUPBOX "",IDC_STATIC,112,0,148,52 + GROUPBOX "",IDC_STATIC,4,56,256,88 + EDITTEXT IDC_EDIT_LICENSE,12,68,240,68,ES_MULTILINE | ES_READONLY | + WS_VSCROLL +END + +IDD_DIALOG_STAT DIALOGEX DISCARDABLE 0, 0, 163, 85 +STYLE DS_SHELLFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Statistics box" +FONT 9, "MS UI Gothic" +BEGIN + LISTBOX IDC_LIST_STAT,4,4,156,40,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "RET",IDC_BUTTON_RET,4,48,36,16 + PUSHBUTTON "LOAD",IDC_BUTTON_LOAD,44,48,36,16 + PUSHBUTTON "CD",IDC_BUTTON_CD,84,48,36,16 + PUSHBUTTON "CAD",IDC_BUTTON_CAD,124,48,36,16 + CTEXT "n=0",IDC_TEXT_NITEMS,4,68,156,12,SS_CENTERIMAGE | + SS_SUNKEN +END + +// Menus + +IDR_MENU_SCIENTIFIC_1 MENU DISCARDABLE +BEGIN + POPUP "編集" + BEGIN + MENUITEM "コピー\tCtrl+C", IDM_EDIT_COPY + MENUITEM "貼\り付け\tCtrl+V", IDM_EDIT_PASTE + END + POPUP "表\示" + BEGIN + MENUITEM "標準", IDM_VIEW_STANDARD, CHECKED + MENUITEM "関数電卓", IDM_VIEW_SCIENTIFIC, CHECKED + MENUITEM "変換", IDM_VIEW_CONVERSION + MENUITEM SEPARATOR + MENUITEM "16進\tF5", IDM_VIEW_HEX, CHECKED + MENUITEM "10進\tF6", IDM_VIEW_DEC, CHECKED + MENUITEM "8進\tF7", IDM_VIEW_OCT, CHECKED + MENUITEM "2進\tF8", IDM_VIEW_BIN, CHECKED + MENUITEM SEPARATOR + MENUITEM "Degrees\tF2", IDM_VIEW_DEG, CHECKED + MENUITEM "Radians\tF3", IDM_VIEW_RAD, CHECKED + MENUITEM "Gradians\tF4", IDM_VIEW_GRAD, CHECKED + MENUITEM SEPARATOR + MENUITEM "桁を区切る", IDM_VIEW_GROUP, CHECKED + END + POPUP "ヘルプ" + BEGIN + MENUITEM "ヘルプ トピック", IDM_HELP_HELP + MENUITEM SEPARATOR + MENUITEM "ReactOS 電卓について", IDM_HELP_ABOUT + END +END + +IDR_MENU_SCIENTIFIC_2 MENU DISCARDABLE +BEGIN + POPUP "編集" + BEGIN + MENUITEM "コピー\tCtrl+C", IDM_EDIT_COPY + MENUITEM "貼\り付け\tCtrl+V", IDM_EDIT_PASTE + END + POPUP "表\示" + BEGIN + MENUITEM "標準", IDM_VIEW_STANDARD, CHECKED + MENUITEM "関数電卓", IDM_VIEW_SCIENTIFIC, CHECKED + MENUITEM "変換", IDM_VIEW_CONVERSION + MENUITEM SEPARATOR + MENUITEM "16進\tF5", IDM_VIEW_HEX, CHECKED + MENUITEM "10進\tF6", IDM_VIEW_DEC, CHECKED + MENUITEM "8進\tF7", IDM_VIEW_OCT, CHECKED + MENUITEM "2進\tF8", IDM_VIEW_BIN, CHECKED + MENUITEM SEPARATOR + MENUITEM "Qword\tF12", IDM_VIEW_QWORD, CHECKED + MENUITEM "Dword\tF2", IDM_VIEW_DWORD, CHECKED + MENUITEM "Word\tF3", IDM_VIEW_WORD, CHECKED + MENUITEM "Byte\tF4", IDM_VIEW_BYTE, CHECKED + MENUITEM SEPARATOR + MENUITEM "桁を区切る", IDM_VIEW_GROUP, CHECKED + END + POPUP "ヘルプ" + BEGIN + MENUITEM "ヘルプ トピック", IDM_HELP_HELP + MENUITEM SEPARATOR + MENUITEM "ReactOS 電卓について", IDM_HELP_ABOUT + END +END + +IDR_MENU_STANDARD MENU DISCARDABLE +BEGIN + POPUP "編集" + BEGIN + MENUITEM "コピー\tCtrl+C", IDM_EDIT_COPY + MENUITEM "貼\り付け\tCtrl+V", IDM_EDIT_PASTE + END + POPUP "表\示" + BEGIN + MENUITEM "標準", IDM_VIEW_STANDARD, CHECKED + MENUITEM "関数電卓", IDM_VIEW_SCIENTIFIC, CHECKED + MENUITEM "変換", IDM_VIEW_CONVERSION + MENUITEM SEPARATOR + MENUITEM "桁を区切る", IDM_VIEW_GROUP, CHECKED + END + POPUP "ヘルプ" + BEGIN + MENUITEM "ヘルプ トピック", IDM_HELP_HELP + MENUITEM SEPARATOR + MENUITEM "ReactOS 電卓について", IDM_HELP_ABOUT + END +END + +// String Tables + +STRINGTABLE DISCARDABLE +BEGIN + IDS_STRING_LICENSE "ReactOS 電卓はGNU GPLライセンスの元で提供されるフリーソ\フトウェアです。\r\n\r\nこちらでGNU GPLライセンスのコピーを得ることができます:\r\nhttp://www.gnu.org/licenses/gpl.html\r\n\r\nこちらでGNU GPLライセンスの翻訳を得ることができます:\r\nhttp://www.gnu.org/licenses/translations.html" + IDS_MATH_ERROR "エラー" + IDS_QUICKHELP "クイックヘルプ" +END + +// Types of conversion +STRINGTABLE DISCARDABLE +BEGIN + IDS_CONV_ANGLE "角度" + IDS_CONV_AREA "面積" + IDS_CONV_CONSUMPTION "消費 (エンジン)" + IDS_CONV_CURRENCY "通貨" + IDS_CONV_ENERGY "エネルギー" + IDS_CONV_LENGTH "長さ" + IDS_CONV_POWER "力" + IDS_CONV_PRESSURE "圧力" + IDS_CONV_TEMPERATURE "温度" + IDS_CONV_TIME "時間" + IDS_CONV_VELOCITY "速度" + IDS_CONV_VOLUME "体積" + IDS_CONV_WEIGHT "重さ" +END + +// TYPES OF ANGLES +STRINGTABLE DISCARDABLE +BEGIN + IDS_ANGLE_DEGREES "Degrees" + IDS_ANGLE_GRADIANS "Gradians" + IDS_ANGLE_RADIANS "Radians" +END + +// TYPES OF AREAS +STRINGTABLE DISCARDABLE +BEGIN + IDS_AREA_ACRES "エーカー" + IDS_AREA_ACRES_BRAZIL "エーカー (ブラジル)" + IDS_AREA_ACRES_FRANCE "エーカー (フランス)" + IDS_AREA_ACRES_SCOTS "エーカー (スコットランド)" + IDS_AREA_ACRES_US "エーカー (米国)" + IDS_AREA_ARES "アール" + IDS_AREA_CHOU "Chou" + IDS_AREA_DANBO "Danbo" + IDS_AREA_HECTARES "ヘクタール" + IDS_AREA_JEONGBO "Jeongbo" + IDS_AREA_MORGEN_HUNGARY "Morgen (Hungary)" + IDS_AREA_MU "Mu" + IDS_AREA_PING "Ping" + IDS_AREA_PYEONG "Pyeong" + IDS_AREA_PYEONGBANGJA "Pyeongbangja" + IDS_AREA_RAI "Rai" + IDS_AREA_SE "Se" + IDS_AREA_SQUARE_CENTIMETERS "平方センチメートル" + IDS_AREA_SQUARE_CHR "Square chr" + IDS_AREA_SQUARE_FATHOMS "平方尋" + IDS_AREA_SQUARE_FATHOMS_HUNGARY "平方尋 (ハンガリー)" + IDS_AREA_SQUARE_FEET "平方フィート" + IDS_AREA_SQUARE_INCHES "平方インチ" + IDS_AREA_SQUARE_KILOMETERS "平方キロメーター" + IDS_AREA_SQUARE_LAR "Square lar" + IDS_AREA_SQUARE_METER "平方メートル" + IDS_AREA_SQUARE_MILES "平方マイル" + IDS_AREA_SQUARE_MILLIMETERS "平方ミリメートル" + IDS_AREA_SQUARE_SHAKU "平方尺" + IDS_AREA_SQUARE_TSUEN "Square tsuen" + IDS_AREA_SQUARE_VA "Square va" + IDS_AREA_SQUARE_YARD "平方ヤード" + IDS_AREA_TAN "Tan" + IDS_AREA_TSUBO "坪" +END + +// TYPES OF COMSUMPTIONS +STRINGTABLE DISCARDABLE +BEGIN + IDS_CONSUMPTION_KM_PER_L "キロメートル/リットル" + IDS_CONSUMPTION_L_PER_100_KM "リットル/100キロメートル" + IDS_CONSUMPTION_MILES_GALLON_UK "マイル/ガロン (英国)" + IDS_CONSUMPTION_MILES_GALLON_US "マイル/ガロン (米国)" +END + +// TYPES OF CURRENCIES +STRINGTABLE DISCARDABLE +BEGIN + IDS_CURRENCY_AUSTRIAN_SCHILLING "オーストリア シリング" + IDS_CURRENCY_BELGIAN_FRANC "ベルギー フラン" + IDS_CURRENCY_CYPRIOT_POUND "キプロス ポンド" + IDS_CURRENCY_CZECH_KORUNA "チェコ コルナ" + IDS_CURRENCY_DEUTSCHE_MARK "ドイツ マルク" + IDS_CURRENCY_DUTCH_GUILDER "オランダ ギルダー" + IDS_CURRENCY_EURO "ユーロ" + IDS_CURRENCY_FINNISH_MARKKA "フィンランド マルッカ" + IDS_CURRENCY_FRENCH_FRANC "フランス フラン" + IDS_CURRENCY_GREEK_DRACHMA "ギリシャ ドラクマ" + IDS_CURRENCY_IRISH_POUND "アイルランド ポンド" + IDS_CURRENCY_ITALIAN_LIRA "イタリア リラ" + IDS_CURRENCY_LUXEMBOURG_FRANC "ルクセンブルク フラン" + IDS_CURRENCY_MALTESE_LIRA "マルタ リラ" + IDS_CURRENCY_PORTOGUESE_ESCUDO "ポルトガル エスクード" + IDS_CURRENCY_SLOVAK_KORUNA "スロバキア コルナ" + IDS_CURRENCY_SLOVENIAN_TOLAR "スロベニア tolar" + IDS_CURRENCY_SPANISH_PESETA "スペイン ペセタ" +END + +// TYPES OF ENERGIES +STRINGTABLE DISCARDABLE +BEGIN + IDS_ENERGY_15_C_CALORIES "15℃カロリー" + IDS_ENERGY_BTUS "英国熱単位" + IDS_ENERGY_ERGS "エルグ" + IDS_ENERGY_EVS "電子ボルト" + IDS_ENERGY_FOOT_POUNDS "フートポンド" + IDS_ENERGY_IT_CALORIES "International Table calories" + IDS_ENERGY_IT_KILOCALORIES "International Table kilocalories" + IDS_ENERGY_JOULES "ジュール" + IDS_ENERGY_KILOJOULES "キロジュール" + IDS_ENERGY_KILOWATT_HOURS "キロワット時" + IDS_ENERGY_NUTRITION_CALORIES "栄養カロリー" + IDS_ENERGY_TH_CALORIES "熱化学カロリー" +END + +// TYPES OF LENGTHS +STRINGTABLE DISCARDABLE +BEGIN + IDS_LENGTH_ANGSTROMS "オングストローム" + IDS_LENGTH_ASTRONOMICAL_UNITS "天文単位" + IDS_LENGTH_BARLEYCORNS "バーレーコーン" + IDS_LENGTH_CENTIMETERS "センチメートル" + IDS_LENGTH_CHAINS_UK "チェーン (英国)" + IDS_LENGTH_CHI "Chi" + IDS_LENGTH_CHOU "Chou" + IDS_LENGTH_CHR "Chr" + IDS_LENGTH_CUN "Cun" + IDS_LENGTH_FATHOMS "ファゾム" + IDS_LENGTH_FATHOMS_HUNGARY "ファゾム (ハンガリー)" + IDS_LENGTH_FEET "フィート" + IDS_LENGTH_FURLONGS "ハロン" + IDS_LENGTH_GAN "Gan" + IDS_LENGTH_HANDS "ハンド" + IDS_LENGTH_HUNH "Hunh" + IDS_LENGTH_INCHES "インチ" + IDS_LENGTH_JA "Ja" + IDS_LENGTH_JEONG "Jeong" + IDS_LENGTH_KABIET "Kabiet" + IDS_LENGTH_KEN "Ken" + IDS_LENGTH_KEUB "Keub" + IDS_LENGTH_KILOMETERS "キロメートル" + IDS_LENGTH_LAR "Lar" + IDS_LENGTH_LIGHT_YEARS "光年" + IDS_LENGTH_LINKS_UK "リンク (英国)" + IDS_LENGTH_METERS "メートル" + IDS_LENGTH_MICRONS "ミクロン" + IDS_LENGTH_MILES "マイル" + IDS_LENGTH_MILLIMETERS "ミリメートル" + IDS_LENGTH_NAUTICAL_MILES "海里" + IDS_LENGTH_NIEU "Nieu" + IDS_LENGTH_PARSECS "パーセク" + IDS_LENGTH_PICAS "パイカ" + IDS_LENGTH_RI_JAPAN "里 (日本)" + IDS_LENGTH_RI_KOREA "Ri (韓国)" + IDS_LENGTH_RODS "ロッド" + IDS_LENGTH_SAWK "Sawk" + IDS_LENGTH_SEN "Sen" + IDS_LENGTH_SHAKU "尺" + IDS_LENGTH_SPAN "スパン" + IDS_LENGTH_SUN "寸" + IDS_LENGTH_TSUEN "Tsuen" + IDS_LENGTH_VA "Va" + IDS_LENGTH_YARDS "ヤード" + IDS_LENGTH_YOTE "Yote" + IDS_LENGTH_ZHANG "Zhang" +END + +// TYPES OF POWERS +STRINGTABLE DISCARDABLE +BEGIN + IDS_POWER_BTUS_PER_MINUTE "英国熱単位/分" + IDS_POWER_FPS_PER_MINUTE "フートポンド/分" + IDS_POWER_HORSEPOWER "馬力" + IDS_POWER_KILOWATTS "キロワット" + IDS_POWER_MEGAWATTS "メガワット" + IDS_POWER_WATTS "ワット" +END + +// TYPE OF PRESSURES +STRINGTABLE DISCARDABLE +BEGIN + IDS_PRESSURE_ATMOSPHERES "気圧" + IDS_PRESSURE_BARS "Bars" + IDS_PRESSURE_HECTOPASCALS "ヘクトパスカル" + IDS_PRESSURE_KILOPASCALS "キロパスカル" + IDS_PRESSURE_MM_OF_MERCURY "水銀ミリメートル" + IDS_PRESSURE_PASCALS "パスカル" + IDS_PRESSURE_PSI "ポンド重/平方インチ" +END + +// TYPES OF TEMPERATURES +STRINGTABLE DISCARDABLE +BEGIN + IDS_TEMPERATURE_CELSIUS "摂氏" + IDS_TEMPERATURE_FAHRENHEIT "華氏" + IDS_TEMPERATURE_KELVIN "ケルビン" + IDS_TEMPERATURE_RANKINE "Rankine" +END + +// TYPES OF TIME +STRINGTABLE DISCARDABLE +BEGIN + IDS_TIME_DAYS "日数" + IDS_TIME_HOURS "時間" + IDS_TIME_NANOSECONDS "ナノ秒" + IDS_TIME_MICROSECONDS "マイクロ秒" + IDS_TIME_MILLISECONDS "ミリ秒" + IDS_TIME_MINUTES "分" + IDS_TIME_SECONDS "秒" + IDS_TIME_WEEKS "週" + IDS_TIME_YEARS "年" +END + +// TYPES OF VELOCITIES +STRINGTABLE DISCARDABLE +BEGIN + IDS_VELOCITY_CMS_SECOND "センチメートル/時" + IDS_VELOCITY_FEET_HOUR "フィート/時" + IDS_VELOCITY_FEET_SECOND "フィート/秒" + IDS_VELOCITY_KILOMETERS_HOUR "キロメートル/時" + IDS_VELOCITY_KNOTS "ノット" + IDS_VELOCITY_MACH "マッハ" + IDS_VELOCITY_METERS_SECOND "メートル/秒" + IDS_VELOCITY_MILES_HOUR "マイル/時" +END + +// TYPES OF VOLUMES +STRINGTABLE DISCARDABLE +BEGIN + IDS_VOLUME_BARRELS_UK "バレル (英国)" + IDS_VOLUME_BARRELS_OIL "石油バレル" + IDS_VOLUME_BUN "Bun" + IDS_VOLUME_BUSHELS_UK "ブッシェル (英国)" + IDS_VOLUME_BUSHELS_US "ブッシェル (米国)" + IDS_VOLUME_CUBIC_CENTIMETERS "立法センチメートル" + IDS_VOLUME_CUBIC_FEET "立法フィート" + IDS_VOLUME_CUBIC_INCHES "立法インチ" + IDS_VOLUME_CUBIC_METERS "立法メートル" + IDS_VOLUME_CUBIC_YARDS "立法ヤード" + IDS_VOLUME_DOE "Doe" + IDS_VOLUME_FLUID_OUNCES_UK "流体オンス (英国)" + IDS_VOLUME_FLUID_OUNCES_US "流体オンス (米国)" + IDS_VOLUME_GALLONS_UK "ガロン (英国)" + IDS_VOLUME_GALLONS_DRY_US "ガロン、乾燥 (米国)" + IDS_VOLUME_GALLONS_LIQUID_US "ガロン、液体 (米国)" + IDS_VOLUME_GOU "Gou" + IDS_VOLUME_HOP "Hop" + IDS_VOLUME_ICCE "Icce" + IDS_VOLUME_KWIAN "Kwian" + IDS_VOLUME_LITERS "リットル" + IDS_VOLUME_MAL "Mal" + IDS_VOLUME_MILLILITERS "ミリリットル" + IDS_VOLUME_PINTS_UK "パイント (英国)" + IDS_VOLUME_PINTS_DRY_US "パイント、乾燥 (米国)" + IDS_VOLUME_PINTS_LIQUID_US "パイント、液体 (米国)" + IDS_VOLUME_QUARTS_UK "クォート (英国)" + IDS_VOLUME_QUARTS_DRY_US "クォート、乾燥 (米国)" + IDS_VOLUME_QUARTS_LIQUID_US "クォート、液体 (米国)" + IDS_VOLUME_SEKI "Seki" + IDS_VOLUME_SYOU "Syou" + IDS_VOLUME_TANANLOUNG "Tananloung" + IDS_VOLUME_TANG "Tang" + IDS_VOLUME_TO "To" +END + +// TYPES OF WEIGHTS +STRINGTABLE DISCARDABLE +BEGIN + IDS_WEIGHT_BAHT "Baht" + IDS_WEIGHT_CARATS "カラット" + IDS_WEIGHT_CHUNG "Chung" + IDS_WEIGHT_DON "Don" + IDS_WEIGHT_GEUN "Geun" + IDS_WEIGHT_GRAMS "グラム" + IDS_WEIGHT_GWAN "Gwan" + IDS_WEIGHT_HARB "Harb" + IDS_WEIGHT_JIN_CHINA "Jin (中国)" + IDS_WEIGHT_JIN_TAIWAN "Jin (台湾)" + IDS_WEIGHT_KAN "Kan" + IDS_WEIGHT_KILOGRAMS "キログラム" + IDS_WEIGHT_KIN "Kin" + IDS_WEIGHT_LIANG_CHINA "Liang (中国)" + IDS_WEIGHT_LIANG_TAIWAN "Liang (台湾)" + IDS_WEIGHT_MONME "匁 (もんめ)" + IDS_WEIGHT_OUNCES_AVOIRDUPOIS "常衡オンス" + IDS_WEIGHT_OUNCES_TROY "トロイ オンス" + IDS_WEIGHT_POUNDS "ポンド" + IDS_WEIGHT_QUINTAL_METRIC "キンタル (metric)" + IDS_WEIGHT_SALOUNG "Saloung" + IDS_WEIGHT_STONES "ストーン" + IDS_WEIGHT_TAMLUNG "Tamlung" + IDS_WEIGHT_TONNES "トン" + IDS_WEIGHT_TONS_UK "トン (英国)" + IDS_WEIGHT_TONS_US "トン (米国)" +END diff --git a/base/applications/calc/resource.rc b/base/applications/calc/resource.rc index 53d6e76ca12..945df68922a 100644 --- a/base/applications/calc/resource.rc +++ b/base/applications/calc/resource.rc @@ -52,6 +52,7 @@ IDB_BITMAP_ROS BITMAP DISCARDABLE "res/ROS_logo.bmp" #include "lang/es-ES.rc" #include "lang/fr-FR.rc" #include "lang/it-IT.rc" +#include "lang/ja-JP.rc" #include "lang/ko-KR.rc" #include "lang/nl-NL.rc" #include "lang/no-NO.rc" diff --git a/base/applications/cmdutils/reg/It.rc b/base/applications/cmdutils/reg/It.rc index 09d38fd4025..0a79a7d510f 100644 --- a/base/applications/cmdutils/reg/It.rc +++ b/base/applications/cmdutils/reg/It.rc @@ -35,6 +35,6 @@ STRINGTABLE STRING_SUCCESS, "Operazione completata con successo\n" STRING_INVALID_KEY, "Errore: nome della chiave non valido\n" STRING_INVALID_CMDLINE, "Errore: parametri della linea di comando non validi\n" - STRING_NO_REMOTE, "Errore: Impossibile aggiungere chiavi alla macchina remota\n" - STRING_CANNOT_FIND, "Errore: Il sistema non ティ riuscito a trovare la chiave di registro o il valore specificati\n" + STRING_NO_REMOTE, "Errore: impossibile aggiungere chiavi alla macchina remota\n" + STRING_CANNOT_FIND, "Errore: il sistema non ティ riuscito a trovare la chiave di registro o il valore specificati\n" } diff --git a/base/applications/cmdutils/reg/Sv.rc b/base/applications/cmdutils/reg/Sv.rc new file mode 100644 index 00000000000..cdb5a84014f --- /dev/null +++ b/base/applications/cmdutils/reg/Sv.rc @@ -0,0 +1,39 @@ +/* + * REG.EXE - Wine-compatible reg program. + * Swedish language support + * + * Copyright 2010 Anders Jonsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "reg.h" + +#pragma code_page(65001) + +LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL + +STRINGTABLE +{ + STRING_USAGE, "Syntax fテカr detta kommando テ、r:\n\nREG [ ADD | DELETE | QUERY ]\nREG kommando /?\n" + STRING_ADD_USAGE, "REG ADD nyckelnamn [/v vテ、rdenamn | /ve] [/t typ] [/s separator] [/d data] [/f]\n" + STRING_DELETE_USAGE, "REG DELETE nyckelnamn [/v vテ、rdenamn | /ve | /va] [/f]\n" + STRING_QUERY_USAGE, "REG QUERY nyckelnamn [/v vテ、rdenamn | /ve] [/s]\n" + STRING_SUCCESS, "Operation slutfテカrdes utan problem\n" + STRING_INVALID_KEY, "Fel: ogiltigt nyckelnamn\n" + STRING_INVALID_CMDLINE, "Fel: ogiltiga kommandoradsparametrar\n" + STRING_NO_REMOTE, "Fel: Kan inte lテ、gga till nycklar till fjテ、rrmaskin\n" + STRING_CANNOT_FIND, "Fel: Systemet kunde inte hitta angiven nyckel eller vテ、rde i registret\n" +} diff --git a/base/applications/cmdutils/reg/rsrc.rc b/base/applications/cmdutils/reg/rsrc.rc index 58f6115c2a2..9ff5e373f44 100644 --- a/base/applications/cmdutils/reg/rsrc.rc +++ b/base/applications/cmdutils/reg/rsrc.rc @@ -16,4 +16,5 @@ #include "Ro.rc" #include "Ru.rc" #include "Si.rc" +#include "Sv.rc" #include "Uk.rc" diff --git a/base/applications/cmdutils/xcopy/Da.rc b/base/applications/cmdutils/xcopy/Da.rc index 902f8311614..59fe19f9571 100644 --- a/base/applications/cmdutils/xcopy/Da.rc +++ b/base/applications/cmdutils/xcopy/Da.rc @@ -30,8 +30,8 @@ STRINGTABLE STRING_PAUSE, "Tryk Enter for at begynde at kopiere\n" STRING_SIMCOPY, "%d fil(er) vil blive kopieret\n" STRING_COPY, "%d fil(er) kopieret\n" - STRING_QISDIR, "Er ォ%sサ et filnavn eller katalog\n \ - p destinationen?\n \ + STRING_QISDIR, "Er ォ%sサ et filnavn eller katalog\n\ + p destinationen?\n\ (F - Fil, K - Katalog)\n" STRING_SRCPROMPT,"%s? (Ja|Nei)\n" STRING_OVERWRITE,"Overskrive ォ%sサ? (Ja|Nei|Alle)\n" diff --git a/base/applications/cmdutils/xcopy/De.rc b/base/applications/cmdutils/xcopy/De.rc index 0e8c2a8090d..944bc953908 100644 --- a/base/applications/cmdutils/xcopy/De.rc +++ b/base/applications/cmdutils/xcopy/De.rc @@ -33,8 +33,8 @@ STRINGTABLE STRING_PAUSE, "Eingabetaste betテ、tigen, um mit dem Kopieren zu beginnen\n" STRING_SIMCOPY, "%d Datei(en) wテシrden kopiert\n" STRING_COPY, "%d Datei(en) kopiert\n" - STRING_QISDIR, "Ist '%s' eine Datei oder ein Verzeichnis\n \ - am Zielsort?\n \ + STRING_QISDIR, "Ist '%s' eine Datei oder ein Verzeichnis\n\ + am Zielsort?\n\ (D - Datei, V - Verzeichnis)\n" STRING_SRCPROMPT,"%s? (Ja|Nein)\n" STRING_OVERWRITE,"%s テシberschreiben? (Ja|Nein|Alle)\n" diff --git a/base/applications/cmdutils/xcopy/En.rc b/base/applications/cmdutils/xcopy/En.rc index 4e380624867..a34257ae51f 100644 --- a/base/applications/cmdutils/xcopy/En.rc +++ b/base/applications/cmdutils/xcopy/En.rc @@ -30,8 +30,8 @@ STRINGTABLE STRING_PAUSE, "Press to begin copying\n" STRING_SIMCOPY, "%d file(s) would be copied\n" STRING_COPY, "%d file(s) copied\n" - STRING_QISDIR, "Is '%s' a filename or directory\n \ - on the target?\n \ + STRING_QISDIR, "Is '%s' a filename or directory\n\ + on the target?\n\ (F - File, D - Directory)\n" STRING_SRCPROMPT,"%s? (Yes|No)\n" STRING_OVERWRITE,"Overwrite %s? (Yes|No|All)\n" diff --git a/base/applications/cmdutils/xcopy/Fr.rc b/base/applications/cmdutils/xcopy/Fr.rc index e5b2e4094ad..bf8fb7e80a1 100644 --- a/base/applications/cmdutils/xcopy/Fr.rc +++ b/base/applications/cmdutils/xcopy/Fr.rc @@ -33,8 +33,8 @@ STRINGTABLE STRING_PAUSE, "Appuyez sur ENTRテ右 pour dテゥmarrer la copie\n" STRING_SIMCOPY, "%d fichier(s) seront copiテゥs\n" STRING_COPY, "%d fichier(s) copiテゥs\n" - STRING_QISDIR, "ツォツ%sツツサ est-il un fichier ou un rテゥpertoire\n \ - dans la destinationツ?\n \ + STRING_QISDIR, "ツォツ%sツツサ est-il un fichier ou un rテゥpertoire\n\ + dans la destinationツ?\n\ (F - Fichier, R - Rテゥpertoire)\n" STRING_SRCPROMPT,"%sツ? (Oui|Non)\n" STRING_OVERWRITE,"テ営raser %sツ? (Oui|Non|Tous)\n" diff --git a/base/applications/cmdutils/xcopy/It.rc b/base/applications/cmdutils/xcopy/It.rc index 5820591b772..2978c0db147 100644 --- a/base/applications/cmdutils/xcopy/It.rc +++ b/base/applications/cmdutils/xcopy/It.rc @@ -31,10 +31,10 @@ STRINGTABLE STRING_INVPARMS, "Numero non valido di parametri - Digita xcopy /? per l'aiuto\n" STRING_INVPARM, "Parametro '%s' non valido - Digita xcopy /? per l'aiuto\n" STRING_PAUSE, "Premi Invio per iniziare a copiare\n" - STRING_SIMCOPY, "%d file sarebbero copiati\n" + STRING_SIMCOPY, "%d file saranno copiati\n" STRING_COPY, "%d file copiato/i\n" - STRING_QISDIR, "'%s' ティ il nome di un file o una cartella\n \ - nell'obiettivo?\n \ + STRING_QISDIR, "'%s' ティ il nome di un file o una cartella\n\ + nell'obiettivo?\n\ (F - File, C - Cartella)\n" STRING_SRCPROMPT,"%s? (Sテャ|No)\n" STRING_OVERWRITE,"Sovrascrivere %s? (Sテャ|No|Tutti)\n" @@ -48,7 +48,7 @@ STRINGTABLE STRING_DIR_CHAR, "C" STRING_HELP, -"XCOPY - Copia file(s) e alberi di cartelle sorgenti in una destinazione\n\ +"XCOPY - Copia file e alberi di cartelle sorgenti in una destinazione\n\ \n\ Sintassi:\n\ XCOPY sorgente [destinazione] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U]\n\ @@ -59,18 +59,18 @@ Dove:\n\ [/I] Assume una cartella se la destinazione non esiste e sono copiati 2 o\n\ \tpiテケ file\n\ [/S] Copia le cartelle e le sottocartelle\n\ -[/E] Copia le cartelle e le sottocartelle, includendo quelle vuote\n\ +[/E] Copia le cartelle e le sottocartelle, incluse quelle vuote\n\ [/Q] Non mostra i nomi durante la copia, modalitテ silenziosa.\n\ [/F] Mostra i nomi completi della sorgente e della destinazione durante la copia\n\ -[/L] Simula l'operazione, mostrando i nome che sarebbero copiati\n\ +[/L] Simula l'operazione, mostrando i nomi che sarebbero copiati\n\ [/W] Richiede prima di iniziare l'operazione di copia\n\ -[/T] Crea la struttura di cartelle vuote ma non copia i files\n\ -[/Y] Disabilita le conferme quando sono sovrascritti files\n\ -[/-Y] Abilita le conferme quando sono sovrascritti files\n\ +[/T] Crea la struttura di cartelle vuote ma non copia i file\n\ +[/Y] Disabilita le conferme quando sono sovrascritti dei file\n\ +[/-Y] Abilita le conferme quando sono sovrascritti dei file\n\ [/P] Chiede conferma per ogni file sorgente prima di copiare\n\ [/N] Copia usando i nomi abbreviati\n\ [/U] Copia solo files che sono giテ presenti nella destinazione\n\ -[/R] Sovrascrive ogni file che abbia l'attributo Sola Lettura\n\ +[/R] Sovrascrive ogni file che abbia l'attributo Sola lettura\n\ [/H] Include i file nascosti e di sistema nella copia\n\ [/C] Continua anche se accade un errore durante la copia\n\ [/A] Copia solo i file che abbiano l'attributo Archivio\n\ diff --git a/base/applications/cmdutils/xcopy/Ja.rc b/base/applications/cmdutils/xcopy/Ja.rc index 8fd7d03eaa1..2146edfb21f 100644 --- a/base/applications/cmdutils/xcopy/Ja.rc +++ b/base/applications/cmdutils/xcopy/Ja.rc @@ -33,8 +33,8 @@ STRINGTABLE STRING_PAUSE, "繧ウ繝斐シ繧帝幕蟋九☆繧九↓縺ッ 繧呈款縺励※縺上□縺輔>\n" STRING_SIMCOPY, "%d 繝輔ぃ繧、繝ォ縺後さ繝斐シ縺輔l繧玖ヲ玖セシ縺ソ縺ァ縺吶\n" STRING_COPY, "%d 繝輔ぃ繧、繝ォ繧偵さ繝斐シ縺励∪縺励◆\n" - STRING_QISDIR, "騾√j蜈医ョ '%s' 縺ッ繝輔ぃ繧、繝ォ蜷阪〒縺吶°縲―n \ - 繝繧」繝ャ繧ッ繝医Μ縺ァ縺吶°?\n \ + STRING_QISDIR, "騾√j蜈医ョ '%s' 縺ッ繝輔ぃ繧、繝ォ蜷阪〒縺吶°縲―n\ + 繝繧」繝ャ繧ッ繝医Μ縺ァ縺吶°?\n\ (F - 繝輔ぃ繧、繝ォ縲. - 繝繧」繝ャ繧ッ繝医Μ)\n" STRING_SRCPROMPT,"%s? (Yes|No)\n" STRING_OVERWRITE,"%s 繧剃ク頑嶌縺阪@縺セ縺吶°? (Yes|No|All)\n" diff --git a/base/applications/cmdutils/xcopy/Ko.rc b/base/applications/cmdutils/xcopy/Ko.rc index 19c6c5fc422..bb46a36499a 100644 --- a/base/applications/cmdutils/xcopy/Ko.rc +++ b/base/applications/cmdutils/xcopy/Ko.rc @@ -22,23 +22,25 @@ #include "xcopy.h" +#pragma code_page(65001) + LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT STRINGTABLE { - STRING_INVPARMS, "ソテケルク」チ セハタコ クナーウコッシタヌ ーケシ - xcopy /?キホ オオソクサタサ コクステソタ\n" - STRING_INVPARM, "ソテケルク」チ セハタコ クナーウコッシ '%s' - xcopy /?キホ オオソクサタサ コクステソタ\n" - STRING_PAUSE, " クヲ エゥク」ク コケサ邁。 ステタロオノ ーヘタヤエマエル\n" - STRING_SIMCOPY, "%d ニトタマタフ コケサ邨ノ ーヘタヤエマエル\n" - STRING_COPY, "%d ニトタマタフ コケサ邨ヌセスタエマエル\n" - STRING_QISDIR, "'%s'タフ コケサ酩メ ニトタマタフウェ オキコナ荳ョ?\n\ - タヤエマア?\n\ - (F - ニトタマ, D - オキコナ荳ョ)\n" - STRING_SRCPROMPT,"%s? (ソケ|セニエマソタ)\n" - STRING_OVERWRITE,"%sクヲ オ、セセイーレスタエマア? (ソケ|セニエマソタ|クオホ)\n" + STRING_INVPARMS, "豫ー罷・エァ 賦捩 ァ、ー罹ウ們攪 ーッ - xcopy /?。 巡它ァ川揆 ウエ亨丶\n" + STRING_INVPARM, "豫ー罷・エァ 賦捩 ァ、ー罹ウ '%s' - xcopy /?。 巡它ァ川揆 ウエ亨丶\n" + STRING_PAUSE, " ・シ ・エゥエ ウオぎー 亨梠摺 イ桿笈共\n" + STRING_SIMCOPY, "%d 血攵擽 ウオぎ摺 イ桿笈共\n" + STRING_COPY, "%d 血攵擽 ウオぎ据来慣笈共\n" + STRING_QISDIR, "'%s'擽 ウオぎ腹 血攵擽x 粕駕ヲャ?\n\ + 桿笈ケ?\n\ + (F - 血攵, D - 粕駕ヲャ)\n" + STRING_SRCPROMPT,"%s? (|符笈丶)\n" + STRING_OVERWRITE,"%s・シ 紺牟堂イ慣笈ケ? (|符笈丶|ェィ草)\n" STRING_COPYFAIL, "Copying of '%s' to '%s' failed with r/c %d\n" - STRING_OPENFAIL, "'%s' ソュア スヌニミ\n" - STRING_READFAIL, "'%sクヲ タミチ クヌ゚スタエマエル'\n" + STRING_OPENFAIL, "'%s' 龍クー 共肩\n" + STRING_READFAIL, "'%s・シ 攷ァ ェサ毎慣笈共'\n" STRING_YES_CHAR, "Y" STRING_NO_CHAR, "N" STRING_ALL_CHAR, "A" @@ -46,36 +48,36 @@ STRINGTABLE STRING_DIR_CHAR, "D" STRING_HELP, -"XCOPY - ソコサ ニトタマタフウェ オキコナ荳ョ アクチカクヲ クタチキホ コケサ軆n\ +"XCOPY - 寳ウク 血攵擽x 粕駕ヲャ オャ。ー・シ ェゥァ。 ウオぎ\n\ \n\ -ケョケ:\n\ -XCOPY ソコサ [エサ] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U]\n\ +ャクイ:\n\ +XCOPY 寳ウク [劇メ] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U]\n\ \t [/R] [/H] [/C] [/P] [/A] [/M] [/E] [/D] [/Y] [/-Y]\n\ \n\ Where:\n\ \n\ -[/I] ククセ エサタフ チクタ酩マチ セハエツ ー豼 オキコナ荳ョキホ ー。チ、ヌマー オホーウウェ エ クケタコ ニトタマタサ \n\ -\tコケサ軆n\ -[/S] オキコナ荳ョヌマー ヌマタァ オキコナ荳ョ コケサ軆n\ -[/E] コ オキコナ荳ョクヲ ニヌヤヌリシュ オキコナ荳ョソヘ ヌマタァ オキコナ荳ョ コケサ軆n\ -[/Q] チカソヌマーヤ コケサ邨ヌエツ ニトタマタフウェ オキコナ荳ョクヲ ヌ・ステヌマチ セハー コケサ.\n\ -[/F] コケサ酩マエツ オソセネ ソマタヌム ソコサー エサ コクソゥチヨア秉n\ -[/L] コケサ邨ノ ーヘタサ コクソゥチヨク鮠ュ ー。サタクキホ タロセ\n\ -[/W] コケサ鄂テタロ ヌマア タソ。 ネョタホヌマア秉n\ -[/T] ニトタマタコ コケサ酩マチ セハー コ オキコナ荳ョ アクチカクク コケサ軆n\ -[/Y] ニトタマ オ、セ セオ カァ ネョタホヌマチ セハア秉n\ -[/-Y] ニトタマタサ オ、セ セオ カァ ネョタホヌマア秉n\ -[/P] コケサ酩マエツ オソセネソ。 ー「ー。タヌ ソコサ ニトタマクカエル ネョタホ\n\ -[/N] ツェタコ タフクァタサ サ鄙ヌリシュ コケサ軆n\ -[/U] タフケフ エサ オキコナ荳ョソ。 チクタ酩マエツ ニトタマクク コケサ軆n\ -[/R] タミア タソ ニトタマオオ オ、セ セイア秉n\ -[/H] シタコ ニトタマタフウェ ステスコナロ ニトタマオオ ニヌヤヌリシュ コケサ軆n\ -[/C] コケサ酩マエツ オソセネソ。 ソ。キッー。 ケ゚サヌリオオ ー霈モ チヌ濬n\ -[/A] ソタチ セミテ シモシコタフ シウチ、オヌセタヨエツ ニトタマクク コケサ軆n\ -[/M] ソタチ セミテ シモシコタサ チヲーナヌマク鮠ュ セミテ シモシコタフ シウチ、オヌセタヨエツ\n\ -\tニトタマクク コケサ軆n\ -[/D | /D:m-d-y] チチ、オネ ウッツ・ ネトソ。 シチ、オヌーナウェ サキホソ ニトタマ コケサ.\n\ -\t\tIククセ セカーヌム ウッツ・オオ チチ、オヌチ セハタクク,ソタチ ソコサコクエル\n\ -\t\tエサタフ ソタキ。オネ ーヘクク コケサ軆n\n" +[/I] ァ護平 劇メ擽 。エ椪葺ァ 賦株 イス垈 粕駕ヲャ。 ー倣葺ウ 草ー罹x 鵠 ァ珠捩 血攵揆 \n\ +\tウオぎ\n\ +[/S] 粕駕ヲャ葺ウ 葺怱 粕駕ヲャ ウオぎ\n\ +[/E] ケ 粕駕ヲャ・シ 小物紛 粕駕ヲャ凰 葺怱 粕駕ヲャ ウオぎ\n\ +[/Q] 。ー圸葺イ ウオぎ据株 血攵擽x 粕駕ヲャ・シ 像亨葺ァ 賦ウ ウオぎ.\n\ +[/F] ウオぎ葺株 徐譜 刋復 寳ウクウシ 劇メ ウエ流」シクー\n\ +[/L] ウオぎ摺 イ揆 ウエ流」シゥエ ーメ愍。 梠羅\n\ +[/W] ウオぎ亨梠 葺クー 乱 剳攤葺クー\n\ +[/T] 血攵捩 ウオぎ葺ァ 賦ウ ケ 粕駕ヲャ オャ。ーァ ウオぎ\n\ +[/Y] 血攵 紺牟 萄 阜 剳攤葺ァ 賦クー\n\ +[/-Y] 血攵揆 紺牟 萄 阜 剳攤葺クー\n\ +[/P] ウオぎ葺株 徐譜乱 ーー攪 寳ウク 血攵ァ壱共 剳攤\n\ +[/N] ァァ捩 擽ヲ揆 ぎ圸紛 ウオぎ\n\ +[/U] 擽ック 劇メ 粕駕ヲャ乱 。エ椪葺株 血攵ァ ウオぎ\n\ +[/R] 攷クー 圸 血攵巡 紺牟 堂クー\n\ +[/H] 葵捩 血攵擽x 亨侃 血攵巡 小物紛 ウオぎ\n\ +[/C] ウオぎ葺株 徐譜乱 乱洳ー ー懍晨紛巡 ウ ァ哩\n\ +[/A] 丶ァ 封カ 作┳擽 └簿据牟梭株 血攵ァ ウオぎ\n\ +[/M] 丶ァ 封カ 作┳揆 懋アー葺ゥエ 封カ 作┳擽 └簿据牟梭株\n\ +\t血攵ァ ウオぎ\n\ +[/D | /D:m-d-y] ァ簿頗 あァ 寇乱 們簿据アーx ヨ。懍垓 血攵 ウオぎ.\n\ +\t\tIァ護平 牟蔓復 あァ罹巡 ァ簿据ァ 賦愍ゥエ,丶ァ 寳ウクウエ共\n\ +\t\t劇メ擽 丶椈頗 イァ ウオぎ\n\n" } diff --git a/base/applications/cmdutils/xcopy/Sv.rc b/base/applications/cmdutils/xcopy/Sv.rc new file mode 100644 index 00000000000..dae6ce04cec --- /dev/null +++ b/base/applications/cmdutils/xcopy/Sv.rc @@ -0,0 +1,82 @@ +/* + * XCOPY - Wine-compatible xcopy program + * Swedish language support + * + * Copyright (C) 2010 Anders Jonsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "xcopy.h" + +#pragma code_page(65001) + +LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL + +STRINGTABLE +{ + STRING_INVPARMS, "Ogiltigt antal parametrar - Anvテ、nd xcopy /? fテカr hjテ、lp\n" + STRING_INVPARM, "Ogiltig parameter '%s' - Anvテ、nd xcopy /? fテカr hjテ、lp\n" + STRING_PAUSE, "Tryck fテカr att bテカrja kopiera\n" + STRING_SIMCOPY, "%d fil(er) skulle kopieras\n" + STRING_COPY, "%d fil(er) kopierade\n" + STRING_QISDIR, "テв '%s' ett filnamn eller en katalog\n" \ + "pテ・ mテ・let?\n" \ + "(F - Fil, K - Katalog)\n" + STRING_SRCPROMPT,"%s? (Ja|Nej)\n" + STRING_OVERWRITE,"Skriv テカver %s? (Ja|Nej|Alla)\n" + STRING_COPYFAIL, "Kunde inte kopiera '%s' till '%s'; misslyckades med r/c %d\n" + STRING_OPENFAIL, "Kunde inte テカppna '%s'\n" + STRING_READFAIL, "Kunde inte lテ、sa '%s'\n" + STRING_YES_CHAR, "J" + STRING_NO_CHAR, "N" + STRING_ALL_CHAR, "A" + STRING_FILE_CHAR,"F" + STRING_DIR_CHAR, "K" + + STRING_HELP, +"XCOPY - Kopierar kテ、llfiler eller katalogtrテ、d till ett mテ・l\n\ +\n\ +Syntax:\n\ +XCOPY kテ、lla [dest] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U]\n\ +\t [/R] [/H] [/C] [/P] [/A] [/M] [/E] [/D] [/Y] [/-Y]\n\ +\n\ +Dテ、r:\n\ +\n\ +[/I] Antag att mテ・let テ、r en katalog om mテ・let inte existerar och\n\ +\ tvテ・ eller fler filer kopieras\n\ +[/S] Kopiera kataloger och underkataloger\n\ +[/E] Kopiera kataloger och underkataloger, inklusive tomma sテ・dana\n\ +[/Q] Tyst lテ、ge: Lista inte filnamn under kopiering\n\ +[/F] Visa fulla kテ、ll- och mテ・lnamn under kopiering\n\ +[/L] Simulerar operationen och visar namn som skulle kopieras\n\ +[/W] Frテ・gar innan kopieringen pテ・bテカrjas\n\ +[/T] Skapar tom katalogstruktur men kopierar inga filer\n\ +[/Y] Frテ・ga inte nテ、r filer skrivs テカver\n\ +[/-Y] Frテ・ga innan filer skrivs テカver\n\ +[/P] Frテ・ga fテカr varje kテ、llfil som kopieras.\n\ +[/N] Kopiera som korta filnamn\n\ +[/U] Kopiera enbart filer som redan existerar i destinationskatalogen\n\ +[/R] Skriv テカver skrivskyddade filer\n\ +[/H] Inkludera gテカmda filer och systemfiler i kopian\n\ +[/C] Fortsテ、tt テ、ven om ett fel uppstテ・r under kopieringen\n\ +[/A] Kopiera enbart filer markerade som arkiv\n\ +[/M] Kopiera enbart filer markerade som arkiv, ta bort\n\ +\ markeringen\n\ +[/D | /D:m-d-y] Kopiera nya filer eller de テ、ndrade efter angivet datum.\n\ +\t\tOm inget datum angivits utfテカrs endast kopiering om mテ・let テ、r\n\ +\t\tテ、ldre テ、n kテ、llan\n\n" + +} diff --git a/base/applications/cmdutils/xcopy/rsrc.rc b/base/applications/cmdutils/xcopy/rsrc.rc index 60fa9083732..7d4990c5763 100644 --- a/base/applications/cmdutils/xcopy/rsrc.rc +++ b/base/applications/cmdutils/xcopy/rsrc.rc @@ -28,7 +28,6 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL #include "Da.rc" #include "En.rc" -#include "Ko.rc" #include "No.rc" #include "Pl.rc" @@ -37,10 +36,12 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL #include "Fr.rc" #include "It.rc" #include "Ja.rc" +#include "Ko.rc" #include "Lt.rc" #include "Nl.rc" #include "Pt.rc" #include "Ro.rc" #include "Ru.rc" #include "Si.rc" +#include "Sv.rc" #include "Uk.rc" diff --git a/base/applications/games/games.rbuild b/base/applications/games/games.rbuild index 45554170199..53c751369f3 100644 --- a/base/applications/games/games.rbuild +++ b/base/applications/games/games.rbuild @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/base/applications/games/winemine/README b/base/applications/games/winemine/README deleted file mode 100644 index 31d7b277fa9..00000000000 --- a/base/applications/games/winemine/README +++ /dev/null @@ -1,21 +0,0 @@ -WineMine README - -WineMine, copyright March 2000, Joshua Thielen -WineMine is to be distributed under the Wine License -See the Wine License for further information. - - -This is minesweeper for wine... -Enjoy. I wrote it because it rhymes ;). - -KNOWN BUGS: - Chokes on fastest times names greater than 16 characters long, must have - something to do with GetDlgItemText. - - No help file. - - Starting a new game causes the window to drop one pixel (Peter Hunnisett) - I don't know if it's a window manager problem (KDE) - -UNKNOWN BUGS: - ??? diff --git a/base/applications/games/winemine/dialog.c b/base/applications/games/winemine/dialog.c deleted file mode 100644 index f9ed4ac6f4d..00000000000 --- a/base/applications/games/winemine/dialog.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * WineMine (dialog.c) - * - * Copyright 2000 Joshua Thielen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "main.h" -#include "dialog.h" -#include "resource.h" - -INT_PTR CALLBACK CustomDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) -{ - static BOARD *pBoard; - - switch(uMsg) - { - case WM_INITDIALOG: - pBoard = (BOARD*) lParam; - SetDlgItemInt( hDlg, IDC_EDITROWS, pBoard->uRows, FALSE ); - SetDlgItemInt( hDlg, IDC_EDITCOLS, pBoard->uCols, FALSE ); - SetDlgItemInt( hDlg, IDC_EDITMINES, pBoard->uMines, FALSE ); - return TRUE; - - case WM_COMMAND: - switch( LOWORD( wParam ) ) - { - case IDOK: - pBoard->uRows = GetDlgItemInt( hDlg, IDC_EDITROWS, NULL, FALSE ); - pBoard->uCols = GetDlgItemInt( hDlg, IDC_EDITCOLS, NULL, FALSE ); - pBoard->uMines = GetDlgItemInt( hDlg, IDC_EDITMINES, NULL, FALSE ); - CheckLevel( pBoard ); - /* Fall through */ - case IDCANCEL: - EndDialog( hDlg, LOWORD(wParam) ); - return TRUE; - } - break; - } - - return FALSE; -} - -INT_PTR CALLBACK CongratsDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) -{ - static BOARD *pBoard; - - switch(uMsg) - { - case WM_INITDIALOG: - pBoard = (BOARD*) lParam; - SetDlgItemText( hDlg, IDC_EDITNAME, pBoard->szBestName[pBoard->Difficulty] ); - return TRUE; - - case WM_COMMAND: - switch( LOWORD(wParam) ) - { - case IDOK: - GetDlgItemText( hDlg, IDC_EDITNAME, - pBoard->szBestName[pBoard->Difficulty], - sizeof( pBoard->szBestName[pBoard->Difficulty] ) ); - EndDialog( hDlg, 0 ); - return TRUE; - - case IDCANCEL: - EndDialog( hDlg, 0 ); - return TRUE; - } - break; - } - return FALSE; -} - -INT_PTR CALLBACK TimesDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) -{ - static BOARD *pBoard; - HKEY hKey; - UCHAR i; - TCHAR szData[16]; - TCHAR szKeyName[8]; - TCHAR szTimes[35]; - TCHAR szSeconds[23]; - TCHAR szNobody[15]; - - switch(uMsg) - { - case WM_INITDIALOG: - pBoard = (BOARD*) lParam; - - /* set best names */ - for( i = 0; i < 3; i++ ) - SetDlgItemText( hDlg, (IDC_NAME1) + i, pBoard->szBestName[i] ); - - /* set best times */ - LoadString( pBoard->hInst, IDS_SECONDS, szSeconds, sizeof(szSeconds) / sizeof(TCHAR) ); - - for( i = 0; i < 3; i++ ) - { - wsprintf(szTimes, TEXT("%d %s"), pBoard->uBestTime[i], szSeconds); - SetDlgItemText( hDlg, (IDC_TIME1) + i, szTimes ); - } - - return TRUE; - - case WM_COMMAND: - switch( LOWORD( wParam ) ) - { - case IDOK: - case IDCANCEL: - EndDialog( hDlg, 0 ); - return TRUE; - - case IDRESET: - if( RegCreateKeyEx( HKEY_CURRENT_USER, szWineMineRegKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL ) != ERROR_SUCCESS) - return TRUE; - - LoadString( pBoard->hInst, IDS_NOBODY, szNobody, sizeof(szNobody) / sizeof(TCHAR) ); - LoadString( pBoard->hInst, IDS_SECONDS, szSeconds, sizeof(szSeconds) / sizeof(TCHAR) ); - - for (i = 0; i < 3; i++) - { - pBoard->uBestTime[i] = 999; - _tcscpy(pBoard->szBestName[i], szNobody); - wsprintf(szTimes, TEXT("%d %s"), pBoard->uBestTime[i], szSeconds); - - SetDlgItemText( hDlg, (IDC_NAME1) + i, pBoard->szBestName[i] ); - SetDlgItemText( hDlg, (IDC_TIME1) + i, szTimes ); - } - - /* Write the changes to the registry - As we write to the same registry key as MS WinMine does, we have to start at 1 for the registry keys */ - for( i = 0; i < 3; i++ ) - { - wsprintf( szKeyName, TEXT("Name%u"), i + 1 ); - _tcsncpy( szData, pBoard->szBestName[i], sizeof(szData) / sizeof(TCHAR) ); - RegSetValueEx( hKey, szKeyName, 0, REG_SZ, (LPBYTE)szData, (_tcslen(szData) + 1) * sizeof(TCHAR) ); - } - - for( i = 0; i < 3; i++ ) - { - wsprintf( szKeyName, TEXT("Time%u"), i + 1 ); - RegSetValueEx( hKey, szKeyName, 0, REG_DWORD, (LPBYTE)&pBoard->uBestTime[i], sizeof(DWORD) ); - } - - RegCloseKey(hKey); - return TRUE; - } - } - - return FALSE; -} - diff --git a/base/applications/games/winemine/dialog.h b/base/applications/games/winemine/dialog.h deleted file mode 100644 index 11a5d9bbcfc..00000000000 --- a/base/applications/games/winemine/dialog.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * WineMine (dialog.h) - * - * Copyright 2000 Joshua Thielen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -INT_PTR CALLBACK CustomDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); -INT_PTR CALLBACK CongratsDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); -INT_PTR CALLBACK TimesDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); -INT_PTR CALLBACK AboutDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); diff --git a/base/applications/games/winemine/lang/bg-BG.rc b/base/applications/games/winemine/lang/bg-BG.rc deleted file mode 100644 index 293c05ef845..00000000000 --- a/base/applications/games/winemine/lang/bg-BG.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "seconds" - IDS_NOBODY, "Nobody" - IDS_ABOUT, "by Joshua Thielen and ReactOS developers" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&ネ胙" - BEGIN - MENUITEM "&ヘ籵\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "ネ鉐鈔瑙 &糶頸褄 (?)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "ヘ&竟瑯", IDM_BEGINNER - MENUITEM "ヘ&裝琿", IDM_ADVANCED - MENUITEM "&ツ襌", IDM_EXPERT - MENUITEM "&ヘ璢琥萵", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&ヘ琺- 糅褌褊", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "ネ&", IDM_EXIT - END - POPUP "&ム粢蒟" - BEGIN - MENUITEM "&ヌ", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "ヘ琺- 糅褌褊" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "ヘ琺- 糅褌褊", IDNONE, 10, 10, 182, 45 - LTEXT "ヘ璞竟瑯:", IDNONE, 20, 20, 58, 8 - LTEXT "ヘ瑜裝琿:", IDNONE, 20, 30, 58, 8 - LTEXT "ツ襌:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "ト碣", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&ホ頌籵 裼瑣頸", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "マ鈕珞!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "ツ粢蒟 韲褪 ", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "ト碣", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "ヘ璢蒟 鞳" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "ミ裝:", IDNONE, 5, 15, 43, 10 - LTEXT "ム矜:", IDNONE, 5, 35, 43, 10 - LTEXT "フ竟:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "ト碣", IDOK, 86, 32, 45, 15 - PUSHBUTTON "ホ珸", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/ca-ES.rc b/base/applications/games/winemine/lang/ca-ES.rc deleted file mode 100644 index 0c300dd6423..00000000000 --- a/base/applications/games/winemine/lang/ca-ES.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_CATALAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "seconds" - IDS_NOBODY, "Nobody" - IDS_ABOUT, "by Joshua Thielen and ReactOS developers" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Game" - BEGIN - MENUITEM "&Nou\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "In&terrogant (?)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Principiant", IDM_BEGINNER - MENUITEM "&Avan軋t", IDM_ADVANCED - MENUITEM "&Expert", IDM_EXPERT - MENUITEM "Pe&rsonalitzat", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Millors Temps", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&Sortida", IDM_EXIT - END - POPUP "&Info" - BEGIN - MENUITEM "En q&uant a", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Millors Temps" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Millors Temps", IDNONE, 10, 10, 182, 45 - LTEXT "Principiant:", IDNONE, 20, 20, 58, 8 - LTEXT "Avan軋t:", IDNONE, 20, 30, 58, 8 - LTEXT "Expert:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "Acceptar", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Reset Scores", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Felicitats!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Si us plau, entreu el vostre nom", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "Acceptar", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Joc Personalitzat" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Files:", IDNONE, 5, 15, 43, 10 - LTEXT "Columnes:", IDNONE, 5, 35, 43, 10 - LTEXT "Mines:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "Acceptar", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Cancelキlar", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/cs-CZ.rc b/base/applications/games/winemine/lang/cs-CZ.rc deleted file mode 100644 index bd776bafc40..00000000000 --- a/base/applications/games/winemine/lang/cs-CZ.rc +++ /dev/null @@ -1,87 +0,0 @@ -/* FILE: applications/games/winemine/lang/cs-CZ.rc - * PURPOSE: Czech Language File - * TRANSLATORS: Stepan Gabriel - SGABA (sgaba@centrum.cz); Radek Liska aka Black_Fox (radekliska at gmail dot com) - * TRANSLATED FROM: Slovak translation by Kario (kario@szm.sk) - * UPDATED: 2010-05-25 - * - * Czech translation - * Copyleft 2007 Kario (kario@szm.sk),2008 SGABA (sgaba@centrum.cz) - */ - -#include "resource.h" - -LANGUAGE LANG_CZECH, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "sek." - IDS_NOBODY, "Bezejmenn" //windows = Anonym - IDS_ABOUT, "od Joshua Thielena a vvoj碾 syst駑u ReactOS" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Hra" - BEGIN - MENUITEM "&Nov hra\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "Zn&a鑢y (?)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Za鞦te鈩k", IDM_BEGINNER - MENUITEM "&Pokro鑛l", IDM_ADVANCED - MENUITEM "&Expert", IDM_EXPERT - MENUITEM "&Vlastn...", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Nejlep夬 鐶sy...", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&Konec", IDM_EXIT - END - POPUP "&N疳ovda" //windows = &Pomocnk - BEGIN - MENUITEM "&O programu...", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Nejrychlej夬 hleda鑛 min" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Nejlep夬 鐶sy", IDNONE, 10, 10, 182, 45 - LTEXT "Za鞦te鈩k:", IDNONE, 20, 20, 58, 8 - LTEXT "Pokro鑛l:", IDNONE, 20, 30, 58, 8 - LTEXT "Expert:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Vynulovat vsledky", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Blahopeji!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Zadejte prosm svoje jm駭o", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Vlastn pole" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "V嗅a:", IDNONE, 5, 15, 43, 10 - LTEXT "器ka:", IDNONE, 5, 35, 43, 10 - LTEXT "Miny:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Storno", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/de-DE.rc b/base/applications/games/winemine/lang/de-DE.rc deleted file mode 100644 index 69676c41f56..00000000000 --- a/base/applications/games/winemine/lang/de-DE.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL - -STRINGTABLE -BEGIN - IDS_SECONDS, "Sekunden" - IDS_NOBODY, "Niemand" - IDS_ABOUT, "von Joshua Thielen und ReactOS-Entwicklern" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Spiel" - BEGIN - MENUITEM "&Neu\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&Merker (?)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Anf舅ger", IDM_BEGINNER - MENUITEM "&Fortgeschrittene", IDM_ADVANCED - MENUITEM "&Profis", IDM_EXPERT - MENUITEM "&Benutzerdefiniert...", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Bestzeiten", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "Beenden", IDM_EXIT - END - POPUP "&?" - BEGIN - MENUITEM "&Info...", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Bestzeiten" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Bestzeiten", IDNONE, 10, 10, 182, 45 - LTEXT "Anf舅ger:", IDNONE, 20, 20, 58, 8 - LTEXT "Fortgeschrittene:", IDNONE, 20, 30, 58, 8 - LTEXT "Profis:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "Bestzeiten &lschen", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Glckwunsch!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Bitte geben Sie Ihren Namen ein", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Benutzerdefiniertes Spiel" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Hhe:", IDNONE, 5, 15, 43, 10 - LTEXT "Breite:", IDNONE, 5, 35, 43, 10 - LTEXT "Minen:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Abbrechen", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/el-GR.rc b/base/applications/games/winemine/lang/el-GR.rc deleted file mode 100644 index 6ff2605861d..00000000000 --- a/base/applications/games/winemine/lang/el-GR.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_GREEK, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "seconds" - IDS_NOBODY, "Nobody" - IDS_ABOUT, "by Joshua Thielen and ReactOS developers" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Game" - BEGIN - MENUITEM "&ヘン\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&モ銕裃 褥銕磑鳰", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&チワ鴆", IDM_BEGINNER - MENUITEM "&ミ銕ン", IDM_ADVANCED - MENUITEM "&ナ鱠鳰", IDM_EXPERT - MENUITEM "&ミ鳰", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&テ鈬褥 ラ", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "ク&蔡", IDM_EXIT - END - POPUP "&?" - BEGIN - MENUITEM "&モ襁鳰ワ...", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "テ鈬褥 ラ" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "テ鈬褥 ラ", IDNONE, 10, 10, 182, 45 - LTEXT "チワ鴆:", IDNONE, 20, 20, 58, 8 - LTEXT "ミ銕ン:", IDNONE, 20, 30, 58, 8 - LTEXT "ナ鱠鳰:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "Oハ", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Reset Scores", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "モ胱碵鉚゙鱆!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "ミ碵碎硴 銖 ワ 碪", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "Oハ", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "ミ鳰 ミ硅゚蓚" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "テ碆ン:", IDNONE, 5, 15, 43, 10 - LTEXT "モ゙褪:", IDNONE, 5, 35, 43, 10 - LTEXT "ヘワ褪:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "Oハ", IDOK, 86, 32, 45, 15 - PUSHBUTTON "チ", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/en-US.rc b/base/applications/games/winemine/lang/en-US.rc deleted file mode 100644 index 7dfe642b65f..00000000000 --- a/base/applications/games/winemine/lang/en-US.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -STRINGTABLE -BEGIN - IDS_SECONDS, "seconds" - IDS_NOBODY, "Nobody" - IDS_ABOUT, "by Joshua Thielen and ReactOS developers" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Game" - BEGIN - MENUITEM "&New\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&Marks (?)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Beginner", IDM_BEGINNER - MENUITEM "&Intermediate", IDM_ADVANCED - MENUITEM "&Expert", IDM_EXPERT - MENUITEM "&Custom...", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Best Times...", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "E&xit", IDM_EXIT - END - POPUP "&Help" - BEGIN - MENUITEM "&About", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Fastest Times" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Fastest Times", IDNONE, 10, 10, 182, 45 - LTEXT "Beginner:", IDNONE, 20, 20, 58, 8 - LTEXT "Intermediate:", IDNONE, 20, 30, 58, 8 - LTEXT "Expert:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Reset Scores", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Congratulations!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Please enter your name", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Custom Game" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Height:", IDNONE, 5, 15, 43, 10 - LTEXT "Width:", IDNONE, 5, 35, 43, 10 - LTEXT "Mines:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Cancel", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/es-ES.rc b/base/applications/games/winemine/lang/es-ES.rc deleted file mode 100644 index 2a9296c27b0..00000000000 --- a/base/applications/games/winemine/lang/es-ES.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL - -STRINGTABLE -BEGIN - IDS_SECONDS, "segundos" - IDS_NOBODY, "Nadie" - IDS_ABOUT, "por Joshua Thielen y los programadores de ReactOS" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Juego" - BEGIN - MENUITEM "&Nuevo\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&Marcar Interrogantes", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Principiante", IDM_BEGINNER - MENUITEM "&Advanzado", IDM_ADVANCED - MENUITEM "&Experto", IDM_EXPERT - MENUITEM "&Juego Personalizado", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Mejores Tiempos", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&Salir", IDM_EXIT - END - POPUP "&Informacin" - BEGIN - MENUITEM "&Acerca de", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Mejores Tiempos" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Mejores Tiempos", IDNONE, 10, 10, 182, 45 - LTEXT "Principiante:", IDNONE, 20, 20, 58, 8 - LTEXT "Advanzado:", IDNONE, 20, 30, 58, 8 - LTEXT "Experto:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "Aceptar", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Limpiar lista", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "。Felicidades!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Por Favor, introduce tu nombre", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "Aceptar", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Juego Personalizado" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Filas:", IDNONE, 5, 15, 43, 10 - LTEXT "Columnas:", IDNONE, 5, 35, 43, 10 - LTEXT "Minas:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "Aceptar", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Cancelar", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/eu-ES.rc b/base/applications/games/winemine/lang/eu-ES.rc deleted file mode 100644 index d470b1f5273..00000000000 --- a/base/applications/games/winemine/lang/eu-ES.rc +++ /dev/null @@ -1,78 +0,0 @@ - -#include "resource.h" - -LANGUAGE LANG_BASQUE, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "segunduak" - IDS_NOBODY, "Inor ez" - IDS_ABOUT, "Joshua Thielen eta ReactOS programatzaileek" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Jokoa" - BEGIN - MENUITEM "&Berria\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&Galdera ikurrak jarri", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Hasiberria", IDM_BEGINNER - MENUITEM "&Tartekoa", IDM_ADVANCED - MENUITEM "&Aditua", IDM_EXPERT - MENUITEM "&Pertsonalizatu jokoa", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Denborarik onenak", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&Irten", IDM_EXIT - END - POPUP "&Informazioa" - BEGIN - MENUITEM "&Buscaminas-i buruz", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Denborarik onenak" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Denborarik onenak ", IDNONE, 10, 10, 182, 45 - LTEXT "Hasiberria:", IDNONE, 20, 20, 58, 8 - LTEXT "Tartekoa:", IDNONE, 20, 30, 58, 8 - LTEXT "Aditua:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "Ados", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Berrezarri markagailuak", IDRESET, 18, 57, 84, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Zorionak!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Mesedez, idatzi zure izena", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "Ados", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Pertsonalizatu jokoa" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Zabalera:", IDNONE, 5, 15, 43, 10 - LTEXT "Altuera:", IDNONE, 5, 35, 43, 10 - LTEXT "Minak:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "Ados", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Utzi", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/fr-FR.rc b/base/applications/games/winemine/lang/fr-FR.rc deleted file mode 100644 index 12f8ad5526a..00000000000 --- a/base/applications/games/winemine/lang/fr-FR.rc +++ /dev/null @@ -1,84 +0,0 @@ -#include "resource.h" - -/* - * Translation made by Jerome Signouret, 2006. - * Initial file : http://svn.reactos.org/viewcvs/trunk/reactos/base/applications/games/winemine/En.rc - * Revision : 85 - */ - -LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL - -STRINGTABLE -BEGIN - IDS_SECONDS, "secondes" - IDS_NOBODY, "Anonyme" - IDS_ABOUT, "par Joshua Thielen et les d騅eloppeurs de ReactOS" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Jeu" - BEGIN - MENUITEM "&Nouveau\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&Marqueur ?", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&D饕utant", IDM_BEGINNER - MENUITEM "&Amateur", IDM_ADVANCED - MENUITEM "&Expert", IDM_EXPERT - MENUITEM "&Sur mesure", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Temps acc駘駻", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&Quitter", IDM_EXIT - END - POPUP "&Informations" - BEGIN - MENUITEM "&Temps acc駘駻", IDM_TIMES - MENUITEM "&タ propos", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Temps acc駘駻" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Temps acc駘駻", IDNONE, 10, 10, 182, 45 - LTEXT "D饕utant:", IDNONE, 20, 20, 58, 8 - LTEXT "Amateur:", IDNONE, 20, 30, 58, 8 - LTEXT "Expert:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&R駟nitialiser les scores", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "F駘icitations !" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Veuillez entrer votre nom :", IDNONE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "Ok", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Sur mesure" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Lignes:", IDNONE, 5, 15, 43, 10 - LTEXT "Colonnes:", IDNONE, 5, 35, 43, 10 - LTEXT "Mines:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Annuler", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/id-ID.rc b/base/applications/games/winemine/lang/id-ID.rc deleted file mode 100644 index 1fa62a26dc4..00000000000 --- a/base/applications/games/winemine/lang/id-ID.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_INDONESIAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "seconds" - IDS_NOBODY, "Nobody" - IDS_ABOUT, "by Joshua Thielen and ReactOS developers" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Game" - BEGIN - MENUITEM "&Baru\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&Tandai Pertanyaan", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Pemula", IDM_BEGINNER - MENUITEM "&Lanjutan", IDM_ADVANCED - MENUITEM "&Ahli", IDM_EXPERT - MENUITEM "&Kustom", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Waktu Tercepat", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "E&xit", IDM_EXIT - END - POPUP "&Info" - BEGIN - MENUITEM "&Tentang", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Waktu Tercepat" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Waktu Tercepat", IDNONE, 10, 10, 182, 45 - LTEXT "Pemula:", IDNONE, 20, 20, 58, 8 - LTEXT "Lanjutan:", IDNONE, 20, 30, 58, 8 - LTEXT "Ahli:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Reset Scores", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Selamat!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Silahkan masukkan nama anda", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Game Kustom" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Baris:", IDNONE, 5, 15, 43, 10 - LTEXT "Kolom:", IDNONE, 5, 35, 43, 10 - LTEXT "Mines:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Batal", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/it-IT.rc b/base/applications/games/winemine/lang/it-IT.rc deleted file mode 100644 index 5b96df7d989..00000000000 --- a/base/applications/games/winemine/lang/it-IT.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL - -STRINGTABLE -BEGIN - IDS_SECONDS, "seconds" - IDS_NOBODY, "Anonimo" - IDS_ABOUT, "by Joshua Thielen and ReactOS developers" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Game" - BEGIN - MENUITEM "&Nuova\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&Segno (?)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Principiante", IDM_BEGINNER - MENUITEM "&Intermedio", IDM_ADVANCED - MENUITEM "Espe&rto", IDM_EXPERT - MENUITEM "Personali&zza", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Tempi migliori", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&Esci", IDM_EXIT - END - POPUP "&Info" - BEGIN - MENUITEM "&Informazioni su Campo minato", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Tempi migliori" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Tempi migliori", IDNONE, 10, 10, 182, 45 - LTEXT "Principiante:", IDNONE, 20, 20, 58, 8 - LTEXT "Intermedio:", IDNONE, 20, 30, 58, 8 - LTEXT "Esperto:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Reset Scores", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Complimenti!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Digita il tuo nome per favore", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "Ok", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Campo personalizzato" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Righe:", IDNONE, 5, 15, 43, 10 - LTEXT "Colonne:", IDNONE, 5, 35, 43, 10 - LTEXT "Mine:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Annulla", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/ja-JP.rc b/base/applications/games/winemine/lang/ja-JP.rc deleted file mode 100644 index 6f6e3699f25..00000000000 --- a/base/applications/games/winemine/lang/ja-JP.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "秒" - IDS_NOBODY, "名前なし" - IDS_ABOUT, "Joshua ThielenとReactOS developersより" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "ゲーム(&G)" - BEGIN - MENUITEM "新規ゲーム(&N)\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "?マーク(&M)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "初心者(&B)", IDM_BEGINNER - MENUITEM "中級者(&I)", IDM_ADVANCED - MENUITEM "玄人(&E)", IDM_EXPERT - MENUITEM "カスタム(&C)...", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "ベストタイム(&B)...", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "終了(&X)", IDM_EXIT - END - POPUP "ヘルプ(&H)" - BEGIN - MENUITEM "WineMineについて(&A)", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "最速タイム" -FONT 9, "MS UI Gothic" -BEGIN - GROUPBOX "最速タイム", IDNONE, 10, 10, 182, 45 - LTEXT "初心者:", IDNONE, 20, 20, 58, 8 - LTEXT "中級者:", IDNONE, 20, 30, 58, 8 - LTEXT "玄人:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "スコアのリセット(&R)", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "おめでとう!" -FONT 9, "MS UI Gothic" -BEGIN - LTEXT "お名前を入力してください", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "カスタムゲーム" -FONT 9, "MS UI Gothic" -BEGIN - LTEXT "高さ:", IDNONE, 5, 15, 43, 10 - LTEXT "幅:", IDNONE, 5, 35, 43, 10 - LTEXT "地雷の数:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "キャンセル", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/ko-KR.rc b/base/applications/games/winemine/lang/ko-KR.rc deleted file mode 100644 index 68759496e51..00000000000 --- a/base/applications/games/winemine/lang/ko-KR.rc +++ /dev/null @@ -1,80 +0,0 @@ -/* - *Korean translation by manatails007(www.manatails007.org) - */ -#include "resource.h" - -LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "テハ" - IDS_NOBODY, "タヘク" - IDS_ABOUT, "by Joshua Thielen and ReactOS developers" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "ーヤタモ(&G)" - BEGIN - MENUITEM "サ ーヤタモ(&N)\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "? ヌ・ステ(&M)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "テハア゙(&B)", IDM_BEGINNER - MENUITEM "チ゚ア゙(&I)", IDM_ADVANCED - MENUITEM "ーア゙(&E)", IDM_EXPERT - MENUITEM "トソスコナメ(&C)", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "テヨー ア箙マ(&B)", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "チセキ(&E)", IDM_EXIT - END - POPUP "オオソクサ(&H)" - BEGIN - MENUITEM "チ、コク(&A)", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Fastest Times" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "テヨー ア箙マ", IDNONE, 10, 10, 182, 45 - LTEXT "テハア゙:", IDNONE, 20, 20, 58, 8 - LTEXT "チ゚ア゙:", IDNONE, 20, 30, 58, 8 - LTEXT "ーア゙:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "ネョタホ", IDOK, 127, 57, 50, 15 - PUSHBUTTON "チ。シ テハア篳ュ(&R)", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "テ猊マヌユエマエル!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "タフクァタサ タヤキツヌマシシソ", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "ネョタホ", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "トソスコナメ ーヤタモ" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "ウタフ:", IDNONE, 5, 15, 43, 10 - LTEXT "ウハコ:", IDNONE, 5, 35, 43, 10 - LTEXT "チキレ:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "ネョタホ", IDOK, 86, 32, 45, 15 - PUSHBUTTON "テシメ", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/lt-LT.rc b/base/applications/games/winemine/lang/lt-LT.rc deleted file mode 100644 index 76055285b1e..00000000000 --- a/base/applications/games/winemine/lang/lt-LT.rc +++ /dev/null @@ -1,80 +0,0 @@ -/* Translation by Vytis "CMan" Girdijauskas (cman@cman.us) */ - -#include "resource.h" - -LANGUAGE LANG_LITHUANIAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "sekunds" - IDS_NOBODY, "Niekas" - IDS_ABOUT, "Joshua Thielen ir ReactOS krjai" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&゙aidimas" - BEGIN - MENUITEM "&Naujas aidimas\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "゙ymti &spjamus (?)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Pradedantis", IDM_BEGINNER - MENUITEM "Pa&eng誑", IDM_ADVANCED - MENUITEM "&Ekspertas", IDM_EXPERT - MENUITEM "Pasi&rinktas", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Grei鑛ausi laikai", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&Baigti", IDM_EXIT - END - POPUP "&Info" - BEGIN - - MENUITEM "&Apie", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Grei鑛ausi laikai" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Grei鑛ausi laikai", IDNONE, 10, 10, 182, 45 - LTEXT "Pradedantis:", IDNONE, 20, 20, 58, 8 - LTEXT "Paeng誑:", IDNONE, 20, 30, 58, 8 - LTEXT "Ekspertas:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Atstatyti", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Sveikiname!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Praome 疵ayti savo vard", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "Gerai", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Pasirinktas aidimas" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Eiluts:", IDNONE, 5, 15, 43, 10 - LTEXT "Stulpeliai:", IDNONE, 5, 35, 43, 10 - LTEXT "Minos:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "Gerai", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Atsisakyti", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/nl-NL.rc b/base/applications/games/winemine/lang/nl-NL.rc deleted file mode 100644 index 16a89f5c304..00000000000 --- a/base/applications/games/winemine/lang/nl-NL.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_DUTCH, SUBLANG_NEUTRAL - -STRINGTABLE -BEGIN - IDS_SECONDS, "seconds" - IDS_NOBODY, "Nobody" - IDS_ABOUT, "by Joshua Thielen and ReactOS developers" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Game" - BEGIN - MENUITEM "&Nieuw\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "Vraagteken a&ctiveren", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Beginner", IDM_BEGINNER - MENUITEM "&Gevorderde", IDM_ADVANCED - MENUITEM "&Expert", IDM_EXPERT - MENUITEM "Aan&passen", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "Bes&te tijd", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&Afsluiten", IDM_EXIT - END - POPUP "&Info" - BEGIN - MENUITEM "&Over", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Beste tijd" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Beste tijd", IDNONE, 10, 10, 182, 45 - LTEXT "Beginner:", IDNONE, 20, 20, 58, 8 - LTEXT "Gevorderde:", IDNONE, 20, 30, 58, 8 - LTEXT "Expert:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Reset Scores", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Gefeleciteerd!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Gelieve uw naam in te voeren", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "Ok", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Aangepast spel" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Rijen:", IDNONE, 5, 15, 43, 10 - LTEXT "Kolommen:", IDNONE, 5, 35, 43, 10 - LTEXT "Mijnen:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Annuleren", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/no-NO.rc b/base/applications/games/winemine/lang/no-NO.rc deleted file mode 100644 index 99620f3b157..00000000000 --- a/base/applications/games/winemine/lang/no-NO.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_NORWEGIAN, SUBLANG_NEUTRAL - -STRINGTABLE -BEGIN - IDS_SECONDS, "Sekunder" - IDS_NOBODY, "Ingen" - IDS_ABOUT, "av Joshua Thielen og ReactOS utviklere" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Spill" - BEGIN - MENUITEM "&Nytt\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&Merk sprsm虱", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Nybegynner", IDM_BEGINNER - MENUITEM "&Avansert", IDM_ADVANCED - MENUITEM "&Ekspert", IDM_EXPERT - MENUITEM "&Egendefinert", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Beste tider", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "A&vslutt", IDM_EXIT - END - POPUP "&Informasjon" - BEGIN - MENUITEM "&Om", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Beste tider" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Beste tider", IDNONE, 10, 10, 182, 45 - LTEXT "Nybegynner:", IDNONE, 20, 20, 58, 8 - LTEXT "Avansert:", IDNONE, 20, 30, 58, 8 - LTEXT "Ekspert:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Nullstill poeng", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Gratulerer!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Skriv inn navnet ditt", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Tilpasse spill" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Hyde:", IDNONE, 5, 15, 43, 10 - LTEXT "Bredde:", IDNONE, 5, 35, 43, 10 - LTEXT "Miner:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Avbryt", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/pl-PL.rc b/base/applications/games/winemine/lang/pl-PL.rc deleted file mode 100644 index 7e181073988..00000000000 --- a/base/applications/games/winemine/lang/pl-PL.rc +++ /dev/null @@ -1,86 +0,0 @@ -/* Polish translation Emil Kaczmarek 2006 - * - * small changes by TestamenT - * testament@users.sourceforge.net - * https://sourceforge.net/projects/reactospl - * updated by Caemyr - Olaf Siejka (Jan, 2008) - */ - -#include "resource.h" - -LANGUAGE LANG_POLISH, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "sekund" - IDS_NOBODY, "Anonim" - IDS_ABOUT, "Autorzy: Joshua Thielen i Ekipa ReactOS" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Gra" - BEGIN - MENUITEM "&Nowa gra\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "Poz&iom", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Poczケtkujケcy", IDM_BEGINNER - MENUITEM "Z&aawansowany", IDM_ADVANCED - MENUITEM "&Ekspert", IDM_EXPERT - MENUITEM "&Wウasny poziom", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "N&ajlepsze czasy", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&Zamknij", IDM_EXIT - END - POPUP "&Informacje" - BEGIN - MENUITEM "N&ajlepsze czasy", IDM_TIMES - MENUITEM "&WineMine - informacje", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Najlepsze czasy" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Najlepsze czasy", IDNONE, 10, 10, 182, 45 - LTEXT "Poczケtkujケcy:", IDNONE, 20, 20, 58, 8 - LTEXT "Zaawansowany:", IDNONE, 20, 30, 58, 8 - LTEXT "Ekspert:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Zresetuj wyniki", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Gratulacje!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Wpisz swoje imi", IDNONE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Ustawienia gry" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Ilo懈 rz鹽w:", IDNONE, 5, 15, 43, 10 - LTEXT "Ilo懈 kolumn:", IDNONE, 5, 35, 43, 10 - LTEXT "Ilo懈 min:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Anuluj", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/ru-RU.rc b/base/applications/games/winemine/lang/ru-RU.rc deleted file mode 100644 index c398b67137f..00000000000 --- a/base/applications/games/winemine/lang/ru-RU.rc +++ /dev/null @@ -1,79 +0,0 @@ -// Russian language resource file (Dmitry Chapyshev, 2007-06-10) - -#include "resource.h" - -LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "裲" - IDS_NOBODY, "裴鈔褥" - IDS_ABOUT, "Joshua Thielen 珸珮韭 ReactOS" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&ネ胙" - BEGIN - MENUITEM "&ヘ籵 鞳濬tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&フ褪", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "ヘ&粨", IDM_BEGINNER - MENUITEM "&ヒ礪褄", IDM_ADVANCED - MENUITEM "&マ褥韶琿", IDM_EXPERT - MENUITEM "ホ&磊...", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&ヒ裹 糅褌", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&ツ", IDM_EXIT - END - POPUP "&ム珞" - BEGIN - MENUITEM "&ホ 胙瑟", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "ヒ裹 糅褌" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "ヒ裹 糅褌", IDNONE, 10, 10, 182, 45 - LTEXT "ヘ粨:", IDNONE, 20, 20, 58, 8 - LTEXT "ヒ礪褄:", IDNONE, 20, 30, 58, 8 - LTEXT "マ褥韶琿:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&ム碣頸", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "マ鈕珞褌!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "マ赳鴦籵 粐裝頸 籵 韲", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "ホ矜 鞳" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "ツ:", IDNONE, 5, 15, 43, 10 - LTEXT "リ頏竟:", IDNONE, 5, 35, 43, 10 - LTEXT "ラ頌 竟:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "ホ褊", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/sk-SK.rc b/base/applications/games/winemine/lang/sk-SK.rc deleted file mode 100644 index 80965475f9e..00000000000 --- a/base/applications/games/winemine/lang/sk-SK.rc +++ /dev/null @@ -1,81 +0,0 @@ -/* Slovak translation - * Copyleft 2007 Kario (kario@szm.sk) - */ - -#include "resource.h" - -LANGUAGE LANG_SLOVAK, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "sek." - IDS_NOBODY, "Nikto" //windows = Anonym - IDS_ABOUT, "od Joshua Thielen a vvoj疵ov syst駑u ReactOS" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Hra" - BEGIN - MENUITEM "&Nov hra\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "Zn&a鑢y (?)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&Za鑛ato鈩k", IDM_BEGINNER - MENUITEM "&Pokro鑛l", IDM_ADVANCED - MENUITEM "&Expert", IDM_EXPERT - MENUITEM "&Vlastn...", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&Najlep喨e 鐶sy...", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "S&kon鑛", IDM_EXIT - END - POPUP "&Info" //windows = &Pomocnk - BEGIN - MENUITEM "ネ&o je hra Mny...", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Najrchlej夬 hセada鑛 mn" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "Najlep喨e 鐶sy", IDNONE, 10, 10, 182, 45 - LTEXT "Za鑛ato鈩k:", IDNONE, 20, 20, 58, 8 - LTEXT "Pokro鑛l:", IDNONE, 20, 30, 58, 8 - LTEXT "Expert:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Vynulova vsledky", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Blaho枡l疥!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "Zadajte svoje meno, prosm!", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "Vlastn pole" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "V嗅a:", IDNONE, 5, 15, 43, 10 - LTEXT "器rka:", IDNONE, 5, 35, 43, 10 - LTEXT "Mny:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "Zru喨", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/th-TH.rc b/base/applications/games/winemine/lang/th-TH.rc deleted file mode 100644 index 805c0f95e80..00000000000 --- a/base/applications/games/winemine/lang/th-TH.rc +++ /dev/null @@ -1,77 +0,0 @@ -#include "resource.h" - -LANGUAGE LANG_THAI, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "seconds" - IDS_NOBODY, "Nobody" - IDS_ABOUT, "by Joshua Thielen and ReactOS developers" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&Game" - BEGIN - MENUITEM "&ヒチ鐔tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&、テラ靉ァヒチメツ、モカメチ", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "&テヤ霖オ鮖", IDM_BEGINNER - MENUITEM "&「ム鮖ハルァ「ヨ鮖", IDM_ADVANCED - MENUITEM "&シル鬪モケメュ", IDM_EXPERT - MENUITEM "&、ケキム霽莉", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&ヌナメキユ鞨テ酩キユ靆リエ", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "&ヘヘ。", IDM_EXIT - END - POPUP "&ィ鬧耆鮃テメコ" - BEGIN - MENUITEM "&。ユ霙ヌ。ムコ", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "猊ナメキユ鞨テ酩キユ靆リエ" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "猊ナメキユ鞨テ酩キユ靆リエ", IDNONE, 10, 10, 182, 45 - LTEXT "狹ヤ霖オ鮖:", IDNONE, 20, 20, 58, 8 - LTEXT "「ム鮖ハルァ「ヨ鮖:", IDNONE, 20, 30, 58, 8 - LTEXT "シル鬪モケメュ:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "オ。ナァ", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&Reset Scores", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "「ヘ睫エァ、ヌメチツヤケエユエ鯢ツ!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "篏テエ。テヘ。ェラ靉「ヘァ、リウ", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "オ。ナァ", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "爍チテミエムコ、ケキム霽莉" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "盡ヌ:", IDNONE, 5, 15, 43, 10 - LTEXT "ヒナム。:", IDNONE, 5, 35, 43, 10 - LTEXT "。ムコテミ犲ヤエ:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "オ。ナァ", IDOK, 86, 32, 45, 15 - PUSHBUTTON "ツ。倏ヤ。", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/uk-UA.rc b/base/applications/games/winemine/lang/uk-UA.rc deleted file mode 100644 index f7ee136b366..00000000000 --- a/base/applications/games/winemine/lang/uk-UA.rc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * PROJECT: WineMine - * LICENSE: GPL - See COPYING in the top level directory - * FILE: base/applications/games/winemine/lang/uk-UA.rc - * PURPOSE: Ukraianian Language File for WineMine - * TRANSLATORS: Artem Reznikov, Igor Paliychuk - */ - -#include "resource.h" - -LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT - -STRINGTABLE -BEGIN - IDS_SECONDS, "裲" - IDS_NOBODY, "ヘウ" - IDS_ABOUT, "箋 Joshua Thielen ウ 銜硼韭ウ ReactOS" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "&テ" - BEGIN - MENUITEM "&ヘ籵\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "&フウ (?)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "ヘ籵&", IDM_BEGINNER - MENUITEM "&タ瑣", IDM_ADVANCED - MENUITEM "マ&褥ウ琿", IDM_EXPERT - MENUITEM "&ホ硴鞣ウ...", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "&ラ褌ウ...", IDM_TIMES - MENUITEM SEPARATOR - MENUITEM "ツ&頷ウ", IDM_EXIT - END - POPUP "&イ璋ウ" - BEGIN - MENUITEM "&マ 胙瑟...", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "ラ褌ウ 鈞 瑣裙ウ" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "ハ瓊ウ 瑰", IDNONE, 10, 10, 182, 45 - LTEXT "ヘ籵:", IDNONE, 20, 20, 58, 8 - LTEXT "タ瑣:", IDNONE, 20, 30, 58, 8 - LTEXT "マ褥ウ琿:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "OK", IDOK, 127, 57, 50, 15 - PUSHBUTTON "&ム鞴瑙 裼瑣ウ", IDRESET, 18, 57, 77, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "ツウ瑙!" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "ツ粢莎 ツ璧 ウ'", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "ム褻ウ琿 " -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "ツ頌:", IDNONE, 5, 15, 43, 10 - LTEXT "リ頏竟:", IDNONE, 5, 35, 43, 10 - LTEXT "フウ:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "OK", IDOK, 86, 32, 45, 15 - PUSHBUTTON "ム瑰籵", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/lang/zh-CN.rc b/base/applications/games/winemine/lang/zh-CN.rc deleted file mode 100644 index 6d3dbe80842..00000000000 --- a/base/applications/games/winemine/lang/zh-CN.rc +++ /dev/null @@ -1,96 +0,0 @@ -/* - * WineMine (Simplified Chinese resources) - * - * Copyright 2007 zhangbing - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "resource.h" - -LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED - -STRINGTABLE DISCARDABLE -BEGIN - IDS_SECONDS, "テ" - IDS_NOBODY, "ト菘" - IDS_ABOUT, "by Joshua Thielen and ReactOS developers" -END - -IDM_WINEMINE MENU DISCARDABLE -BEGIN - POPUP "ノィタラ(&G)" - BEGIN - MENUITEM "ソェセヨ(&N)\tF2", IDM_NEW - MENUITEM SEPARATOR - MENUITEM "アシヌ(?)(&M)", IDM_MARKQ - MENUITEM SEPARATOR - MENUITEM "ウシカ(&B)", IDM_BEGINNER - MENUITEM "ヨミシカ(&A)", IDM_ADVANCED - MENUITEM "ク゚シカ(&E)", IDM_EXPERT - MENUITEM "ラヤカィメ(&C)...", IDM_CUSTOM - MENUITEM SEPARATOR - MENUITEM "ヘヒウ(&X)", IDM_EXIT - END - POPUP "ミナマ「(&I)" - BEGIN - MENUITEM "ノィタラモ「ミロー(&F)...", IDM_TIMES - MENUITEM "ケリモレノィタラ(&A)...", IDM_ABOUT - END -END - -IDD_TIMES DIALOGEX DISCARDABLE 0, 0, 200, 75 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "ノィタラモ「ミロー" -FONT 8, "MS Shell Dlg" -BEGIN - GROUPBOX "ラク゚シヘツシ", IDNONE, 10, 10, 182, 45 - LTEXT "ウシカ:", IDNONE, 20, 20, 58, 8 - LTEXT "ヨミシカ:", IDNONE, 20, 30, 58, 8 - LTEXT "ク゚シカ:", IDNONE, 20, 40, 58, 8 - LTEXT "", IDC_TIME1, 80, 20, 50, 8 - LTEXT "", IDC_TIME2, 80, 30, 50, 8 - LTEXT "", IDC_TIME3, 80, 40, 50, 8 - LTEXT "", IDC_NAME1, 132, 20, 55, 8 - LTEXT "", IDC_NAME2, 132, 30, 55, 8 - LTEXT "", IDC_NAME3, 132, 40, 55, 8 - DEFPUSHBUTTON "ネキカィ", IDOK, 127, 57, 50, 15 - PUSHBUTTON "ヨリミツシヌキヨ(&R)", IDRESET, 18, 57, 67, 15 -END - -IDD_CONGRATS DIALOGEX DISCARDABLE 0, 0, 160, 60 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -FONT 8, "MS Shell Dlg" -CAPTION "ヒ「ミツシヘツシ!" -BEGIN - LTEXT "メムニニシヘツシ,ヌハ菠ト羞トテラヨ", IDIGNORE, 25, 10, 150, 10 - EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 - DEFPUSHBUTTON "ネキカィ", IDOK, 60, 40, 40, 15 -END - -IDD_CUSTOM DIALOGEX DISCARDABLE 0, 0, 139, 80 -STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT -CAPTION "ラヤカィメ蠡ラヌ" -FONT 8, "MS Shell Dlg" -BEGIN - LTEXT "ク゚カネ:", IDNONE, 5, 15, 43, 10 - LTEXT "ソカネ:", IDNONE, 5, 35, 43, 10 - LTEXT "タラハ:", IDNONE, 5, 55, 43, 10 - EDITTEXT IDC_EDITROWS, 49, 15, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITCOLS, 49, 35, 30, 12, ES_NUMBER - EDITTEXT IDC_EDITMINES, 49, 55, 30, 12, ES_NUMBER - DEFPUSHBUTTON "ネキカィ", IDOK, 86, 32, 45, 15 - PUSHBUTTON "ネ。マ", IDCANCEL, 86, 52, 45, 15 -END diff --git a/base/applications/games/winemine/main.c b/base/applications/games/winemine/main.c deleted file mode 100644 index c3a4097f85a..00000000000 --- a/base/applications/games/winemine/main.c +++ /dev/null @@ -1,1085 +0,0 @@ -/* - * WineMine (main.c) - * - * Copyright 2000 Joshua Thielen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include "main.h" -#include "dialog.h" -#include "resource.h" - -#ifdef DUMB_DEBUG -#include -#define DEBUG(x) fprintf(stderr,x) -#else -#define DEBUG(x) -#endif - -static const TCHAR szAppName[] = TEXT("WineMine"); - - -int WINAPI _tWinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPTSTR cmdline, int cmdshow ) -{ - MSG msg; - WNDCLASS wc; - HWND hWnd; - HACCEL haccel; - - wc.style = 0; - wc.lpfnWndProc = MainProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = hInst; - wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(IDI_WINEMINE) ); - wc.hCursor = LoadCursor( NULL, (LPCTSTR)IDI_APPLICATION ); - wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); - wc.lpszMenuName = MAKEINTRESOURCE(IDM_WINEMINE); - wc.lpszClassName = szAppName; - - if ( !RegisterClass(&wc) ) - return 1; - - hWnd = CreateWindow( szAppName, szAppName, - WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - NULL, NULL, hInst, NULL ); - - if (!hWnd) - return 1; - - ShowWindow( hWnd, cmdshow ); - UpdateWindow( hWnd ); - - haccel = LoadAccelerators( hInst, MAKEINTRESOURCE(IDA_WINEMINE) ); - SetTimer( hWnd, ID_TIMER, 1000, NULL ); - - while( GetMessage(&msg, NULL, 0, 0) ) - { - if ( !TranslateAccelerator(hWnd, haccel, &msg) ) - TranslateMessage(&msg); - - DispatchMessage(&msg); - } - - return msg.wParam; -} - -LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - static BOARD board; - - switch(msg) - { - case WM_CREATE: - board.hInst = ((LPCREATESTRUCT) lParam)->hInstance; - board.hWnd = hWnd; - InitBoard( &board ); - CreateBoard( &board ); - return 0; - - case WM_PAINT: - { - HDC hDC; - HDC hMemDC; - PAINTSTRUCT ps; - - DEBUG("WM_PAINT\n"); - hDC = BeginPaint( hWnd, &ps ); - hMemDC = CreateCompatibleDC(hDC); - - DrawBoard( hDC, hMemDC, &ps, &board ); - - DeleteDC( hMemDC ); - EndPaint( hWnd, &ps ); - - return 0; - } - - case WM_MOVE: - DEBUG("WM_MOVE\n"); - board.Pos.x = (LONG) LOWORD(lParam); - board.Pos.y = (LONG) HIWORD(lParam); - return 0; - - case WM_DESTROY: - SaveBoard( &board ); - DestroyBoard( &board ); - PostQuitMessage( 0 ); - return 0; - - case WM_TIMER: - if( board.Status == PLAYING ) - { - board.uTime++; - RedrawWindow( hWnd, &board.TimerRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW ); - } - return 0; - - case WM_LBUTTONDOWN: - DEBUG("WM_LBUTTONDOWN\n"); - - if( wParam & MK_RBUTTON ) - msg = WM_MBUTTONDOWN; - - TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); - SetCapture( hWnd ); - return 0; - - case WM_LBUTTONUP: - DEBUG("WM_LBUTTONUP\n"); - - if( wParam & MK_RBUTTON ) - msg = WM_MBUTTONUP; - - TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); - ReleaseCapture(); - return 0; - - case WM_RBUTTONDOWN: - DEBUG("WM_RBUTTONDOWN\n"); - - if( wParam & MK_LBUTTON ) - { - board.Press.x = 0; - board.Press.y = 0; - msg = WM_MBUTTONDOWN; - } - - TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); - return 0; - - case WM_RBUTTONUP: - DEBUG("WM_RBUTTONUP\n"); - if( wParam & MK_LBUTTON ) - msg = WM_MBUTTONUP; - TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); - return 0; - - case WM_MBUTTONDOWN: - DEBUG("WM_MBUTTONDOWN\n"); - TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); - return 0; - - case WM_MBUTTONUP: - DEBUG("WM_MBUTTONUP\n"); - TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); - return 0; - - case WM_MOUSEMOVE: - { - if( (wParam & MK_LBUTTON) && (wParam & MK_RBUTTON) ) - msg = WM_MBUTTONDOWN; - else if( wParam & MK_LBUTTON ) - msg = WM_LBUTTONDOWN; - else - return 0; - - TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg ); - - return 0; - } - - case WM_COMMAND: - switch(LOWORD(wParam)) - { - case IDM_NEW: - CreateBoard( &board ); - return 0; - - case IDM_MARKQ: - { - HMENU hMenu; - - hMenu = GetMenu( hWnd ); - board.bMark = !board.bMark; - - if( board.bMark ) - CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED ); - else - CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED ); - - return 0; - } - - case IDM_BEGINNER: - SetDifficulty( &board, BEGINNER ); - CreateBoard( &board ); - return 0; - - case IDM_ADVANCED: - SetDifficulty( &board, ADVANCED ); - CreateBoard( &board ); - return 0; - - case IDM_EXPERT: - SetDifficulty( &board, EXPERT ); - CreateBoard( &board ); - return 0; - - case IDM_CUSTOM: - SetDifficulty( &board, CUSTOM ); - CreateBoard( &board ); - return 0; - - case IDM_EXIT: - SendMessage( hWnd, WM_CLOSE, 0, 0); - return 0; - - case IDM_TIMES: - DialogBoxParam( board.hInst, MAKEINTRESOURCE(IDD_TIMES), hWnd, TimesDlgProc, (LPARAM) &board); - return 0; - - case IDM_ABOUT: - { - TCHAR szOtherStuff[255]; - - LoadString( board.hInst, IDS_ABOUT, szOtherStuff, sizeof(szOtherStuff) / sizeof(TCHAR) ); - - ShellAbout( hWnd, szAppName, szOtherStuff, (HICON)SendMessage(hWnd, WM_GETICON, ICON_BIG, 0) ); - return 0; - } - - default: - DEBUG("Unknown WM_COMMAND command message received\n"); - break; - } - } - - return( DefWindowProc( hWnd, msg, wParam, lParam )); -} - -void InitBoard( BOARD *pBoard ) -{ - HMENU hMenu; - - pBoard->hMinesBMP = LoadBitmap( pBoard->hInst, (LPCTSTR) IDB_MINES); - pBoard->hFacesBMP = LoadBitmap( pBoard->hInst, (LPCTSTR) IDB_FACES); - pBoard->hLedsBMP = LoadBitmap( pBoard->hInst, (LPCTSTR) IDB_LEDS); - - LoadBoard( pBoard ); - - if( pBoard->Pos.x < GetSystemMetrics( SM_CXFIXEDFRAME ) ) - pBoard->Pos.x = GetSystemMetrics( SM_CXFIXEDFRAME ); - - if( pBoard->Pos.x > (GetSystemMetrics( SM_CXSCREEN ) - GetSystemMetrics( SM_CXFIXEDFRAME ))) - { - pBoard->Pos.x = GetSystemMetrics( SM_CXSCREEN ) - - GetSystemMetrics( SM_CXFIXEDFRAME ); - } - - if( pBoard->Pos.y < (GetSystemMetrics( SM_CYMENU ) + GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYFIXEDFRAME ))) - { - pBoard->Pos.y = GetSystemMetrics( SM_CYMENU ) + - GetSystemMetrics( SM_CYCAPTION ) + - GetSystemMetrics( SM_CYFIXEDFRAME ); - } - - if( pBoard->Pos.y > (GetSystemMetrics( SM_CYSCREEN ) - GetSystemMetrics( SM_CYFIXEDFRAME ))) - { - pBoard->Pos.y = GetSystemMetrics( SM_CYSCREEN ) - - GetSystemMetrics( SM_CYFIXEDFRAME ); - } - - hMenu = GetMenu( pBoard->hWnd ); - CheckMenuItem( hMenu, IDM_BEGINNER + pBoard->Difficulty, MF_CHECKED ); - - if( pBoard->bMark ) - CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED ); - else - CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED ); - CheckLevel( pBoard ); -} - -static DWORD LoadDWord(HKEY hKey, TCHAR *szKeyName, DWORD dwDefaultValue) -{ - DWORD dwSize; - DWORD dwValue; - - dwSize = sizeof(DWORD); - - if( RegQueryValueEx( hKey, szKeyName, NULL, NULL, (LPBYTE) &dwValue, &dwSize ) == ERROR_SUCCESS ) - return dwValue; - - return dwDefaultValue; -} - -void LoadBoard( BOARD *pBoard ) -{ - DWORD dwSize; - HKEY hKey; - TCHAR szData[16]; - TCHAR szKeyName[8]; - TCHAR szNobody[15]; - UCHAR i; - - RegOpenKeyEx( HKEY_CURRENT_USER, szWineMineRegKey, 0, KEY_QUERY_VALUE, &hKey ); - - pBoard->Pos.x = (LONG) LoadDWord( hKey, TEXT("Xpos"), GetSystemMetrics(SM_CXFIXEDFRAME) ); - pBoard->Pos.y = (LONG) LoadDWord( hKey, TEXT("Ypos"), GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFIXEDFRAME) ); - pBoard->uRows = (ULONG) LoadDWord( hKey, TEXT("Height"), BEGINNER_ROWS ); - pBoard->uCols = (ULONG) LoadDWord( hKey, TEXT("Width"), BEGINNER_COLS ); - pBoard->uMines = (ULONG) LoadDWord( hKey, TEXT("Mines"), BEGINNER_MINES ); - pBoard->Difficulty = (DIFFICULTY) LoadDWord( hKey, TEXT("Difficulty"), BEGINNER ); - pBoard->bMark = (BOOL) LoadDWord( hKey, TEXT("Mark"), TRUE ); - - LoadString( pBoard->hInst, IDS_NOBODY, szNobody, sizeof(szNobody) / sizeof(TCHAR) ); - - for( i = 0; i < 3; i++ ) - { - // As we write to the same registry key as MS WinMine does, we have to start at 1 for the registry keys - wsprintf( szKeyName, TEXT("Name%d"), i + 1 ); - dwSize = sizeof(szData); - - if( RegQueryValueEx( hKey, szKeyName, NULL, NULL, (LPBYTE)szData, (LPDWORD) &dwSize ) == ERROR_SUCCESS ) - _tcsncpy( pBoard->szBestName[i], szData, sizeof(szData) / sizeof(TCHAR) ); - else - _tcscpy( pBoard->szBestName[i], szNobody); - } - - for( i = 0; i < 3; i++ ) - { - wsprintf( szKeyName, TEXT("Time%d"), i + 1 ); - pBoard->uBestTime[i] = LoadDWord( hKey, szKeyName, 999 ); - } - - RegCloseKey(hKey); -} - -void SaveBoard( BOARD *pBoard ) -{ - DWORD dwValue; - HKEY hKey; - UCHAR i; - TCHAR szData[16]; - TCHAR szKeyName[8]; - - if( RegCreateKeyEx( HKEY_CURRENT_USER, szWineMineRegKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL ) != ERROR_SUCCESS) - return; - - RegSetValueEx( hKey, TEXT("Xpos"), 0, REG_DWORD, (LPBYTE) &pBoard->Pos.x, sizeof(DWORD) ); - RegSetValueEx( hKey, TEXT("Ypos"), 0, REG_DWORD, (LPBYTE) &pBoard->Pos.y, sizeof(DWORD) ); - RegSetValueEx( hKey, TEXT("Difficulty"), 0, REG_DWORD, (LPBYTE) &pBoard->Difficulty, sizeof(DWORD) ); - RegSetValueEx( hKey, TEXT("Height"), 0, REG_DWORD, (LPBYTE) &pBoard->uRows, sizeof(DWORD) ); - RegSetValueEx( hKey, TEXT("Width"), 0, REG_DWORD, (LPBYTE) &pBoard->uCols, sizeof(DWORD) ); - RegSetValueEx( hKey, TEXT("Mines"), 0, REG_DWORD, (LPBYTE) &pBoard->uMines, sizeof(DWORD) ); - RegSetValueEx( hKey, TEXT("Mark"), 0, REG_DWORD, (LPBYTE) &pBoard->bMark, sizeof(DWORD) ); - - for( i = 0; i < 3; i++ ) - { - // As we write to the same registry key as MS WinMine does, we have to start at 1 for the registry keys - wsprintf( szKeyName, TEXT("Name%u"), i + 1); - _tcsncpy( szData, pBoard->szBestName[i], sizeof(szData) / sizeof(TCHAR) ); - RegSetValueEx( hKey, szKeyName, 0, REG_SZ, (LPBYTE)szData, (_tcslen(szData) + 1) * sizeof(TCHAR) ); - } - - for( i = 0; i < 3; i++ ) - { - wsprintf( szKeyName, TEXT("Time%u"), i + 1); - dwValue = pBoard->uBestTime[i]; - RegSetValueEx( hKey, szKeyName, 0, REG_DWORD, (LPBYTE)(LPDWORD)&dwValue, sizeof(DWORD) ); - } - - RegCloseKey(hKey); -} - -void DestroyBoard( BOARD *pBoard ) -{ - DeleteObject( pBoard->hFacesBMP ); - DeleteObject( pBoard->hLedsBMP ); - DeleteObject( pBoard->hMinesBMP ); -} - -void SetDifficulty( BOARD *pBoard, DIFFICULTY Difficulty ) -{ - HMENU hMenu; - - switch(Difficulty) - { - case BEGINNER: - pBoard->uCols = BEGINNER_COLS; - pBoard->uRows = BEGINNER_ROWS; - pBoard->uMines = BEGINNER_MINES; - break; - - case ADVANCED: - pBoard->uCols = ADVANCED_COLS; - pBoard->uRows = ADVANCED_ROWS; - pBoard->uMines = ADVANCED_MINES; - break; - - case EXPERT: - pBoard->uCols = EXPERT_COLS; - pBoard->uRows = EXPERT_ROWS; - pBoard->uMines = EXPERT_MINES; - break; - - case CUSTOM: - if( DialogBoxParam( pBoard->hInst, MAKEINTRESOURCE(IDD_CUSTOM), pBoard->hWnd, CustomDlgProc, (LPARAM) pBoard) != IDOK ) - return; - - break; - } - - hMenu = GetMenu(pBoard->hWnd); - CheckMenuItem( hMenu, IDM_BEGINNER + pBoard->Difficulty, MF_UNCHECKED ); - pBoard->Difficulty = Difficulty; - CheckMenuItem( hMenu, IDM_BEGINNER + Difficulty, MF_CHECKED ); - -} - -void CreateBoard( BOARD *pBoard ) -{ - ULONG uLeft, uTop, uBottom, uRight, uWndX, uWndY, uWndWidth, uWndHeight; - - pBoard->uBoxesLeft = pBoard->uCols * pBoard->uRows - pBoard->uMines; - pBoard->uNumFlags = 0; - - CreateBoxes( pBoard ); - - pBoard->uWidth = pBoard->uCols * MINE_WIDTH + BOARD_WMARGIN * 2; - - pBoard->uHeight = pBoard->uRows * MINE_HEIGHT + LED_HEIGHT - + BOARD_HMARGIN * 3; - - uWndX = pBoard->Pos.x - GetSystemMetrics( SM_CXFIXEDFRAME ); - uWndY = pBoard->Pos.y - GetSystemMetrics( SM_CYMENU ) - - GetSystemMetrics( SM_CYCAPTION ) - - GetSystemMetrics( SM_CYFIXEDFRAME ); - uWndWidth = pBoard->uWidth + GetSystemMetrics( SM_CXFIXEDFRAME ) * 2; - uWndHeight = pBoard->uHeight - + GetSystemMetrics( SM_CYMENU ) - + GetSystemMetrics( SM_CYCAPTION ) - + GetSystemMetrics( SM_CYFIXEDFRAME ) * 2; - - /* setting the mines rectangle boundary */ - uLeft = BOARD_WMARGIN; - uTop = BOARD_HMARGIN * 2 + LED_HEIGHT; - uRight = uLeft + pBoard->uCols * MINE_WIDTH; - uBottom = uTop + pBoard->uRows * MINE_HEIGHT; - SetRect( &pBoard->MinesRect, uLeft, uTop, uRight, uBottom ); - - /* setting the face rectangle boundary */ - uLeft = pBoard->uWidth / 2 - FACE_WIDTH / 2; - uTop = BOARD_HMARGIN; - uRight = uLeft + FACE_WIDTH; - uBottom = uTop + FACE_HEIGHT; - SetRect( &pBoard->FaceRect, uLeft, uTop, uRight, uBottom ); - - /* setting the timer rectangle boundary */ - uLeft = BOARD_WMARGIN; - uTop = BOARD_HMARGIN; - uRight = uLeft + LED_WIDTH * 3; - uBottom = uTop + LED_HEIGHT; - SetRect( &pBoard->CounterRect, uLeft, uTop, uRight, uBottom ); - - /* setting the counter rectangle boundary */ - uLeft = pBoard->uWidth - BOARD_WMARGIN - LED_WIDTH * 3; - uTop = BOARD_HMARGIN; - uRight = pBoard->uWidth - BOARD_WMARGIN; - uBottom = uTop + LED_HEIGHT; - SetRect( &pBoard->TimerRect, uLeft, uTop, uRight, uBottom ); - - pBoard->Status = WAITING; - pBoard->FaceBmp = SMILE_BMP; - pBoard->uTime = 0; - - MoveWindow( pBoard->hWnd, uWndX, uWndY, uWndWidth, uWndHeight, TRUE ); - RedrawWindow( pBoard->hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE ); -} - -void CheckLevel( BOARD *pBoard ) -{ - if( pBoard->uRows < BEGINNER_ROWS ) - pBoard->uRows = BEGINNER_ROWS; - - if( pBoard->uRows > MAX_ROWS ) - pBoard->uRows = MAX_ROWS; - - if( pBoard->uCols < BEGINNER_COLS ) - pBoard->uCols = BEGINNER_COLS; - - if( pBoard->uCols > MAX_COLS ) - pBoard->uCols = MAX_COLS; - - if( pBoard->uMines < BEGINNER_MINES ) - pBoard->uMines = BEGINNER_MINES; - - if( pBoard->uMines > pBoard->uCols * pBoard->uRows - 1 ) - pBoard->uMines = pBoard->uCols * pBoard->uRows - 1; -} - -void CreateBoxes( BOARD *pBoard ) -{ - LONG i, j; - ULONG uCol, uRow; - - srand( (unsigned int)time( NULL ) ); - - /* Create the boxes... - * We actually create them with an empty border, - * so special care doesn't have to be taken on the edges - */ - - for( uCol = 0; uCol <= pBoard->uCols + 1; uCol++ ) - { - for( uRow = 0; uRow <= pBoard->uRows + 1; uRow++ ) - { - pBoard->Box[uCol][uRow].bIsPressed = FALSE; - pBoard->Box[uCol][uRow].bIsMine = FALSE; - pBoard->Box[uCol][uRow].uFlagType = NORMAL; - pBoard->Box[uCol][uRow].uNumMines = 0; - } - } - - /* create mines */ - i = 0; - while( (ULONG)i < pBoard->uMines ) - { - uCol = (ULONG)(pBoard->uCols * (float)rand() / RAND_MAX + 1); - uRow = (ULONG)(pBoard->uRows * (float)rand() / RAND_MAX + 1); - - if( !pBoard->Box[uCol][uRow].bIsMine ) - { - i++; - pBoard->Box[uCol][uRow].bIsMine = TRUE; - } - } - - /* - * Now we label the remaining boxes with the - * number of mines surrounding them. - */ - for( uCol = 1; uCol < pBoard->uCols + 1; uCol++ ) - { - for( uRow = 1; uRow < pBoard->uRows + 1; uRow++ ) - { - for( i = -1; i <= 1; i++ ) - { - for( j = -1; j <= 1; j++ ) - { - if( pBoard->Box[uCol + i][uRow + j].bIsMine ) - { - pBoard->Box[uCol][uRow].uNumMines++; - } - } - } - } - } -} - -void DrawMines ( HDC hdc, HDC hMemDC, BOARD *pBoard ) -{ - HGDIOBJ hOldObj; - ULONG uCol, uRow; - hOldObj = SelectObject (hMemDC, pBoard->hMinesBMP); - - for( uRow = 1; uRow <= pBoard->uRows; uRow++ ) - { - for( uCol = 1; uCol <= pBoard->uCols; uCol++ ) - { - DrawMine( hdc, hMemDC, pBoard, uCol, uRow, FALSE ); - } - } - - SelectObject( hMemDC, hOldObj ); -} - -void DrawMine( HDC hdc, HDC hMemDC, BOARD *pBoard, ULONG uCol, ULONG uRow, BOOL bIsPressed ) -{ - MINEBMP_OFFSET offset = BOX_BMP; - - if( uCol == 0 || uCol > pBoard->uCols || uRow == 0 || uRow > pBoard->uRows ) - return; - - if( pBoard->Status == GAMEOVER ) - { - if( pBoard->Box[uCol][uRow].bIsMine ) - { - switch( pBoard->Box[uCol][uRow].uFlagType ) - { - case FLAG: - offset = FLAG_BMP; - break; - case COMPLETE: - offset = EXPLODE_BMP; - break; - case QUESTION: - /* fall through */ - case NORMAL: - offset = MINE_BMP; - } - } - else - { - switch( pBoard->Box[uCol][uRow].uFlagType ) - { - case QUESTION: - offset = QUESTION_BMP; - break; - case FLAG: - offset = WRONG_BMP; - break; - case NORMAL: - offset = BOX_BMP; - break; - case COMPLETE: - /* Do nothing */ - break; - default: - DEBUG("Unknown FlagType during game over in DrawMine\n"); - break; - } - } - } - else - { /* WAITING or PLAYING */ - switch( pBoard->Box[uCol][uRow].uFlagType ) - { - case QUESTION: - if( !bIsPressed ) - offset = QUESTION_BMP; - else - offset = QPRESS_BMP; - break; - case FLAG: - offset = FLAG_BMP; - break; - case NORMAL: - if( !bIsPressed ) - offset = BOX_BMP; - else - offset = MPRESS_BMP; - break; - case COMPLETE: - /* Do nothing */ - break; - default: - DEBUG("Unknown FlagType while playing in DrawMine\n"); - break; - } - } - - if( pBoard->Box[uCol][uRow].uFlagType == COMPLETE && !pBoard->Box[uCol][uRow].bIsMine ) - offset = (MINEBMP_OFFSET) pBoard->Box[uCol][uRow].uNumMines; - - BitBlt( hdc, - (uCol - 1) * MINE_WIDTH + pBoard->MinesRect.left, - (uRow - 1) * MINE_HEIGHT + pBoard->MinesRect.top, - MINE_WIDTH, MINE_HEIGHT, - hMemDC, 0, offset * MINE_HEIGHT, SRCCOPY ); -} - -void DrawLeds( HDC hDC, HDC hMemDC, BOARD *pBoard, LONG nNumber, LONG x, LONG y ) -{ - HGDIOBJ hOldObj; - UCHAR i; - ULONG uLED[3]; - LONG nCount; - - nCount = nNumber; - - if( nCount < 1000 ) - { - if( nCount >= 0 ) - { - uLED[0] = nCount / 100 ; - nCount -= uLED[0] * 100; - } - else - { - uLED[0] = 10; /* negative sign */ - nCount = -nCount; - } - - uLED[1] = nCount / 10; - nCount -= uLED[1] * 10; - uLED[2] = nCount; - } - else - { - for( i = 0; i < 3; i++ ) - uLED[i] = 10; - } - - /* use unlit led if not playing */ - /* if( pBoard->Status == WAITING ) - for( i = 0; i < 3; i++ ) - uLED[i] = 11;*/ - - hOldObj = SelectObject (hMemDC, pBoard->hLedsBMP); - - for( i = 0; i < 3; i++ ) - { - BitBlt( hDC, - i * LED_WIDTH + x, - y, - LED_WIDTH, - LED_HEIGHT, - hMemDC, - 0, - uLED[i] * LED_HEIGHT, - SRCCOPY); - } - - SelectObject( hMemDC, hOldObj ); -} - -void DrawFace( HDC hDC, HDC hMemDC, BOARD *pBoard ) -{ - HGDIOBJ hOldObj; - - hOldObj = SelectObject (hMemDC, pBoard->hFacesBMP); - - BitBlt( hDC, - pBoard->FaceRect.left, - pBoard->FaceRect.top, - FACE_WIDTH, - FACE_HEIGHT, - hMemDC, 0, pBoard->FaceBmp * FACE_HEIGHT, SRCCOPY); - - SelectObject( hMemDC, hOldObj ); -} - -void DrawBoard( HDC hDC, HDC hMemDC, PAINTSTRUCT *ps, BOARD *pBoard ) -{ - RECT TempRect; - - if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->CounterRect) ) - DrawLeds( hDC, hMemDC, pBoard, pBoard->uMines - pBoard->uNumFlags, - pBoard->CounterRect.left, - pBoard->CounterRect.top ); - - if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->TimerRect ) ) - DrawLeds( hDC, hMemDC, pBoard, pBoard->uTime, - pBoard->TimerRect.left, - pBoard->TimerRect.top); - - if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->FaceRect ) ) - DrawFace( hDC, hMemDC, pBoard ); - - if( IntersectRect( &TempRect, &ps->rcPaint, &pBoard->MinesRect ) ) - DrawMines( hDC, hMemDC, pBoard ); -} - - -void TestBoard( HWND hWnd, BOARD *pBoard, LONG x, LONG y, int msg ) -{ - POINT pt; - ULONG uCol, uRow; - - pt.x = x; - pt.y = y; - - if( PtInRect( &pBoard->MinesRect, pt ) && pBoard->Status != GAMEOVER && pBoard->Status != WON ) - TestMines( pBoard, pt, msg ); - else - { - UnpressBoxes( pBoard, pBoard->Press.x, pBoard->Press.y ); - pBoard->Press.x = 0; - pBoard->Press.y = 0; - } - - if( pBoard->uBoxesLeft == 0 ) - { - // MG - 2006-02-21 - // mimic MS minesweeper behaviour - when autocompleting a board, flag mines - pBoard->Status = WON; - - for( uCol = 0; uCol <= pBoard->uCols + 1; uCol++ ) - { - for( uRow = 0; uRow <= pBoard->uRows + 1; uRow++ ) - { - if(pBoard->Box[uCol][uRow].bIsMine) - { - pBoard->Box[uCol][uRow].uFlagType = FLAG; - } - } - } - - pBoard->uNumFlags = pBoard->uMines; - RedrawWindow( pBoard->hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW ); - - if( pBoard->Difficulty != CUSTOM && pBoard->uTime < pBoard->uBestTime[pBoard->Difficulty] ) - { - pBoard->uBestTime[pBoard->Difficulty] = pBoard->uTime; - - DialogBoxParam( pBoard->hInst, MAKEINTRESOURCE(IDD_CONGRATS), hWnd, CongratsDlgProc, (LPARAM) pBoard); - DialogBoxParam( pBoard->hInst, MAKEINTRESOURCE(IDD_TIMES), hWnd, TimesDlgProc, (LPARAM) pBoard); - } - } - - TestFace( pBoard, pt, msg ); -} - -void TestMines( BOARD *pBoard, POINT pt, int msg ) -{ - BOOL bDraw = TRUE; - ULONG uCol, uRow; - - uCol = (pt.x - pBoard->MinesRect.left) / MINE_WIDTH + 1; - uRow = (pt.y - pBoard->MinesRect.top ) / MINE_HEIGHT + 1; - - switch (msg) - { - case WM_LBUTTONDOWN: - if( pBoard->Press.x != uCol || pBoard->Press.y != uRow ) - { - UnpressBox( pBoard, pBoard->Press.x, pBoard->Press.y ); - pBoard->Press.x = uCol; - pBoard->Press.y = uRow; - PressBox( pBoard, uCol, uRow ); - } - - bDraw = FALSE; - break; - - case WM_LBUTTONUP: - if( pBoard->Press.x != uCol || pBoard->Press.y != uRow ) - UnpressBox( pBoard, pBoard->Press.x, pBoard->Press.y ); - - pBoard->Press.x = 0; - pBoard->Press.y = 0; - - if( pBoard->Box[uCol][uRow].uFlagType != FLAG ) - pBoard->Status = PLAYING; - - CompleteBox( pBoard, uCol, uRow ); - break; - - case WM_MBUTTONDOWN: - PressBoxes( pBoard, uCol, uRow ); - bDraw = FALSE; - break; - - case WM_MBUTTONUP: - if( pBoard->Press.x != uCol || pBoard->Press.y != uRow ) - UnpressBoxes( pBoard, pBoard->Press.x, pBoard->Press.y ); - - pBoard->Press.x = 0; - pBoard->Press.y = 0; - CompleteBoxes( pBoard, uCol, uRow ); - break; - - case WM_RBUTTONDOWN: - AddFlag( pBoard, uCol, uRow ); - pBoard->Status = PLAYING; - break; - - default: - DEBUG("Unknown message type received in TestMines\n"); - break; - } - - if(bDraw) - RedrawWindow( pBoard->hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW ); -} - -void TestFace( BOARD *pBoard, POINT pt, int msg ) -{ - if( pBoard->Status == PLAYING || pBoard->Status == WAITING ) - { - if( msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN ) - pBoard->FaceBmp = OOH_BMP; - else pBoard->FaceBmp = SMILE_BMP; - } - else if( pBoard->Status == GAMEOVER ) - pBoard->FaceBmp = DEAD_BMP; - else if( pBoard->Status == WON ) - pBoard->FaceBmp = COOL_BMP; - - if( PtInRect( &pBoard->FaceRect, pt ) ) - { - if( msg == WM_LBUTTONDOWN ) - pBoard->FaceBmp = SPRESS_BMP; - - if( msg == WM_LBUTTONUP ) - CreateBoard( pBoard ); - } - - RedrawWindow( pBoard->hWnd, &pBoard->FaceRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW ); -} - -void CompleteBox( BOARD *pBoard, ULONG uCol, ULONG uRow ) -{ - CHAR i, j; - - if( pBoard->Box[uCol][uRow].uFlagType != COMPLETE && - pBoard->Box[uCol][uRow].uFlagType != FLAG && - uCol > 0 && uCol < pBoard->uCols + 1 && - uRow > 0 && uRow < pBoard->uRows + 1 ) - { - pBoard->Box[uCol][uRow].uFlagType = COMPLETE; - - if( pBoard->Box[uCol][uRow].bIsMine ) - { - pBoard->FaceBmp = DEAD_BMP; - pBoard->Status = GAMEOVER; - } - else if( pBoard->Status != GAMEOVER ) - pBoard->uBoxesLeft--; - - if( pBoard->Box[uCol][uRow].uNumMines == 0 ) - { - for( i = -1; i <= 1; i++ ) - for( j = -1; j <= 1; j++ ) - CompleteBox( pBoard, uCol + i, uRow + j ); - } - } -} - -void CompleteBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow ) -{ - CHAR i, j; - ULONG uNumFlags = 0; - - if( pBoard->Box[uCol][uRow].uFlagType == COMPLETE ) - { - for( i = -1; i <= 1; i++ ) - { - for( j = -1; j <= 1; j++ ) - { - if( pBoard->Box[uCol + i][uRow + j].uFlagType == FLAG ) - uNumFlags++; - } - } - - if( uNumFlags == pBoard->Box[uCol][uRow].uNumMines ) - { - for( i = -1; i <= 1; i++ ) - { - for( j = -1; j <= 1; j++ ) - { - if( pBoard->Box[uCol + i][uRow + j].uFlagType != FLAG ) - CompleteBox( pBoard, uCol + i, uRow + j ); - } - } - } - } -} - -void AddFlag( BOARD *pBoard, ULONG uCol, ULONG uRow ) -{ - if( pBoard->Box[uCol][uRow].uFlagType != COMPLETE ) - { - switch( pBoard->Box[uCol][uRow].uFlagType ) - { - case FLAG: - if( pBoard->bMark ) - pBoard->Box[uCol][uRow].uFlagType = QUESTION; - else - pBoard->Box[uCol][uRow].uFlagType = NORMAL; - - pBoard->uNumFlags--; - break; - - case QUESTION: - pBoard->Box[uCol][uRow].uFlagType = NORMAL; - break; - - default: - pBoard->Box[uCol][uRow].uFlagType = FLAG; - pBoard->uNumFlags++; - } - } -} - -void PressBox( BOARD *pBoard, ULONG uCol, ULONG uRow ) -{ - HDC hDC; - HGDIOBJ hOldObj; - HDC hMemDC; - - hDC = GetDC( pBoard->hWnd ); - hMemDC = CreateCompatibleDC(hDC); - hOldObj = SelectObject (hMemDC, pBoard->hMinesBMP); - - DrawMine( hDC, hMemDC, pBoard, uCol, uRow, TRUE ); - - SelectObject( hMemDC, hOldObj ); - DeleteDC( hMemDC ); - ReleaseDC( pBoard->hWnd, hDC ); -} - -void PressBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow ) -{ - CHAR i, j; - - for( i = -1; i <= 1; i++ ) - { - for( j = -1; j <= 1; j++ ) - { - pBoard->Box[uCol + i][uRow + j].bIsPressed = TRUE; - PressBox( pBoard, uCol + i, uRow + j ); - } - } - - for( i = -1; i <= 1; i++ ) - { - for( j = -1; j <= 1; j++ ) - { - if( !pBoard->Box[pBoard->Press.x + i][pBoard->Press.y + j].bIsPressed ) - UnpressBox( pBoard, pBoard->Press.x + i, pBoard->Press.y + j ); - } - } - - for( i = -1; i <= 1; i++ ) - { - for( j = -1; j <= 1; j++ ) - { - pBoard->Box[uCol + i][uRow + j].bIsPressed = FALSE; - PressBox( pBoard, uCol + i, uRow + j ); - } - } - - pBoard->Press.x = uCol; - pBoard->Press.y = uRow; -} - -void UnpressBox( BOARD *pBoard, ULONG uCol, ULONG uRow ) -{ - HDC hDC; - HGDIOBJ hOldObj; - HDC hMemDC; - - hDC = GetDC( pBoard->hWnd ); - hMemDC = CreateCompatibleDC( hDC ); - hOldObj = SelectObject( hMemDC, pBoard->hMinesBMP ); - - DrawMine( hDC, hMemDC, pBoard, uCol, uRow, FALSE ); - - SelectObject( hMemDC, hOldObj ); - DeleteDC( hMemDC ); - ReleaseDC( pBoard->hWnd, hDC ); -} - -void UnpressBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow ) -{ - CHAR i, j; - - for( i = -1; i <= 1; i++ ) - { - for( j = -1; j <= 1; j++ ) - { - UnpressBox( pBoard, uCol + i, uRow + j ); - } - } -} diff --git a/base/applications/games/winemine/main.h b/base/applications/games/winemine/main.h deleted file mode 100644 index 7ea91efbff3..00000000000 --- a/base/applications/games/winemine/main.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2000 Joshua Thielen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -static const TCHAR szWineMineRegKey[] = TEXT("Software\\Microsoft\\WinMine"); - -// Common Controls 6.0 for MSVC 2005 or later -#if _MSC_VER >= 1400 -# pragma comment(linker, "/manifestdependency:\"type='win32' " \ - "name='Microsoft.Windows.Common-Controls' " \ - "version='6.0.0.0' " \ - "processorArchitecture='x86' " \ - "publicKeyToken='6595b64144ccf1df' " \ - "language='*'\"") -#endif - -#define ID_TIMER 1000 - -#define BEGINNER_MINES 10 -#define BEGINNER_COLS 9 -#define BEGINNER_ROWS 9 - -#define ADVANCED_MINES 40 -#define ADVANCED_COLS 16 -#define ADVANCED_ROWS 16 - -#define EXPERT_MINES 99 -#define EXPERT_COLS 30 -#define EXPERT_ROWS 16 - -#define MAX_COLS 30 -#define MAX_ROWS 24 - -#define BOTTOM_MARGIN 20 -#define BOARD_WMARGIN 5 -#define BOARD_HMARGIN 5 - -/* mine defines */ -#define MINE_WIDTH 16 -#define MINE_HEIGHT 16 -#define LED_WIDTH 12 -#define LED_HEIGHT 23 -#define FACE_WIDTH 24 -#define FACE_HEIGHT 24 - -typedef enum { SPRESS_BMP, COOL_BMP, DEAD_BMP, OOH_BMP, SMILE_BMP } FACE_BMP; - -typedef enum { WAITING, PLAYING, GAMEOVER, WON } GAME_STATUS; - -typedef enum -{ - MPRESS_BMP, ONE_BMP, TWO_BMP, THREE_BMP, FOUR_BMP, FIVE_BMP, SIX_BMP, - SEVEN_BMP, EIGHT_BMP, BOX_BMP, FLAG_BMP, QUESTION_BMP, EXPLODE_BMP, - WRONG_BMP, MINE_BMP, QPRESS_BMP -} MINEBMP_OFFSET; - -typedef enum { BEGINNER, ADVANCED, EXPERT, CUSTOM } DIFFICULTY; - -typedef struct tagBOARD -{ - BOOL bMark; - HINSTANCE hInst; - HWND hWnd; - HBITMAP hMinesBMP; - HBITMAP hFacesBMP; - HBITMAP hLedsBMP; - RECT MinesRect; - RECT FaceRect; - RECT TimerRect; - RECT CounterRect; - - ULONG uWidth; - ULONG uHeight; - POINT Pos; - - ULONG uTime; - ULONG uNumFlags; - ULONG uBoxesLeft; - ULONG uNumMines; - - ULONG uRows; - ULONG uCols; - ULONG uMines; - TCHAR szBestName[3][16]; - ULONG uBestTime[3]; - DIFFICULTY Difficulty; - - POINT Press; - - FACE_BMP FaceBmp; - GAME_STATUS Status; - - struct BOX_STRUCT - { - UINT bIsMine : 1; - UINT bIsPressed : 1; - UINT uFlagType : 2; - UINT uNumMines : 4; - } Box [MAX_COLS + 2] [MAX_ROWS + 2]; - - /* defines for uFlagType */ - #define NORMAL 0 - #define QUESTION 1 - #define FLAG 2 - #define COMPLETE 3 - -} BOARD; - -void ExitApp( int error ); -void InitBoard( BOARD *pBoard ); -void LoadBoard( BOARD *pBoard ); -void SaveBoard( BOARD *pBoard ); -void DestroyBoard( BOARD *pBoard ); -void SetDifficulty( BOARD *pBoard, DIFFICULTY difficulty ); -void CheckLevel( BOARD *pBoard ); -void CreateBoard( BOARD *pBoard ); -void CreateBoxes( BOARD *pBoard ); -void TestBoard( HWND hWnd, BOARD *pBoard, LONG x, LONG y, int msg ); -void TestMines( BOARD *pBoard, POINT pt, int msg ); -void TestFace( BOARD *pBoard, POINT pt, int msg ); -void DrawBoard( HDC hdc, HDC hMemDC, PAINTSTRUCT *ps, BOARD *pBoard ); -void DrawMines( HDC hdc, HDC hMemDC, BOARD *pBoard ); -void DrawMine( HDC hdc, HDC hMemDC, BOARD *pBoard, ULONG uCol, ULONG uRow, BOOL IsPressed ); -void AddFlag( BOARD *pBoard, ULONG uCol, ULONG uRow ); -void CompleteBox( BOARD *pBoard, ULONG uCol, ULONG uRow ); -void CompleteBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow ); -void PressBox( BOARD *pBoard, ULONG uCol, ULONG uRow ); -void PressBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow ); -void UnpressBox( BOARD *pBoard, ULONG uCol, ULONG uRow ); -void UnpressBoxes( BOARD *pBoard, ULONG uCol, ULONG uRow ); -void UpdateTimer( BOARD *pBoard ); -void DrawLeds( HDC hdc, HDC hMemDC, BOARD *pBoard, LONG nNumber, LONG x, LONG y); -void DrawFace( HDC hdc, HDC hMemDC, BOARD *pBoard ); -LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK CustomDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); -INT_PTR CALLBACK CongratsDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); -INT_PTR CALLBACK TimesDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); - -/* end of header */ diff --git a/base/applications/games/winemine/rsrc.rc b/base/applications/games/winemine/rsrc.rc deleted file mode 100644 index 97e20879a2f..00000000000 --- a/base/applications/games/winemine/rsrc.rc +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include "resource.h" - -/* define language neutral resources */ -LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL - -IDA_WINEMINE ACCELERATORS DISCARDABLE -BEGIN - VK_F2, IDM_NEW, VIRTKEY, NOINVERT - "X", IDM_EXIT, VIRTKEY, ALT, NOINVERT -END - -IDI_WINEMINE ICON MOVEABLE "rc/winemine.ico" -IDB_FACES BITMAP MOVEABLE "rc/faces.bmp" -IDB_LEDS BITMAP MOVEABLE "rc/leds.bmp" -IDB_MINES BITMAP MOVEABLE "rc/mines.bmp" - -/* include localised resources */ -#include "lang/bg-BG.rc" -#include "lang/ca-ES.rc" -#include "lang/cs-CZ.rc" -#include "lang/de-DE.rc" -#include "lang/el-GR.rc" -#include "lang/en-US.rc" -#include "lang/es-ES.rc" -#include "lang/eu-ES.rc" -#include "lang/fr-FR.rc" -#include "lang/id-ID.rc" -#include "lang/it-IT.rc" -#include "lang/ja-JP.rc" -#include "lang/ko-KR.rc" -#include "lang/lt-LT.rc" -#include "lang/no-NO.rc" -#include "lang/nl-NL.rc" -#include "lang/pl-PL.rc" -#include "lang/ru-RU.rc" -#include "lang/sk-SK.rc" -#include "lang/th-TH.rc" -#include "lang/uk-UA.rc" -#include "lang/zh-CN.rc" diff --git a/base/applications/games/winmine/dialog.c b/base/applications/games/winmine/dialog.c new file mode 100644 index 00000000000..dc4ab349fd6 --- /dev/null +++ b/base/applications/games/winmine/dialog.c @@ -0,0 +1,116 @@ +/* + * WineMine (dialog.c) + * + * Copyright 2000 Joshua Thielen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define WIN32_LEAN_AND_MEAN + +#include +#include "main.h" +#include "resource.h" + +INT_PTR CALLBACK CustomDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + BOOL IsRet; + static BOARD *p_board; + + switch( uMsg ) { + case WM_INITDIALOG: + p_board = (BOARD*) lParam; + SetDlgItemInt( hDlg, IDC_EDITROWS, p_board->rows, FALSE ); + SetDlgItemInt( hDlg, IDC_EDITCOLS, p_board->cols, FALSE ); + SetDlgItemInt( hDlg, IDC_EDITMINES, p_board->mines, FALSE ); + return TRUE; + + case WM_COMMAND: + switch( LOWORD( wParam ) ) { + case IDOK: + p_board->rows = GetDlgItemInt( hDlg, IDC_EDITROWS, &IsRet, FALSE ); + p_board->cols = GetDlgItemInt( hDlg, IDC_EDITCOLS, &IsRet, FALSE ); + p_board->mines = GetDlgItemInt( hDlg, IDC_EDITMINES, &IsRet, FALSE ); + CheckLevel( p_board ); + EndDialog( hDlg, 0 ); + return TRUE; + + case IDCANCEL: + EndDialog( hDlg, 1 ); + return TRUE; + } + break; + } + return FALSE; +} + +INT_PTR CALLBACK CongratsDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + static BOARD *p_board; + + switch( uMsg ) { + case WM_INITDIALOG: + p_board = (BOARD*) lParam; + SetDlgItemText( hDlg, IDC_EDITNAME, + p_board->best_name[p_board->difficulty] ); + return TRUE; + + case WM_COMMAND: + switch( LOWORD( wParam ) ) { + case IDOK: + GetDlgItemText( hDlg, IDC_EDITNAME, + p_board->best_name[p_board->difficulty], + sizeof( p_board->best_name[p_board->difficulty] ) ); + EndDialog( hDlg, 0 ); + return TRUE; + + case IDCANCEL: + EndDialog( hDlg, 0 ); + return TRUE; + } + break; + } + return FALSE; +} + +INT_PTR CALLBACK TimesDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + static BOARD *p_board; + unsigned i; + + switch( uMsg ) { + case WM_INITDIALOG: + p_board = (BOARD*) lParam; + + /* set best names */ + for( i = 0; i < 3; i++ ) + SetDlgItemText( hDlg, (IDC_NAME1) + i, p_board->best_name[i] ); + + /* set best times */ + for( i = 0; i < 3; i++ ) + SetDlgItemInt( hDlg, (IDC_TIME1) + i, p_board->best_time[i], FALSE ); + return TRUE; + + case WM_COMMAND: + switch( LOWORD( wParam ) ) { + case IDOK: + case IDCANCEL: + EndDialog( hDlg, 0 ); + return TRUE; + } + break; + } + return FALSE; +} diff --git a/base/applications/games/winmine/lang/cs-CZ.rc b/base/applications/games/winmine/lang/cs-CZ.rc new file mode 100644 index 00000000000..5e17ea274cb --- /dev/null +++ b/base/applications/games/winmine/lang/cs-CZ.rc @@ -0,0 +1,98 @@ +/* Hey, Emacs, open this file with -*- coding: cp1250 -*- + * + * WineMine + * Czech Language Support + * + * Copyright 2000 Joshua Thielen + * Copyright 2003 Marcelo Duarte + * Copyright 2004 David Kredba + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_CZECH, SUBLANG_DEFAULT + +/* Czech strings in CP1250 */ + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Nikdo" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "N&astaven" BEGIN + MENUITEM "&Nov畚tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Ozna鑰vat nerozhodnut", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Za鞦te鈩k", IDM_BEGINNER + MENUITEM "&Pokro鑛l", IDM_ADVANCED + MENUITEM "&Expert", IDM_EXPERT + MENUITEM "&Dle libosti", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&Konec\tAlt+K", IDM_EXIT + END + POPUP "&Informace" BEGIN + MENUITEM "Ne&jlep夬 鐶sy", IDM_TIMES + MENUITEM "&O aplikaci", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Nejlep夬 鐶sy" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Nejlep夬 鐶sy", -1, 10, 10, 140, 45 + LTEXT "Za鞦te鈩k", -1, 20, 20, 40, 8 + LTEXT "Pokro鑛l", -1, 20, 30, 40, 8 + LTEXT "Expert", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Gratulujeme !" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Prosm, zadejte sv jm駭o", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Vlastn hra" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "リ疆ky", -1, 5, 5, 30, 10 + LTEXT "Sloupce", -1, 5, 35, 30, 10 + LTEXT "Miny", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Storno", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/da-DK.rc b/base/applications/games/winmine/lang/da-DK.rc new file mode 100644 index 00000000000..2173a705029 --- /dev/null +++ b/base/applications/games/winmine/lang/da-DK.rc @@ -0,0 +1,93 @@ +/* + * WineMine + * Danish language support + * + * Copyright 2008 Jens Albretsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_DANISH, SUBLANG_DEFAULT + +STRINGTABLE BEGIN + IDS_APPNAME, "Minerydder" + IDS_NOBODY, "Ingen" + IDS_ABOUT, "Ophavsret 2000 tilhrer Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Indstillinger" BEGIN + MENUITEM "&Nyt spil\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Brug sprgsm虱stegn",IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "Ny&begynder", IDM_BEGINNER + MENUITEM "&Avanceret", IDM_ADVANCED + MENUITEM "&Ekspert", IDM_EXPERT + MENUITEM "B&rugerdefineret" IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "Afslut&t\tAlt+X", IDM_EXIT + END + POPUP "&Hj詬p" BEGIN + MENUITEM "&Bedste tider", IDM_TIMES + MENUITEM "&Om", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Bedste tider" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Bedste tider", -1, 10, 10, 140, 45 + LTEXT "Nybegynder", -1, 20, 20, 40, 8 + LTEXT "Avanceret", -1, 20, 30, 40, 8 + LTEXT "Ekspert", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Tillykke!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Indtast dit navn", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Brugerdefineret spil" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "R詭ker", -1, 5, 5, 30, 10 + LTEXT "Kolonner", -1, 5, 35, 30, 10 + LTEXT "Miner", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Annuller", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/de-DE.rc b/base/applications/games/winmine/lang/de-DE.rc new file mode 100644 index 00000000000..d1e25b1a65d --- /dev/null +++ b/base/applications/games/winmine/lang/de-DE.rc @@ -0,0 +1,95 @@ +/* + * WineMine + * German Language Support + * + * Copyright 2004 Henning Gerhardt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +#pragma code_page(65001) + +LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Niemand" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Optionen" BEGIN + MENUITEM "&Neu\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Merker", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Anfテ、nger", IDM_BEGINNER + MENUITEM "&Fortgeschrittene", IDM_ADVANCED + MENUITEM "&Experten", IDM_EXPERT + MENUITEM "Benutzer&definiert...", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&Beenden\tAlt+X", IDM_EXIT + END + POPUP "&Info" BEGIN + MENUITEM "&Beste Zeiten", IDM_TIMES + MENUITEM "&テ彙er", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Beste Zeiten" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Beste Zeiten", -1, 10, 10, 140, 45 + LTEXT "Anfテ、nger", -1, 20, 20, 40, 8 + LTEXT "Fortgeschrittene", -1, 20, 30, 40, 8 + LTEXT "Experten", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Herzlichen Glテシckwunsch!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Bitte geben Sie Ihren Namen ein", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Benutzerdefiniertes Spiel" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Reihen", -1, 5, 5, 30, 10 + LTEXT "Zeilen", -1, 5, 35, 30, 10 + LTEXT "Minen", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Abbrechen", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/en-US.rc b/base/applications/games/winmine/lang/en-US.rc new file mode 100644 index 00000000000..6536d32f38c --- /dev/null +++ b/base/applications/games/winmine/lang/en-US.rc @@ -0,0 +1,94 @@ +/* + * WineMine + * English Language Support + * + * Copyright 2000 Joshua Thielen + * Copyright 2003 Marcelo Duarte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Nobody" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Options" BEGIN + MENUITEM "&New\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Mark Question", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Beginner", IDM_BEGINNER + MENUITEM "&Advanced", IDM_ADVANCED + MENUITEM "&Expert", IDM_EXPERT + MENUITEM "&Custom...", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "E&xit\tAlt+X", IDM_EXIT + END + POPUP "&Info" BEGIN + MENUITEM "&Fastest Times...", IDM_TIMES + MENUITEM "&About", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Fastest Times" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Fastest Times", -1, 10, 10, 140, 45 + LTEXT "Beginner", -1, 20, 20, 40, 8 + LTEXT "Advanced", -1, 20, 30, 40, 8 + LTEXT "Expert", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Congratulations!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Please enter your name", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Custom Game" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Rows", -1, 5, 5, 30, 10 + LTEXT "Cols", -1, 5, 35, 30, 10 + LTEXT "Mines", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Cancel", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/es-ES.rc b/base/applications/games/winmine/lang/es-ES.rc new file mode 100644 index 00000000000..a6cca74a676 --- /dev/null +++ b/base/applications/games/winmine/lang/es-ES.rc @@ -0,0 +1,93 @@ +/* + * WineMine + * Spanish Language Support + * + * Copyright 2003 Jos Manuel Ferrer Ortiz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Nadie" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Opciones" BEGIN + MENUITEM "&Nuevo\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Interrogacin", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Principiante", IDM_BEGINNER + MENUITEM "&Avanzado", IDM_ADVANCED + MENUITEM "&Experto", IDM_EXPERT + MENUITEM "P&ersonalizado", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&Salir\tAlt+X", IDM_EXIT + END + POPUP "&Ayuda" BEGIN + MENUITEM "&Mejores tiempos", IDM_TIMES + MENUITEM "&Acerca de", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Mejores tiempos" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Mejores tiempos", -1, 10, 10, 140, 45 + LTEXT "Principiante", -1, 20, 20, 40, 8 + LTEXT "Avanzado", -1, 20, 30, 40, 8 + LTEXT "Experto", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "Aceptar", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "。Enhorabuena!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Por favor, introduzca su nombre", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "Aceptar", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Juego personalizado" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Filas", -1, 5, 5, 30, 10 + LTEXT "Columnas", -1, 5, 35, 30, 10 + LTEXT "Minas", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "Aceptar", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Cancelar", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/fi-FI.rc b/base/applications/games/winmine/lang/fi-FI.rc new file mode 100644 index 00000000000..c24cd172f75 --- /dev/null +++ b/base/applications/games/winmine/lang/fi-FI.rc @@ -0,0 +1,93 @@ +/* + * WineMine + * Finnish Language Support + * + * Copyright 2005 Kimmo Myllyvirta + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_FINNISH, SUBLANG_DEFAULT + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Ei kukaan" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Valinnat" BEGIN + MENUITEM "&Uusi\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Merkitse", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Aloittelija", IDM_BEGINNER + MENUITEM "&Kehittynyt", IDM_ADVANCED + MENUITEM "&Expertti", IDM_EXPERT + MENUITEM "Mukau&ta", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&Poistu\tAlt+X", IDM_EXIT + END + POPUP "&Info" BEGIN + MENUITEM "&Nopeimmat ajat", IDM_TIMES + MENUITEM "&Tietoja", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Nopeimmat Ajat" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Nopeimmat Ajat",-1, 10, 10, 140, 45 + LTEXT "Aloittelija", -1, 20, 20, 40, 8 + LTEXT "Kehittynyt", -1, 20, 30, 40, 8 + LTEXT "Expertti", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Onnittelut!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Anna nimesi", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Mukautettu Peli" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Rivej", -1, 5, 5, 30, 10 + LTEXT "Palstoja", -1, 5, 35, 30, 10 + LTEXT "Miinoja", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Peruuta", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/fr-FR.rc b/base/applications/games/winmine/lang/fr-FR.rc new file mode 100644 index 00000000000..4730642c92d --- /dev/null +++ b/base/applications/games/winmine/lang/fr-FR.rc @@ -0,0 +1,99 @@ +/* + * WineMine + * French Language Support + * + * Copyright 2000 Joshua Thielen + * Copyright 2003 Marcelo Duarte + * Copyright 2003 Vincent Bテゥron + * Copyright 2006 JonathanツErnst + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +/* UTF-8 */ +#pragma code_page(65001) + +LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL + +STRINGTABLE BEGIN + IDS_APPNAME, "Dテゥmineur de Wine" + IDS_NOBODY, "Anonyme" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Options" BEGIN + MENUITEM "&Nouveau\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Marquage des cases suspectes", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Dテゥbutant", IDM_BEGINNER + MENUITEM "&Avancテゥ", IDM_ADVANCED + MENUITEM "&Expert", IDM_EXPERT + MENUITEM "&Personnalisテゥ...", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&Quitter\tAlt+X", IDM_EXIT + END + POPUP "&Info" BEGIN + MENUITEM "Meilleurs &temps", IDM_TIMES + MENUITEM "テ &propos", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Meilleurs temps" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Meilleurs temps", -1, 10, 10, 140, 45 + LTEXT "Dテゥbutant", -1, 20, 20, 40, 8 + LTEXT "Avancテゥ", -1, 20, 30, 40, 8 + LTEXT "Expert", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Fテゥlicitationsツ!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Veuillez saisir votre nom :", -1, 25, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Grille personnalisテゥe" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Lignes", -1, 5, 5, 30, 10 + LTEXT "Colonnes", -1, 5, 35, 30, 10 + LTEXT "Mines", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 30, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 30, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 30, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 44, 30, 50, 15 + PUSHBUTTON "Annuler", IDCANCEL, 44, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/it-IT.rc b/base/applications/games/winmine/lang/it-IT.rc new file mode 100644 index 00000000000..9bf28fca948 --- /dev/null +++ b/base/applications/games/winmine/lang/it-IT.rc @@ -0,0 +1,98 @@ +/* + * WineMine + * Italian Language Support + * + * Copyright 2000 Joshua Thielen + * Copyright 2003 Marcelo Duarte + * Copyright 2003-2004 Ivan Leo Puoti + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +/* UTF-8 */ +#pragma code_page(65001) + +LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Nessuno" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Opzioni" BEGIN + MENUITEM "&Nuovo\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Contrassegna domanda", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Principiante", IDM_BEGINNER + MENUITEM "&Avanzato", IDM_ADVANCED + MENUITEM "&Esperto", IDM_EXPERT + MENUITEM "&Personalizza", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "E&sci\tAlt+X", IDM_EXIT + END + POPUP "&Informazioni" BEGIN + MENUITEM "&Tempi migliori", IDM_TIMES + MENUITEM "&Riguardo a WineMine", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Tempi migliori" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Tempi migliori", -1, 10, 10, 140, 45 + LTEXT "Principiante", -1, 20, 20, 40, 8 + LTEXT "Avanzato", -1, 20, 30, 40, 8 + LTEXT "Esperto", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Congratulazioni!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Inserisci il tuo nome", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Gioco personalizzato" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Righe", -1, 5, 5, 30, 10 + LTEXT "Colonne", -1, 5, 35, 30, 10 + LTEXT "Mine", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Annulla", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/ja-JP.rc b/base/applications/games/winmine/lang/ja-JP.rc new file mode 100644 index 00000000000..677d08fca96 --- /dev/null +++ b/base/applications/games/winmine/lang/ja-JP.rc @@ -0,0 +1,97 @@ +/* + * WineMine + * Japanese Language Support + * + * Copyright 2000 Joshua Thielen + * Copyright 2003 Marcelo Duarte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +/* UTF-8 */ +#pragma code_page(65001) + +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Nobody" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "繧ェ繝励す繝ァ繝ウ(&O)" BEGIN + MENUITEM "繧ケ繧ソ繝シ繝(&N)\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "?繝槭シ繧ッ繧剃スソ逕ィ(&M)", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "蛻晉エ(&B)", IDM_BEGINNER + MENUITEM "荳ュ邏(&A)", IDM_ADVANCED + MENUITEM "荳顔エ(&E)", IDM_EXPERT + MENUITEM "逶、髱「縺ョ螟画峩(&C)", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "邨ゆコ(&X)\tAlt+X", IDM_EXIT + END + POPUP "諠蝣ア(&I)" BEGIN + MENUITEM "繝上う繧ケ繧ウ繧「(&F)", IDM_TIMES + MENUITEM "WineMine 縺ォ縺、縺縺ヲ(&A)", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Fastest Times" +FONT 9, "MS UI Gothic" +BEGIN + GROUPBOX "繝上う繧ケ繧ウ繧「", -1, 10, 10, 140, 45 + LTEXT "蛻晉エ", -1, 20, 20, 40, 8 + LTEXT "荳ュ邏", -1, 20, 30, 40, 8 + LTEXT "荳顔エ", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "縺翫a縺ァ縺ィ縺!" +FONT 9, "MS UI Gothic" +BEGIN + LTEXT "蜷榊燕繧貞・蜉帙@縺セ縺励g縺", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "逶、髱「縺ョ螟画峩" +FONT 9, "MS UI Gothic" +BEGIN + LTEXT "Rows", -1, 5, 5, 30, 10 + LTEXT "Cols", -1, 5, 35, 30, 10 + LTEXT "Mines", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Cancel", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/ko-KR.rc b/base/applications/games/winmine/lang/ko-KR.rc new file mode 100644 index 00000000000..ade7c7a0a8c --- /dev/null +++ b/base/applications/games/winmine/lang/ko-KR.rc @@ -0,0 +1,95 @@ +/* + * WineMine + * Korean Language Support + * + * Copyright 2000 Joshua Thielen + * Copyright 2003 Marcelo Duarte + * Copyright 2005,2007 YunSong Hwang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT + +STRINGTABLE BEGIN + IDS_APPNAME, "Wineチキレテ」ア" + IDS_NOBODY, "セニケォーウ" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "ソノシヌ(&O)" BEGIN + MENUITEM "サ ーヤタモ(&N)\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "ケータスヌ・ ヌ・ステ(&M)", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "テハコクタレ(&B)", IDM_BEGINNER + MENUITEM "チ゚ア゙タレ(&A)", IDM_ADVANCED + MENUITEM "サア゙タレ(&E)", IDM_EXPERT + MENUITEM "サ鄙タレ チ、タヌ(&C)", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "ウェー。ア(&X)\tAlt+X", IDM_EXIT + END + POPUP "オオソクサ(&I)" BEGIN + MENUITEM "ー。タ コク・ ステー」(&F)", IDM_TIMES + MENUITEM "チ、コク(&A)", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "ー。タ コク・ ステー」" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "テヨエワ ステー」", -1, 10, 10, 140, 45 + LTEXT "テハコクタレ", -1, 20, 20, 40, 8 + LTEXT "チ゚ア゙タレ", -1, 20, 30, 40, 8 + LTEXT "タケョー。", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "ネョタホ", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "テ猊マヌユエマエル!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "エ鄂ナタヌ タフクァタサ タセチヨシシソ", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "ネョタホ", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "ーヤタモ サ鄙タレ チ、タヌ " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "ー。キホチル", -1, 5, 5, 30, 10 + LTEXT "サキホチル", -1, 5, 35, 30, 10 + LTEXT "チキレ", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "ネョタホ", IDOK, 40, 30, 50, 15 + PUSHBUTTON "テシメ", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/lt-LT.rc b/base/applications/games/winmine/lang/lt-LT.rc new file mode 100644 index 00000000000..b566d324250 --- /dev/null +++ b/base/applications/games/winmine/lang/lt-LT.rc @@ -0,0 +1,96 @@ +/* + * WineMine + * Lithuanian Language Support + * + * Copyright 2009 Aurimas Fiナ。eras + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +/* UTF-8 */ +#pragma code_page(65001) + +LANGUAGE LANG_LITHUANIAN, SUBLANG_NEUTRAL + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Niekas" + IDS_ABOUT, "Autoriaus teisト耀 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Parinktys" BEGIN + MENUITEM "&Naujas\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&ナスymト葉i klaustuku", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "P&radedantis", IDM_BEGINNER + MENUITEM "&Paナセengト冱", IDM_ADVANCED + MENUITEM "Ek&spertas", IDM_EXPERT + MENUITEM "Pasirin&ktas...", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "Iナ。&eiti\tAlt+X", IDM_EXIT + END + POPUP "&Informacija" BEGIN + MENUITEM "&Geriausi laikai", IDM_TIMES + MENUITEM "&Apie", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Geriausi laikai" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Geriausi laikai", -1, 10, 10, 140, 45 + LTEXT "Pradedantis", -1, 20, 20, 40, 8 + LTEXT "Paナセengト冱", -1, 20, 30, 40, 8 + LTEXT "Ekspertas", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "Gerai", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Sveikiname!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "トョveskite savo vardト", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "Gerai", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Pasirinktas ナセaidimas" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Eilutト耀", -1, 5, 5, 30, 10 + LTEXT "Stulpeliai", -1, 5, 35, 30, 10 + LTEXT "Minos", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "Gerai", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Atsisakyti", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/nl-NL.rc b/base/applications/games/winmine/lang/nl-NL.rc new file mode 100644 index 00000000000..eaee1ba2187 --- /dev/null +++ b/base/applications/games/winmine/lang/nl-NL.rc @@ -0,0 +1,92 @@ +/* + * WineMine (Dutch resources) + * + * Copyright 2003 Hans Leidekker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_DUTCH, SUBLANG_NEUTRAL + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Niemand" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Opties" BEGIN + MENUITEM "&Nieuw spel\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Markeer vraag", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Beginner", IDM_BEGINNER + MENUITEM "&Gevorderde", IDM_ADVANCED + MENUITEM "&Expert", IDM_EXPERT + MENUITEM "Aange&past spel", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&Afsluiten\tAlt+X", IDM_EXIT + END + POPUP "&Info" BEGIN + MENUITEM "&Snelste tijden...", IDM_TIMES + MENUITEM "&Over WineMine", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Snelste tijden" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Snelste tijden", -1, 10, 10, 140, 45 + LTEXT "Beginner", -1, 20, 20, 40, 8 + LTEXT "Gevorderde", -1, 20, 30, 40, 8 + LTEXT "Expert", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Gefeliciteerd!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Voer uw naam in", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Aangepast spel" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Rijen", -1, 5, 5, 30, 10 + LTEXT "Kolommen", -1, 5, 35, 30, 10 + LTEXT "Mijnen", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Annuleren", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/no-NO.rc b/base/applications/games/winmine/lang/no-NO.rc new file mode 100644 index 00000000000..c03bf179a38 --- /dev/null +++ b/base/applications/games/winmine/lang/no-NO.rc @@ -0,0 +1,93 @@ +/* + * WineMine + * Norwegian Bokm虱 language support + * + * Copyright 2005 Alexander N. Srnes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL + +STRINGTABLE BEGIN + IDS_APPNAME, "Minesveiper" + IDS_NOBODY, "Ingen" + IDS_ABOUT, "Kopirett 2000 tilhrer Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Innstillinger" BEGIN + MENUITEM "&Ny\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Merk sprsm虱", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "Ny&begynner", IDM_BEGINNER + MENUITEM "&Avansert", IDM_ADVANCED + MENUITEM "&Ekspert", IDM_EXPERT + MENUITEM "E&gendefinert", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "Avslut&t\tAlt+X", IDM_EXIT + END + POPUP "&Hjelp" BEGIN + MENUITEM "&Beste tider", IDM_TIMES + MENUITEM "&Om", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Beste tider" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Beste tider", -1, 10, 10, 140, 45 + LTEXT "Nybegynner", -1, 20, 20, 40, 8 + LTEXT "Avansert", -1, 20, 30, 40, 8 + LTEXT "Ekspert", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Gratulerer!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Skriv inn navnet ditt", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Egendefinert spill" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Rader", -1, 5, 5, 30, 10 + LTEXT "Kolonner", -1, 5, 35, 30, 10 + LTEXT "Miner", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Avbryt", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/pl-PL.rc b/base/applications/games/winmine/lang/pl-PL.rc new file mode 100644 index 00000000000..ff60f15de67 --- /dev/null +++ b/base/applications/games/winmine/lang/pl-PL.rc @@ -0,0 +1,95 @@ +/* + * WineMine + * Polish Language Support + * + * Copyright 2000 Joshua Thielen + * Copyright 2003 Marcelo Duarte + * Copyright 2004 Jacek Caban + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_POLISH, SUBLANG_DEFAULT + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Anonim" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Opcje" BEGIN + MENUITEM "&Nowa\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Znaczniki", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Poczケtkujケcy", IDM_BEGINNER + MENUITEM "Z&aawansowany", IDM_ADVANCED + MENUITEM "&Ekspert", IDM_EXPERT + MENUITEM "&Uソytkownika", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "W&yj彡ie\tAlt+X", IDM_EXIT + END + POPUP "&Info" BEGIN + MENUITEM "&Najlepsze Wyniki", IDM_TIMES + MENUITEM "&O Programie", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 170, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Najlepsze Wyniki" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Najlepsze Wyniki", -1, 10, 10, 150, 45 + LTEXT "Poczケtkujケcy", -1, 20, 20, 50, 8 + LTEXT "Zaawansowany", -1, 20, 30, 50, 8 + LTEXT "Ekspert", -1, 20, 40, 50, 8 + LTEXT "999", IDC_TIME1, 80, 20, 15, 8 + LTEXT "999", IDC_TIME2, 80, 30, 15, 8 + LTEXT "999", IDC_TIME3, 80, 40, 15, 8 + LTEXT "", IDC_NAME1, 100, 20, 55, 8 + LTEXT "", IDC_NAME2, 100, 30, 55, 8 + LTEXT "", IDC_NAME3, 100, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 60, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Gratulacje!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Podaj swoje imi", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Ustawienia Uソytkownika" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Wiersze", -1, 5, 5, 30, 10 + LTEXT "Kolumny", -1, 5, 35, 30, 10 + LTEXT "Miny", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Anuluj", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/pt-PT.rc b/base/applications/games/winmine/lang/pt-PT.rc new file mode 100644 index 00000000000..e86e6ff2c0d --- /dev/null +++ b/base/applications/games/winmine/lang/pt-PT.rc @@ -0,0 +1,131 @@ +/* + * WineMine + * Portuguese Language Support + * + * Copyright 2003 Marcelo Duarte + * Copyright 2004 Amテゥrico Josテゥ Melo + * Copyright 2010 Gustavo Henrique Milarテゥ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +#pragma code_page(65001) + +LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Ninguテゥm" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Opテァテオes" BEGIN + MENUITEM "&Novo\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Marcas", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Principiante", IDM_BEGINNER + MENUITEM "&Intermediテ。rio", IDM_ADVANCED + MENUITEM "&Experiente", IDM_EXPERT + MENUITEM "Personali&zar...", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "Sai&r\tAlt+X", IDM_EXIT + END + POPUP "Aj&uda" BEGIN + MENUITEM "&Melhores tempos", IDM_TIMES + MENUITEM "&Sobre", IDM_ABOUT + END +END + +LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Ninguテゥm" + IDS_ABOUT, "Direitos de autor 2000, Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Opテァテオes" BEGIN + MENUITEM "&Novo\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Marcas", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Principiante", IDM_BEGINNER + MENUITEM "&Intermediテ。rio", IDM_ADVANCED + MENUITEM "&Experiente", IDM_EXPERT + MENUITEM "Personali&zar...", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "Sai&r\tAlt+X", IDM_EXIT + END + POPUP "Aj&uda" BEGIN + MENUITEM "&Melhores tempos", IDM_TIMES + MENUITEM "&Acerca", IDM_ABOUT + END +END + + +LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL + +DLG_TIMES DIALOGEX 0, 0, 170, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Melhores tempos" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Melhores tempos", -1, 10, 10, 150, 45 + LTEXT "Principiante", -1, 20, 20, 50, 8 + LTEXT "Intermediテ。rio", -1, 20, 30, 50, 8 + LTEXT "Experiente", -1, 20, 40, 50, 8 + LTEXT "999", IDC_TIME1, 80, 20, 15, 8 + LTEXT "999", IDC_TIME2, 80, 30, 15, 8 + LTEXT "999", IDC_TIME3, 80, 40, 15, 8 + LTEXT "", IDC_NAME1, 100, 20, 55, 8 + LTEXT "", IDC_NAME2, 100, 30, 55, 8 + LTEXT "", IDC_NAME3, 100, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 60, 60, 50, 15 +END + + +LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Parabテゥns!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Por favor, indique o seu nome", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Jogo personalizado" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Linhas", -1, 5, 5, 30, 10 + LTEXT "Colunas", -1, 5, 35, 30, 10 + LTEXT "Minas", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Cancelar", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/ro-RO.rc b/base/applications/games/winmine/lang/ro-RO.rc new file mode 100644 index 00000000000..d0e05cd19eb --- /dev/null +++ b/base/applications/games/winmine/lang/ro-RO.rc @@ -0,0 +1,95 @@ +/* + * WineMine + * Romanian Language Support + * + * Copyright 2007 Michael Stefaniuc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_ROMANIAN, SUBLANG_NEUTRAL + +#pragma code_page(65001) + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Nimeni" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Opナ」iuni" BEGIN + MENUITEM "&Nou\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Mark Question", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&テ始cepトフor", IDM_BEGINNER + MENUITEM "&Avansat", IDM_ADVANCED + MENUITEM "&Expert", IDM_EXPERT + MENUITEM "&Personalizat", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&Ieナ殃re\tAlt+X", IDM_EXIT + END + POPUP "&Informaナ」ii" BEGIN + MENUITEM "&Scoruri maxime", IDM_TIMES + MENUITEM "&Despre", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Scoruri maxime" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Scoruri maxime", -1, 10, 10, 140, 45 + LTEXT "&テ始cepトフor", -1, 20, 20, 40, 8 + LTEXT "Avansat", -1, 20, 30, 40, 8 + LTEXT "Expert", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Felicitトビi!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Scrieナ」i-vト numele", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Joc personalizat" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Linii", -1, 5, 5, 30, 10 + LTEXT "Coloane", -1, 5, 35, 30, 10 + LTEXT "Mine", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Renunナ」ト", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/ru-RU.rc b/base/applications/games/winmine/lang/ru-RU.rc new file mode 100644 index 00000000000..3c7905ac095 --- /dev/null +++ b/base/applications/games/winmine/lang/ru-RU.rc @@ -0,0 +1,96 @@ +/* + * WineMine + * Russian Language Support + * + * Copyright 2003 Pavel Roskin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +/* UTF-8 */ +#pragma code_page(65001) + +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Nobody" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&ミ侑ウムミー" BEGIN + MENUITEM "&ミ斷セミイミーム ミクミウムミー\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&ミ漬セミソムミセムミクムひオミサム糊スム巾オ ミキミスミーミコミク", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "ミ斷セミイミク&ムミセミコ", IDM_BEGINNER + MENUITEM "&ミ慴ームムひオム", IDM_ADVANCED + MENUITEM "&ミュミコムミソミオムム", IDM_EXPERT + MENUITEM "ミ斷オムムひーミスミエミームムひスム巾オ &ミソミームミーミシミオムびム", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&ミ厘ーミコムム錦び圭tAlt+X", IDM_EXIT + END + POPUP "&ミ。ミソムミーミイミコミー" BEGIN + MENUITEM "&ミ嶝τム威オミオ ミイムミオミシム", IDM_TIMES + MENUITEM "&ミ ミソムミセミウムミーミシミシミオ", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "ミ嶝τム威オミオ ミイムミオミシム" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "ミ嶝τム威オミオ ミイムミオミシム", -1, 10, 10, 140, 45 + LTEXT "ミ斷セミイミクムミセミコ", -1, 20, 20, 40, 8 + LTEXT "ミ慴ームムひオム", -1, 20, 30, 40, 8 + LTEXT "ミュミコムミソミオムム", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "ミ渙セミキミエムミーミイミサム肖!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "ミ渙セミカミーミサムσケムムひー, ミイミイミオミエミクムひオ ミイミーム威オ ミクミシム", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 140, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "ミ斷オムムひーミスミエミームムひスム巾オ ミソミームミーミシミオムびム" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "ミ渙セ ミイミオムムひクミコミーミサミク", -1, 5, 5, 70, 10 + LTEXT "ミ渙セ ミウミセムミクミキミセミスムひーミサミク", -1, 5, 35, 70, 10 + LTEXT "ミァミクムミサミセ ミシミクミス", -1, 5, 65, 70, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 80, 30, 50, 15 + PUSHBUTTON "ミ樮ひシミオミスミー", IDCANCEL, 80, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/sl-SI.rc b/base/applications/games/winmine/lang/sl-SI.rc new file mode 100644 index 00000000000..855d606f77d --- /dev/null +++ b/base/applications/games/winmine/lang/sl-SI.rc @@ -0,0 +1,95 @@ +/* + * WineMine + * Slovenian Language Support + * + * Copyright 2003, 2008 Rok Mandeljc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +#pragma code_page(65001) + +LANGUAGE LANG_SLOVENIAN, SUBLANG_DEFAULT + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Nihト稿" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Moナセnosti" BEGIN + MENUITEM "&Nova igra\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Vpraナ。aji", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Zaト稿tnik", IDM_BEGINNER + MENUITEM "&Napreden", IDM_ADVANCED + MENUITEM "&Strokovnjak", IDM_EXPERT + MENUITEM "&Poljubno", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "Iz&hod\tAlt+X", IDM_EXIT + END + POPUP "&Info" BEGIN + MENUITEM "&Najboljナ。i ト溝si", IDM_TIMES + MENUITEM "&O programu", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Najboljナ。i ト溝si" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Najboljナ。i ト溝si", -1, 10, 10, 140, 45 + LTEXT "Zaト稿tnik", -1, 20, 20, 40, 8 + LTEXT "Napreden", -1, 20, 30, 40, 8 + LTEXT "Strokovnjak", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "V redu", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "ト憩stitamo!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Vnesite svoje ime", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "V redu", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Poljubna igra" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Vrstice", -1, 5, 5, 30, 10 + LTEXT "Stolpci", -1, 5, 35, 30, 10 + LTEXT "Mine", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "V redu", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Prekliト絞", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/sv-SE.rc b/base/applications/games/winmine/lang/sv-SE.rc new file mode 100644 index 00000000000..75785a48e90 --- /dev/null +++ b/base/applications/games/winmine/lang/sv-SE.rc @@ -0,0 +1,98 @@ +/* + * WineMine + * Swedish Language Support + * + * Copyright 2000 Joshua Thielen + * Copyright 2003 Marcelo Duarte + * Copyright 2008 Daniel Nylander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +/* UTF-8 */ +#pragma code_page(65001) + +LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL + +STRINGTABLE BEGIN + IDS_APPNAME, "Minor" + IDS_NOBODY, "Ingen" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "A<ernativ" BEGIN + MENUITEM "&Nytt\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Markera frテ・ga", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "Ny&bテカrjare", IDM_BEGINNER + MENUITEM "&Avancerad", IDM_ADVANCED + MENUITEM "&Expert", IDM_EXPERT + MENUITEM "An&passad...", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "A&vsluta\tAlt+X", IDM_EXIT + END + POPUP "&Info" BEGIN + MENUITEM "&Snabbaste tider...", IDM_TIMES + MENUITEM "&Om", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Snabbaste tider" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Snabbaste tider", -1, 10, 10, 140, 45 + LTEXT "Nybテカrjare", -1, 20, 20, 40, 8 + LTEXT "Avancerad", -1, 20, 30, 40, 8 + LTEXT "Expert", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Gratulerar!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Ange ditt namn", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Anpassat spel" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Rader", -1, 5, 5, 30, 10 + LTEXT "Kolumner", -1, 5, 35, 35, 10 + LTEXT "Minor", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "Avbryt", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/tr-TR.rc b/base/applications/games/winmine/lang/tr-TR.rc new file mode 100644 index 00000000000..dc9849350ec --- /dev/null +++ b/base/applications/games/winmine/lang/tr-TR.rc @@ -0,0 +1,93 @@ +/* + * WineMine + * Turkish Language Support + * + * Copyright 2006 Fatih Ac + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_TURKISH, SUBLANG_DEFAULT + +STRINGTABLE BEGIN + IDS_APPNAME, "Wine Mayn Tarlas" + IDS_NOBODY, "Hi kimse" + IDS_ABOUT, "Telif hakk 2000, Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&Se軻nekler" BEGIN + MENUITEM "&Yeni\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&Soru ンareti", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "&Acemi", IDM_BEGINNER + MENUITEM "&Gelimi", IDM_ADVANCED + MENUITEM "&Uzman", IDM_EXPERT + MENUITEM "&ヨzel", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&ヌk\tAlt+X", IDM_EXIT + END + POPUP "&Bilgi" BEGIN + MENUITEM "&En Ksa Sreler", IDM_TIMES + MENUITEM "&Hakknda", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "En Ksa Sreler" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "En Ksa Sreler", -1, 10, 10, 140, 45 + LTEXT "Acemi", -1, 20, 20, 40, 8 + LTEXT "Gelimi", -1, 20, 30, 40, 8 + LTEXT "Uzman", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "Tamam", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "Tebrikler!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Ltfen adnz girin", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "Tamam", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "ヨzel Oyun" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Satrlar", -1, 5, 5, 30, 10 + LTEXT "Stunlar", -1, 5, 35, 30, 10 + LTEXT "Maynlar", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "Tamam", IDOK, 40, 30, 50, 15 + PUSHBUTTON "ンptal", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/uk-UA.rc b/base/applications/games/winmine/lang/uk-UA.rc new file mode 100644 index 00000000000..a43d6b83703 --- /dev/null +++ b/base/applications/games/winmine/lang/uk-UA.rc @@ -0,0 +1,99 @@ +/* +/* + * WineMine + * Ukrainian Language Support + * + * Copyright 2000 Joshua Thielen + * Copyright 2003 Marcelo Duarte + * Copyright 2010 Igor Paliychuk + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +/* UTF-8 */ +#pragma code_page(65001) + +LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT + +STRINGTABLE BEGIN + IDS_APPNAME, "WineMine" + IDS_NOBODY, "Nobody" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "&ミ酉ミー" BEGIN + MENUITEM "&ミ斷セミイミー\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "&ミ厘スミーミコミク ミソミクムひーミスミスム", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "ミ&ミセミイミームミセミコ", IDM_BEGINNER + MENUITEM "&ミ慴ーミケムムひオム", IDM_ADVANCED + MENUITEM "&ミ片コムミソミオムム", IDM_EXPERT + MENUITEM "&ミ。ミイミセム ミソミームミーミシミオムびミク...", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&ミ漬クムム孟エ\tAlt+X", IDM_EXIT + END + POPUP "&ミ頒セミイム孟エミコミー" BEGIN + MENUITEM "&ミ墫ミーム禍クミケ ムミーム...", IDM_TIMES + MENUITEM "&ミ湲ミセ ミソムミセミウムミーミシム", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "ミ墫ミーム禍クミケ ムミーム" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "ミ墫ミーム禍クミケ ムミーム", -1, 10, 10, 140, 45 + LTEXT "ミ斷セミイミームミセミコ", -1, 20, 20, 40, 8 + LTEXT "ミ慴ーミケムムひオム", -1, 20, 30, 40, 8 + LTEXT "ミ片コムミソミオムム", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "OK", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "ミ柘毛ひーミスミスム!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "ミ漬イミオミエム毛び ミイミーム威オ ム孟シ'ム", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "OK", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "ミ。ミイミセム ミソミームミーミシミオムびミク" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "ミム紹エミコム孟イ", -1, 5, 5, 30, 10 + LTEXT "ミ。ムひセミイミソムム孟イ", -1, 5, 35, 30, 10 + LTEXT "ミ慯孟ス", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "OK", IDOK, 40, 30, 50, 15 + PUSHBUTTON "ミ。ミコミームムσイミームひク", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/lang/zh-CN.rc b/base/applications/games/winmine/lang/zh-CN.rc new file mode 100644 index 00000000000..2b3a1ae7450 --- /dev/null +++ b/base/applications/games/winmine/lang/zh-CN.rc @@ -0,0 +1,167 @@ +/* + * WineMine (Simplified and Traditional Chinese Resource) + * + * Copyright 2008 Hongbo Ni + * Copyright 2010 Cheer Xiao + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +/* Chinese text is encoded in UTF-8 */ +#pragma code_page(65001) + +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED + +STRINGTABLE BEGIN + IDS_APPNAME, "Wine蝨ー髮キ" + IDS_NOBODY, "譌莠コ" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "騾蛾。ケ(&O)" BEGIN + MENUITEM "譁ー貂ク謌(&N)\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "蜈∬ョク髣ョ蜿キ譬隶ー(&M)", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "蛻晏ュヲ閠(&B)", IDM_BEGINNER + MENUITEM "鬮倡コァ(&A)", IDM_ADVANCED + MENUITEM "荳灘ョカ(&E)", IDM_EXPERT + MENUITEM "閾ェ螳壻ケ(&C)", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "騾蜃コ(&X)\tAlt+X", IDM_EXIT + END + POPUP "菫。諱ッ(&I)" BEGIN + MENUITEM "譛蠢ォ譌カ髣エ(&F)", IDM_TIMES + MENUITEM "蜈ウ莠(&A)", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "譛蠢ォ譌カ髣エ" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "譛蠢ォ譌カ髣エ", -1, 10, 10, 140, 45 + LTEXT "蛻晏ュヲ閠", -1, 20, 20, 40, 8 + LTEXT "鬮倡コァ", -1, 20, 30, 40, 8 + LTEXT "荳灘ョカ", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "遑ョ螳", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "逾晁エコシ" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "隸キ霎灘・菴逧蜷榊ュ", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "遑ョ螳", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "閾ェ螳壻ケ画クク謌" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "陦", -1, 5, 5, 30, 10 + LTEXT "蛻", -1, 5, 35, 30, 10 + LTEXT "蝨ー髮キ", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "遑ョ螳", IDOK, 40, 30, 50, 15 + PUSHBUTTON "蜿匁カ", IDCANCEL, 40, 50, 50, 15 +END + +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL + +STRINGTABLE BEGIN + IDS_APPNAME, "Wine蝨ー髮キ" + IDS_NOBODY, "辟。莠コ" + IDS_ABOUT, "Copyright 2000 Joshua Thielen" +END + +MENU_WINEMINE MENU +BEGIN + POPUP "驕ク鬆(&O)" BEGIN + MENUITEM "譁ー驕頑梓(&N)\tF2", IDM_NEW + MENUITEM SEPARATOR + MENUITEM "讓吝コ蝠城。(&M)", IDM_MARKQ + MENUITEM SEPARATOR + MENUITEM "蛻晏ュク閠(&B)", IDM_BEGINNER + MENUITEM "鬮倡エ(&A)", IDM_ADVANCED + MENUITEM "蟆亥ョカ(&E)", IDM_EXPERT + MENUITEM "閾ェ螳夂セゥ(&C)", IDM_CUSTOM + MENUITEM SEPARATOR + MENUITEM "邨先據(&X)\tAlt+X", IDM_EXIT + END + POPUP "雉險(&I)" BEGIN + MENUITEM "譛蠢ォ譎る俣(&F)", IDM_TIMES + MENUITEM "髣懈名(&A)", IDM_ABOUT + END +END + +DLG_TIMES DIALOGEX 0, 0, 160, 80 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "譛蠢ォ譎る俣" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "譛蠢ォ譎る俣", -1, 10, 10, 140, 45 + LTEXT "蛻晏ュク閠", -1, 20, 20, 40, 8 + LTEXT "鬮倡エ", -1, 20, 30, 40, 8 + LTEXT "蟆亥ョカ", -1, 20, 40, 40, 8 + LTEXT "999", IDC_TIME1, 70, 20, 15, 8 + LTEXT "999", IDC_TIME2, 70, 30, 15, 8 + LTEXT "999", IDC_TIME3, 70, 40, 15, 8 + LTEXT "", IDC_NAME1, 90, 20, 55, 8 + LTEXT "", IDC_NAME2, 90, 30, 55, 8 + LTEXT "", IDC_NAME3, 90, 40, 55, 8 + DEFPUSHBUTTON "遒コ螳", IDOK, 55, 60, 50, 15 +END + +DLG_CONGRATS DIALOGEX 0, 0, 160, 60 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "逾晁ウ!" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "隲玖シク蜈・菴逧蜷榊ュ", -1, 10, 10, 150, 10 + EDITTEXT IDC_EDITNAME, 25, 20, 110, 12 + DEFPUSHBUTTON "遒コ螳", IDOK, 60, 40, 40, 15 +END + +DLG_CUSTOM DIALOGEX 0, 0, 100, 100 +STYLE DS_MODALFRAME | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_SHELLFONT +CAPTION "閾ェ螳夂セゥ驕頑梓" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "陦", -1, 5, 5, 30, 10 + LTEXT "蛻", -1, 5, 35, 30, 10 + LTEXT "蝨ー髮キ", -1, 5, 65, 30, 10 + EDITTEXT IDC_EDITROWS, 5, 15, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITCOLS, 5, 45, 20, 12, ES_NUMBER + EDITTEXT IDC_EDITMINES, 5, 75, 20, 12, ES_NUMBER + DEFPUSHBUTTON "遒コ螳", IDOK, 40, 30, 50, 15 + PUSHBUTTON "蜿匁カ", IDCANCEL, 40, 50, 50, 15 +END diff --git a/base/applications/games/winmine/main.c b/base/applications/games/winmine/main.c new file mode 100644 index 00000000000..b000b04e9be --- /dev/null +++ b/base/applications/games/winmine/main.c @@ -0,0 +1,1053 @@ +/* + * WineMine (main.c) + * + * Copyright 2000 Joshua Thielen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include "main.h" +#include "resource.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(winemine); + +static const DWORD wnd_style = WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX; +static const char* registry_key = "Software\\Microsoft\\WinMine"; + + +void CheckLevel( BOARD *p_board ) +{ + if( p_board->rows < BEGINNER_ROWS ) + p_board->rows = BEGINNER_ROWS; + + if( p_board->rows > MAX_ROWS ) + p_board->rows = MAX_ROWS; + + if( p_board->cols < BEGINNER_COLS ) + p_board->cols = BEGINNER_COLS; + + if( p_board->cols > MAX_COLS ) + p_board->cols = MAX_COLS; + + if( p_board->mines < BEGINNER_MINES ) + p_board->mines = BEGINNER_MINES; + + if( p_board->mines > ( p_board->cols - 1 ) * ( p_board->rows - 1 ) ) + p_board->mines = ( p_board->cols - 1 ) * ( p_board->rows - 1 ); +} + +static void LoadBoard( BOARD *p_board ) +{ + DWORD size; + DWORD type; + HKEY hkey; + char data[MAX_PLAYER_NAME_SIZE+1]; + char key_name[8]; + unsigned i; + + RegOpenKeyEx( HKEY_CURRENT_USER, registry_key, + 0, KEY_QUERY_VALUE, &hkey ); + + size = sizeof( p_board->pos.x ); + if( !RegQueryValueEx( hkey, "Xpos", NULL, &type, + (LPBYTE) &p_board->pos.x, &size ) == ERROR_SUCCESS ) + p_board->pos.x = 0; + + size = sizeof( p_board->pos.y ); + if( !RegQueryValueEx( hkey, "Ypos", NULL, &type, + (LPBYTE) &p_board->pos.y, &size ) == ERROR_SUCCESS ) + p_board->pos.y = 0; + + size = sizeof( p_board->rows ); + if( !RegQueryValueEx( hkey, "Height", NULL, &type, + (LPBYTE) &p_board->rows, &size ) == ERROR_SUCCESS ) + p_board->rows = BEGINNER_ROWS; + + size = sizeof( p_board->cols ); + if( !RegQueryValueEx( hkey, "Width", NULL, &type, + (LPBYTE) &p_board->cols, &size ) == ERROR_SUCCESS ) + p_board->cols = BEGINNER_COLS; + + size = sizeof( p_board->mines ); + if( !RegQueryValueEx( hkey, "Mines", NULL, &type, + (LPBYTE) &p_board->mines, &size ) == ERROR_SUCCESS ) + p_board->mines = BEGINNER_MINES; + + size = sizeof( p_board->difficulty ); + if( !RegQueryValueEx( hkey, "Difficulty", NULL, &type, + (LPBYTE) &p_board->difficulty, &size ) == ERROR_SUCCESS ) + p_board->difficulty = BEGINNER; + + size = sizeof( p_board->IsMarkQ ); + if( !RegQueryValueEx( hkey, "Mark", NULL, &type, + (LPBYTE) &p_board->IsMarkQ, &size ) == ERROR_SUCCESS ) + p_board->IsMarkQ = TRUE; + + for( i = 0; i < 3; i++ ) { + wsprintf( key_name, "Name%d", i+1 ); + size = sizeof( data ); + if( RegQueryValueEx( hkey, key_name, NULL, &type, + (LPBYTE) data, &size ) == ERROR_SUCCESS ) + lstrcpynA( p_board->best_name[i], data, sizeof(p_board->best_name[i]) ); + else + LoadString( p_board->hInst, IDS_NOBODY, p_board->best_name[i], MAX_PLAYER_NAME_SIZE+1 ); + } + + for( i = 0; i < 3; i++ ) { + wsprintf( key_name, "Time%d", i+1 ); + size = sizeof( p_board->best_time[i] ); + if( !RegQueryValueEx( hkey, key_name, NULL, &type, + (LPBYTE) &p_board->best_time[i], &size ) == ERROR_SUCCESS ) + p_board->best_time[i] = 999; + } + RegCloseKey( hkey ); +} + +static void InitBoard( BOARD *p_board ) +{ + HMENU hMenu; + + p_board->hMinesBMP = LoadBitmap( p_board->hInst, "mines"); + p_board->hFacesBMP = LoadBitmap( p_board->hInst, "faces"); + p_board->hLedsBMP = LoadBitmap( p_board->hInst, "leds"); + + LoadBoard( p_board ); + + hMenu = GetMenu( p_board->hWnd ); + CheckMenuItem( hMenu, IDM_BEGINNER + (unsigned) p_board->difficulty, + MF_CHECKED ); + if( p_board->IsMarkQ ) + CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED ); + else + CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED ); + CheckLevel( p_board ); +} + +static void SaveBoard( BOARD *p_board ) +{ + HKEY hkey; + unsigned i; + char data[MAX_PLAYER_NAME_SIZE+1]; + char key_name[8]; + + if( RegCreateKeyEx( HKEY_CURRENT_USER, registry_key, + 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, + &hkey, NULL ) != ERROR_SUCCESS) + return; + + RegSetValueEx( hkey, "Xpos", 0, REG_DWORD, (LPBYTE) &p_board->pos.x, sizeof(p_board->pos.x) ); + RegSetValueEx( hkey, "Ypos", 0, REG_DWORD, (LPBYTE) &p_board->pos.y, sizeof(p_board->pos.y) ); + RegSetValueEx( hkey, "Difficulty", 0, REG_DWORD, (LPBYTE) &p_board->difficulty, sizeof(p_board->difficulty) ); + RegSetValueEx( hkey, "Height", 0, REG_DWORD, (LPBYTE) &p_board->rows, sizeof(p_board->rows) ); + RegSetValueEx( hkey, "Width", 0, REG_DWORD, (LPBYTE) &p_board->cols, sizeof(p_board->cols) ); + RegSetValueEx( hkey, "Mines", 0, REG_DWORD, (LPBYTE) &p_board->mines, sizeof(p_board->mines) ); + RegSetValueEx( hkey, "Mark", 0, REG_DWORD, (LPBYTE) &p_board->IsMarkQ, sizeof(p_board->IsMarkQ) ); + + for( i = 0; i < 3; i++ ) { + wsprintf( key_name, "Name%u", i+1 ); + lstrcpyn( data, p_board->best_name[i], sizeof( data ) ); + RegSetValueEx( hkey, key_name, 0, REG_SZ, (LPBYTE) data, strlen(data)+1 ); + } + + for( i = 0; i < 3; i++ ) { + wsprintf( key_name, "Time%u", i+1 ); + RegSetValueEx( hkey, key_name, 0, REG_DWORD, (LPBYTE) &p_board->best_time[i], sizeof(p_board->best_time[i]) ); + } + RegCloseKey( hkey ); +} + +static void DestroyBoard( BOARD *p_board ) +{ + DeleteObject( p_board->hFacesBMP ); + DeleteObject( p_board->hLedsBMP ); + DeleteObject( p_board->hMinesBMP ); +} + +static void SetDifficulty( BOARD *p_board, DIFFICULTY difficulty ) +{ + HMENU hMenu; + + if ( difficulty == CUSTOM ) + if (DialogBoxParam( p_board->hInst, "DLG_CUSTOM", p_board->hWnd, + CustomDlgProc, (LPARAM) p_board) != 0) + return; + + hMenu = GetMenu( p_board->hWnd ); + CheckMenuItem( hMenu, IDM_BEGINNER + p_board->difficulty, MF_UNCHECKED ); + p_board->difficulty = difficulty; + CheckMenuItem( hMenu, IDM_BEGINNER + difficulty, MF_CHECKED ); + + switch( difficulty ) { + case BEGINNER: + p_board->cols = BEGINNER_COLS; + p_board->rows = BEGINNER_ROWS; + p_board->mines = BEGINNER_MINES; + break; + + case ADVANCED: + p_board->cols = ADVANCED_COLS; + p_board->rows = ADVANCED_ROWS; + p_board->mines = ADVANCED_MINES; + break; + + case EXPERT: + p_board->cols = EXPERT_COLS; + p_board->rows = EXPERT_ROWS; + + p_board->mines = EXPERT_MINES; + break; + + case CUSTOM: + break; + } +} + +static void ShiftBetween(LONG* x, LONG* y, LONG a, LONG b) +{ + if (*x < a) { + *y += a - *x; + *x = a; + } + else if (*y > b) { + *x -= *y - b; + *y = b; + } +} + +static void MoveOnScreen(RECT* rect) +{ + HMONITOR hMonitor; + MONITORINFO mi; + + /* find the nearest monitor ... */ + hMonitor = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST); + + /* ... and move it into the work area (ie excluding task bar)*/ + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + + ShiftBetween(&rect->left, &rect->right, mi.rcWork.left, mi.rcWork.right); + ShiftBetween(&rect->top, &rect->bottom, mi.rcWork.top, mi.rcWork.bottom); +} + +static void CreateBoard( BOARD *p_board ) +{ + int left, top, bottom, right; + unsigned col, row; + RECT wnd_rect; + + p_board->mb = MB_NONE; + p_board->boxes_left = p_board->cols * p_board->rows - p_board->mines; + p_board->num_flags = 0; + + /* Create the boxes... + * We actually create them with an empty border, + * so special care doesn't have to be taken on the edges + */ + for( col = 0; col <= p_board->cols + 1; col++ ) + for( row = 0; row <= p_board->rows + 1; row++ ) { + p_board->box[col][row].IsPressed = FALSE; + p_board->box[col][row].IsMine = FALSE; + p_board->box[col][row].FlagType = NORMAL; + p_board->box[col][row].NumMines = 0; + } + + p_board->width = p_board->cols * MINE_WIDTH + BOARD_WMARGIN * 2; + + p_board->height = p_board->rows * MINE_HEIGHT + LED_HEIGHT + + BOARD_HMARGIN * 3; + + /* setting the mines rectangle boundary */ + left = BOARD_WMARGIN; + top = BOARD_HMARGIN * 2 + LED_HEIGHT; + right = left + p_board->cols * MINE_WIDTH; + bottom = top + p_board->rows * MINE_HEIGHT; + SetRect( &p_board->mines_rect, left, top, right, bottom ); + + /* setting the face rectangle boundary */ + left = p_board->width / 2 - FACE_WIDTH / 2; + top = BOARD_HMARGIN; + right = left + FACE_WIDTH; + bottom = top + FACE_HEIGHT; + SetRect( &p_board->face_rect, left, top, right, bottom ); + + /* setting the timer rectangle boundary */ + left = BOARD_WMARGIN; + top = BOARD_HMARGIN; + right = left + LED_WIDTH * 3; + bottom = top + LED_HEIGHT; + SetRect( &p_board->timer_rect, left, top, right, bottom ); + + /* setting the counter rectangle boundary */ + left = p_board->width - BOARD_WMARGIN - LED_WIDTH * 3; + top = BOARD_HMARGIN; + right = p_board->width - BOARD_WMARGIN; + bottom = top + LED_HEIGHT; + SetRect( &p_board->counter_rect, left, top, right, bottom ); + + p_board->status = WAITING; + p_board->face_bmp = SMILE_BMP; + p_board->time = 0; + + wnd_rect.left = p_board->pos.x; + wnd_rect.right = p_board->pos.x + p_board->width; + wnd_rect.top = p_board->pos.y; + wnd_rect.bottom = p_board->pos.y + p_board->height; + AdjustWindowRect(&wnd_rect, wnd_style, TRUE); + + /* Make sure the window is completely on the screen */ + MoveOnScreen(&wnd_rect); + MoveWindow( p_board->hWnd, wnd_rect.left, wnd_rect.top, + wnd_rect.right - wnd_rect.left, + wnd_rect.bottom - wnd_rect.top, + TRUE ); + RedrawWindow( p_board->hWnd, NULL, 0, + RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); +} + + +/* Randomly places mines everywhere except the selected box. */ +static void PlaceMines ( BOARD *p_board, int selected_col, int selected_row ) +{ + int i, j; + unsigned col, row; + + srand( (unsigned) time( NULL ) ); + + /* Temporarily place a mine at the selected box until all the other + * mines are placed, this avoids checking in the mine creation loop. */ + p_board->box[selected_col][selected_row].IsMine = TRUE; + + /* create mines */ + i = 0; + while( (unsigned) i < p_board->mines ) { + col = (int) (p_board->cols * (float) rand() / RAND_MAX + 1); + row = (int) (p_board->rows * (float) rand() / RAND_MAX + 1); + + if( !p_board->box[col][row].IsMine ) { + i++; + p_board->box[col][row].IsMine = TRUE; + } + } + + /* Remove temporarily placed mine for selected box */ + p_board->box[selected_col][selected_row].IsMine = FALSE; + + /* + * Now we label the remaining boxes with the + * number of mines surrounding them. + */ + for( col = 1; col < p_board->cols + 1; col++ ) + for( row = 1; row < p_board->rows + 1; row++ ) { + for( i = -1; i <= 1; i++ ) + for( j = -1; j <= 1; j++ ) { + if( p_board->box[col + i][row + j].IsMine ) { + p_board->box[col][row].NumMines++ ; + } + } + } +} + +static void DrawMine( HDC hdc, HDC hMemDC, BOARD *p_board, unsigned col, unsigned row, BOOL IsPressed ) +{ + MINEBMP_OFFSET offset = BOX_BMP; + + if( col == 0 || col > p_board->cols || row == 0 || row > p_board->rows ) + return; + + if( p_board->status == GAMEOVER ) { + if( p_board->box[col][row].IsMine ) { + switch( p_board->box[col][row].FlagType ) { + case FLAG: + offset = FLAG_BMP; + break; + case COMPLETE: + offset = EXPLODE_BMP; + break; + case QUESTION: + /* fall through */ + case NORMAL: + offset = MINE_BMP; + } + } else { + switch( p_board->box[col][row].FlagType ) { + case QUESTION: + offset = QUESTION_BMP; + break; + case FLAG: + offset = WRONG_BMP; + break; + case NORMAL: + offset = BOX_BMP; + break; + case COMPLETE: + /* Do nothing */ + break; + default: + WINE_TRACE("Unknown FlagType during game over in DrawMine\n"); + break; + } + } + } else { /* WAITING or PLAYING */ + switch( p_board->box[col][row].FlagType ) { + case QUESTION: + if( !IsPressed ) + offset = QUESTION_BMP; + else + offset = QPRESS_BMP; + break; + case FLAG: + offset = FLAG_BMP; + break; + case NORMAL: + if( !IsPressed ) + offset = BOX_BMP; + else + offset = MPRESS_BMP; + break; + case COMPLETE: + /* Do nothing */ + break; + default: + WINE_TRACE("Unknown FlagType while playing in DrawMine\n"); + break; + } + } + + if( p_board->box[col][row].FlagType == COMPLETE + && !p_board->box[col][row].IsMine ) + offset = (MINEBMP_OFFSET) p_board->box[col][row].NumMines; + + BitBlt( hdc, + (col - 1) * MINE_WIDTH + p_board->mines_rect.left, + (row - 1) * MINE_HEIGHT + p_board->mines_rect.top, + MINE_WIDTH, MINE_HEIGHT, + hMemDC, 0, offset * MINE_HEIGHT, SRCCOPY ); +} + +static void DrawMines ( HDC hdc, HDC hMemDC, BOARD *p_board ) +{ + HGDIOBJ hOldObj; + unsigned col, row; + hOldObj = SelectObject (hMemDC, p_board->hMinesBMP); + + for( row = 1; row <= p_board->rows; row++ ) { + for( col = 1; col <= p_board->cols; col++ ) { + DrawMine( hdc, hMemDC, p_board, col, row, FALSE ); + } + } + SelectObject( hMemDC, hOldObj ); +} + +static void DrawLeds( HDC hdc, HDC hMemDC, BOARD *p_board, int number, int x, int y ) +{ + HGDIOBJ hOldObj; + unsigned led[3], i; + int count; + + count = number; + if( count < 1000 ) { + if( count >= 0 ) { + led[0] = count / 100 ; + count -= led[0] * 100; + } + else { + led[0] = 10; /* negative sign */ + count = -count; + } + led[1] = count / 10; + count -= led[1] * 10; + led[2] = count; + } + else { + for( i = 0; i < 3; i++ ) + led[i] = 10; + } + + hOldObj = SelectObject (hMemDC, p_board->hLedsBMP); + + for( i = 0; i < 3; i++ ) { + BitBlt( hdc, + i * LED_WIDTH + x, + y, + LED_WIDTH, + LED_HEIGHT, + hMemDC, + 0, + led[i] * LED_HEIGHT, + SRCCOPY); + } + + SelectObject( hMemDC, hOldObj ); +} + + +static void DrawFace( HDC hdc, HDC hMemDC, BOARD *p_board ) +{ + HGDIOBJ hOldObj; + + hOldObj = SelectObject (hMemDC, p_board->hFacesBMP); + + BitBlt( hdc, + p_board->face_rect.left, + p_board->face_rect.top, + FACE_WIDTH, + FACE_HEIGHT, + hMemDC, 0, p_board->face_bmp * FACE_HEIGHT, SRCCOPY); + + SelectObject( hMemDC, hOldObj ); +} + + +static void DrawBoard( HDC hdc, HDC hMemDC, PAINTSTRUCT *ps, BOARD *p_board ) +{ + RECT tmp_rect; + + if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->counter_rect ) ) + DrawLeds( hdc, hMemDC, p_board, p_board->mines - p_board->num_flags, + p_board->counter_rect.left, + p_board->counter_rect.top ); + + if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->timer_rect ) ) + DrawLeds( hdc, hMemDC, p_board, p_board->time, + p_board->timer_rect.left, + p_board->timer_rect.top ); + + if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->face_rect ) ) + DrawFace( hdc, hMemDC, p_board ); + + if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->mines_rect ) ) + DrawMines( hdc, hMemDC, p_board ); +} + + +static void AddFlag( BOARD *p_board, unsigned col, unsigned row ) +{ + if( p_board->box[col][row].FlagType != COMPLETE ) { + switch( p_board->box[col][row].FlagType ) { + case FLAG: + if( p_board->IsMarkQ ) + p_board->box[col][row].FlagType = QUESTION; + else + p_board->box[col][row].FlagType = NORMAL; + p_board->num_flags--; + break; + + case QUESTION: + p_board->box[col][row].FlagType = NORMAL; + break; + + default: + p_board->box[col][row].FlagType = FLAG; + p_board->num_flags++; + } + } +} + + +static void UnpressBox( BOARD *p_board, unsigned col, unsigned row ) +{ + HDC hdc; + HGDIOBJ hOldObj; + HDC hMemDC; + + hdc = GetDC( p_board->hWnd ); + hMemDC = CreateCompatibleDC( hdc ); + hOldObj = SelectObject( hMemDC, p_board->hMinesBMP ); + + DrawMine( hdc, hMemDC, p_board, col, row, FALSE ); + + SelectObject( hMemDC, hOldObj ); + DeleteDC( hMemDC ); + ReleaseDC( p_board->hWnd, hdc ); +} + + +static void UnpressBoxes( BOARD *p_board, unsigned col, unsigned row ) +{ + int i, j; + + for( i = -1; i <= 1; i++ ) + for( j = -1; j <= 1; j++ ) { + UnpressBox( p_board, col + i, row + j ); + } +} + + +static void PressBox( BOARD *p_board, unsigned col, unsigned row ) +{ + HDC hdc; + HGDIOBJ hOldObj; + HDC hMemDC; + + hdc = GetDC( p_board->hWnd ); + hMemDC = CreateCompatibleDC( hdc ); + hOldObj = SelectObject (hMemDC, p_board->hMinesBMP); + + DrawMine( hdc, hMemDC, p_board, col, row, TRUE ); + + SelectObject( hMemDC, hOldObj ); + DeleteDC( hMemDC ); + ReleaseDC( p_board->hWnd, hdc ); +} + + +static void PressBoxes( BOARD *p_board, unsigned col, unsigned row ) +{ + int i, j; + + for( i = -1; i <= 1; i++ ) + for( j = -1; j <= 1; j++ ) { + p_board->box[col + i][row + j].IsPressed = TRUE; + PressBox( p_board, col + i, row + j ); + } + + for( i = -1; i <= 1; i++ ) + for( j = -1; j <= 1; j++ ) { + if( !p_board->box[p_board->press.x + i][p_board->press.y + j].IsPressed ) + UnpressBox( p_board, p_board->press.x + i, p_board->press.y + j ); + } + + for( i = -1; i <= 1; i++ ) + for( j = -1; j <= 1; j++ ) { + p_board->box[col + i][row + j].IsPressed = FALSE; + PressBox( p_board, col + i, row + j ); + } + + p_board->press.x = col; + p_board->press.y = row; +} + + +static void CompleteBox( BOARD *p_board, unsigned col, unsigned row ) +{ + int i, j; + + if( p_board->box[col][row].FlagType != COMPLETE && + p_board->box[col][row].FlagType != FLAG && + col > 0 && col < p_board->cols + 1 && + row > 0 && row < p_board->rows + 1 ) { + p_board->box[col][row].FlagType = COMPLETE; + + if( p_board->box[col][row].IsMine ) { + p_board->face_bmp = DEAD_BMP; + p_board->status = GAMEOVER; + } + else if( p_board->status != GAMEOVER ) + p_board->boxes_left--; + + if( p_board->box[col][row].NumMines == 0 ) + { + for( i = -1; i <= 1; i++ ) + for( j = -1; j <= 1; j++ ) + CompleteBox( p_board, col + i, row + j ); + } + } +} + + +static void CompleteBoxes( BOARD *p_board, unsigned col, unsigned row ) +{ + unsigned numFlags = 0; + int i, j; + + if( p_board->box[col][row].FlagType == COMPLETE ) { + for( i = -1; i <= 1; i++ ) + for( j = -1; j <= 1; j++ ) { + if( p_board->box[col+i][row+j].FlagType == FLAG ) + numFlags++; + } + + if( numFlags == p_board->box[col][row].NumMines ) { + for( i = -1; i <= 1; i++ ) + for( j = -1; j <= 1; j++ ) { + if( p_board->box[col+i][row+j].FlagType != FLAG ) + CompleteBox( p_board, col+i, row+j ); + } + } + } +} + + +static void TestMines( BOARD *p_board, POINT pt, int msg ) +{ + BOOL draw = TRUE; + int col, row; + + col = (pt.x - p_board->mines_rect.left) / MINE_WIDTH + 1; + row = (pt.y - p_board->mines_rect.top ) / MINE_HEIGHT + 1; + + switch ( msg ) { + case WM_LBUTTONDOWN: + if( p_board->press.x != col || p_board->press.y != row ) { + UnpressBox( p_board, + p_board->press.x, p_board->press.y ); + p_board->press.x = col; + p_board->press.y = row; + PressBox( p_board, col, row ); + } + draw = FALSE; + break; + + case WM_LBUTTONUP: + if( p_board->press.x != col || p_board->press.y != row ) + UnpressBox( p_board, + p_board->press.x, p_board->press.y ); + p_board->press.x = 0; + p_board->press.y = 0; + if( p_board->box[col][row].FlagType != FLAG + && p_board->status != PLAYING ) + { + p_board->status = PLAYING; + PlaceMines( p_board, col, row ); + } + CompleteBox( p_board, col, row ); + break; + + case WM_MBUTTONDOWN: + PressBoxes( p_board, col, row ); + draw = FALSE; + break; + + case WM_MBUTTONUP: + if( p_board->press.x != col || p_board->press.y != row ) + UnpressBoxes( p_board, + p_board->press.x, p_board->press.y ); + p_board->press.x = 0; + p_board->press.y = 0; + CompleteBoxes( p_board, col, row ); + break; + + case WM_RBUTTONDOWN: + AddFlag( p_board, col, row ); + break; + default: + WINE_TRACE("Unknown message type received in TestMines\n"); + break; + } + + if( draw ) + { + RedrawWindow( p_board->hWnd, NULL, 0, + RDW_INVALIDATE | RDW_UPDATENOW ); + } +} + + +static void TestFace( BOARD *p_board, POINT pt, int msg ) +{ + if( p_board->status == PLAYING || p_board->status == WAITING ) { + if( msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN ) + p_board->face_bmp = OOH_BMP; + else p_board->face_bmp = SMILE_BMP; + } + else if( p_board->status == GAMEOVER ) + p_board->face_bmp = DEAD_BMP; + else if( p_board->status == WON ) + p_board->face_bmp = COOL_BMP; + + if( PtInRect( &p_board->face_rect, pt ) ) { + if( msg == WM_LBUTTONDOWN ) + p_board->face_bmp = SPRESS_BMP; + + if( msg == WM_LBUTTONUP ) + CreateBoard( p_board ); + } + + RedrawWindow( p_board->hWnd, &p_board->face_rect, 0, + RDW_INVALIDATE | RDW_UPDATENOW ); +} + + +static void TestBoard( HWND hWnd, BOARD *p_board, int x, int y, int msg ) +{ + POINT pt; + unsigned col,row; + + pt.x = x; + pt.y = y; + + if( PtInRect( &p_board->mines_rect, pt ) && p_board->status != GAMEOVER + && p_board->status != WON ) + TestMines( p_board, pt, msg ); + else { + UnpressBoxes( p_board, + p_board->press.x, + p_board->press.y ); + p_board->press.x = 0; + p_board->press.y = 0; + } + + if( p_board->boxes_left == 0 ) { + p_board->status = WON; + + if (p_board->num_flags < p_board->mines) { + for( row = 1; row <= p_board->rows; row++ ) { + for( col = 1; col <= p_board->cols; col++ ) { + if (p_board->box[col][row].IsMine && p_board->box[col][row].FlagType != FLAG) + p_board->box[col][row].FlagType = FLAG; + } + } + + p_board->num_flags = p_board->mines; + + RedrawWindow( p_board->hWnd, NULL, 0, + RDW_INVALIDATE | RDW_UPDATENOW ); + } + + if( p_board->difficulty != CUSTOM && + p_board->time < p_board->best_time[p_board->difficulty] ) { + p_board->best_time[p_board->difficulty] = p_board->time; + + DialogBoxParam( p_board->hInst, "DLG_CONGRATS", hWnd, + CongratsDlgProc, (LPARAM) p_board); + + DialogBoxParam( p_board->hInst, "DLG_TIMES", hWnd, + TimesDlgProc, (LPARAM) p_board); + } + } + TestFace( p_board, pt, msg ); +} + + +static LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HDC hdc; + PAINTSTRUCT ps; + HMENU hMenu; + static BOARD board; + + switch( msg ) { + case WM_CREATE: + board.hInst = ((LPCREATESTRUCT) lParam)->hInstance; + board.hWnd = hWnd; + InitBoard( &board ); + CreateBoard( &board ); + return 0; + + case WM_PAINT: + { + HDC hMemDC; + + WINE_TRACE("WM_PAINT\n"); + hdc = BeginPaint( hWnd, &ps ); + hMemDC = CreateCompatibleDC( hdc ); + + DrawBoard( hdc, hMemDC, &ps, &board ); + + DeleteDC( hMemDC ); + EndPaint( hWnd, &ps ); + + return 0; + } + + case WM_MOVE: + WINE_TRACE("WM_MOVE\n"); + board.pos.x = (short)LOWORD(lParam); + board.pos.y = (short)HIWORD(lParam); + return 0; + + case WM_DESTROY: + SaveBoard( &board ); + DestroyBoard( &board ); + PostQuitMessage( 0 ); + return 0; + + case WM_TIMER: + if( board.status == PLAYING ) { + board.time++; + RedrawWindow( hWnd, &board.timer_rect, 0, + RDW_INVALIDATE | RDW_UPDATENOW ); + } + return 0; + + case WM_LBUTTONDOWN: + WINE_TRACE("WM_LBUTTONDOWN\n"); + if( wParam & MK_RBUTTON ) + msg = WM_MBUTTONDOWN; + TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg ); + SetCapture( hWnd ); + return 0; + + case WM_LBUTTONUP: + WINE_TRACE("WM_LBUTTONUP\n"); + if( wParam & MK_RBUTTON ) + msg = WM_MBUTTONUP; + TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg ); + ReleaseCapture(); + return 0; + + case WM_RBUTTONDOWN: + WINE_TRACE("WM_RBUTTONDOWN\n"); + if( wParam & MK_LBUTTON ) { + board.press.x = 0; + board.press.y = 0; + msg = WM_MBUTTONDOWN; + } + TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg ); + return 0; + + case WM_RBUTTONUP: + WINE_TRACE("WM_RBUTTONUP\n"); + if( wParam & MK_LBUTTON ) + msg = WM_MBUTTONUP; + TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg ); + return 0; + + case WM_MBUTTONDOWN: + WINE_TRACE("WM_MBUTTONDOWN\n"); + TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg ); + return 0; + + case WM_MBUTTONUP: + WINE_TRACE("WM_MBUTTONUP\n"); + TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg ); + return 0; + + case WM_MOUSEMOVE: + { + if( ( wParam & MK_MBUTTON ) || + ( ( wParam & MK_LBUTTON ) && ( wParam & MK_RBUTTON ) ) ) { + msg = WM_MBUTTONDOWN; + } + else if( wParam & MK_LBUTTON ) { + msg = WM_LBUTTONDOWN; + } + else { + return 0; + } + + TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg ); + + return 0; + } + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDM_NEW: + CreateBoard( &board ); + return 0; + + case IDM_MARKQ: + hMenu = GetMenu( hWnd ); + board.IsMarkQ = !board.IsMarkQ; + if( board.IsMarkQ ) + CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED ); + else + CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED ); + return 0; + + case IDM_BEGINNER: + SetDifficulty( &board, BEGINNER ); + CreateBoard( &board ); + return 0; + + case IDM_ADVANCED: + SetDifficulty( &board, ADVANCED ); + CreateBoard( &board ); + return 0; + + case IDM_EXPERT: + SetDifficulty( &board, EXPERT ); + CreateBoard( &board ); + return 0; + + case IDM_CUSTOM: + SetDifficulty( &board, CUSTOM ); + CreateBoard( &board ); + return 0; + + case IDM_EXIT: + SendMessage( hWnd, WM_CLOSE, 0, 0); + return 0; + + case IDM_TIMES: + DialogBoxParam( board.hInst, "DLG_TIMES", hWnd, + TimesDlgProc, (LPARAM) &board); + return 0; + + case IDM_ABOUT: + { + WCHAR appname[256], other[256]; + LoadStringW( board.hInst, IDS_APPNAME, appname, sizeof(appname)/sizeof(WCHAR) ); + LoadStringW( board.hInst, IDS_ABOUT, other, sizeof(other)/sizeof(WCHAR) ); + ShellAboutW( hWnd, appname, other, + LoadImageA( board.hInst, "WINEMINE", IMAGE_ICON, 48, 48, LR_SHARED )); + return 0; + } + default: + WINE_TRACE("Unknown WM_COMMAND command message received\n"); + break; + } + } + return( DefWindowProc( hWnd, msg, wParam, lParam )); +} + +int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow ) +{ + MSG msg; + WNDCLASSEX wc; + HWND hWnd; + HACCEL haccel; + char appname[20]; + + LoadString( hInst, IDS_APPNAME, appname, sizeof(appname)); + + wc.cbSize = sizeof(wc); + wc.style = 0; + wc.lpfnWndProc = MainProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, "WINEMINE" ); + wc.hCursor = LoadCursor( 0, IDI_APPLICATION ); + wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); //MOD for ROS + wc.lpszMenuName = "MENU_WINEMINE"; + wc.lpszClassName = appname; + wc.hIconSm = LoadImage( hInst, "WINEMINE", IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED ); + + if (!RegisterClassEx(&wc)) ExitProcess(1); + hWnd = CreateWindow( appname, appname, + wnd_style, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, hInst, NULL ); + + if (!hWnd) ExitProcess(1); + + ShowWindow( hWnd, cmdshow ); + UpdateWindow( hWnd ); + + haccel = LoadAccelerators( hInst, MAKEINTRESOURCE(IDA_WINEMINE) ); + SetTimer( hWnd, ID_TIMER, 1000, NULL ); + + while( GetMessage(&msg, 0, 0, 0) ) { + if (!TranslateAccelerator( hWnd, haccel, &msg )) + TranslateMessage( &msg ); + + DispatchMessage( &msg ); + } + return msg.wParam; +} diff --git a/base/applications/games/winmine/main.h b/base/applications/games/winmine/main.h new file mode 100644 index 00000000000..396f19e0eb7 --- /dev/null +++ b/base/applications/games/winmine/main.h @@ -0,0 +1,132 @@ +/* + * Copyright 2000 Joshua Thielen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#define BEGINNER_MINES 10 +#define BEGINNER_COLS 9 +#define BEGINNER_ROWS 9 + +#define ADVANCED_MINES 40 +#define ADVANCED_COLS 16 +#define ADVANCED_ROWS 16 + +#define EXPERT_MINES 99 +#define EXPERT_COLS 30 +#define EXPERT_ROWS 16 + +#define MAX_COLS 30 +#define MAX_ROWS 24 + +#define BOTTOM_MARGIN 20 +#define BOARD_WMARGIN 5 +#define BOARD_HMARGIN 5 + +/* mine defines */ +#define MINE_WIDTH 16 +#define MINE_HEIGHT 16 +#define LED_WIDTH 12 +#define LED_HEIGHT 23 +#define FACE_WIDTH 24 +#define FACE_HEIGHT 24 + +#define MAX_PLAYER_NAME_SIZE 31 + +typedef enum { SPRESS_BMP, COOL_BMP, DEAD_BMP, OOH_BMP, SMILE_BMP } FACE_BMP; + +typedef enum { WAITING, PLAYING, GAMEOVER, WON } GAME_STATUS; + +typedef enum { + MPRESS_BMP, ONE_BMP, TWO_BMP, THREE_BMP, FOUR_BMP, FIVE_BMP, SIX_BMP, + SEVEN_BMP, EIGHT_BMP, BOX_BMP, FLAG_BMP, QUESTION_BMP, EXPLODE_BMP, + WRONG_BMP, MINE_BMP, QPRESS_BMP +} MINEBMP_OFFSET; + +typedef enum { BEGINNER, ADVANCED, EXPERT, CUSTOM } DIFFICULTY; + +typedef struct tagBOARD +{ + BOOL IsMarkQ; + HDC hdc; + HINSTANCE hInst; + HWND hWnd; + HBITMAP hMinesBMP; + HBITMAP hFacesBMP; + HBITMAP hLedsBMP; + RECT mines_rect; + RECT face_rect; + RECT timer_rect; + RECT counter_rect; + + unsigned width; + unsigned height; + POINT pos; + + unsigned time; + unsigned num_flags; + unsigned boxes_left; + unsigned num_mines; + + /* difficulty info */ + unsigned rows; + unsigned cols; + unsigned mines; + char best_name [3][MAX_PLAYER_NAME_SIZE+1]; + DWORD best_time [3]; + DIFFICULTY difficulty; + + POINT press; + + /* defines for mb */ +#define MB_NONE 0 +#define MB_LEFTDOWN 1 +#define MB_LEFTUP 2 +#define MB_RIGHTDOWN 3 +#define MB_RIGHTUP 4 +#define MB_BOTHDOWN 5 +#define MB_BOTHUP 6 + unsigned mb; + + FACE_BMP face_bmp; + GAME_STATUS status; + struct BOX_STRUCT + { + unsigned IsMine : 1; + unsigned IsPressed : 1; + unsigned FlagType : 2; + unsigned NumMines : 4; + } box [MAX_COLS + 2] [MAX_ROWS + 2]; + + /* defines for FlagType */ +#define NORMAL 0 +#define QUESTION 1 +#define FLAG 2 +#define COMPLETE 3 + +} BOARD; + +void CheckLevel( BOARD *p_board ); + +INT_PTR CALLBACK CustomDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); + +INT_PTR CALLBACK CongratsDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); + +INT_PTR CALLBACK TimesDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); + + +/* end of header */ diff --git a/base/applications/games/winemine/rc/faces.bmp b/base/applications/games/winmine/rc/faces.bmp similarity index 100% rename from base/applications/games/winemine/rc/faces.bmp rename to base/applications/games/winmine/rc/faces.bmp diff --git a/base/applications/games/winemine/rc/leds.bmp b/base/applications/games/winmine/rc/leds.bmp similarity index 100% rename from base/applications/games/winemine/rc/leds.bmp rename to base/applications/games/winmine/rc/leds.bmp diff --git a/base/applications/games/winemine/rc/mines.bmp b/base/applications/games/winmine/rc/mines.bmp similarity index 100% rename from base/applications/games/winemine/rc/mines.bmp rename to base/applications/games/winmine/rc/mines.bmp diff --git a/base/applications/games/winemine/rc/winemine.ico b/base/applications/games/winmine/rc/winemine.ico similarity index 100% rename from base/applications/games/winemine/rc/winemine.ico rename to base/applications/games/winmine/rc/winemine.ico diff --git a/base/applications/games/winemine/resource.h b/base/applications/games/winmine/resource.h similarity index 73% rename from base/applications/games/winemine/resource.h rename to base/applications/games/winmine/resource.h index 651678884a6..c3272a29d7d 100644 --- a/base/applications/games/winemine/resource.h +++ b/base/applications/games/winmine/resource.h @@ -1,7 +1,7 @@ /* * WineMine (resource.h) * - * Copyright 2000 Joshua Thielen + * Copyright 2000 Joshua Thielen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,14 +15,15 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include -#define IDNONE -1 +#define ID_TIMER 1000 /* menu defines */ -#define IDM_WINEMINE 1000 #define IDM_NEW 1001 #define IDM_EXIT 1002 #define IDM_TIMES 1003 @@ -46,19 +47,8 @@ #define IDC_EDITROWS 1032 #define IDC_EDITMINES 1033 -#define IDS_SECONDS 1101 +#define IDS_APPNAME 1101 #define IDS_NOBODY 1102 #define IDS_ABOUT 1103 -#define IDI_WINEMINE 1201 -#define IDA_WINEMINE 1202 - -#define IDB_FACES 1301 -#define IDB_LEDS 1302 -#define IDB_MINES 1303 - -#define IDRESET 1401 - -#define IDD_CONGRATS 1501 -#define IDD_TIMES 1502 -#define IDD_CUSTOM 1503 +#define IDA_WINEMINE 1201 diff --git a/base/applications/games/winmine/rsrc.rc b/base/applications/games/winmine/rsrc.rc new file mode 100644 index 00000000000..ecdaa8ed17e --- /dev/null +++ b/base/applications/games/winmine/rsrc.rc @@ -0,0 +1,68 @@ +/* + * WineMine (rsrc.rc) + * + * Copyright 2000 Joshua Thielen + * Copyright 2003 Marcelo Duarte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resource.h" + +LANGUAGE LANG_NEUTRAL,SUBLANG_NEUTRAL + +IDA_WINEMINE ACCELERATORS +{ + VK_F2, IDM_NEW, VIRTKEY, NOINVERT + "X", IDM_EXIT, VIRTKEY, ALT, NOINVERT +} + +/* @makedep: winemine.ico */ +WINEMINE ICON rc/winemine.ico + +/* @makedep: faces.bmp */ +FACES BITMAP rc/faces.bmp + +/* @makedep: leds.bmp */ +LEDS BITMAP rc/leds.bmp + +/* @makedep: mines.bmp */ +MINES BITMAP rc/mines.bmp + +/* include localised resources */ +#include "lang/cs-CZ.rc" +#include "lang/da-DK.rc" +#include "lang/en-US.rc" +#include "lang/es-ES.rc" +#include "lang/fi-FI.rc" +#include "lang/ko-KR.rc" +#include "lang/nl-NL.rc" +#include "lang/no-NO.rc" +#include "lang/pl-PL.rc" +#include "lang/tr-TR.rc" + +/* UTF-8 */ +#include "lang/de-DE.rc" +#include "lang/fr-FR.rc" +#include "lang/it-IT.rc" +#include "lang/ja-JP.rc" +#include "lang/lt-LT.rc" +#include "lang/pt-PT.rc" +#include "lang/ro-RO.rc" +#include "lang/ru-RU.rc" +#include "lang/sl-SI.rc" +#include "lang/sv-SE.rc" +#include "lang/uk-UA.rc" +#include "lang/zh-CN.rc" diff --git a/base/applications/games/winemine/winemine.rbuild b/base/applications/games/winmine/winmine.rbuild similarity index 62% rename from base/applications/games/winemine/winemine.rbuild rename to base/applications/games/winmine/winmine.rbuild index 5f6a52a51af..97abb45666d 100644 --- a/base/applications/games/winemine/winemine.rbuild +++ b/base/applications/games/winmine/winmine.rbuild @@ -1,7 +1,8 @@ - - . + + . + wine gdi32 user32 advapi32 diff --git a/base/applications/paint/rsrc.rc b/base/applications/paint/rsrc.rc index a1e776d6720..75db8c8e108 100644 --- a/base/applications/paint/rsrc.rc +++ b/base/applications/paint/rsrc.rc @@ -37,14 +37,3 @@ #include "lang/ru-RU.rc" #include "lang/uk-UA.rc" -// -// SUPPORT FOR WINDOWS XP THEMES: -// THIS WILL MAKE THE PROGRAM USE THE COMMON CONTROLS -// LIBRARY VERSION 6.0 (IF IT IS AVAILABLE) -// -#ifdef _AMD64_ -1 24 "paint.exe.amd64.manifest" -#elif _X86_ -1 24 "paint.exe.manifest" -#endif - diff --git a/base/applications/winhlp32/Bg.rc b/base/applications/winhlp32/Bg.rc index ee9c07bbe71..191b5d0f78d 100644 --- a/base/applications/winhlp32/Bg.rc +++ b/base/applications/winhlp32/Bg.rc @@ -63,16 +63,13 @@ MAIN_MENU MENU MENUITEM "ツ竟璢 &胛", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&ネ璋...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&ホ WINE", MNID_HELP_WINE -#endif } } /* Strings */ STRINGTABLE { -STID_WINE_HELP, "WINE マ" +STID_WINE_HELP, "Wine マ" STID_WHERROR, "テミナリハタ" STID_WARNING, "マミナトモマミナニトナヘネナ" STID_INFO, "ネ璋" diff --git a/base/applications/winhlp32/Cs.rc b/base/applications/winhlp32/Cs.rc index 7b89d24dc3c..16fc70be2c1 100644 --- a/base/applications/winhlp32/Cs.rc +++ b/base/applications/winhlp32/Cs.rc @@ -68,16 +68,13 @@ MAIN_MENU MENU MENUITEM "V枦y na &vrchu", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Informace...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "O &WINE", MNID_HELP_WINE -#endif } } /* Strings */ STRINGTABLE { -STID_WINE_HELP, "N疳ovda WINE" +STID_WINE_HELP, "N疳ovda Wine" STID_WHERROR, "CHYBA" STID_WARNING, "VAROVチNヘ" STID_INFO, "Informace" diff --git a/base/applications/winhlp32/Da.rc b/base/applications/winhlp32/Da.rc index 4db0268522e..7b2b6d114c7 100644 --- a/base/applications/winhlp32/Da.rc +++ b/base/applications/winhlp32/Da.rc @@ -68,9 +68,6 @@ MAIN_MENU MENU MENUITEM "Altid &テクverst", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Information...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "Om &Wine", MNID_HELP_WINE -#endif } } diff --git a/base/applications/winhlp32/De.rc b/base/applications/winhlp32/De.rc index a5d2e3c8190..f2bb638dea8 100644 --- a/base/applications/winhlp32/De.rc +++ b/base/applications/winhlp32/De.rc @@ -66,9 +66,6 @@ MAIN_MENU MENU MENUITEM "Immer im &Vordergrund", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Info...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&テ彙er WINE", MNID_HELP_WINE -#endif } } @@ -92,7 +89,7 @@ CAPTION "Suche" STRINGTABLE { -STID_WINE_HELP, "WINE Hilfe" +STID_WINE_HELP, "Wine Hilfe" STID_WHERROR, "FEHLER" STID_WARNING, "ACHTUNG" STID_INFO, "Information" diff --git a/base/applications/winhlp32/En.rc b/base/applications/winhlp32/En.rc index 7526d1c71ed..6d987dbc7aa 100644 --- a/base/applications/winhlp32/En.rc +++ b/base/applications/winhlp32/En.rc @@ -63,10 +63,7 @@ MAIN_MENU MENU MENUITEM "Help &on help", MNID_HELP_HELPON MENUITEM "Always on &top", MNID_HELP_HELPTOP MENUITEM SEPARATOR - MENUITEM "&Info...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&About Wine", MNID_HELP_WINE -#endif + MENUITEM "&About Wine Help", MNID_HELP_ABOUT } } @@ -110,10 +107,7 @@ MAIN_MENU MENU MENUITEM "Help &on help", MNID_HELP_HELPON MENUITEM "Always on &top", MNID_HELP_HELPTOP MENUITEM SEPARATOR - MENUITEM "&Info...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&About Wine", MNID_HELP_WINE -#endif + MENUITEM "&About Wine Help", MNID_HELP_ABOUT } } diff --git a/base/applications/winhlp32/Es.rc b/base/applications/winhlp32/Es.rc index f39ddbde6b9..940585e653d 100644 --- a/base/applications/winhlp32/Es.rc +++ b/base/applications/winhlp32/Es.rc @@ -65,9 +65,6 @@ MAIN_MENU MENU MENUITEM "&Siempre visible", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Info...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&Acerca de WINE", MNID_HELP_WINE -#endif } } @@ -90,7 +87,7 @@ CAPTION "Buscar" /* Strings */ STRINGTABLE { -STID_WINE_HELP, "Ayuda de WINE" +STID_WINE_HELP, "Ayuda de Wine" STID_WHERROR, "ERROR" STID_WARNING, "ADVERTENCIA" STID_INFO, "Informacin" diff --git a/base/applications/winhlp32/Fi.rc b/base/applications/winhlp32/Fi.rc index bb6b7848153..eb5a95283bb 100644 --- a/base/applications/winhlp32/Fi.rc +++ b/base/applications/winhlp32/Fi.rc @@ -65,9 +65,6 @@ MAIN_MENU MENU MENUITEM "&Aina p蒿llimm臺sen", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "T&ietoja...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&Tietoja WINEst", MNID_HELP_WINE -#endif } } @@ -75,7 +72,7 @@ MAIN_MENU MENU STRINGTABLE { -STID_WINE_HELP, "WINE Ohje" +STID_WINE_HELP, "Wine Ohje" STID_WHERROR, "VIRHE" STID_WARNING, "VAROITUS" STID_INFO, "Tietoja" diff --git a/base/applications/winhlp32/Fr.rc b/base/applications/winhlp32/Fr.rc index 4178810588c..2b8ea8f6200 100644 --- a/base/applications/winhlp32/Fr.rc +++ b/base/applications/winhlp32/Fr.rc @@ -69,10 +69,7 @@ MAIN_MENU MENU MENUITEM "&Utiliser l'aide", MNID_HELP_HELPON MENUITEM "&Toujours visible", MNID_HELP_HELPTOP MENUITEM SEPARATOR - MENUITEM "&Info...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "テ &propos de Wine", MNID_HELP_WINE -#endif + MENUITEM "&A propos de l'Aide de Wine", MNID_HELP_ABOUT } } diff --git a/base/applications/winhlp32/He.rc b/base/applications/winhlp32/He.rc new file mode 100644 index 00000000000..2b9da4631a9 --- /dev/null +++ b/base/applications/winhlp32/He.rc @@ -0,0 +1,131 @@ +/* + * Help Viewer (Hebrew resources) + * + * Copyright 1996 Ulrich Schmid + * Copyright 2002 Sylvain Petreolle + * Copyright 2010 Yaron Shahrabani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "winhelp_res.h" + +#pragma code_page(65001) + +/* Menu */ + +LANGUAGE LANG_HEBREW, SUBLANG_DEFAULT + +MAIN_MENU MENU +{ + POPUP "&ラァラ勉泰・" { + MENUITEM "&ラ、ラェラ燮隣", MNID_FILE_OPEN + MENUITEM SEPARATOR + MENUITEM "ラ&ラ沌、ラ。ラ", MNID_FILE_PRINT + MENUITEM "ラ&ラ潰沌ィラェ ラ蕃槞沌、ラ。ラェ...", MNID_FILE_SETUP + MENUITEM SEPARATOR + MENUITEM "ラ&ラヲラ燮碩", MNID_FILE_EXIT + } + POPUP "ラ「&ラィラ燮嶼" { + MENUITEM "ラ&ラ「ラェラァラ...", MNID_EDIT_COPYDLG + MENUITEM SEPARATOR + MENUITEM "ラ蕃勉。ラ、ラェ ラ蕃「&ラィラ勉ェ...", MNID_EDIT_ANNOTATE + } + POPUP "&ラ。ラ燮槞ラ燮" { + MENUITEM "ラ&ラ潰沌ィラ...", MNID_BKMK_DEFINE + } + POPUP "&ラ碩、ラゥラィラ勉燮勉ェ" { + POPUP "ラ蕃「ラ儲ィラ ラ潰慵勉燮 ラェラ槞燮" + BEGIN + MENUITEM "ラ泰ィラィラェ ラ槞隣沌", MNID_OPTS_HELP_DEFAULT + MENUITEM "ラ潰慵勉燮", MNID_OPTS_HELP_VISIBLE + MENUITEM "ラ槞勉。ラェラィラェ", MNID_OPTS_HELP_NONVISIBLE + END + MENUITEM "ラ蕃燮。ラ俎勉ィラ燮", MNID_OPTS_HISTORY + POPUP "ラ潰勉、ララ燮" + BEGIN + MENUITEM "ラァラ俎ラ燮", MNID_OPTS_FONTS_SMALL + MENUITEM "ラィラ潰燮慵燮", MNID_OPTS_FONTS_NORMAL + MENUITEM "ラ潰沌勉慵燮", MNID_OPTS_FONTS_LARGE + END + MENUITEM "ラゥラ燮槞勉ゥ ラ泰ヲラ泰「ラ ラ蕃槞「ラィラ嶼ェ", MNID_OPTS_SYSTEM_COLORS + } + POPUP "ラ「&ラ儲ィラ" { + MENUITEM "ラ「ラ儲ィラ ラ「&ラ ラ蕃「ラ儲ィラ", MNID_HELP_HELPON + MENUITEM "ラェラ槞燮 &ラ「ラ慵燮勉", MNID_HELP_HELPTOP + MENUITEM SEPARATOR + MENUITEM "&ラ槞燮沌「...", MNID_HELP_ABOUT + } +} + +IDD_INDEX DIALOG 0, 0, 200, 190 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_LAYOUTRTL +FONT 8, "MS Shell Dlg" +CAPTION "ラ槞、ラェラ" +{ + LISTBOX IDC_INDEXLIST, 10, 10, 180, 150, LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_BORDER +} + +IDD_SEARCH DIALOG 0, 0, 200, 190 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_LAYOUTRTL +FONT 8, "MS Shell Dlg" +CAPTION "ラ隣燮、ラ勉ゥ" +{ + LTEXT "ラ慵 ラ槞勉俎槞「 ラ「ラ沌燮燮", -1, 10, 10, 180, 150 +} + +/* Strings */ +STRINGTABLE +{ +STID_WINE_HELP, "ラ蕃「ラ儲ィラ ラゥラ Wine" +STID_WHERROR, "ラゥラ潰燮碩" +STID_WARNING, "ラ碩儲蕃ィラ" +STID_INFO, "ラ槞燮沌「" +STID_NOT_IMPLEMENTED, "ラ慵 ラ槞勉俎槞「" +STID_HLPFILE_ERROR_s, "ラ碩燮ィラ「ラ ラゥラ潰燮碩 ラ泰「ラェ ラァラィラ燮碩ェ ラァラ勉泰・ ラ蕃「ラ儲ィラ `%s'" +STID_INDEX, "ラ&ラ、ラェラ" +STID_CONTENTS, "ラェラァラヲラ燮ィ" +STID_BACK, "&ラ隣儲ィラ" +STID_ALL_FILES, "ラ嶼 ラ蕃ァラ泰ヲラ燮 (*.*)" +STID_HELP_FILES_HLP, "ラァラ勉泰ヲラ ラ「ラ儲ィラ (*.hlp)" +STID_FILE_NOT_FOUND_s "ラ慵 ララ燮ェラ ラ慵槞ヲラ勉 ラ碩ェ '%s'. ラ蕃碩 ラ泰ィラヲラ勉ラ ラ慵槞ヲラ勉 ラァラ勉泰・ ラ儲 ラ泰「ラヲラ槞?" +STID_NO_RICHEDIT "ラ慵 ララ燮ェラ ラ慵槞ヲラ勉 ラ燮燮ゥラ勉 ラゥラ richedit... ラ蕃ェラ嶼ラ燮ェ ラェラヲラ" +STID_PSH_INDEX, "ララ勉ゥラ碩 ラ蕃「ラ儲ィラ: " +} + +CONTEXT_MENU MENU +BEGIN + POPUP "" + BEGIN + MENUITEM "ラ蕃「ラィラ勉ェ...", MNID_CTXT_ANNOTATE + MENUITEM "ラ蕃「ラェラァラ", MNID_CTXT_COPY + MENUITEM "ラ蕃沌、ラ。ラ...", MNID_CTXT_PRINT + POPUP "ラ潰勉、ララ燮" + BEGIN + MENUITEM "ラァラ俎ラ燮", MNID_CTXT_FONTS_SMALL + MENUITEM "ラィラ潰燮慵燮", MNID_CTXT_FONTS_NORMAL + MENUITEM "ラ潰沌勉慵燮", MNID_CTXT_FONTS_LARGE + END + POPUP "ラ蕃「ラ儲ィラ ラェラ槞燮 ラ儲槞燮ラ" + BEGIN + MENUITEM "ラ泰ィラィラェ ラ槞隣沌", MNID_CTXT_HELP_DEFAULT + MENUITEM "ラ潰慵勉燮", MNID_CTXT_HELP_VISIBLE + MENUITEM "ラ槞勉。ラェラィラェ", MNID_CTXT_HELP_NONVISIBLE + END + MENUITEM "ラゥラ燮槞勉ゥ ラ泰ヲラ泰「ラ ラ蕃槞「ラィラ嶼ェ", MNID_CTXT_SYSTEM_COLORS + END +END diff --git a/base/applications/winhlp32/Hu.rc b/base/applications/winhlp32/Hu.rc index 4fccbebccd2..7699677b1e2 100644 --- a/base/applications/winhlp32/Hu.rc +++ b/base/applications/winhlp32/Hu.rc @@ -65,9 +65,6 @@ MAIN_MENU MENU MENUITEM "Mindig &legfell", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Inform當i...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&&A WINE-rl", MNID_HELP_WINE -#endif } } @@ -75,7 +72,7 @@ MAIN_MENU MENU STRINGTABLE { -STID_WINE_HELP, "WINE sg" +STID_WINE_HELP, "Wine sg" STID_WHERROR, "HIBA" STID_WARNING, "FIGYELMEZTETノS" STID_INFO, "Inform當i" diff --git a/base/applications/winhlp32/It.rc b/base/applications/winhlp32/It.rc index b146b1009c1..f0dfd0c8573 100644 --- a/base/applications/winhlp32/It.rc +++ b/base/applications/winhlp32/It.rc @@ -21,10 +21,11 @@ #include "winhelp_res.h" -LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL - +/* UTF-8 */ #pragma code_page(65001) +LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL + /* Menu */ MAIN_MENU MENU @@ -55,20 +56,17 @@ MAIN_MENU MENU MENUITEM "Cronologia", MNID_OPTS_HISTORY POPUP "Caratteri" BEGIN - MENUITEM "Piccolo", MNID_OPTS_FONTS_SMALL - MENUITEM "Normale", MNID_OPTS_FONTS_NORMAL - MENUITEM "Grande", MNID_OPTS_FONTS_LARGE + MENUITEM "Piccoli", MNID_OPTS_FONTS_SMALL + MENUITEM "Normali", MNID_OPTS_FONTS_NORMAL + MENUITEM "Grandi", MNID_OPTS_FONTS_LARGE END - MENUITEM "Usa colori di sistema", MNID_OPTS_SYSTEM_COLORS + MENUITEM "Usa i colori di sistema", MNID_OPTS_SYSTEM_COLORS } POPUP "&Aiuto" { MENUITEM "&Aiuto sulla guida", MNID_HELP_HELPON MENUITEM "Sempre in primo &piano", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Informazioni su...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&Informazioni su WINE", MNID_HELP_WINE -#endif } } @@ -91,7 +89,7 @@ CAPTION "Cerca" /* Strings */ STRINGTABLE { -STID_WINE_HELP, "Guida di WINE" +STID_WINE_HELP, "Guida di Wine" STID_WHERROR, "ERRORE" STID_WARNING, "ATTENZIONE" STID_INFO, "Informazione" @@ -101,7 +99,7 @@ STID_INDEX, "Indice" STID_CONTENTS, "&Sommario" STID_BACK, "&Precedente" STID_ALL_FILES, "Tutti i file (*.*)" -STID_HELP_FILES_HLP, "File della Guida (*.hlp)" +STID_HELP_FILES_HLP, "File della guida (*.hlp)" STID_FILE_NOT_FOUND_s "Non ティ stato possibile trovare '%s'. Vuoi cercare questo file?" STID_NO_RICHEDIT "Non ティ stato possibile trovare un'implementazione richedit... Annullando" STID_PSH_INDEX, "Argomenti di aiuto: " @@ -116,13 +114,13 @@ BEGIN MENUITEM "Stampa...", MNID_CTXT_PRINT POPUP "Caratteri" BEGIN - MENUITEM "Piccolo", MNID_CTXT_FONTS_SMALL - MENUITEM "Normale", MNID_CTXT_FONTS_NORMAL - MENUITEM "Grande", MNID_CTXT_FONTS_LARGE + MENUITEM "Piccoli", MNID_CTXT_FONTS_SMALL + MENUITEM "Normali", MNID_CTXT_FONTS_NORMAL + MENUITEM "Grandi", MNID_CTXT_FONTS_LARGE END POPUP "Aiuto sempre visibile" BEGIN - MENUITEM "Default", MNID_CTXT_HELP_DEFAULT + MENUITEM "Predefinito", MNID_CTXT_HELP_DEFAULT MENUITEM "Visibile", MNID_CTXT_HELP_VISIBLE MENUITEM "Non visibile", MNID_CTXT_HELP_NONVISIBLE END diff --git a/base/applications/winhlp32/Ja.rc b/base/applications/winhlp32/Ja.rc index 8d3f3542575..a93e5a3b5e9 100644 --- a/base/applications/winhlp32/Ja.rc +++ b/base/applications/winhlp32/Ja.rc @@ -67,9 +67,6 @@ MAIN_MENU MENU MENUITEM "蟶ク縺ォ謇句燕縺ォ陦ィ遉コ(&T)", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "諠蝣ア(&I)...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "WINE 縺ォ縺、縺縺ヲ(&A)", MNID_HELP_WINE -#endif } } @@ -92,7 +89,7 @@ CAPTION "讀懃エ「" /* Strings */ STRINGTABLE { -STID_WINE_HELP, "WINE 繝倥Ν繝" +STID_WINE_HELP, "Wine 繝倥Ν繝" STID_WHERROR, "繧ィ繝ゥ繝シ" STID_WARNING, "隴ヲ蜻" STID_INFO, "諠蝣ア" diff --git a/base/applications/winhlp32/Ko.rc b/base/applications/winhlp32/Ko.rc index e4eff0ba0b3..32b32b21865 100644 --- a/base/applications/winhlp32/Ko.rc +++ b/base/applications/winhlp32/Ko.rc @@ -64,9 +64,6 @@ MAIN_MENU MENU MENUITEM "ヌラサ タァ(&T)", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "チ、コク(&I)...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "Wineソ。 ーヌマソゥ(&A)", MNID_HELP_WINE -#endif } } diff --git a/base/applications/winhlp32/Lt.rc b/base/applications/winhlp32/Lt.rc index b6ee9c792fb..195740c3e3c 100644 --- a/base/applications/winhlp32/Lt.rc +++ b/base/applications/winhlp32/Lt.rc @@ -66,9 +66,6 @@ MAIN_MENU MENU MENUITEM "&Visada virナ。uje", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Informacija...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&Apie Wine", MNID_HELP_WINE -#endif } } diff --git a/base/applications/winhlp32/Nl.rc b/base/applications/winhlp32/Nl.rc index 228667df4ba..08860098f3f 100644 --- a/base/applications/winhlp32/Nl.rc +++ b/base/applications/winhlp32/Nl.rc @@ -66,9 +66,6 @@ MAIN_MENU MENU MENUITEM "Altijd &zichtbaar", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Info...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&Over Wine", MNID_HELP_WINE -#endif } } diff --git a/base/applications/winhlp32/No.rc b/base/applications/winhlp32/No.rc index 141ad3a1b84..a18dbc4f19e 100644 --- a/base/applications/winhlp32/No.rc +++ b/base/applications/winhlp32/No.rc @@ -63,9 +63,6 @@ MAIN_MENU MENU MENUITEM "All&tid verst", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Informasjon...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&Om Wine", MNID_HELP_WINE -#endif } } diff --git a/base/applications/winhlp32/Pl.rc b/base/applications/winhlp32/Pl.rc index 2a561f2c0b8..fb2114d754b 100644 --- a/base/applications/winhlp32/Pl.rc +++ b/base/applications/winhlp32/Pl.rc @@ -66,9 +66,6 @@ MAIN_MENU MENU MENUITEM "&Zawsze na wierzchu", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&O programie...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&O programie WINE", MNID_HELP_WINE -#endif } } @@ -91,7 +88,7 @@ CAPTION "Szukaj" /* Strings */ STRINGTABLE { -STID_WINE_HELP, "WINE - Pomoc" +STID_WINE_HELP, "Wine - Pomoc" STID_WHERROR, "B」・D" STID_WARNING, "OSTRZEッENIE" STID_INFO, "Informacja" diff --git a/base/applications/winhlp32/Pt.rc b/base/applications/winhlp32/Pt.rc index 4b62a5fbb19..86216754128 100644 --- a/base/applications/winhlp32/Pt.rc +++ b/base/applications/winhlp32/Pt.rc @@ -72,9 +72,6 @@ MAIN_MENU MENU MENUITEM "Sempre &visテュvel", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Informaテァテオes...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&Sobre o WINE", MNID_HELP_WINE -#endif } } @@ -119,9 +116,6 @@ MAIN_MENU MENU MENUITEM "Sempre &visテュvel", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Informaテァテオes...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&Acerca do WINE", MNID_HELP_WINE -#endif } } @@ -149,7 +143,7 @@ LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN STRINGTABLE { -STID_WINE_HELP, "Ajuda WINE" +STID_WINE_HELP, "Ajuda Wine" STID_WHERROR, "ERRO" STID_WARNING, "AVISO" STID_INFO, "Informaテァテ」o" @@ -169,7 +163,7 @@ LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE STRINGTABLE { -STID_WINE_HELP, "Ajuda WINE" +STID_WINE_HELP, "Ajuda Wine" STID_WHERROR, "ERRO" STID_WARNING, "AVISO" STID_INFO, "Informaテァテ」o" diff --git a/base/applications/winhlp32/Rm.rc b/base/applications/winhlp32/Rm.rc index a5462b87a32..60403f02d79 100644 --- a/base/applications/winhlp32/Rm.rc +++ b/base/applications/winhlp32/Rm.rc @@ -70,16 +70,13 @@ MAIN_MENU MENU MENUITEM "Adソスna da&vant", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "I&nfuormaziuns", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "Davart &Wine", MNID_HELP_WINE -#endif } } /* Strings */ STRINGTABLE { -STID_WINE_HELP, "WINE agソスd" +STID_WINE_HELP, "Wine agソスd" STID_WHERROR, "SBAGL" STID_WARNING, "ATTENZIUN" STID_INFO, "INFUORMAZIUN" diff --git a/base/applications/winhlp32/Ro.rc b/base/applications/winhlp32/Ro.rc index 845b1cc5754..69b088fef2a 100644 --- a/base/applications/winhlp32/Ro.rc +++ b/base/applications/winhlp32/Ro.rc @@ -67,9 +67,6 @@ MAIN_MENU MENU MENUITEM "テ始&totdeauna deasupra", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Informaネ嬖i...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&Despre Wine", MNID_HELP_WINE -#endif } } diff --git a/base/applications/winhlp32/Ru.rc b/base/applications/winhlp32/Ru.rc index f8bc3cbe924..456f7e2b168 100644 --- a/base/applications/winhlp32/Ru.rc +++ b/base/applications/winhlp32/Ru.rc @@ -66,9 +66,6 @@ MAIN_MENU MENU MENUITEM "ミ柘ミオミウミエミー &ムミイミオムムム", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&ミ侑スムミセムミシミームミクム...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&ミ Wine", MNID_HELP_WINE -#endif } } @@ -91,7 +88,7 @@ CAPTION "ミ渙セミクムミコ" /* Strings */ STRINGTABLE { -STID_WINE_HELP, "ミ。ミソムミーミイミコミー WINE" +STID_WINE_HELP, "ミ。ミソムミーミイミコミー Wine" STID_WHERROR, "ミ榧ィミ侑岱墟" STID_WARNING, "ミ漬斷侑慴籍斷侑" STID_INFO, "ミ侑スムミセムミシミームミクム" diff --git a/base/applications/winhlp32/Si.rc b/base/applications/winhlp32/Si.rc index 340b9414551..023b28a984a 100644 --- a/base/applications/winhlp32/Si.rc +++ b/base/applications/winhlp32/Si.rc @@ -65,9 +65,6 @@ MAIN_MENU MENU MENUITEM "Vedno na &vrhu", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Informacije ...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&O WINE", MNID_HELP_WINE -#endif } } @@ -90,7 +87,7 @@ CAPTION "Iskanje" /* Strings */ STRINGTABLE { -STID_WINE_HELP, "WINE Pomoト" +STID_WINE_HELP, "Wine Pomoト" STID_WHERROR, "NAPAKA" STID_WARNING, "OPOZORILO" STID_INFO, "Informacija" diff --git a/base/applications/winhlp32/Sk.rc b/base/applications/winhlp32/Sk.rc index 773f5405e9d..d1abcff02c7 100644 --- a/base/applications/winhlp32/Sk.rc +++ b/base/applications/winhlp32/Sk.rc @@ -61,16 +61,13 @@ MAIN_MENU MENU MENUITEM "V枦y na &vrchu", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Info...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "O& WINE", MNID_HELP_WINE -#endif } } /* Strings */ STRINGTABLE { -STID_WINE_HELP, "WINE Pomoc" +STID_WINE_HELP, "Wine Pomoc" STID_WHERROR, "CHYBA" STID_WARNING, "VAROVANIE" STID_INFO, "Inform當ie" diff --git a/base/applications/winhlp32/Sr.rc b/base/applications/winhlp32/Sr.rc new file mode 100644 index 00000000000..a33e75adfce --- /dev/null +++ b/base/applications/winhlp32/Sr.rc @@ -0,0 +1,128 @@ +/* + * Help Viewer + * + * Copyright 2010 Nenad Vujic + * Paul Vriens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "winhelp_res.h" + +#pragma code_page(65001) + +/* Menu */ + +LANGUAGE LANG_SERBIAN, SUBLANG_SERBIAN_LATIN + +MAIN_MENU MENU +{ + POPUP "&Fajl" { + MENUITEM "&Otvori", MNID_FILE_OPEN + MENUITEM SEPARATOR + MENUITEM "&ナtampaj", MNID_FILE_PRINT + MENUITEM "ナtampaト &podeナ。avanje...", MNID_FILE_SETUP + MENUITEM SEPARATOR + MENUITEM "I&zlaz", MNID_FILE_EXIT + } + POPUP "&Izmeni" { + MENUITEM "&Kopiraj...", MNID_EDIT_COPYDLG + MENUITEM SEPARATOR + MENUITEM "&Pribeleナセi...", MNID_EDIT_ANNOTATE + } + POPUP "&Oznaト絞" { + MENUITEM "&Odredi...", MNID_BKMK_DEFINE + } + POPUP "&Opcije" { + POPUP "Pomoト uvek vidljiva" + BEGIN + MENUITEM "Osnovno", MNID_OPTS_HELP_DEFAULT + MENUITEM "Vidljivo", MNID_OPTS_HELP_VISIBLE + MENUITEM "Nije vidljivo", MNID_OPTS_HELP_NONVISIBLE + END + MENUITEM "Istorija", MNID_OPTS_HISTORY + POPUP "Fontovi" + BEGIN + MENUITEM "Mali", MNID_OPTS_FONTS_SMALL + MENUITEM "Normalan", MNID_OPTS_FONTS_NORMAL + MENUITEM "Veliki", MNID_OPTS_FONTS_LARGE + END + MENUITEM "Koristi sistenske boje", MNID_OPTS_SYSTEM_COLORS + } + POPUP "&Pomoト" { + MENUITEM "Pomoト &na pomoトi", MNID_HELP_HELPON + MENUITEM "Uvek na &vrhu", MNID_HELP_HELPTOP + MENUITEM SEPARATOR + MENUITEM "&Info...", MNID_HELP_ABOUT + } +} + +IDD_INDEX DIALOG 0, 0, 200, 190 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Shell Dlg" +CAPTION "Index" +{ + LISTBOX IDC_INDEXLIST, 10, 10, 180, 150, LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_BORDER +} + +IDD_SEARCH DIALOG 0, 0, 200, 190 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Shell Dlg" +CAPTION "Traナセi" +{ + LTEXT "Not implemented yet", -1, 10, 10, 180, 150 +} + +/* Strings */ +STRINGTABLE +{ +STID_WINE_HELP, "Wine Pomoト" +STID_WHERROR, "GREナKA" +STID_WARNING, "UPOZORENJE" +STID_INFO, "Informacija" +STID_NOT_IMPLEMENTED, "Nije jos u programu" +STID_HLPFILE_ERROR_s, "Greナ。ka tokom ト絞tanja pomoトnog fajla `%s'" +STID_INDEX, "&Index" +STID_CONTENTS, "Pregled" +STID_BACK, "&Nazad" +STID_ALL_FILES, "Svi fajlovi (*.*)" +STID_HELP_FILES_HLP, "Pomoト肱i fajlovi (*.hlp)" +STID_FILE_NOT_FOUND_s "Ne mogu naトi '%s'. Da li ナセelite da sami naト粗te fajl?" +STID_NO_RICHEDIT "Ne mogu naトi richedit ubacen... Prekidam" +STID_PSH_INDEX, "Teme pomoトi: " +} + +CONTEXT_MENU MENU +BEGIN + POPUP "" + BEGIN + MENUITEM "Beleナ。ke...", MNID_CTXT_ANNOTATE + MENUITEM "Kopiraj", MNID_CTXT_COPY + MENUITEM "ナtampaj...", MNID_CTXT_PRINT + POPUP "Fontovi" + BEGIN + MENUITEM "Mali", MNID_CTXT_FONTS_SMALL + MENUITEM "Normalno", MNID_CTXT_FONTS_NORMAL + MENUITEM "Veliki", MNID_CTXT_FONTS_LARGE + END + POPUP "Pomoト uvek vidljiva" + BEGIN + MENUITEM "Osnovno", MNID_CTXT_HELP_DEFAULT + MENUITEM "Vidljiv", MNID_CTXT_HELP_VISIBLE + MENUITEM "Nije vidljiv", MNID_CTXT_HELP_NONVISIBLE + END + MENUITEM "Koristi sistemske boje", MNID_CTXT_SYSTEM_COLORS + END +END diff --git a/base/applications/winhlp32/Sv.rc b/base/applications/winhlp32/Sv.rc index 8a5da49ce54..c91332093ee 100644 --- a/base/applications/winhlp32/Sv.rc +++ b/base/applications/winhlp32/Sv.rc @@ -67,9 +67,6 @@ MAIN_MENU MENU MENUITEM "Alltid &verst", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Info...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&Om Wine", MNID_HELP_WINE -#endif } } diff --git a/base/applications/winhlp32/Tr.rc b/base/applications/winhlp32/Tr.rc index 62202f7fc95..41af0c5387c 100644 --- a/base/applications/winhlp32/Tr.rc +++ b/base/applications/winhlp32/Tr.rc @@ -63,16 +63,13 @@ MAIN_MENU MENU MENUITEM "&Her Zaman ワstte", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&Bilgi...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "WINE &Hakknda", MNID_HELP_WINE -#endif } } /* Strings */ STRINGTABLE { -STID_WINE_HELP, "WINE Yardm" +STID_WINE_HELP, "Wine Yardm" STID_WHERROR, "HATA" STID_WARNING, "UYARI" STID_INFO, "Bilgi" diff --git a/base/applications/winhlp32/Uk.rc b/base/applications/winhlp32/Uk.rc index f4e72d0e8ee..8009697172f 100644 --- a/base/applications/winhlp32/Uk.rc +++ b/base/applications/winhlp32/Uk.rc @@ -71,9 +71,6 @@ MAIN_MENU MENU MENUITEM "ミ厘ーミイミカミエミク &ミキミイミオムムム", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "&ミミスムミセムミシミームム毛...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "&ミ湲ミセ Wine", MNID_HELP_WINE -#endif } } diff --git a/base/applications/winhlp32/Zh.rc b/base/applications/winhlp32/Zh.rc index 9401e74a84c..f9d27461223 100644 --- a/base/applications/winhlp32/Zh.rc +++ b/base/applications/winhlp32/Zh.rc @@ -68,16 +68,13 @@ MAIN_MENU MENU MENUITEM "諤サ譏ッ蝨ィ譛蜑埼擇(&T)", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "襍譁吩ソ。諱ッ(&I)...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "蜈ウ莠 WINE(&A)", MNID_HELP_WINE -#endif } } /* Strings */ STRINGTABLE { -STID_WINE_HELP, "WINE 蟶ョ蜉ゥ" +STID_WINE_HELP, "Wine 蟶ョ蜉ゥ" STID_WHERROR, "髞呵ッッ" STID_WARNING, "隴ヲ蜻" STID_INFO, "菫。諱ッ" @@ -136,16 +133,13 @@ MAIN_MENU MENU MENUITEM "邵ス譏ッ蝨ィ譛荳企擇(&T)", MNID_HELP_HELPTOP MENUITEM SEPARATOR MENUITEM "雉險(&I)...", MNID_HELP_ABOUT -#ifdef WINELIB - MENUITEM "髣懈名 WINE(&A)", MNID_HELP_WINE -#endif } } /* Strings */ STRINGTABLE { -STID_WINE_HELP, "WINE 蟷ォ蜉ゥ" +STID_WINE_HELP, "Wine 蟷ォ蜉ゥ" STID_WHERROR, "骭ッ隱、" STID_WARNING, "隴ヲ蜻" STID_INFO, "雉險" diff --git a/base/applications/winhlp32/macro.c b/base/applications/winhlp32/macro.c index ca0fc8c54d2..3151e0dac14 100644 --- a/base/applications/winhlp32/macro.c +++ b/base/applications/winhlp32/macro.c @@ -151,7 +151,11 @@ void CALLBACK MACRO_JumpContents(LPCSTR lpszPath, LPCSTR lpszWindow) void CALLBACK MACRO_About(void) { - WINE_FIXME("()\n"); + WCHAR name[256]; + HICON icon = LoadImageW( Globals.hInstance, MAKEINTRESOURCEW(IDI_WINHELP), + IMAGE_ICON, 48, 48, LR_SHARED ); + LoadStringW( Globals.hInstance, STID_WINE_HELP, name, sizeof(name)/sizeof(WCHAR) ); + ShellAboutW( MACRO_CurrentWindow()->hMainWnd, name, NULL, icon ); } static void CALLBACK MACRO_AddAccelerator(LONG u1, LONG u2, LPCSTR str) diff --git a/base/applications/winhlp32/rsrc.rc b/base/applications/winhlp32/rsrc.rc index 8b877ed0162..6bf84754f48 100644 --- a/base/applications/winhlp32/rsrc.rc +++ b/base/applications/winhlp32/rsrc.rc @@ -22,7 +22,7 @@ #include "winhelp_res.h" /* @makedep: winhelp.ico */ -IDI_WINHELP ICON DISCARDABLE winhelp.ico +IDI_WINHELP ICON winhelp.ico #include "Bg.rc" #include "Cs.rc" @@ -41,6 +41,7 @@ IDI_WINHELP ICON DISCARDABLE winhelp.ico #include "Da.rc" #include "De.rc" #include "Fr.rc" +#include "He.rc" #include "It.rc" #include "Ja.rc" #include "Lt.rc" @@ -50,5 +51,6 @@ IDI_WINHELP ICON DISCARDABLE winhelp.ico #include "Ro.rc" #include "Ru.rc" #include "Si.rc" +#include "Sr.rc" #include "Uk.rc" #include "Zh.rc" diff --git a/base/applications/winhlp32/winhelp.c b/base/applications/winhlp32/winhelp.c index 582ab3e1c52..5e4bce4c4cf 100644 --- a/base/applications/winhlp32/winhelp.c +++ b/base/applications/winhlp32/winhelp.c @@ -1368,7 +1368,6 @@ static LRESULT CALLBACK WINHELP_MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, case MNID_HELP_HELPON: MACRO_HelpOn(); break; case MNID_HELP_HELPTOP: MACRO_HelpOnTop(); break; case MNID_HELP_ABOUT: MACRO_About(); break; - case MNID_HELP_WINE: ShellAbout(hWnd, "WINE", "Help", 0); break; /* Context help */ case MNID_CTXT_ANNOTATE:MACRO_Annotate(); break; diff --git a/base/applications/winhlp32/winhelp_res.h b/base/applications/winhlp32/winhelp_res.h index ba2f9799d25..a6d183096de 100644 --- a/base/applications/winhlp32/winhelp_res.h +++ b/base/applications/winhlp32/winhelp_res.h @@ -23,7 +23,6 @@ #define MNID_HELP_HELPON 0x141 #define MNID_HELP_HELPTOP 0x142 #define MNID_HELP_ABOUT 0x143 -#define MNID_HELP_WINE 0x144 #define MNID_CTXT_ANNOTATE 0x200 #define MNID_CTXT_COPY 0x201 diff --git a/base/applications/wordpad/It.rc b/base/applications/wordpad/It.rc index de9828eb1b9..79278d5d5c4 100644 --- a/base/applications/wordpad/It.rc +++ b/base/applications/wordpad/It.rc @@ -54,7 +54,7 @@ BEGIN MENUITEM "Trova s&uccessivo\tF3", ID_FIND_NEXT MENUITEM "S&ostituisci...\tCtrl+H", ID_REPLACE MENUITEM SEPARATOR - MENUITEM "Solo &Lettura", ID_EDIT_READONLY + MENUITEM "Sola &lettura", ID_EDIT_READONLY MENUITEM "&Modificato", ID_EDIT_MODIFIED MENUITEM SEPARATOR POPUP "E&xtra" @@ -71,7 +71,7 @@ BEGIN MENUITEM "Barra degli &strumenti", ID_TOGGLE_TOOLBAR MENUITEM "Barra del &formato", ID_TOGGLE_FORMATBAR MENUITEM "&Righello", ID_TOGGLE_RULER - MENUITEM "Barra dello s&tato", ID_TOGGLE_STATUSBAR + MENUITEM "Barra di s&tato", ID_TOGGLE_STATUSBAR MENUITEM SEPARATOR MENUITEM "&Opzioni...", ID_VIEWPROPERTIES END @@ -93,7 +93,7 @@ BEGIN END POPUP "&Aiuto" BEGIN - MENUITEM "&Riguardo A Wine Wordpad" ID_ABOUT + MENUITEM "&Riguardo a Wine Wordpad" ID_ABOUT END END @@ -119,8 +119,8 @@ BEGIN MENUITEM "Verde" ID_COLOR_GREEN MENUITEM "Verde oliva" ID_COLOR_OLIVE MENUITEM "Blu oltremare" ID_COLOR_NAVY - MENUITEM "Propora" ID_COLOR_PURPLE - MENUITEM "Teal" ID_COLOR_TEAL + MENUITEM "Porpora" ID_COLOR_PURPLE + MENUITEM "Foglia di Tティ" ID_COLOR_TEAL MENUITEM "Grigio" ID_COLOR_GRAY MENUITEM "Argento" ID_COLOR_SILVER MENUITEM "Rosso" ID_COLOR_RED @@ -128,9 +128,9 @@ BEGIN MENUITEM "Giallo" ID_COLOR_YELLOW MENUITEM "Blu" ID_COLOR_BLUE MENUITEM "Fucsia" ID_COLOR_FUCHSIA - MENUITEM "Aqua" ID_COLOR_AQUA + MENUITEM "Acqua" ID_COLOR_AQUA MENUITEM "Bianco" ID_COLOR_WHITE - MENUITEM "Automatic" ID_COLOR_AUTOMATIC + MENUITEM "Automatico" ID_COLOR_AUTOMATIC END END @@ -161,7 +161,7 @@ STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU Caption "Formato del paragrafo" FONT 8, "MS Shell Dlg" BEGIN - GROUPBOX "Indentazione", -1, 10, 10, 120, 68 + GROUPBOX "Rientro", -1, 10, 10, 120, 68 LTEXT "Sinistra", -1, 15, 22, 40, 13 EDITTEXT IDC_PARA_LEFT, 55, 20, 60, 13 LTEXT "Destra", -1, 15, 40, 40, 13 @@ -201,17 +201,17 @@ BEGIN CHECKBOX "Barra degli &strumenti", IDC_PAGEFMT_TB, 180, 20, 80, 15 CHECKBOX "Barra del &formato", IDC_PAGEFMT_FB, 180, 38, 80, 15 CHECKBOX "&Righello", IDC_PAGEFMT_RU, 180, 56, 80, 15 - CHECKBOX "Barra dello s&tato", IDC_PAGEFMT_SB, 180, 74, 80, 15 + CHECKBOX "Barra di s&tato", IDC_PAGEFMT_SB, 180, 74, 80, 15 LTEXT "", IDC_PAGEFMT_ID, 0,0,0,0 END STRINGTABLE BEGIN - STRING_ALL_FILES, "Tutti i files (*.*)" + STRING_ALL_FILES, "Tutti i file (*.*)" STRING_TEXT_FILES_TXT, "File di testo (*.txt)" STRING_TEXT_FILES_UNICODE_TXT, "File di testo Unicode (*.txt)" STRING_RICHTEXT_FILES_RTF, "Formato rich text (*.rtf)" - STRING_NEWFILE_RICHTEXT, "Documento Rich text" + STRING_NEWFILE_RICHTEXT, "Documento rich text" STRING_NEWFILE_TXT, "Documento di testo" STRING_NEWFILE_TXT_UNICODE, "Documento di testo Unicode" STRING_PRINTER_FILES_PRN, "File di stampa (*.PRN)" @@ -264,6 +264,6 @@ BEGIN STRING_WRITE_ACCESS_DENIED, "Non hai i diritti di accesso per salvare il file." STRING_OPEN_FAILED, "Impossibile aprire il file." STRING_OPEN_ACCESS_DENIED, "Non hai i diritti di accesso per aprire il file." - STRING_PRINTING_NOT_IMPLEMENTED, "Stampa non implementata" + STRING_PRINTING_NOT_IMPLEMENTED, "Stampa non implementata" STRING_MAX_TAB_STOPS, "Non si possono aggiungere piテケ di 32 punti di fermata delle tabulazioni." END diff --git a/base/applications/wordpad/Zh.rc b/base/applications/wordpad/Zh.rc index 63bfc57c3b6..cd13c719a85 100644 --- a/base/applications/wordpad/Zh.rc +++ b/base/applications/wordpad/Zh.rc @@ -56,12 +56,12 @@ BEGIN MENUITEM "謇セ荳倶ク荳ェ(&N)\tF3", ID_FIND_NEXT MENUITEM "譖ソ謐「(&R)...\tCtrl+H", ID_REPLACE MENUITEM SEPARATOR - MENUITEM "蜿ェ隸サ(&0))", ID_EDIT_READONLY - MENUITEM "蟾イ謾ケ蜉ィ(M)", ID_EDIT_MODIFIED + MENUITEM "蜿ェ隸サ(&O)", ID_EDIT_READONLY + MENUITEM "蟾イ謾ケ蜉ィ(&M)", ID_EDIT_MODIFIED MENUITEM SEPARATOR POPUP "蜈カ莉(&X)" BEGIN - MENUITEM "騾画叫菫。諱ッ(&I)", ID_EDIT_SELECTIONINFO + MENUITEM "騾我クュ菫。諱ッ(&I)", ID_EDIT_SELECTIONINFO MENUITEM "譁蟄玲シ蠑(&F)", ID_EDIT_CHARFORMAT MENUITEM "鮟倩ョ、譬シ蠑(&D)", ID_EDIT_DEFCHARFORMAT MENUITEM "谿オ關ス譬シ蠑(&H)", ID_EDIT_PARAFORMAT @@ -116,22 +116,22 @@ IDM_COLOR_POPUP MENU BEGIN POPUP "" BEGIN - MENUITEM "鮟", ID_COLOR_BLACK - MENUITEM "Maroon", ID_COLOR_MAROON - MENUITEM "扈ソ", ID_COLOR_GREEN - MENUITEM "Olive" ID_COLOR_OLIVE - MENUITEM "Navy" ID_COLOR_NAVY - MENUITEM "邏ォ" ID_COLOR_PURPLE - MENUITEM "Teal" ID_COLOR_TEAL - MENUITEM "轣ー" ID_COLOR_GRAY - MENUITEM "Silver" ID_COLOR_SILVER - MENUITEM "郤「" ID_COLOR_RED - MENUITEM "Lime" ID_COLOR_LIME - MENUITEM "鮟" ID_COLOR_YELLOW - MENUITEM "闢" ID_COLOR_BLUE - MENUITEM "Fuchsia" ID_COLOR_FUCHSIA - MENUITEM "Aqua" ID_COLOR_AQUA - MENUITEM "逋ス" ID_COLOR_WHITE + MENUITEM "鮟題牡", ID_COLOR_BLACK /* 譛ェ謇セ蛻ー窶懈蜃隸大錐陦ィ窶 */ + MENUITEM "譬苓牡", ID_COLOR_MAROON + MENUITEM "隹蜥檎サソ", ID_COLOR_GREEN + MENUITEM "讖讎濶イ" ID_COLOR_OLIVE + MENUITEM "阯城搨" ID_COLOR_NAVY + MENUITEM "邏ォ濶イ" ID_COLOR_PURPLE + MENUITEM "鮑ュ鄙扈ソ" ID_COLOR_TEAL + MENUITEM "轣ー濶イ" ID_COLOR_GRAY + MENUITEM "體カ濶イ" ID_COLOR_SILVER + MENUITEM "郤「濶イ" ID_COLOR_RED + MENUITEM "扈ソ濶イ" ID_COLOR_LIME + MENUITEM "鮟濶イ" ID_COLOR_YELLOW + MENUITEM "闢晁牡" ID_COLOR_BLUE + MENUITEM "豢狗コ「" ID_COLOR_FUCHSIA /* =magenta? */ + MENUITEM "豌エ扈ソ" ID_COLOR_AQUA + MENUITEM "逋ス濶イ" ID_COLOR_WHITE MENUITEM "閾ェ蜉ィ" ID_COLOR_AUTOMATIC END END @@ -262,20 +262,18 @@ END STRINGTABLE BEGIN STRING_DEFAULT_FILENAME, "譁譯」" - STRING_PROMPT_SAVE_CHANGES, "隕∽ソ晏ュ '%s' 蜷?" - STRING_SEARCH_FINISHED, "譁莉カ譟・謇セ扈捺據." - STRING_LOAD_RICHED_FAILED, "RichEdit 陬霓ス螟ア雍・." - STRING_SAVE_LOSEFORMATTING, "菴蟾イ扈城画叫菫晏ュ倅クコ譁譛ャ譁莉カ, " \ - "霑吝庄閭ス蟇シ閾エ譬シ蠑丈ク「螟ア. " \ - "菴遑ョ螳夊ヲ∬ソ吩ケ亥★蜷?" + STRING_PROMPT_SAVE_CHANGES, "隕∽ソ晏ュ倪%s窶晏雛シ" + STRING_SEARCH_FINISHED, "譁莉カ譟・謇セ扈捺據縲" + STRING_LOAD_RICHED_FAILED, "RichEdit 陬霓ス螟ア雍・縲" + STRING_SAVE_LOSEFORMATTING, "菴蟾イ扈城画叫菫晏ュ倅クコ譁譛ャ譁莉カシ瑚瑚ソ吝庄閭ス蟇シ閾エ譬シ蠑丈ク「螟ア縲ゆス遑ョ螳夊ヲ∬ソ吩ケ亥★蜷暦シ" STRING_INVALID_NUMBER, "謨ー蟄玲シ蠑乗裏謨" STRING_OLE_STORAGE_NOT_SUPPORTED, "荳肴髪謖 OLE 蛯ィ蟄俶枚莉カ" - STRING_WRITE_FAILED, "荳崎ス菫晏ュ俶枚莉カ." - STRING_WRITE_ACCESS_DENIED, "菴豐。譛我ソ晏ュ俶枚莉カ逧譚髯." - STRING_OPEN_FAILED, "荳崎ス謇灘シ譁莉カ." - STRING_OPEN_ACCESS_DENIED, "菴豐。譛画遠蠑譁莉カ逧譚髯." + STRING_WRITE_FAILED, "荳崎ス菫晏ュ俶枚莉カ縲" + STRING_WRITE_ACCESS_DENIED, "菴豐。譛我ソ晏ュ俶枚莉カ逧譚髯舌" + STRING_OPEN_FAILED, "荳崎ス謇灘シ譁莉カ縲" + STRING_OPEN_ACCESS_DENIED, "菴豐。譛画遠蠑譁莉カ逧譚髯舌" STRING_PRINTING_NOT_IMPLEMENTED, "謇灘魂蜉溯ス蟆壽悴螳樒鴫" - STRING_MAX_TAB_STOPS, "譛螟壼宵閭ス豺サ蜉 32 荳ェ蛻カ陦ィ菴榊ョス蠎ヲ." + STRING_MAX_TAB_STOPS, "譛螟壼宵閭ス豺サ蜉 32 荳ェ蛻カ陦ィ菴榊ョス蠎ヲ縲" END LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL diff --git a/base/setup/usetup/lang/ja-JP.h b/base/setup/usetup/lang/ja-JP.h index 0db36acab83..1bcd002f042 100644 --- a/base/setup/usetup/lang/ja-JP.h +++ b/base/setup/usetup/lang/ja-JP.h @@ -986,13 +986,13 @@ static MUI_ENTRY jaJPBootLoaderEntries[] = { 8, 12, - "Install bootloader on the harddisk (MBR and VBR).", + "ハードディスクニ ブートローダーヲ インストール シマス (MBR ト VBR).", TEXT_STYLE_NORMAL }, { 8, 13, - "Install bootloader on the harddisk (VBR only).", + "ハードディスクニ ブートローダーヲ インストール シマス (VBR ノミ).", TEXT_STYLE_NORMAL }, { diff --git a/base/system/lsass/lsass.c b/base/system/lsass/lsass.c index 30fa05de17d..57216a4d9a6 100644 --- a/base/system/lsass/lsass.c +++ b/base/system/lsass/lsass.c @@ -55,6 +55,14 @@ wWinMain(IN HINSTANCE hInstance, goto ByeBye; } + /* Start the Netlogon service. */ + Status = ServiceInit(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ServiceInit() failed (Status 0x%08lX)\n", Status); + goto ByeBye; + } + #if 0 /* Initialize the SAM server DLL. */ Status = SamIInitialize(); diff --git a/base/system/msiexec/rsrc.rc b/base/system/msiexec/rsrc.rc index cfc89370039..2594c51b0b1 100644 --- a/base/system/msiexec/rsrc.rc +++ b/base/system/msiexec/rsrc.rc @@ -32,4 +32,4 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL /* @makedep: msiexec.ico */ -1 ICON DISCARDABLE msiexec.ico +1 ICON msiexec.ico diff --git a/base/system/services/rpcserver.c b/base/system/services/rpcserver.c index 914e013327b..b4b5bb88335 100644 --- a/base/system/services/rpcserver.c +++ b/base/system/services/rpcserver.c @@ -24,7 +24,6 @@ typedef struct _SCMGR_HANDLE { DWORD Tag; - DWORD RefCount; DWORD DesiredAccess; } SCMGR_HANDLE; @@ -43,7 +42,6 @@ typedef struct _SERVICE_HANDLE { SCMGR_HANDLE Handle; - DWORD DesiredAccess; PSERVICE ServiceEntry; /* FIXME: Insert more data here */ @@ -169,7 +167,6 @@ ScmCreateManagerHandle(LPWSTR lpDatabaseName, return ERROR_NOT_ENOUGH_MEMORY; Ptr->Handle.Tag = MANAGER_TAG; - Ptr->Handle.RefCount = 1; /* FIXME: initialize more data here */ @@ -194,7 +191,6 @@ ScmCreateServiceHandle(PSERVICE lpServiceEntry, return ERROR_NOT_ENOUGH_MEMORY; Ptr->Handle.Tag = SERVICE_TAG; - Ptr->Handle.RefCount = 1; /* FIXME: initialize more data here */ Ptr->ServiceEntry = lpServiceEntry; @@ -205,6 +201,46 @@ ScmCreateServiceHandle(PSERVICE lpServiceEntry, } +static PMANAGER_HANDLE +ScmGetServiceManagerFromHandle(SC_RPC_HANDLE Handle) +{ + PMANAGER_HANDLE pManager = NULL; + + _SEH2_TRY + { + if (((PMANAGER_HANDLE)Handle)->Handle.Tag == MANAGER_TAG) + pManager = (PMANAGER_HANDLE)Handle; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + DPRINT1("Exception: Invalid Service Manager handle!\n"); + } + _SEH2_END; + + return pManager; +} + + +static PSERVICE_HANDLE +ScmGetServiceFromHandle(SC_RPC_HANDLE Handle) +{ + PSERVICE_HANDLE pService = NULL; + + _SEH2_TRY + { + if (((PSERVICE_HANDLE)Handle)->Handle.Tag == SERVICE_TAG) + pService = (PSERVICE_HANDLE)Handle; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + DPRINT1("Exception: Invalid Service handle!\n"); + } + _SEH2_END; + + return pService; +} + + static DWORD ScmCheckAccess(SC_HANDLE Handle, DWORD dwDesiredAccess) @@ -423,14 +459,10 @@ DWORD RCloseServiceHandle( { DPRINT("Found manager handle\n"); - hManager->Handle.RefCount--; - if (hManager->Handle.RefCount == 0) - { - /* FIXME: add handle cleanup code */ + /* FIXME: add handle cleanup code */ - HeapFree(GetProcessHeap(), 0, hManager); - hManager = NULL; - } + HeapFree(GetProcessHeap(), 0, hManager); + hManager = NULL; DPRINT("RCloseServiceHandle() done\n"); return ERROR_SUCCESS; @@ -442,17 +474,11 @@ DWORD RCloseServiceHandle( /* Get the pointer to the service record */ lpService = hService->ServiceEntry; - ASSERT(hService->Handle.RefCount > 0); + /* FIXME: add handle cleanup code */ - hService->Handle.RefCount--; - if (hService->Handle.RefCount == 0) - { - /* FIXME: add handle cleanup code */ - - /* Free the handle */ - HeapFree(GetProcessHeap(), 0, hService); - hService = NULL; - } + /* Free the handle */ + HeapFree(GetProcessHeap(), 0, hService); + hService = NULL; ASSERT(lpService->dwRefCount > 0); @@ -543,18 +569,19 @@ DWORD RControlService( return ERROR_SHUTDOWN_IN_PROGRESS; /* Check the service handle */ - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } + /* Check the service entry point */ lpService = hSvc->ServiceEntry; if (lpService == NULL) { - DPRINT("lpService == NULL!\n"); + DPRINT1("lpService == NULL!\n"); return ERROR_INVALID_HANDLE; } @@ -674,9 +701,12 @@ DWORD RDeleteService( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) + { + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; + } if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess, DELETE)) @@ -721,9 +751,12 @@ DWORD RLockServiceDatabase( *lpLock = 0; - hMgr = (PMANAGER_HANDLE)hSCManager; - if (!hMgr || hMgr->Handle.Tag != MANAGER_TAG) + hMgr = ScmGetServiceManagerFromHandle(hSCManager); + if (hMgr == NULL) + { + DPRINT1("Invalid service manager handle!\n"); return ERROR_INVALID_HANDLE; + } if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess, SC_MANAGER_LOCK)) @@ -758,10 +791,10 @@ DWORD RQueryServiceObjectSecurity( DPRINT("RQueryServiceObjectSecurity() called\n"); - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -840,10 +873,10 @@ DWORD RSetServiceObjectSecurity( DPRINT("RSetServiceObjectSecurity() called\n"); - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -957,10 +990,10 @@ DWORD RQueryServiceStatus( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -1143,10 +1176,10 @@ DWORD RChangeServiceConfigW( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -1763,10 +1796,10 @@ DWORD RCreateServiceW( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hManager = (PMANAGER_HANDLE)hSCManager; - if (!hManager || hManager->Handle.Tag != MANAGER_TAG) + hManager = ScmGetServiceManagerFromHandle(hSCManager); + if (hManager == NULL) { - DPRINT("Invalid manager handle!\n"); + DPRINT1("Invalid service manager handle!\n"); return ERROR_INVALID_HANDLE; } @@ -2073,7 +2106,6 @@ DWORD REnumDependentServicesW( DWORD dwServicesReturned = 0; DWORD dwServiceCount; HKEY hServicesKey = NULL; - LPSC_RPC_HANDLE hSCObject; PSERVICE_HANDLE hSvc; PSERVICE lpService = NULL; PSERVICE *lpServicesArray = NULL; @@ -2085,8 +2117,13 @@ DWORD REnumDependentServicesW( DPRINT("REnumDependentServicesW() called\n"); - hSCObject = &hService; - hSvc = (PSERVICE_HANDLE) *hSCObject; + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) + { + DPRINT1("Invalid service handle!\n"); + return ERROR_INVALID_HANDLE; + } + lpService = hSvc->ServiceEntry; /* Check access rights */ @@ -2218,13 +2255,14 @@ DWORD REnumServicesStatusW( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hManager = (PMANAGER_HANDLE)hSCManager; - if (!hManager || hManager->Handle.Tag != MANAGER_TAG) + hManager = ScmGetServiceManagerFromHandle(hSCManager); + if (hManager == NULL) { - DPRINT("Invalid manager handle!\n"); + DPRINT1("Invalid service manager handle!\n"); return ERROR_INVALID_HANDLE; } + *pcbBytesNeeded = 0; *lpServicesReturned = 0; @@ -2469,10 +2507,10 @@ DWORD ROpenServiceW( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hManager = (PMANAGER_HANDLE)hSCManager; - if (!hManager || hManager->Handle.Tag != MANAGER_TAG) + hManager = ScmGetServiceManagerFromHandle(hSCManager); + if (hManager == NULL) { - DPRINT("Invalid manager handle!\n"); + DPRINT1("Invalid service manager handle!\n"); return ERROR_INVALID_HANDLE; } @@ -2549,10 +2587,10 @@ DWORD RQueryServiceConfigW( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -2757,10 +2795,10 @@ DWORD RStartServiceW( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -2983,10 +3021,10 @@ DWORD RChangeServiceConfigA( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -3401,7 +3439,6 @@ DWORD REnumDependentServicesA( DWORD dwServicesReturned = 0; DWORD dwServiceCount; HKEY hServicesKey = NULL; - LPSC_RPC_HANDLE hSCObject; PSERVICE_HANDLE hSvc; PSERVICE lpService = NULL; PSERVICE *lpServicesArray = NULL; @@ -3413,8 +3450,13 @@ DWORD REnumDependentServicesA( DPRINT("REnumDependentServicesA() called\n"); - hSCObject = &hService; - hSvc = (PSERVICE_HANDLE) *hSCObject; + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) + { + DPRINT1("Invalid service handle!\n"); + return ERROR_INVALID_HANDLE; + } + lpService = hSvc->ServiceEntry; /* Check access rights */ @@ -3723,10 +3765,10 @@ DWORD RQueryServiceConfigA( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -3964,10 +4006,10 @@ DWORD RStartServiceA( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -4345,10 +4387,10 @@ DWORD RChangeServiceConfig2W( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -4446,10 +4488,10 @@ DWORD RQueryServiceConfig2A( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -4566,10 +4608,10 @@ DWORD RQueryServiceConfig2W( if (ScmShutdown) return ERROR_SHUTDOWN_IN_PROGRESS; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -4719,10 +4761,10 @@ DWORD RQueryServiceStatusEx( if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS)) return ERROR_INSUFFICIENT_BUFFER; - hSvc = (PSERVICE_HANDLE)hService; - if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG) + hSvc = ScmGetServiceFromHandle(hService); + if (hSvc == NULL) { - DPRINT("Invalid handle tag!\n"); + DPRINT1("Invalid service handle!\n"); return ERROR_INVALID_HANDLE; } @@ -4912,10 +4954,10 @@ DWORD REnumServicesStatusExW( if (InfoLevel != SC_ENUM_PROCESS_INFO) return ERROR_INVALID_LEVEL; - hManager = (PMANAGER_HANDLE)hSCManager; - if (!hManager || hManager->Handle.Tag != MANAGER_TAG) + hManager = ScmGetServiceManagerFromHandle(hSCManager); + if (hManager == NULL) { - DPRINT("Invalid manager handle!\n"); + DPRINT1("Invalid service manager handle!\n"); return ERROR_INVALID_HANDLE; } diff --git a/boot/bootdata/packages/reactos.dff b/boot/bootdata/packages/reactos.dff index d908162d84f..c5e998d4b36 100644 --- a/boot/bootdata/packages/reactos.dff +++ b/boot/bootdata/packages/reactos.dff @@ -48,7 +48,7 @@ base\applications\extrac32\extrac32.exe 1 base\applications\fontview\fontview.exe 1 base\applications\games\solitaire\sol.exe 1 base\applications\games\spider\spider.exe 1 -base\applications\games\winemine\winemine.exe 1 +base\applications\games\winmine\winmine.exe 1 base\applications\hh\hh.exe 4 base\applications\kbswitch\kbswitch.exe 1 base\applications\kbswitch\kbsdll\kbsdll.dll 1 @@ -858,5 +858,10 @@ modules\rostests\winetests\wldap32\wldap32_winetest.exe 7 o modules\rostests\winetests\ws2_32\ws2_32_winetest.exe 7 optional modules\rostests\winetests\xmllite\xmllite_winetest.exe 7 optional +modules\rostests\apitests\dciman32\dciman32_apitest.exe 7 optional +modules\rostests\apitests\gdi32\gdi32_apitest.exe 7 optional +modules\rostests\apitests\ntdll\ntdll_apitest.exe 7 optional +modules\rostests\apitests\user32\user32_apitest.exe 7 optional +modules\rostests\apitests\ws2_32\ws2_32_apitest.exe 7 optional modules\wallpaper\Angelus_02_ROSWP.bmp 4 optional diff --git a/dll/directx/wine/wined3d/powf.c b/dll/directx/wine/wined3d/powf.c new file mode 100644 index 00000000000..55eb92a2e3f --- /dev/null +++ b/dll/directx/wine/wined3d/powf.c @@ -0,0 +1,11 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#include + +float powf(float x, float y) +{ + return (float)pow((double)x, (double)y); +} diff --git a/dll/directx/wine/wined3d/sqrtf.c b/dll/directx/wine/wined3d/sqrtf.c new file mode 100644 index 00000000000..512e9aeea67 --- /dev/null +++ b/dll/directx/wine/wined3d/sqrtf.c @@ -0,0 +1,11 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#include + +float sqrtf(float x) +{ + return (float)sqrt((double)x); +} diff --git a/dll/directx/wine/wined3d/wined3d.rbuild b/dll/directx/wine/wined3d/wined3d.rbuild index 64cab569ce3..9ca1b9b0f10 100644 --- a/dll/directx/wine/wined3d/wined3d.rbuild +++ b/dll/directx/wine/wined3d/wined3d.rbuild @@ -13,9 +13,6 @@ gdi32 advapi32 uuid - - crt - ati_fragment_shader.c arb_program_shader.c @@ -52,6 +49,10 @@ volumetexture.c wined3d_main.c version.rc + + powf.c + sqrtf.c + wineheaders diff --git a/dll/win32/advapi32/advapi32.rbuild b/dll/win32/advapi32/advapi32.rbuild index 17e120ba9ce..22e1a69af48 100644 --- a/dll/win32/advapi32/advapi32.rbuild +++ b/dll/win32/advapi32/advapi32.rbuild @@ -28,6 +28,7 @@ dllmain.c + efs.c hwprofiles.c logon.c msi.c diff --git a/dll/win32/advapi32/advapi32.spec b/dll/win32/advapi32/advapi32.spec index fe952b95616..810c1ab4754 100644 --- a/dll/win32/advapi32/advapi32.spec +++ b/dll/win32/advapi32/advapi32.spec @@ -345,12 +345,12 @@ @ stub LookupSecurityDescriptorPartsA @ stub LookupSecurityDescriptorPartsW @ stdcall LsaAddAccountRights(ptr ptr ptr long) -@ stub LsaAddPrivilegesToAccount +@ stdcall LsaAddPrivilegesToAccount(ptr ptr) @ stub LsaClearAuditLog @ stdcall LsaClose(ptr) -@ stub LsaCreateAccount +@ stdcall LsaCreateAccount(ptr ptr long ptr) @ stub LsaCreateSecret -@ stub LsaCreateTrustedDomain +@ stdcall LsaCreateTrustedDomain(ptr ptr long ptr) @ stdcall LsaCreateTrustedDomainEx(ptr ptr ptr long ptr) @ stdcall LsaDelete(ptr) @ stdcall LsaDeleteTrustedDomain(ptr ptr) @@ -377,8 +377,8 @@ @ stdcall LsaLookupPrivilegeValue(ptr ptr ptr) @ stdcall LsaLookupSids(ptr long ptr ptr ptr) @ stdcall LsaNtStatusToWinError(long) -@ stub LsaOpenAccount -@ stdcall LsaOpenPolicy(long long long long) +@ stdcall LsaOpenAccount(ptr ptr long ptr) +@ stdcall LsaOpenPolicy(ptr ptr long ptr) @ stub LsaOpenPolicySce @ stub LsaOpenSecret @ stub LsaOpenTrustedDomain @@ -395,10 +395,9 @@ @ stdcall LsaRemoveAccountRights(ptr ptr long ptr long) @ stub LsaRemovePrivilegesFromAccount @ stdcall LsaRetrievePrivateData(ptr ptr ptr) -# @ stub LsaSetDomainInformationPolicy -# @ stub LsaSetForestTrustInformation -@ stdcall LsaSetInformationPolicy(long long ptr) +@ stdcall LsaSetDomainInformationPolicy(ptr long ptr) @ stub LsaSetForestTrustInformation +@ stdcall LsaSetInformationPolicy(long long ptr) @ stub LsaSetInformationTrustedDomain @ stub LsaSetQuotasForAccount @ stdcall LsaSetSecret(ptr ptr ptr) diff --git a/dll/win32/advapi32/misc/efs.c b/dll/win32/advapi32/misc/efs.c new file mode 100644 index 00000000000..11fb8e588ed --- /dev/null +++ b/dll/win32/advapi32/misc/efs.c @@ -0,0 +1,85 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: dlls/win32/advapi32/misc/efs.c + * PURPOSE: Encrypted File System support + * PROGRAMMER: Christoph_vW + */ + +#include +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(advapi); + + +/* + * @implemented + */ +BOOL WINAPI +DecryptFileA(LPCSTR lpFileName, DWORD dwReserved) +{ + UNICODE_STRING FileName; + NTSTATUS Status; + BOOL ret; + + Status = RtlCreateUnicodeStringFromAsciiz(&FileName, lpFileName); + if (!NT_SUCCESS(Status)) + { + SetLastError(RtlNtStatusToDosError(Status)); + return FALSE; + } + + ret = DecryptFileW(FileName.Buffer, dwReserved); + + if (FileName.Buffer != NULL) + RtlFreeUnicodeString(&FileName); + return ret; +} + + +/* + * @unimplemented + */ +BOOL WINAPI DecryptFileW(LPCWSTR lpFileName, DWORD dwReserved) +{ + FIXME("%s(%S) not implemented!\n", __FUNCTION__, lpFileName); + return TRUE; +} + + +/* + * @implemented + */ +BOOL WINAPI +EncryptFileA(LPCSTR lpFileName) +{ + UNICODE_STRING FileName; + NTSTATUS Status; + BOOL ret; + + Status = RtlCreateUnicodeStringFromAsciiz(&FileName, lpFileName); + if (!NT_SUCCESS(Status)) + { + SetLastError(RtlNtStatusToDosError(Status)); + return FALSE; + } + + ret = EncryptFileW(FileName.Buffer); + + if (FileName.Buffer != NULL) + RtlFreeUnicodeString(&FileName); + return ret; +} + + +/* + * @unimplemented + */ +BOOL WINAPI +EncryptFileW(LPCWSTR lpFileName) +{ + FIXME("%s() not implemented!\n", __FUNCTION__); + return TRUE; +} + +/* EOF */ diff --git a/dll/win32/advapi32/sec/cred.c b/dll/win32/advapi32/sec/cred.c index e8e0283b2ee..cd74a6a17d0 100644 --- a/dll/win32/advapi32/sec/cred.c +++ b/dll/win32/advapi32/sec/cred.c @@ -103,16 +103,22 @@ static DWORD registry_read_credential(HKEY hkey, PCREDENTIALW credential, credential->TargetName = (LPWSTR)buffer; ret = RegQueryValueExW(hkey, NULL, 0, &type, (LPVOID)credential->TargetName, &count); - if (ret != ERROR_SUCCESS || type != REG_SZ) return ret; + if (ret != ERROR_SUCCESS) + return ret; + else if (type != REG_SZ) + return ERROR_REGISTRY_CORRUPT; buffer += count; } ret = RegQueryValueExW(hkey, wszCommentValue, 0, &type, NULL, &count); - if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS) - return ret; - else if (type != REG_SZ) - return ERROR_REGISTRY_CORRUPT; - *len += count; + if (ret != ERROR_FILE_NOT_FOUND) + { + if (ret != ERROR_SUCCESS) + return ret; + else if (type != REG_SZ) + return ERROR_REGISTRY_CORRUPT; + *len += count; + } if (credential) { credential->Comment = (LPWSTR)buffer; @@ -129,11 +135,14 @@ static DWORD registry_read_credential(HKEY hkey, PCREDENTIALW credential, } ret = RegQueryValueExW(hkey, wszTargetAliasValue, 0, &type, NULL, &count); - if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS) - return ret; - else if (type != REG_SZ) - return ERROR_REGISTRY_CORRUPT; - *len += count; + if (ret != ERROR_FILE_NOT_FOUND) + { + if (ret != ERROR_SUCCESS) + return ret; + else if (type != REG_SZ) + return ERROR_REGISTRY_CORRUPT; + *len += count; + } if (credential) { credential->TargetAlias = (LPWSTR)buffer; @@ -150,11 +159,14 @@ static DWORD registry_read_credential(HKEY hkey, PCREDENTIALW credential, } ret = RegQueryValueExW(hkey, wszUserNameValue, 0, &type, NULL, &count); - if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS) - return ret; - else if (type != REG_SZ) - return ERROR_REGISTRY_CORRUPT; - *len += count; + if (ret != ERROR_FILE_NOT_FOUND) + { + if (ret != ERROR_SUCCESS) + return ret; + else if (type != REG_SZ) + return ERROR_REGISTRY_CORRUPT; + *len += count; + } if (credential) { credential->UserName = (LPWSTR)buffer; @@ -171,9 +183,12 @@ static DWORD registry_read_credential(HKEY hkey, PCREDENTIALW credential, } ret = read_credential_blob(hkey, key_data, NULL, &count); - if (ret != ERROR_FILE_NOT_FOUND && ret != ERROR_SUCCESS) - return ret; - *len += count; + if (ret != ERROR_FILE_NOT_FOUND) + { + if (ret != ERROR_SUCCESS) + return ret; + *len += count; + } if (credential) { credential->CredentialBlob = (LPBYTE)buffer; diff --git a/dll/win32/advapi32/sec/lsa.c b/dll/win32/advapi32/sec/lsa.c index 90a257b8a60..604dcb26b98 100644 --- a/dll/win32/advapi32/sec/lsa.c +++ b/dll/win32/advapi32/sec/lsa.c @@ -16,59 +16,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(advapi); -/* imported from wine 1.1.14 */ -static void* ADVAPI_GetDomainName(unsigned sz, unsigned ofs) -{ - HKEY key; - LONG ret; - BYTE* ptr = NULL; - UNICODE_STRING* ustr; - - static const WCHAR wVNETSUP[] = { - 'S','y','s','t','e','m','\\', - 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', - 'S','e','r','v','i','c','e','s','\\', - 'V','x','D','\\','V','N','E','T','S','U','P','\0'}; - - ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wVNETSUP, 0, KEY_READ, &key); - if (ret == ERROR_SUCCESS) - { - DWORD size = 0; - static const WCHAR wg[] = { 'W','o','r','k','g','r','o','u','p',0 }; - - ret = RegQueryValueExW(key, wg, NULL, NULL, NULL, &size); - if (ret == ERROR_MORE_DATA || ret == ERROR_SUCCESS) - { - ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz + size); - if (!ptr) return NULL; - ustr = (UNICODE_STRING*)(ptr + ofs); - ustr->MaximumLength = size; - ustr->Buffer = (WCHAR*)(ptr + sz); - ret = RegQueryValueExW(key, wg, NULL, NULL, (LPBYTE)ustr->Buffer, &size); - if (ret != ERROR_SUCCESS) - { - HeapFree(GetProcessHeap(), 0, ptr); - ptr = NULL; - } - else ustr->Length = size - sizeof(WCHAR); - } - RegCloseKey(key); - } - if (!ptr) - { - static const WCHAR wDomain[] = {'D','O','M','A','I','N','\0'}; - ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, - sz + sizeof(wDomain)); - if (!ptr) return NULL; - ustr = (UNICODE_STRING*)(ptr + ofs); - ustr->MaximumLength = sizeof(wDomain); - ustr->Buffer = (WCHAR*)(ptr + sz); - ustr->Length = sizeof(wDomain) - sizeof(WCHAR); - memcpy(ustr->Buffer, wDomain, sizeof(wDomain)); - } - return ptr; -} - static BOOL LsapIsLocalComputer(PLSA_UNICODE_STRING ServerName) { @@ -148,8 +95,9 @@ PLSAPR_SERVER_NAME_unbind(PLSAPR_SERVER_NAME pszSystemName, /* * @implemented */ -NTSTATUS WINAPI -LsaClose(LSA_HANDLE ObjectHandle) +NTSTATUS +WINAPI +LsaClose(IN LSA_HANDLE ObjectHandle) { NTSTATUS Status; @@ -172,8 +120,9 @@ LsaClose(LSA_HANDLE ObjectHandle) /* * @implemented */ -NTSTATUS WINAPI -LsaDelete(LSA_HANDLE ObjectHandle) +NTSTATUS +WINAPI +LsaDelete(IN LSA_HANDLE ObjectHandle) { NTSTATUS Status; @@ -208,6 +157,97 @@ LsaAddAccountRights( return STATUS_OBJECT_NAME_NOT_FOUND; } + +/* + * @implemented + */ +NTSTATUS +WINAPI +LsaAddPrivilegesToAccount(IN LSA_HANDLE AccountHandle, + IN PPRIVILEGE_SET PrivilegeSet) +{ + NTSTATUS Status; + + TRACE("(%p,%p) stub\n", AccountHandle, PrivilegeSet); + + RpcTryExcept + { + Status = LsarAddPrivilegesToAccount((LSAPR_HANDLE)AccountHandle, + (PLSAPR_PRIVILEGE_SET)PrivilegeSet); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + Status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept; + + return Status; +} + + +/* + * @implemented + */ +NTSTATUS +WINAPI +LsaCreateAccount(IN LSA_HANDLE PolicyHandle, + IN PSID AccountSid, + IN ULONG Flags, + OUT PLSA_HANDLE AccountHandle) +{ + NTSTATUS Status; + + TRACE("(%p,%p,0x%08x,%p)\n", PolicyHandle, AccountSid, Flags, AccountHandle); + + RpcTryExcept + { + Status = LsarCreateAccount((LSAPR_HANDLE)PolicyHandle, + AccountSid, + Flags, + AccountHandle); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + Status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept; + + return Status; +} + + +/* + * @implemented + */ +NTSTATUS +WINAPI +LsaCreateTrustedDomain(IN LSA_HANDLE PolicyHandle, + IN PLSA_TRUST_INFORMATION TrustedDomainInformation, + IN ACCESS_MASK DesiredAccess, + OUT PLSA_HANDLE TrustedDomainHandle) +{ + NTSTATUS Status; + + TRACE("(%p,%p,0x%08x,%p)\n", PolicyHandle, TrustedDomainInformation, + DesiredAccess, TrustedDomainHandle); + + RpcTryExcept + { + Status = LsarCreateTrustedDomain((LSAPR_HANDLE)PolicyHandle, + (PLSAPR_TRUST_INFORMATION)TrustedDomainInformation, + DesiredAccess, + (PLSAPR_HANDLE)TrustedDomainHandle); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + Status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept; + + return Status; +} + + /* * @unimplemented */ @@ -226,16 +266,29 @@ LsaCreateTrustedDomainEx( } /* - * @unimplemented + * @implemented */ NTSTATUS WINAPI -LsaDeleteTrustedDomain( - LSA_HANDLE PolicyHandle, - PSID TrustedDomainSid) +LsaDeleteTrustedDomain(IN LSA_HANDLE PolicyHandle, + IN PSID TrustedDomainSid) { - FIXME("(%p,%p) stub\n", PolicyHandle, TrustedDomainSid); - return STATUS_SUCCESS; + NTSTATUS Status; + + TRACE("(%p,%p)\n", PolicyHandle, TrustedDomainSid); + + RpcTryExcept + { + Status = LsarDeleteTrustedDomain((LSAPR_HANDLE)PolicyHandle, + TrustedDomainSid); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + Status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept; + + return Status; } /* @@ -547,12 +600,44 @@ LsaLookupSids(IN LSA_HANDLE PolicyHandle, * @implemented */ ULONG WINAPI -LsaNtStatusToWinError(NTSTATUS Status) +LsaNtStatusToWinError(IN NTSTATUS Status) { TRACE("(%lx)\n", Status); return RtlNtStatusToDosError(Status); } + +/* + * @implemented + */ +NTSTATUS +WINAPI +LsaOpenAccount(IN LSA_HANDLE PolicyHandle, + IN PSID AccountSid, + IN ULONG Flags, + OUT PLSA_HANDLE AccountHandle) +{ + NTSTATUS Status; + + TRACE("(%p,%p,0x%08x,%p)\n", PolicyHandle, AccountSid, Flags, AccountHandle); + + RpcTryExcept + { + Status = LsarOpenAccount((LSAPR_HANDLE)PolicyHandle, + AccountSid, + Flags, + AccountHandle); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + Status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept; + + return Status; +} + + /****************************************************************************** * LsaOpenPolicy * @@ -562,15 +647,14 @@ LsaNtStatusToWinError(NTSTATUS Status) * x3 [] * x4 [] * - * @unimplemented + * @implemented */ NTSTATUS WINAPI -LsaOpenPolicy( - IN PLSA_UNICODE_STRING SystemName, - IN PLSA_OBJECT_ATTRIBUTES ObjectAttributes, - IN ACCESS_MASK DesiredAccess, - IN OUT PLSA_HANDLE PolicyHandle) +LsaOpenPolicy(IN PLSA_UNICODE_STRING SystemName, + IN PLSA_OBJECT_ATTRIBUTES ObjectAttributes, + IN ACCESS_MASK DesiredAccess, + OUT PLSA_HANDLE PolicyHandle) { NTSTATUS Status; @@ -604,20 +688,37 @@ LsaOpenPolicy( /* - * @unimplemented + * @implemented */ NTSTATUS WINAPI -LsaOpenTrustedDomainByName( - LSA_HANDLE PolicyHandle, - PLSA_UNICODE_STRING TrustedDomainName, - ACCESS_MASK DesiredAccess, - PLSA_HANDLE TrustedDomainHandle) +LsaOpenTrustedDomainByName(IN LSA_HANDLE PolicyHandle, + IN PLSA_UNICODE_STRING TrustedDomainName, + IN ACCESS_MASK DesiredAccess, + OUT PLSA_HANDLE TrustedDomainHandle) { - FIXME("(%p,%p,0x%08x,%p) stub\n", PolicyHandle, TrustedDomainName, DesiredAccess, TrustedDomainHandle); - return STATUS_OBJECT_NAME_NOT_FOUND; + NTSTATUS Status; + + TRACE("(%p,%p,0x%08x,%p)\n", PolicyHandle, TrustedDomainName, + DesiredAccess, TrustedDomainHandle); + + RpcTryExcept + { + Status = LsarOpenTrustedDomainByName((LSAPR_HANDLE)PolicyHandle, + (PRPC_UNICODE_STRING)TrustedDomainName, + DesiredAccess, + TrustedDomainHandle); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + Status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept; + + return Status; } + /* * @unimplemented */ @@ -647,103 +748,38 @@ LsaQueryForestTrustInformation( } /* - * @unimplemented + * @implemented */ -NTSTATUS WINAPI -LsaQueryInformationPolicy(LSA_HANDLE PolicyHandle, - POLICY_INFORMATION_CLASS InformationClass, - PVOID *Buffer) +NTSTATUS +WINAPI +LsaQueryInformationPolicy(IN LSA_HANDLE PolicyHandle, + IN POLICY_INFORMATION_CLASS InformationClass, + OUT PVOID *Buffer) { + PLSAPR_POLICY_INFORMATION PolicyInformation = NULL; + NTSTATUS Status; + TRACE("(%p,0x%08x,%p)\n", PolicyHandle, InformationClass, Buffer); - if(!Buffer) return STATUS_INVALID_PARAMETER; - switch (InformationClass) + RpcTryExcept { - case PolicyAuditEventsInformation: /* 2 */ - { - PPOLICY_AUDIT_EVENTS_INFO p = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, - sizeof(POLICY_AUDIT_EVENTS_INFO)); - p->AuditingMode = FALSE; /* no auditing */ - *Buffer = p; - } - break; - case PolicyPrimaryDomainInformation: /* 3 */ - { - /* Only the domain name is valid for the local computer. - * All other fields are zero. - */ - PPOLICY_PRIMARY_DOMAIN_INFO pinfo; - - pinfo = ADVAPI_GetDomainName(sizeof(*pinfo), offsetof(POLICY_PRIMARY_DOMAIN_INFO, Name)); - - TRACE("setting domain to %s\n", debugstr_w(pinfo->Name.Buffer)); - - *Buffer = pinfo; - } - case PolicyAccountDomainInformation: /* 5 */ - { - struct di - { - POLICY_ACCOUNT_DOMAIN_INFO info; - SID sid; - DWORD padding[3]; - WCHAR domain[MAX_COMPUTERNAME_LENGTH + 1]; - }; - - DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1; - struct di * xdi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*xdi)); - - xdi->info.DomainName.MaximumLength = dwSize * sizeof(WCHAR); - xdi->info.DomainName.Buffer = xdi->domain; - if (GetComputerNameW(xdi->info.DomainName.Buffer, &dwSize)) - xdi->info.DomainName.Length = dwSize * sizeof(WCHAR); - - TRACE("setting name to %s\n", debugstr_w(xdi->info.DomainName.Buffer)); - - xdi->info.DomainSid = &xdi->sid; - - /* read the computer SID from the registry */ - if (!ADVAPI_GetComputerSid(&xdi->sid)) - { - HeapFree(GetProcessHeap(), 0, xdi); - - WARN("Computer SID not found\n"); - - return STATUS_UNSUCCESSFUL; - } - - *Buffer = xdi; - } - break; - case PolicyDnsDomainInformation: /* 12 (0xc) */ - { - /* Only the domain name is valid for the local computer. - * All other fields are zero. - */ - PPOLICY_DNS_DOMAIN_INFO pinfo; - - pinfo = ADVAPI_GetDomainName(sizeof(*pinfo), offsetof(POLICY_DNS_DOMAIN_INFO, Name)); - - TRACE("setting domain to %s\n", debugstr_w(pinfo->Name.Buffer)); - - *Buffer = pinfo; - } - break; - case PolicyAuditLogInformation: - case PolicyPdAccountInformation: - case PolicyLsaServerRoleInformation: - case PolicyReplicaSourceInformation: - case PolicyDefaultQuotaInformation: - case PolicyModificationInformation: - case PolicyAuditFullSetInformation: - case PolicyAuditFullQueryInformation: - case PolicyEfsInformation: - { - FIXME("category not implemented\n"); - return STATUS_UNSUCCESSFUL; - } + Status = LsarQueryInformationPolicy((LSAPR_HANDLE)PolicyHandle, + InformationClass, + &PolicyInformation); + *Buffer = PolicyInformation; } - return STATUS_SUCCESS; + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + if (PolicyInformation != NULL) + MIDL_user_free(PolicyInformation); + + Status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept; + + TRACE("Done (Status: 0x%08x)\n", Status); + + return Status; } /* @@ -820,20 +856,36 @@ LsaSetDomainInformationPolicy( return STATUS_UNSUCCESSFUL; } + /* - * @unimplemented + * @implemented */ NTSTATUS WINAPI -LsaSetInformationPolicy( - LSA_HANDLE PolicyHandle, - POLICY_INFORMATION_CLASS InformationClass, - PVOID Buffer) +LsaSetInformationPolicy(IN LSA_HANDLE PolicyHandle, + IN POLICY_INFORMATION_CLASS InformationClass, + IN PVOID Buffer) { - FIXME("(%p,0x%08x,%p) stub\n", PolicyHandle, InformationClass, Buffer); - return STATUS_UNSUCCESSFUL; + NTSTATUS Status; + + TRACE("(%p,0x%08x,%p)\n", PolicyHandle, InformationClass, Buffer); + + RpcTryExcept + { + Status = LsarSetInformationPolicy((LSAPR_HANDLE)PolicyHandle, + InformationClass, + (PLSAPR_POLICY_INFORMATION)Buffer); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + Status = I_RpcMapWin32Status(RpcExceptionCode()); + } + RpcEndExcept; + + return Status; } + /* * @unimplemented */ diff --git a/dll/win32/advapi32/sec/sec.c b/dll/win32/advapi32/sec/sec.c index 2ad2bff688c..99222f3f315 100644 --- a/dll/win32/advapi32/sec/sec.c +++ b/dll/win32/advapi32/sec/sec.c @@ -534,75 +534,4 @@ BuildSecurityDescriptorA(IN PTRUSTEE_A pOwner OPTIONAL, return FALSE; } - -/* - * @unimplemented - */ -BOOL WINAPI DecryptFileW(LPCWSTR lpFileName, DWORD dwReserved) -{ - FIXME("%s(%S) not implemented!\n", __FUNCTION__, lpFileName); - return TRUE; -} - - -/* - * @implemented - */ -BOOL WINAPI -DecryptFileA(LPCSTR lpFileName, DWORD dwReserved) -{ - UNICODE_STRING FileName; - NTSTATUS Status; - BOOL ret; - - Status = RtlCreateUnicodeStringFromAsciiz(&FileName, lpFileName); - if (!NT_SUCCESS(Status)) - { - SetLastError(RtlNtStatusToDosError(Status)); - return FALSE; - } - - ret = DecryptFileW(FileName.Buffer, dwReserved); - - if (FileName.Buffer != NULL) - RtlFreeUnicodeString(&FileName); - return ret; -} - - -/* - * @unimplemented - */ -BOOL WINAPI -EncryptFileW(LPCWSTR lpFileName) -{ - FIXME("%s() not implemented!\n", __FUNCTION__); - return TRUE; -} - - -/* - * @implemented - */ -BOOL WINAPI -EncryptFileA(LPCSTR lpFileName) -{ - UNICODE_STRING FileName; - NTSTATUS Status; - BOOL ret; - - Status = RtlCreateUnicodeStringFromAsciiz(&FileName, lpFileName); - if (!NT_SUCCESS(Status)) - { - SetLastError(RtlNtStatusToDosError(Status)); - return FALSE; - } - - ret = EncryptFileW(FileName.Buffer); - - if (FileName.Buffer != NULL) - RtlFreeUnicodeString(&FileName); - return ret; -} - /* EOF */ diff --git a/dll/win32/lsasrv/lsarpc.c b/dll/win32/lsasrv/lsarpc.c index 6100e0748e3..9055bf608e0 100644 --- a/dll/win32/lsasrv/lsarpc.c +++ b/dll/win32/lsasrv/lsarpc.c @@ -60,9 +60,10 @@ LsapCreateDbHandle(LSA_DB_HANDLE_TYPE HandleType, } -static BOOL +static NTSTATUS LsapValidateDbHandle(LSAPR_HANDLE Handle, - LSA_DB_HANDLE_TYPE HandleType) + LSA_DB_HANDLE_TYPE HandleType, + ACCESS_MASK GrantedAccess) { PLSA_DB_HANDLE DbHandle = (PLSA_DB_HANDLE)Handle; BOOL bValid = FALSE; @@ -83,8 +84,15 @@ LsapValidateDbHandle(LSAPR_HANDLE Handle, } _SEH2_END; + if (bValid == FALSE) + return STATUS_INVALID_HANDLE; - return bValid; + if (GrantedAccess != 0) + { + /* FIXME: Check for granted access rights */ + } + + return STATUS_SUCCESS; } @@ -136,7 +144,7 @@ void __RPC_USER LSAPR_HANDLE_rundown(LSAPR_HANDLE hHandle) /* Function 0 */ -NTSTATUS LsarClose( +NTSTATUS WINAPI LsarClose( LSAPR_HANDLE *ObjectHandle) { NTSTATUS Status = STATUS_SUCCESS; @@ -145,13 +153,15 @@ NTSTATUS LsarClose( // RtlEnterCriticalSection(&PolicyHandleTableLock); - if (LsapValidateDbHandle(*ObjectHandle, LsaDbIgnoreHandle)) + Status = LsapValidateDbHandle(*ObjectHandle, + LsaDbIgnoreHandle, + 0); + + if (Status == STATUS_SUCCESS) { RtlFreeHeap(RtlGetProcessHeap(), 0, *ObjectHandle); *ObjectHandle = NULL; } - else - Status = STATUS_INVALID_HANDLE; // RtlLeaveCriticalSection(&PolicyHandleTableLock); @@ -160,7 +170,7 @@ NTSTATUS LsarClose( /* Function 1 */ -NTSTATUS LsarDelete( +NTSTATUS WINAPI LsarDelete( LSAPR_HANDLE ObjectHandle) { /* Deprecated */ @@ -169,7 +179,7 @@ NTSTATUS LsarDelete( /* Function 2 */ -NTSTATUS LsarEnumeratePrivileges( +NTSTATUS WINAPI LsarEnumeratePrivileges( LSAPR_HANDLE PolicyHandle, DWORD *EnumerationContext, PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer, @@ -181,7 +191,7 @@ NTSTATUS LsarEnumeratePrivileges( /* Function 3 */ -NTSTATUS LsarQuerySecurityObject( +NTSTATUS WINAPI LsarQuerySecurityObject( LSAPR_HANDLE ObjectHandle, SECURITY_INFORMATION SecurityInformation, PLSAPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor) @@ -192,7 +202,7 @@ NTSTATUS LsarQuerySecurityObject( /* Function 4 */ -NTSTATUS LsarSetSecurityObject( +NTSTATUS WINAPI LsarSetSecurityObject( LSAPR_HANDLE ObjectHandle, SECURITY_INFORMATION SecurityInformation, PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor) @@ -203,7 +213,7 @@ NTSTATUS LsarSetSecurityObject( /* Function 5 */ -NTSTATUS LsarChangePassword( +NTSTATUS WINAPI LsarChangePassword( handle_t IDL_handle, PRPC_UNICODE_STRING String1, PRPC_UNICODE_STRING String2, @@ -217,7 +227,7 @@ NTSTATUS LsarChangePassword( /* Function 6 */ -NTSTATUS LsarOpenPolicy( +NTSTATUS WINAPI LsarOpenPolicy( LPWSTR SystemName, PLSAPR_OBJECT_ATTRIBUTES ObjectAttributes, ACCESS_MASK DesiredAccess, @@ -243,18 +253,158 @@ NTSTATUS LsarOpenPolicy( /* Function 7 */ -NTSTATUS LsarQueryInformationPolicy( +NTSTATUS WINAPI LsarQueryInformationPolicy( LSAPR_HANDLE PolicyHandle, POLICY_INFORMATION_CLASS InformationClass, PLSAPR_POLICY_INFORMATION *PolicyInformation) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + + TRACE("LsarQueryInformationPolicy(%p,0x%08x,%p)\n", + PolicyHandle, InformationClass, PolicyInformation); + + if (PolicyInformation) + { + TRACE("*PolicyInformation %p\n", *PolicyInformation); + } + + Status = LsapValidateDbHandle(PolicyHandle, + LsaDbPolicyHandle, + 0); /* FIXME */ + if (!NT_SUCCESS(Status)) + return Status; + + switch (InformationClass) + { + case PolicyAuditEventsInformation: /* 2 */ + { + PLSAPR_POLICY_AUDIT_EVENTS_INFO p = MIDL_user_allocate(sizeof(LSAPR_POLICY_AUDIT_EVENTS_INFO)); + if (p == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + p->AuditingMode = FALSE; /* no auditing */ + p->EventAuditingOptions = NULL; + p->MaximumAuditEventCount = 0; + + *PolicyInformation = (PLSAPR_POLICY_INFORMATION)p; + } + break; + + case PolicyPrimaryDomainInformation: /* 3 */ + { + PLSAPR_POLICY_PRIMARY_DOM_INFO p = MIDL_user_allocate(sizeof(LSAPR_POLICY_PRIMARY_DOM_INFO)); + if (p == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + p->Name.Length = 0; + p->Name.MaximumLength = 0; + p->Name.Buffer = NULL; +#if 0 + p->Name.Length = wcslen(L"COMPUTERNAME"); + p->Name.MaximumLength = p->Name.Length + sizeof(WCHAR); + p->Name.Buffer = MIDL_user_allocate(p->Name.MaximumLength); + if (p->Name.Buffer == NULL) + { + MIDL_user_free(p); + return STATUS_INSUFFICIENT_RESOURCES; + } + + wcscpy(p->Name.Buffer, L"COMPUTERNAME"); +#endif + + p->Sid = NULL; /* no domain, no workgroup */ + + *PolicyInformation = (PLSAPR_POLICY_INFORMATION)p; + } + break; + + case PolicyAccountDomainInformation: /* 5 */ + { + PLSAPR_POLICY_ACCOUNT_DOM_INFO p = MIDL_user_allocate(sizeof(LSAPR_POLICY_ACCOUNT_DOM_INFO)); + if (p == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + p->DomainName.Length = 0; + p->DomainName.MaximumLength = 0; + p->DomainName.Buffer = NULL; +#if 0 + p->DomainName.Length = wcslen(L"COMPUTERNAME"); + p->DomainName.MaximumLength = p->DomainName.Length + sizeof(WCHAR); + p->DomainName.Buffer = MIDL_user_allocate(p->DomainName.MaximumLength); + if (p->DomainName.Buffer == NULL) + { + MIDL_user_free(p); + return STATUS_INSUFFICIENT_RESOURCES; + } + + wcscpy(p->DomainName.Buffer, L"COMPUTERNAME"); +#endif + + p->Sid = NULL; /* no domain, no workgroup */ + + *PolicyInformation = (PLSAPR_POLICY_INFORMATION)p; + } + break; + + case PolicyDnsDomainInformation: /* 12 (0xc) */ + { + PLSAPR_POLICY_DNS_DOMAIN_INFO p = MIDL_user_allocate(sizeof(LSAPR_POLICY_DNS_DOMAIN_INFO)); + if (p == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + p->Name.Length = 0; + p->Name.MaximumLength = 0; + p->Name.Buffer = NULL; +#if 0 + p->Name.Length = wcslen(L"COMPUTERNAME"); + p->Name.MaximumLength = p->Name.Length + sizeof(WCHAR); + p->Name.Buffer = MIDL_user_allocate(p->Name.MaximumLength); + if (p->Name.Buffer == NULL) + { + MIDL_user_free(p); + return STATUS_INSUFFICIENT_RESOURCES; + } + + wcscpy(p->Name.Buffer, L"COMPUTERNAME"); +#endif + + p->DnsDomainName.Length = 0; + p->DnsDomainName.MaximumLength = 0; + p->DnsDomainName.Buffer = NULL; + + p->DnsForestName.Length = 0; + p->DnsForestName.MaximumLength = 0; + p->DnsForestName.Buffer = 0; + + memset(&p->DomainGuid, 0, sizeof(GUID)); + + p->Sid = NULL; /* no domain, no workgroup */ + + *PolicyInformation = (PLSAPR_POLICY_INFORMATION)p; + } + break; + + case PolicyAuditLogInformation: + case PolicyPdAccountInformation: + case PolicyLsaServerRoleInformation: + case PolicyReplicaSourceInformation: + case PolicyDefaultQuotaInformation: + case PolicyModificationInformation: + case PolicyAuditFullSetInformation: + case PolicyAuditFullQueryInformation: + case PolicyEfsInformation: + { + FIXME("category not implemented\n"); + return STATUS_UNSUCCESSFUL; + } + } + + return STATUS_SUCCESS; } /* Function 8 */ -NTSTATUS LsarSetInformationPolicy( +NTSTATUS WINAPI LsarSetInformationPolicy( LSAPR_HANDLE PolicyHandle, POLICY_INFORMATION_CLASS InformationClass, PLSAPR_POLICY_INFORMATION PolicyInformation) @@ -265,7 +415,7 @@ NTSTATUS LsarSetInformationPolicy( /* Function 9 */ -NTSTATUS LsarClearAuditLog( +NTSTATUS WINAPI LsarClearAuditLog( LSAPR_HANDLE ObjectHandle) { /* Deprecated */ @@ -274,7 +424,7 @@ NTSTATUS LsarClearAuditLog( /* Function 10 */ -NTSTATUS LsarCreateAccount( +NTSTATUS WINAPI LsarCreateAccount( LSAPR_HANDLE PolicyHandle, PRPC_SID AccountSid, ACCESS_MASK DesiredAccess, @@ -286,7 +436,7 @@ NTSTATUS LsarCreateAccount( /* Function 11 */ -NTSTATUS LsarEnumerateAccounts( +NTSTATUS WINAPI LsarEnumerateAccounts( LSAPR_HANDLE PolicyHandle, DWORD *EnumerationContext, PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer, @@ -298,7 +448,7 @@ NTSTATUS LsarEnumerateAccounts( /* Function 12 */ -NTSTATUS LsarCreateTrustedDomain( +NTSTATUS WINAPI LsarCreateTrustedDomain( LSAPR_HANDLE PolicyHandle, PLSAPR_TRUST_INFORMATION TrustedDomainInformation, ACCESS_MASK DesiredAccess, @@ -310,7 +460,7 @@ NTSTATUS LsarCreateTrustedDomain( /* Function 13 */ -NTSTATUS LsarEnumerateTrustedDomains( +NTSTATUS WINAPI LsarEnumerateTrustedDomains( LSAPR_HANDLE PolicyHandle, DWORD *EnumerationContext, PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer, @@ -322,7 +472,7 @@ NTSTATUS LsarEnumerateTrustedDomains( /* Function 14 */ -NTSTATUS LsarLookupNames( +NTSTATUS WINAPI LsarLookupNames( LSAPR_HANDLE PolicyHandle, DWORD Count, PRPC_UNICODE_STRING Names, @@ -420,7 +570,7 @@ NTSTATUS LsarLookupNames( /* Function 15 */ -NTSTATUS LsarLookupSids( +NTSTATUS WINAPI LsarLookupSids( LSAPR_HANDLE PolicyHandle, PLSAPR_SID_ENUM_BUFFER SidEnumBuffer, PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains, @@ -513,7 +663,7 @@ NTSTATUS LsarLookupSids( /* Function 16 */ -NTSTATUS LsarCreateSecret( +NTSTATUS WINAPI LsarCreateSecret( LSAPR_HANDLE PolicyHandle, PRPC_UNICODE_STRING SecretName, ACCESS_MASK DesiredAccess, @@ -525,7 +675,7 @@ NTSTATUS LsarCreateSecret( /* Function 17 */ -NTSTATUS LsarOpenAccount( +NTSTATUS WINAPI LsarOpenAccount( LSAPR_HANDLE PolicyHandle, PRPC_SID AccountSid, ACCESS_MASK DesiredAccess, @@ -537,7 +687,7 @@ NTSTATUS LsarOpenAccount( /* Function 18 */ -NTSTATUS LsarEnumeratePrivilegesAccount( +NTSTATUS WINAPI LsarEnumeratePrivilegesAccount( LSAPR_HANDLE AccountHandle, PLSAPR_PRIVILEGE_SET *Privileges) { @@ -547,7 +697,7 @@ NTSTATUS LsarEnumeratePrivilegesAccount( /* Function 19 */ -NTSTATUS LsarAddPrivilegesToAccount( +NTSTATUS WINAPI LsarAddPrivilegesToAccount( LSAPR_HANDLE AccountHandle, PLSAPR_PRIVILEGE_SET Privileges) { @@ -557,7 +707,7 @@ NTSTATUS LsarAddPrivilegesToAccount( /* Function 20 */ -NTSTATUS LsarRemovePrivilegesFromAccount( +NTSTATUS WINAPI LsarRemovePrivilegesFromAccount( LSAPR_HANDLE AccountHandle, BOOL AllPrivileges, PLSAPR_PRIVILEGE_SET Privileges) @@ -568,7 +718,7 @@ NTSTATUS LsarRemovePrivilegesFromAccount( /* Function 21 */ -NTSTATUS LsarGetQuotasForAccount( +NTSTATUS WINAPI LsarGetQuotasForAccount( LSAPR_HANDLE AccountHandle, PQUOTA_LIMITS QuotaLimits) { @@ -578,7 +728,7 @@ NTSTATUS LsarGetQuotasForAccount( /* Function 22 */ -NTSTATUS LsarSetQuotasForAccount( +NTSTATUS WINAPI LsarSetQuotasForAccount( LSAPR_HANDLE AccountHandle, PQUOTA_LIMITS QuotaLimits) { @@ -588,7 +738,7 @@ NTSTATUS LsarSetQuotasForAccount( /* Function 23 */ -NTSTATUS LsarGetSystemAccessAccount( +NTSTATUS WINAPI LsarGetSystemAccessAccount( LSAPR_HANDLE AccountHandle, ACCESS_MASK *SystemAccess) { @@ -598,7 +748,7 @@ NTSTATUS LsarGetSystemAccessAccount( /* Function 24 */ -NTSTATUS LsarSetSystemAccessAccount( +NTSTATUS WINAPI LsarSetSystemAccessAccount( LSAPR_HANDLE AccountHandle, ACCESS_MASK SystemAccess) { @@ -608,7 +758,7 @@ NTSTATUS LsarSetSystemAccessAccount( /* Function 25 */ -NTSTATUS LsarOpenTrustedDomain( +NTSTATUS WINAPI LsarOpenTrustedDomain( LSAPR_HANDLE PolicyHandle, PRPC_SID TrustedDomainSid, ACCESS_MASK DesiredAccess, @@ -620,7 +770,7 @@ NTSTATUS LsarOpenTrustedDomain( /* Function 26 */ -NTSTATUS LsarQueryInfoTrustedDomain( +NTSTATUS WINAPI LsarQueryInfoTrustedDomain( LSAPR_HANDLE TrustedDomainHandle, TRUSTED_INFORMATION_CLASS InformationClass, PLSAPR_TRUSTED_DOMAIN_INFO *TrustedDomainInformation) @@ -631,7 +781,7 @@ NTSTATUS LsarQueryInfoTrustedDomain( /* Function 27 */ -NTSTATUS LsarSetInformationTrustedDomain( +NTSTATUS WINAPI LsarSetInformationTrustedDomain( LSAPR_HANDLE TrustedDomainHandle, TRUSTED_INFORMATION_CLASS InformationClass, PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation) @@ -642,7 +792,7 @@ NTSTATUS LsarSetInformationTrustedDomain( /* Function 28 */ -NTSTATUS LsarOpenSecret( +NTSTATUS WINAPI LsarOpenSecret( LSAPR_HANDLE PolicyHandle, PRPC_UNICODE_STRING SecretName, ACCESS_MASK DesiredAccess, @@ -654,7 +804,7 @@ NTSTATUS LsarOpenSecret( /* Function 29 */ -NTSTATUS LsarSetSecret( +NTSTATUS WINAPI LsarSetSecret( LSAPR_HANDLE *SecretHandle, PLSAPR_CR_CIPHER_VALUE EncryptedCurrentValue, PLSAPR_CR_CIPHER_VALUE EncryptedOldValue) @@ -665,7 +815,7 @@ NTSTATUS LsarSetSecret( /* Function 30 */ -NTSTATUS LsarQuerySecret( +NTSTATUS WINAPI LsarQuerySecret( LSAPR_HANDLE SecretHandle, PLSAPR_CR_CIPHER_VALUE *EncryptedCurrentValue, PLARGE_INTEGER CurrentValueSetTime, @@ -678,7 +828,7 @@ NTSTATUS LsarQuerySecret( /* Function 31 */ -NTSTATUS LsarLookupPrivilegeValue( +NTSTATUS WINAPI LsarLookupPrivilegeValue( LSAPR_HANDLE PolicyHandle, PRPC_UNICODE_STRING Name, PLUID Value) @@ -688,10 +838,13 @@ NTSTATUS LsarLookupPrivilegeValue( TRACE("LsarLookupPrivilegeValue(%p, %wZ, %p)\n", PolicyHandle, Name, Value); - if (!LsapValidateDbHandle(PolicyHandle, LsaDbPolicyHandle)) + Status = LsapValidateDbHandle(PolicyHandle, + LsaDbPolicyHandle, + 0); /* FIXME */ + if (!NT_SUCCESS(Status)) { - ERR("Invalid handle\n"); - return STATUS_INVALID_HANDLE; + ERR("Invalid handle (Status %lx)\n", Status); + return Status; } TRACE("Privilege: %wZ\n", Name); @@ -704,7 +857,7 @@ NTSTATUS LsarLookupPrivilegeValue( /* Function 32 */ -NTSTATUS LsarLookupPrivilegeName( +NTSTATUS WINAPI LsarLookupPrivilegeName( LSAPR_HANDLE PolicyHandle, PLUID Value, PRPC_UNICODE_STRING *Name) @@ -714,10 +867,13 @@ NTSTATUS LsarLookupPrivilegeName( TRACE("LsarLookupPrivilegeName(%p, %p, %p)\n", PolicyHandle, Value, Name); - if (!LsapValidateDbHandle(PolicyHandle, LsaDbPolicyHandle)) + Status = LsapValidateDbHandle(PolicyHandle, + LsaDbPolicyHandle, + 0); /* FIXME */ + if (!NT_SUCCESS(Status)) { ERR("Invalid handle\n"); - return STATUS_INVALID_HANDLE; + return Status; } Status = LsarpLookupPrivilegeName(Value, (PUNICODE_STRING*)Name); @@ -727,7 +883,7 @@ NTSTATUS LsarLookupPrivilegeName( /* Function 33 */ -NTSTATUS LsarLookupPrivilegeDisplayName( +NTSTATUS WINAPI LsarLookupPrivilegeDisplayName( LSAPR_HANDLE PolicyHandle, PRPC_UNICODE_STRING Name, USHORT ClientLanguage, @@ -741,7 +897,7 @@ NTSTATUS LsarLookupPrivilegeDisplayName( /* Function 34 */ -NTSTATUS LsarDeleteObject( +NTSTATUS WINAPI LsarDeleteObject( LSAPR_HANDLE *ObjectHandle) { UNIMPLEMENTED; @@ -750,7 +906,7 @@ NTSTATUS LsarDeleteObject( /* Function 35 */ -NTSTATUS LsarEnumerateAccountsWithUserRight( +NTSTATUS WINAPI LsarEnumerateAccountsWithUserRight( LSAPR_HANDLE PolicyHandle, PRPC_UNICODE_STRING UserRight, PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer) @@ -761,15 +917,20 @@ NTSTATUS LsarEnumerateAccountsWithUserRight( /* Function 36 */ -NTSTATUS LsarEnmuerateAccountRights( +NTSTATUS WINAPI LsarEnmuerateAccountRights( LSAPR_HANDLE PolicyHandle, PRPC_SID AccountSid, PLSAPR_USER_RIGHT_SET UserRights) { + NTSTATUS Status; + FIXME("(%p,%p,%p) stub\n", PolicyHandle, AccountSid, UserRights); - if (!LsapValidateDbHandle(PolicyHandle, LsaDbPolicyHandle)) - return STATUS_INVALID_HANDLE; + Status = LsapValidateDbHandle(PolicyHandle, + LsaDbPolicyHandle, + 0); /* FIXME */ + if (!NT_SUCCESS(Status)) + return Status; UserRights->Entries = 0; UserRights->UserRights = NULL; @@ -778,7 +939,7 @@ NTSTATUS LsarEnmuerateAccountRights( /* Function 37 */ -NTSTATUS LsarAddAccountRights( +NTSTATUS WINAPI LsarAddAccountRights( LSAPR_HANDLE PolicyHandle, PRPC_SID AccountSid, PLSAPR_USER_RIGHT_SET UserRights) @@ -789,7 +950,7 @@ NTSTATUS LsarAddAccountRights( /* Function 38 */ -NTSTATUS LsarRemoveAccountRights( +NTSTATUS WINAPI LsarRemoveAccountRights( LSAPR_HANDLE PolicyHandle, PRPC_SID AccountSid, BOOL AllRights, @@ -801,7 +962,7 @@ NTSTATUS LsarRemoveAccountRights( /* Function 39 */ -NTSTATUS LsarQueryTrustedDomainInfo( +NTSTATUS WINAPI LsarQueryTrustedDomainInfo( LSAPR_HANDLE PolicyHandle, PRPC_SID TrustedDomainSid, TRUSTED_INFORMATION_CLASS InformationClass, @@ -813,7 +974,7 @@ NTSTATUS LsarQueryTrustedDomainInfo( /* Function 40 */ -NTSTATUS LsarSetTrustedDomainInfo( +NTSTATUS WINAPI LsarSetTrustedDomainInfo( LSAPR_HANDLE PolicyHandle, PRPC_SID TrustedDomainSid, TRUSTED_INFORMATION_CLASS InformationClass, @@ -825,7 +986,7 @@ NTSTATUS LsarSetTrustedDomainInfo( /* Function 41 */ -NTSTATUS LsarDeleteTrustedDomain( +NTSTATUS WINAPI LsarDeleteTrustedDomain( LSAPR_HANDLE PolicyHandle, PRPC_SID TrustedDomainSid) { @@ -835,7 +996,7 @@ NTSTATUS LsarDeleteTrustedDomain( /* Function 42 */ -NTSTATUS LsarStorePrivateData( +NTSTATUS WINAPI LsarStorePrivateData( LSAPR_HANDLE PolicyHandle, PRPC_UNICODE_STRING KeyName, PLSAPR_CR_CIPHER_VALUE EncryptedData) @@ -846,7 +1007,7 @@ NTSTATUS LsarStorePrivateData( /* Function 43 */ -NTSTATUS LsarRetrievePrivateData( +NTSTATUS WINAPI LsarRetrievePrivateData( LSAPR_HANDLE PolicyHandle, PRPC_UNICODE_STRING KeyName, PLSAPR_CR_CIPHER_VALUE *EncryptedData) @@ -857,7 +1018,7 @@ NTSTATUS LsarRetrievePrivateData( /* Function 44 */ -NTSTATUS LsarOpenPolicy2( +NTSTATUS WINAPI LsarOpenPolicy2( LPWSTR SystemName, PLSAPR_OBJECT_ATTRIBUTES ObjectAttributes, ACCESS_MASK DesiredAccess, @@ -869,7 +1030,7 @@ NTSTATUS LsarOpenPolicy2( /* Function 45 */ -NTSTATUS LsarGetUserName( +NTSTATUS WINAPI LsarGetUserName( LPWSTR SystemName, PRPC_UNICODE_STRING *UserName, PRPC_UNICODE_STRING *DomainName) @@ -880,7 +1041,7 @@ NTSTATUS LsarGetUserName( /* Function 46 */ -NTSTATUS LsarQueryInformationPolicy2( +NTSTATUS WINAPI LsarQueryInformationPolicy2( LSAPR_HANDLE PolicyHandle, POLICY_INFORMATION_CLASS InformationClass, unsigned long *PolicyInformation) @@ -891,7 +1052,7 @@ NTSTATUS LsarQueryInformationPolicy2( /* Function 47 */ -NTSTATUS LsarSetInformationPolicy2( +NTSTATUS WINAPI LsarSetInformationPolicy2( LSAPR_HANDLE PolicyHandle, POLICY_INFORMATION_CLASS InformationClass, unsigned long PolicyInformation) @@ -902,7 +1063,7 @@ NTSTATUS LsarSetInformationPolicy2( /* Function 48 */ -NTSTATUS LsarQueryTrustedDomainInfoByName( +NTSTATUS WINAPI LsarQueryTrustedDomainInfoByName( LSAPR_HANDLE PolicyHandle, PRPC_UNICODE_STRING TrustedDomainName, POLICY_INFORMATION_CLASS InformationClass, @@ -914,7 +1075,7 @@ NTSTATUS LsarQueryTrustedDomainInfoByName( /* Function 49 */ -NTSTATUS LsarSetTrustedDomainInfoByName( +NTSTATUS WINAPI LsarSetTrustedDomainInfoByName( LSAPR_HANDLE PolicyHandle, PRPC_UNICODE_STRING TrustedDomainName, POLICY_INFORMATION_CLASS InformationClass, @@ -926,7 +1087,7 @@ NTSTATUS LsarSetTrustedDomainInfoByName( /* Function 50 */ -NTSTATUS LsarEnumerateTrustedDomainsEx( +NTSTATUS WINAPI LsarEnumerateTrustedDomainsEx( LSAPR_HANDLE PolicyHandle, DWORD *EnumerationContext, PLSAPR_TRUSTED_ENUM_BUFFER_EX EnumerationBuffer, @@ -938,7 +1099,7 @@ NTSTATUS LsarEnumerateTrustedDomainsEx( /* Function 51 */ -NTSTATUS LsarCreateTrustedDomainEx( +NTSTATUS WINAPI LsarCreateTrustedDomainEx( LSAPR_HANDLE PolicyHandle, PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX TrustedDomainInformation, PLSAPR_TRUSTED_DOMAIN_AUTH_INFORMATION AuthentificationInformation, @@ -951,7 +1112,7 @@ NTSTATUS LsarCreateTrustedDomainEx( /* Function 52 */ -NTSTATUS LsarSetPolicyReplicationHandle( +NTSTATUS WINAPI LsarSetPolicyReplicationHandle( PLSAPR_HANDLE PolicyHandle) { /* Deprecated */ @@ -960,7 +1121,7 @@ NTSTATUS LsarSetPolicyReplicationHandle( /* Function 53 */ -NTSTATUS LsarQueryDomainInformationPolicy( +NTSTATUS WINAPI LsarQueryDomainInformationPolicy( LSAPR_HANDLE PolicyHandle, POLICY_INFORMATION_CLASS InformationClass, unsigned long *PolicyInformation) @@ -971,7 +1132,7 @@ NTSTATUS LsarQueryDomainInformationPolicy( /* Function 54 */ -NTSTATUS LsarSetDomainInformationPolicy( +NTSTATUS WINAPI LsarSetDomainInformationPolicy( LSAPR_HANDLE PolicyHandle, POLICY_INFORMATION_CLASS InformationClass, unsigned long PolicyInformation) @@ -982,7 +1143,7 @@ NTSTATUS LsarSetDomainInformationPolicy( /* Function 55 */ -NTSTATUS LsarOpenTrustedDomainByName( +NTSTATUS WINAPI LsarOpenTrustedDomainByName( LSAPR_HANDLE PolicyHandle, PRPC_UNICODE_STRING TrustedDomainName, ACCESS_MASK DesiredAccess, @@ -994,7 +1155,7 @@ NTSTATUS LsarOpenTrustedDomainByName( /* Function 56 */ -NTSTATUS LsarTestCall( +NTSTATUS WINAPI LsarTestCall( handle_t hBinding) { UNIMPLEMENTED; @@ -1003,7 +1164,7 @@ NTSTATUS LsarTestCall( /* Function 57 */ -NTSTATUS LsarLookupSids2( +NTSTATUS WINAPI LsarLookupSids2( LSAPR_HANDLE PolicyHandle, PLSAPR_SID_ENUM_BUFFER SidEnumBuffer, PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains, @@ -1019,7 +1180,7 @@ NTSTATUS LsarLookupSids2( /* Function 58 */ -NTSTATUS LsarLookupNames2( +NTSTATUS WINAPI LsarLookupNames2( LSAPR_HANDLE PolicyHandle, DWORD Count, PRPC_UNICODE_STRING Names, @@ -1036,7 +1197,7 @@ NTSTATUS LsarLookupNames2( /* Function 59 */ -NTSTATUS LsarCreateTrustedDomainEx2( +NTSTATUS WINAPI LsarCreateTrustedDomainEx2( LSAPR_HANDLE PolicyHandle, PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX TrustedDomainInformation, PLSAPR_TRUSTED_DOMAIN_AUTH_INFORMATION_INTERNAL AuthentificationInformation, @@ -1049,7 +1210,7 @@ NTSTATUS LsarCreateTrustedDomainEx2( /* Function 60 */ -NTSTATUS CredrWrite( +NTSTATUS WINAPI CredrWrite( handle_t hBinding) { UNIMPLEMENTED; @@ -1058,7 +1219,7 @@ NTSTATUS CredrWrite( /* Function 61 */ -NTSTATUS CredrRead( +NTSTATUS WINAPI CredrRead( handle_t hBinding) { UNIMPLEMENTED; @@ -1067,7 +1228,7 @@ NTSTATUS CredrRead( /* Function 62 */ -NTSTATUS CredrEnumerate( +NTSTATUS WINAPI CredrEnumerate( handle_t hBinding) { UNIMPLEMENTED; @@ -1076,7 +1237,7 @@ NTSTATUS CredrEnumerate( /* Function 63 */ -NTSTATUS CredrWriteDomainCredentials( +NTSTATUS WINAPI CredrWriteDomainCredentials( handle_t hBinding) { UNIMPLEMENTED; @@ -1085,7 +1246,7 @@ NTSTATUS CredrWriteDomainCredentials( /* Function 64 */ -NTSTATUS CredrReadDomainCredentials( +NTSTATUS WINAPI CredrReadDomainCredentials( handle_t hBinding) { UNIMPLEMENTED; @@ -1094,7 +1255,7 @@ NTSTATUS CredrReadDomainCredentials( /* Function 65 */ -NTSTATUS CredrDelete( +NTSTATUS WINAPI CredrDelete( handle_t hBinding) { UNIMPLEMENTED; @@ -1103,7 +1264,7 @@ NTSTATUS CredrDelete( /* Function 66 */ -NTSTATUS CredrGetTargetInfo( +NTSTATUS WINAPI CredrGetTargetInfo( handle_t hBinding) { UNIMPLEMENTED; @@ -1112,7 +1273,7 @@ NTSTATUS CredrGetTargetInfo( /* Function 67 */ -NTSTATUS CredrProfileLoaded( +NTSTATUS WINAPI CredrProfileLoaded( handle_t hBinding) { UNIMPLEMENTED; @@ -1121,7 +1282,7 @@ NTSTATUS CredrProfileLoaded( /* Function 68 */ -NTSTATUS LsarLookupNames3( +NTSTATUS WINAPI LsarLookupNames3( LSAPR_HANDLE PolicyHandle, DWORD Count, PRPC_UNICODE_STRING Names, @@ -1138,7 +1299,7 @@ NTSTATUS LsarLookupNames3( /* Function 69 */ -NTSTATUS CredrGetSessionTypes( +NTSTATUS WINAPI CredrGetSessionTypes( handle_t hBinding) { UNIMPLEMENTED; @@ -1147,7 +1308,7 @@ NTSTATUS CredrGetSessionTypes( /* Function 70 */ -NTSTATUS LsarRegisterAuditEvent( +NTSTATUS WINAPI LsarRegisterAuditEvent( handle_t hBinding) { UNIMPLEMENTED; @@ -1156,7 +1317,7 @@ NTSTATUS LsarRegisterAuditEvent( /* Function 71 */ -NTSTATUS LsarGenAuditEvent( +NTSTATUS WINAPI LsarGenAuditEvent( handle_t hBinding) { UNIMPLEMENTED; @@ -1165,7 +1326,7 @@ NTSTATUS LsarGenAuditEvent( /* Function 72 */ -NTSTATUS LsarUnregisterAuditEvent( +NTSTATUS WINAPI LsarUnregisterAuditEvent( handle_t hBinding) { UNIMPLEMENTED; @@ -1174,7 +1335,7 @@ NTSTATUS LsarUnregisterAuditEvent( /* Function 73 */ -NTSTATUS LsarQueryForestTrustInformation( +NTSTATUS WINAPI LsarQueryForestTrustInformation( LSAPR_HANDLE PolicyHandle, PLSA_UNICODE_STRING TrustedDomainName, LSA_FOREST_TRUST_RECORD_TYPE HighestRecordType, @@ -1186,7 +1347,7 @@ NTSTATUS LsarQueryForestTrustInformation( /* Function 74 */ -NTSTATUS LsarSetForestTrustInformation( +NTSTATUS WINAPI LsarSetForestTrustInformation( LSAPR_HANDLE PolicyHandle, PLSA_UNICODE_STRING TrustedDomainName, LSA_FOREST_TRUST_RECORD_TYPE HighestRecordType, @@ -1200,7 +1361,7 @@ NTSTATUS LsarSetForestTrustInformation( /* Function 75 */ -NTSTATUS CredrRename( +NTSTATUS WINAPI CredrRename( handle_t hBinding) { UNIMPLEMENTED; @@ -1209,7 +1370,7 @@ NTSTATUS CredrRename( /* Function 76 */ -NTSTATUS LsarLookupSids3( +NTSTATUS WINAPI LsarLookupSids3( LSAPR_HANDLE PolicyHandle, PLSAPR_SID_ENUM_BUFFER SidEnumBuffer, PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains, @@ -1225,7 +1386,7 @@ NTSTATUS LsarLookupSids3( /* Function 77 */ -NTSTATUS LsarLookupNames4( +NTSTATUS WINAPI LsarLookupNames4( handle_t RpcHandle, DWORD Count, PRPC_UNICODE_STRING Names, @@ -1242,7 +1403,7 @@ NTSTATUS LsarLookupNames4( /* Function 78 */ -NTSTATUS LsarOpenPolicySce( +NTSTATUS WINAPI LsarOpenPolicySce( handle_t hBinding) { UNIMPLEMENTED; @@ -1251,7 +1412,7 @@ NTSTATUS LsarOpenPolicySce( /* Function 79 */ -NTSTATUS LsarAdtRegisterSecurityEventSource( +NTSTATUS WINAPI LsarAdtRegisterSecurityEventSource( handle_t hBinding) { UNIMPLEMENTED; @@ -1260,7 +1421,7 @@ NTSTATUS LsarAdtRegisterSecurityEventSource( /* Function 80 */ -NTSTATUS LsarAdtUnregisterSecurityEventSource( +NTSTATUS WINAPI LsarAdtUnregisterSecurityEventSource( handle_t hBinding) { UNIMPLEMENTED; @@ -1269,7 +1430,7 @@ NTSTATUS LsarAdtUnregisterSecurityEventSource( /* Function 81 */ -NTSTATUS LsarAdtReportSecurityEvent( +NTSTATUS WINAPI LsarAdtReportSecurityEvent( handle_t hBinding) { UNIMPLEMENTED; @@ -1278,7 +1439,7 @@ NTSTATUS LsarAdtReportSecurityEvent( /* Function 82 */ -NTSTATUS CredrFindBestCredential( +NTSTATUS WINAPI CredrFindBestCredential( handle_t hBinding) { UNIMPLEMENTED; @@ -1287,7 +1448,7 @@ NTSTATUS CredrFindBestCredential( /* Function 83 */ -NTSTATUS LsarSetAuditPolicy( +NTSTATUS WINAPI LsarSetAuditPolicy( handle_t hBinding) { UNIMPLEMENTED; @@ -1296,7 +1457,7 @@ NTSTATUS LsarSetAuditPolicy( /* Function 84 */ -NTSTATUS LsarQueryAuditPolicy( +NTSTATUS WINAPI LsarQueryAuditPolicy( handle_t hBinding) { UNIMPLEMENTED; @@ -1305,7 +1466,7 @@ NTSTATUS LsarQueryAuditPolicy( /* Function 85 */ -NTSTATUS LsarEnumerateAuditPolicy( +NTSTATUS WINAPI LsarEnumerateAuditPolicy( handle_t hBinding) { UNIMPLEMENTED; @@ -1314,7 +1475,7 @@ NTSTATUS LsarEnumerateAuditPolicy( /* Function 86 */ -NTSTATUS LsarEnumerateAuditCategories( +NTSTATUS WINAPI LsarEnumerateAuditCategories( handle_t hBinding) { UNIMPLEMENTED; @@ -1323,7 +1484,7 @@ NTSTATUS LsarEnumerateAuditCategories( /* Function 87 */ -NTSTATUS LsarEnumerateAuditSubCategories( +NTSTATUS WINAPI LsarEnumerateAuditSubCategories( handle_t hBinding) { UNIMPLEMENTED; @@ -1332,7 +1493,7 @@ NTSTATUS LsarEnumerateAuditSubCategories( /* Function 88 */ -NTSTATUS LsarLookupAuditCategoryName( +NTSTATUS WINAPI LsarLookupAuditCategoryName( handle_t hBinding) { UNIMPLEMENTED; @@ -1341,7 +1502,7 @@ NTSTATUS LsarLookupAuditCategoryName( /* Function 89 */ -NTSTATUS LsarLookupAuditSubCategoryName( +NTSTATUS WINAPI LsarLookupAuditSubCategoryName( handle_t hBinding) { UNIMPLEMENTED; @@ -1350,7 +1511,7 @@ NTSTATUS LsarLookupAuditSubCategoryName( /* Function 90 */ -NTSTATUS LsarSetAuditSecurity( +NTSTATUS WINAPI LsarSetAuditSecurity( handle_t hBinding) { UNIMPLEMENTED; @@ -1359,7 +1520,7 @@ NTSTATUS LsarSetAuditSecurity( /* Function 91 */ -NTSTATUS LsarQueryAuditSecurity( +NTSTATUS WINAPI LsarQueryAuditSecurity( handle_t hBinding) { UNIMPLEMENTED; @@ -1368,7 +1529,7 @@ NTSTATUS LsarQueryAuditSecurity( /* Function 92 */ -NTSTATUS CredReadByTokenHandle( +NTSTATUS WINAPI CredReadByTokenHandle( handle_t hBinding) { UNIMPLEMENTED; @@ -1377,7 +1538,7 @@ NTSTATUS CredReadByTokenHandle( /* Function 93 */ -NTSTATUS CredrRestoreCredentials( +NTSTATUS WINAPI CredrRestoreCredentials( handle_t hBinding) { UNIMPLEMENTED; @@ -1386,7 +1547,7 @@ NTSTATUS CredrRestoreCredentials( /* Function 94 */ -NTSTATUS CredrBackupCredentials( +NTSTATUS WINAPI CredrBackupCredentials( handle_t hBinding) { UNIMPLEMENTED; diff --git a/dll/win32/lsasrv/lsasrv.c b/dll/win32/lsasrv/lsasrv.c index c6468a47e03..1a007ecef16 100644 --- a/dll/win32/lsasrv/lsasrv.c +++ b/dll/win32/lsasrv/lsasrv.c @@ -64,6 +64,14 @@ LsapInitLsa(VOID) } +NTSTATUS WINAPI +ServiceInit(VOID) +{ + TRACE("ServiceInit() called\n"); + return STATUS_SUCCESS; +} + + void __RPC_FAR * __RPC_USER midl_user_allocate(SIZE_T len) { return RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, len); diff --git a/dll/win32/lsasrv/lsasrv.spec b/dll/win32/lsasrv/lsasrv.spec index 17f5d363039..7372ad6258e 100644 --- a/dll/win32/lsasrv/lsasrv.spec +++ b/dll/win32/lsasrv/lsasrv.spec @@ -30,36 +30,36 @@ @ stub LsaISetupWasRun @ stub LsapAuOpenSam @ stdcall LsapInitLsa() - @ stub LsarAddPrivilegesToAccount - @ stub LsarClose - @ stub LsarCreateAccount - @ stub LsarCreateSecret - @ stub LsarCreateTrustedDomain - @ stub LsarDelete - @ stub LsarEnumerateAccounts - @ stub LsarEnumeratePrivileges - @ stub LsarEnumeratePrivilegesAccount - @ stub LsarEnumerateTrustedDomains - @ stub LsarGetQuotasForAccount - @ stub LsarGetSystemAccessAccount - @ stub LsarLookupNames - @ stub LsarLookupPrivilegeDisplayName - @ stub LsarLookupPrivilegeName - @ stub LsarLookupPrivilegeValue - @ stub LsarLookupSids - @ stub LsarOpenAccount - @ stub LsarOpenPolicy - @ stub LsarOpenSecret - @ stub LsarOpenTrustedDomain - @ stub LsarQueryInfoTrustedDomain - @ stub LsarQueryInformationPolicy - @ stub LsarQuerySecret - @ stub LsarQuerySecurityObject - @ stub LsarRemovePrivilegesFromAccount - @ stub LsarSetInformationPolicy - @ stub LsarSetInformationTrustedDomain - @ stub LsarSetQuotasForAccount - @ stub LsarSetSecret - @ stub LsarSetSecurityObject - @ stub LsarSetSystemAccessAccount - @ stub ServiceInit + @ stdcall LsarAddPrivilegesToAccount(ptr ptr) + @ stdcall LsarClose(ptr) + @ stdcall LsarCreateAccount(ptr ptr long ptr) + @ stdcall LsarCreateSecret(ptr ptr long ptr) + @ stdcall LsarCreateTrustedDomain(ptr ptr long ptr) + @ stdcall LsarDelete(ptr) + @ stdcall LsarEnumerateAccounts(ptr ptr ptr long) + @ stdcall LsarEnumeratePrivileges(ptr ptr ptr long) + @ stdcall LsarEnumeratePrivilegesAccount(ptr ptr) + @ stdcall LsarEnumerateTrustedDomains(ptr ptr ptr long) + @ stdcall LsarGetQuotasForAccount(ptr ptr) + @ stdcall LsarGetSystemAccessAccount(ptr ptr) + @ stdcall LsarLookupNames(ptr long ptr ptr ptr long ptr) + @ stdcall LsarLookupPrivilegeDisplayName(ptr ptr long long ptr ptr) + @ stdcall LsarLookupPrivilegeName(ptr ptr ptr) + @ stdcall LsarLookupPrivilegeValue(ptr ptr ptr) + @ stdcall LsarLookupSids(ptr ptr ptr ptr long ptr) + @ stdcall LsarOpenAccount(ptr ptr long ptr) + @ stdcall LsarOpenPolicy(ptr ptr long ptr) + @ stdcall LsarOpenSecret(ptr ptr long ptr) + @ stdcall LsarOpenTrustedDomain(ptr ptr long ptr) + @ stdcall LsarQueryInfoTrustedDomain(ptr long ptr) + @ stdcall LsarQueryInformationPolicy(ptr long ptr) + @ stdcall LsarQuerySecret(ptr ptr ptr ptr ptr) + @ stdcall LsarQuerySecurityObject(ptr long ptr) + @ stdcall LsarRemovePrivilegesFromAccount(ptr long ptr) + @ stdcall LsarSetInformationPolicy(ptr long ptr) + @ stdcall LsarSetInformationTrustedDomain(ptr long ptr) + @ stdcall LsarSetQuotasForAccount(ptr ptr) + @ stdcall LsarSetSecret(ptr ptr ptr) + @ stdcall LsarSetSecurityObject(ptr long ptr) + @ stdcall LsarSetSystemAccessAccount(ptr long) + @ stdcall ServiceInit() diff --git a/dll/win32/msi/action.c b/dll/win32/msi/action.c index bb559c016af..808386f96e9 100644 --- a/dll/win32/msi/action.c +++ b/dll/win32/msi/action.c @@ -1785,7 +1785,7 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package) BOOL feature_state = ((feature->Level > 0) && (feature->Level <= level)); - if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN)) + if (feature_state && feature->ActionRequest == INSTALLSTATE_UNKNOWN) { if (feature->Attributes & msidbFeatureAttributesFavorSource) msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE); @@ -1814,7 +1814,7 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package) { BOOL selected = feature->Level > 0 && feature->Level <= level; - if (selected && feature->Action == INSTALLSTATE_UNKNOWN) + if (selected && feature->ActionRequest == INSTALLSTATE_UNKNOWN) { msi_feature_set_state(package, feature, feature->Installed); } @@ -1839,7 +1839,7 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package) LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) { if (cl->component->ForceLocalState && - feature->Action == INSTALLSTATE_SOURCE) + feature->ActionRequest == INSTALLSTATE_SOURCE) { msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL); break; @@ -1850,7 +1850,7 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package) { component = cl->component; - switch (feature->Action) + switch (feature->ActionRequest) { case INSTALLSTATE_ABSENT: component->anyAbsent = 1; @@ -1985,7 +1985,7 @@ static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param) VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename ) { static const WCHAR name[] = {'\\',0}; - VS_FIXEDFILEINFO *ret; + VS_FIXEDFILEINFO *ptr, *ret; LPVOID version; DWORD versize, handle; UINT sz; @@ -2002,12 +2002,15 @@ VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename ) GetFileVersionInfoW( filename, 0, versize, version ); - if (!VerQueryValueW( version, name, (LPVOID *)&ret, &sz )) + if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz )) { msi_free( version ); return NULL; } + ret = msi_alloc( sz ); + memcpy( ret, ptr, sz ); + msi_free( version ); return ret; } @@ -2371,17 +2374,43 @@ static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key return ret; } +static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path ) +{ + static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'}; + static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]); + + if (is_64bit && package->platform == PLATFORM_INTEL && + root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len )) + { + UINT size; + WCHAR *path_32node; + + size = (strlenW( path ) + strlenW( szWow6432Node ) + 1) * sizeof(WCHAR); + path_32node = msi_alloc( size ); + if (!path_32node) + return NULL; + + memcpy( path_32node, path, len * sizeof(WCHAR) ); + path_32node[len] = 0; + strcatW( path_32node, szWow6432Node ); + strcatW( path_32node, szBackSlash ); + strcatW( path_32node, path + len ); + return path_32node; + } + + return strdupW( path ); +} + static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param) { MSIPACKAGE *package = param; LPSTR value_data = NULL; HKEY root_key, hkey; DWORD type,size; - LPWSTR deformated; + LPWSTR deformated, uikey, keypath; LPCWSTR szRoot, component, name, key, value; MSICOMPONENT *comp; MSIRECORD * uirow; - LPWSTR uikey; INT root; BOOL check_first = FALSE; UINT rc; @@ -2432,14 +2461,14 @@ static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param) strcpyW(uikey,szRoot); strcatW(uikey,deformated); - if (RegCreateKeyW( root_key, deformated, &hkey)) + keypath = get_keypath( package, root_key, deformated ); + msi_free( deformated ); + if (RegCreateKeyW( root_key, keypath, &hkey )) { - ERR("Could not create key %s\n",debugstr_w(deformated)); - msi_free(deformated); + ERR("Could not create key %s\n", debugstr_w(keypath)); msi_free(uikey); return ERROR_SUCCESS; } - msi_free(deformated); value = MSI_RecordGetString(row,5); if (value) @@ -2554,7 +2583,7 @@ static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID para { MSIPACKAGE *package = param; LPCWSTR component, name, key_str, root_key_str; - LPWSTR deformated_key, deformated_name, ui_key_str; + LPWSTR deformated_key, deformated_name, ui_key_str, keypath; MSICOMPONENT *comp; MSIRECORD *uirow; BOOL delete_key = FALSE; @@ -2610,8 +2639,10 @@ static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID para deformat_string( package, name, &deformated_name ); - delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key ); + keypath = get_keypath( package, hkey_root, deformated_key ); msi_free( deformated_key ); + delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key ); + msi_free( keypath ); uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, ui_key_str ); @@ -2629,7 +2660,7 @@ static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param { MSIPACKAGE *package = param; LPCWSTR component, name, key_str, root_key_str; - LPWSTR deformated_key, deformated_name, ui_key_str; + LPWSTR deformated_key, deformated_name, ui_key_str, keypath; MSICOMPONENT *comp; MSIRECORD *uirow; BOOL delete_key = FALSE; @@ -2682,8 +2713,10 @@ static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param deformat_string( package, name, &deformated_name ); - delete_reg_key_or_value( hkey_root, deformated_key, deformated_name, delete_key ); + keypath = get_keypath( package, hkey_root, deformated_key ); msi_free( deformated_key ); + delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key ); + msi_free( keypath ); uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, ui_key_str ); @@ -3891,7 +3924,7 @@ static UINT msi_publish_patches( MSIPACKAGE *package ) WCHAR *p, *all_patches = NULL; DWORD len = 0; - r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, FALSE ); + r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE ); if (r != ERROR_SUCCESS) return ERROR_FUNCTION_FAILED; @@ -4759,7 +4792,7 @@ static UINT ACTION_RegisterProduct(MSIPACKAGE *package) if (!msi_check_publish(package)) return ERROR_SUCCESS; - rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE); + rc = MSIREG_OpenUninstallKey(package, &hkey, TRUE); if (rc != ERROR_SUCCESS) return rc; @@ -4839,7 +4872,7 @@ static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove) MSIREG_DeleteProductKey(package->ProductCode); MSIREG_DeleteUserDataProductKey(package->ProductCode); - MSIREG_DeleteUninstallKey(package->ProductCode); + MSIREG_DeleteUninstallKey(package); if (package->Context == MSIINSTALLCONTEXT_MACHINE) { @@ -7498,7 +7531,7 @@ UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath, msi_clone_properties( package ); msi_parse_command_line( package, szCommandLine, FALSE ); - msi_adjust_allusers_property( package ); + msi_adjust_privilege_properties( package ); msi_set_context( package ); if (needs_ui_sequence( package)) diff --git a/dll/win32/msi/appsearch.c b/dll/win32/msi/appsearch.c index a516bdad1c8..dfab3960b01 100644 --- a/dll/win32/msi/appsearch.c +++ b/dll/win32/msi/appsearch.c @@ -89,7 +89,7 @@ static UINT ACTION_AppSearchGetSignature(MSIPACKAGE *package, MSISIGNATURE *sig, 'S','i','g','n','a','t','u','r','e',' ', 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e',' ','=',' ', '\'','%','s','\'',0}; - LPWSTR minVersion, maxVersion; + LPWSTR minVersion, maxVersion, p; MSIRECORD *row; DWORD time; @@ -106,6 +106,12 @@ static UINT ACTION_AppSearchGetSignature(MSIPACKAGE *package, MSISIGNATURE *sig, /* get properties */ sig->File = msi_dup_record_field(row,2); + if ((p = strchrW(sig->File, '|'))) + { + p++; + memmove(sig->File, p, (strlenW(p) + 1) * sizeof(WCHAR)); + } + minVersion = msi_dup_record_field(row,3); if (minVersion) { diff --git a/dll/win32/msi/classes.c b/dll/win32/msi/classes.c index 30ee36b4bfd..beaf1f0ff93 100644 --- a/dll/win32/msi/classes.c +++ b/dll/win32/msi/classes.c @@ -804,12 +804,19 @@ static UINT register_appid(const MSIAPPID *appid, LPCWSTR app ) UINT ACTION_RegisterClassInfo(MSIPACKAGE *package) { static const WCHAR szFileType_fmt[] = {'F','i','l','e','T','y','p','e','\\','%','s','\\','%','i',0}; + const WCHAR *keypath; MSIRECORD *uirow; HKEY hkey,hkey2,hkey3; MSICLASS *cls; load_classes_and_such(package); - if (RegCreateKeyW(HKEY_CLASSES_ROOT, szCLSID, &hkey) != ERROR_SUCCESS) + + if (is_64bit && package->platform == PLATFORM_INTEL) + keypath = szWow6432NodeCLSID; + else + keypath = szCLSID; + + if (RegCreateKeyW(HKEY_CLASSES_ROOT, keypath, &hkey) != ERROR_SUCCESS) return ERROR_FUNCTION_FAILED; LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) @@ -963,12 +970,19 @@ UINT ACTION_RegisterClassInfo(MSIPACKAGE *package) UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package ) { static const WCHAR szFileType[] = {'F','i','l','e','T','y','p','e','\\',0}; + const WCHAR *keypath; MSIRECORD *uirow; MSICLASS *cls; HKEY hkey, hkey2; load_classes_and_such( package ); - if (RegOpenKeyW( HKEY_CLASSES_ROOT, szCLSID, &hkey ) != ERROR_SUCCESS) + + if (is_64bit && package->platform == PLATFORM_INTEL) + keypath = szWow6432NodeCLSID; + else + keypath = szCLSID; + + if (RegOpenKeyW( HKEY_CLASSES_ROOT, keypath, &hkey ) != ERROR_SUCCESS) return ERROR_SUCCESS; LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) @@ -1072,7 +1086,7 @@ static UINT register_progid( const MSIPROGID* progid ) if (clsid) msi_reg_set_subkey_val( hkey, szCLSID, NULL, clsid ); else - ERR("%s has no class\n", debugstr_w( progid->ProgID ) ); + TRACE("%s has no class\n", debugstr_w( progid->ProgID ) ); if (progid->Description) msi_reg_set_val_str( hkey, NULL, progid->Description ); diff --git a/dll/win32/msi/cond.tab.c b/dll/win32/msi/cond.tab.c index 2b10d209be6..e68723db89b 100644 --- a/dll/win32/msi/cond.tab.c +++ b/dll/win32/msi/cond.tab.c @@ -2329,11 +2329,21 @@ static INT compare_substring( LPCWSTR a, INT operator, LPCWSTR b ) case COND_LHS: return 0 == strncmpW( a, b, lstrlenW( b ) ); case COND_RHS: - return 0 == lstrcmpW( a + (lstrlenW( a ) - lstrlenW( b )), b ); + { + int l = lstrlenW( a ); + int r = lstrlenW( b ); + if (r > l) return 0; + return 0 == lstrcmpW( a + (l - r), b ); + } case COND_ILHS: return 0 == strncmpiW( a, b, lstrlenW( b ) ); case COND_IRHS: - return 0 == lstrcmpiW( a + (lstrlenW( a ) - lstrlenW( b )), b ); + { + int l = lstrlenW( a ); + int r = lstrlenW( b ); + if (r > l) return 0; + return 0 == lstrcmpiW( a + (l - r), b ); + } default: ERR("invalid substring operator\n"); return 0; diff --git a/dll/win32/msi/cond.y b/dll/win32/msi/cond.y index 9a7a16d67df..03ea4938ade 100644 --- a/dll/win32/msi/cond.y +++ b/dll/win32/msi/cond.y @@ -462,11 +462,21 @@ static INT compare_substring( LPCWSTR a, INT operator, LPCWSTR b ) case COND_LHS: return 0 == strncmpW( a, b, lstrlenW( b ) ); case COND_RHS: - return 0 == lstrcmpW( a + (lstrlenW( a ) - lstrlenW( b )), b ); + { + int l = lstrlenW( a ); + int r = lstrlenW( b ); + if (r > l) return 0; + return 0 == lstrcmpW( a + (l - r), b ); + } case COND_ILHS: return 0 == strncmpiW( a, b, lstrlenW( b ) ); case COND_IRHS: - return 0 == lstrcmpiW( a + (lstrlenW( a ) - lstrlenW( b )), b ); + { + int l = lstrlenW( a ); + int r = lstrlenW( b ); + if (r > l) return 0; + return 0 == lstrcmpiW( a + (l - r), b ); + } default: ERR("invalid substring operator\n"); return 0; diff --git a/dll/win32/msi/custom.c b/dll/win32/msi/custom.c index 5a1787f7d15..b56139fb665 100644 --- a/dll/win32/msi/custom.c +++ b/dll/win32/msi/custom.c @@ -222,13 +222,6 @@ UINT ACTION_CustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script, BOOL if (type & msidbCustomActionTypeNoImpersonate) WARN("msidbCustomActionTypeNoImpersonate not handled\n"); - if (type & msidbCustomActionTypeRollback) - { - FIXME("Rollback only action... rollbacks not supported yet\n"); - schedule_action(package, ROLLBACK_SCRIPT, action); - rc = ERROR_SUCCESS; - goto end; - } if (!execute) { LPWSTR actiondata = msi_dup_property(package->db, action); @@ -238,12 +231,17 @@ UINT ACTION_CustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script, BOOL if (type & msidbCustomActionTypeCommit) { - TRACE("Deferring Commit Action!\n"); + TRACE("Deferring commit action\n"); schedule_action(package, COMMIT_SCRIPT, deferred); } + else if (type & msidbCustomActionTypeRollback) + { + FIXME("Deferring rollback only action... rollbacks not supported yet\n"); + schedule_action(package, ROLLBACK_SCRIPT, deferred); + } else { - TRACE("Deferring Action!\n"); + TRACE("Deferring action\n"); schedule_action(package, INSTALL_SCRIPT, deferred); } @@ -258,20 +256,14 @@ UINT ACTION_CustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script, BOOL { LPWSTR actiondata = msi_dup_property( package->db, action ); - switch (script) - { - case INSTALL_SCRIPT: + if (type & msidbCustomActionTypeInScript) package->scheduled_action_running = TRUE; - break; - case COMMIT_SCRIPT: + + if (type & msidbCustomActionTypeCommit) package->commit_action_running = TRUE; - break; - case ROLLBACK_SCRIPT: + + if (type & msidbCustomActionTypeRollback) package->rollback_action_running = TRUE; - break; - default: - break; - } if (deferred_data) set_deferred_action_props(package, deferred_data); diff --git a/dll/win32/msi/database.c b/dll/win32/msi/database.c index 01c32497a37..3007d485b2b 100644 --- a/dll/win32/msi/database.c +++ b/dll/win32/msi/database.c @@ -241,7 +241,7 @@ static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg ) free_cached_tables( db ); free_streams( db ); free_transforms( db ); - msi_destroy_stringtable( db->strings ); + if (db->strings) msi_destroy_stringtable( db->strings ); IStorage_Release( db->storage ); if (db->deletefile) { @@ -255,6 +255,43 @@ static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg ) } } +static HRESULT db_initialize( IStorage *stg, const GUID *clsid ) +{ + static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 }; + HRESULT hr; + + hr = IStorage_SetClass( stg, clsid ); + if (FAILED( hr )) + { + WARN("failed to set class id 0x%08x\n", hr); + return hr; + } + + /* create the _Tables stream */ + hr = write_stream_data( stg, szTables, NULL, 0, TRUE ); + if (FAILED( hr )) + { + WARN("failed to create _Tables stream 0x%08x\n", hr); + return hr; + } + + hr = msi_init_string_table( stg ); + if (FAILED( hr )) + { + WARN("failed to initialize string table 0x%08x\n", hr); + return hr; + } + + hr = IStorage_Commit( stg, 0 ); + if (FAILED( hr )) + { + WARN("failed to commit changes 0x%08x\n", hr); + return hr; + } + + return S_OK; +} + UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb) { IStorage *stg = NULL; @@ -266,8 +303,6 @@ UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb) BOOL created = FALSE, patch = FALSE; WCHAR path[MAX_PATH]; - static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 }; - TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) ); if( !pdb ) @@ -298,28 +333,28 @@ UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb) r = StgOpenStorage( szDBPath, NULL, STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); } - else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT ) + else if( szPersist == MSIDBOPEN_CREATE ) { - /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be - * used here: */ r = StgCreateDocfile( szDBPath, - STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg); - if( r == ERROR_SUCCESS ) - { - IStorage_SetClass( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase ); - /* create the _Tables stream */ - r = write_stream_data(stg, szTables, NULL, 0, TRUE); - if (SUCCEEDED(r)) - r = msi_init_string_table( stg ); - } + STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg ); + + if( SUCCEEDED(r) ) + r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase ); + created = TRUE; + } + else if( szPersist == MSIDBOPEN_CREATEDIRECT ) + { + r = StgCreateDocfile( szDBPath, + STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg ); + + if( SUCCEEDED(r) ) + r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase ); created = TRUE; } else if( szPersist == MSIDBOPEN_TRANSACT ) { - /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be - * used here: */ r = StgOpenStorage( szDBPath, NULL, - STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg); + STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg); } else if( szPersist == MSIDBOPEN_DIRECT ) { diff --git a/dll/win32/msi/join.c b/dll/win32/msi/join.c index f787c309a41..7fe7f1c2677 100644 --- a/dll/win32/msi/join.c +++ b/dll/win32/msi/join.c @@ -60,8 +60,6 @@ static UINT JOIN_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *v UINT cols = 0; UINT prev_rows = 1; - TRACE("%d, %d\n", row, col); - if (col == 0 || col > jv->columns) return ERROR_FUNCTION_FAILED; @@ -225,10 +223,12 @@ static UINT JOIN_get_column_info( struct tagMSIVIEW *view, static UINT join_find_row( MSIJOINVIEW *jv, MSIRECORD *rec, UINT *row ) { LPCWSTR str; - UINT i, id, data; + UINT r, i, id, data; str = MSI_RecordGetString( rec, 1 ); - msi_string2idW( jv->db->strings, str, &id ); + r = msi_string2idW( jv->db->strings, str, &id ); + if (r != ERROR_SUCCESS) + return r; for (i = 0; i < jv->rows; i++) { diff --git a/dll/win32/msi/media.c b/dll/win32/msi/media.c index a2a90654186..788528e9b37 100644 --- a/dll/win32/msi/media.c +++ b/dll/win32/msi/media.c @@ -211,9 +211,6 @@ static INT_PTR CDECL cabinet_open_stream( char *pszFile, int oflag, int pmode ) UINT r; IStream *stm; - if (oflag) - WARN("ignoring open flags 0x%08x\n", oflag); - r = db_get_raw_stream( cab_stream.db, cab_stream.name, &stm ); if (r != ERROR_SUCCESS) { @@ -358,6 +355,40 @@ done: return res; } +static INT_PTR cabinet_next_cabinet_stream( FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin ) +{ + MSICABDATA *data = pfdin->pv; + MSIMEDIAINFO *mi = data->mi; + UINT rc; + + msi_free( mi->disk_prompt ); + msi_free( mi->cabinet ); + msi_free( mi->volume_label ); + mi->disk_prompt = NULL; + mi->cabinet = NULL; + mi->volume_label = NULL; + + mi->disk_id++; + mi->is_continuous = TRUE; + + rc = msi_media_get_disk_info( data->package, mi ); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to get next cabinet information: %u\n", rc); + return -1; + } + + msi_free( cab_stream.name ); + cab_stream.name = encode_streamname( FALSE, mi->cabinet + 1 ); + if (!cab_stream.name) + return -1; + + TRACE("next cabinet is %s\n", debugstr_w(mi->cabinet)); + + return 0; +} + static INT_PTR cabinet_copy_file(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) { @@ -494,6 +525,12 @@ static INT_PTR CDECL cabinet_notify_stream( FDINOTIFICATIONTYPE fdint, PFDINOTIF { switch (fdint) { + case fdintPARTIAL_FILE: + return cabinet_partial_file( fdint, pfdin ); + + case fdintNEXT_CABINET: + return cabinet_next_cabinet_stream( fdint, pfdin ); + case fdintCOPY_FILE: return cabinet_copy_file( fdint, pfdin ); diff --git a/dll/win32/msi/msi.c b/dll/win32/msi/msi.c index fb7eba5f54e..7d1b9a55095 100644 --- a/dll/win32/msi/msi.c +++ b/dll/win32/msi/msi.c @@ -3779,3 +3779,44 @@ UINT WINAPI MsiInstallMissingComponentW(LPCWSTR szProduct, LPCWSTR szComponent, FIXME("(%s %s %d\n", debugstr_w(szProduct), debugstr_w(szComponent), eInstallState); return ERROR_SUCCESS; } + +/*********************************************************************** + * MsiBeginTransactionA [MSI.@] + */ +UINT WINAPI MsiBeginTransactionA( LPCSTR name, DWORD attrs, MSIHANDLE *id, HANDLE *event ) +{ + WCHAR *nameW; + UINT r; + + FIXME("%s %u %p %p\n", debugstr_a(name), attrs, id, event); + + nameW = strdupAtoW( name ); + if (name && !nameW) + return ERROR_OUTOFMEMORY; + + r = MsiBeginTransactionW( nameW, attrs, id, event ); + msi_free( nameW ); + return r; +} + +/*********************************************************************** + * MsiBeginTransactionW [MSI.@] + */ +UINT WINAPI MsiBeginTransactionW( LPCWSTR name, DWORD attrs, MSIHANDLE *id, HANDLE *event ) +{ + FIXME("%s %u %p %p\n", debugstr_w(name), attrs, id, event); + + *id = (MSIHANDLE)0xdeadbeef; + *event = (HANDLE)0xdeadbeef; + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiEndTransaction [MSI.@] + */ +UINT WINAPI MsiEndTransaction( DWORD state ) +{ + FIXME("%u\n", state); + return ERROR_SUCCESS; +} diff --git a/dll/win32/msi/msi.spec b/dll/win32/msi/msi.spec index 6fdebd86f10..fd047f8a652 100644 --- a/dll/win32/msi/msi.spec +++ b/dll/win32/msi/msi.spec @@ -174,7 +174,7 @@ 178 stdcall MsiGetPatchInfoA(str str ptr ptr) 179 stdcall MsiGetPatchInfoW(wstr wstr ptr ptr) 180 stdcall MsiEnumPatchesA(str long ptr ptr ptr) -181 stdcall MsiEnumPatchesW(str long ptr ptr ptr) +181 stdcall MsiEnumPatchesW(wstr long ptr ptr ptr) 182 stdcall -private DllGetVersion(ptr) 183 stub MsiGetProductCodeFromPackageCodeA 184 stub MsiGetProductCodeFromPackageCodeW @@ -214,7 +214,7 @@ 218 stdcall MsiGetFileHashA(str long ptr) 219 stdcall MsiGetFileHashW(wstr long ptr) 220 stub MsiEnumComponentCostsA -221 stdcall MsiEnumComponentCostsW(long str long long ptr ptr ptr ptr) +221 stdcall MsiEnumComponentCostsW(long wstr long long ptr ptr ptr ptr) 222 stdcall MsiCreateAndVerifyInstallerDirectory(long) 223 stdcall MsiGetFileSignatureInformationA(str long ptr ptr ptr) 224 stdcall MsiGetFileSignatureInformationW(wstr long ptr ptr ptr) @@ -277,9 +277,9 @@ 281 stdcall MsiSetExternalUIRecord(ptr long ptr ptr) 282 stub MsiGetPatchFileListA 283 stub MsiGetPatchFileListW -284 stub MsiBeginTransactionA -285 stub MsiBeginTransactionW -286 stub MsiEndTransaction +284 stdcall MsiBeginTransactionA(str long ptr ptr) +285 stdcall MsiBeginTransactionW(wstr long ptr ptr) +286 stdcall MsiEndTransaction(long) 287 stub MsiJoinTransaction 288 stub MsiSetOfflineContextW 289 stub MsiEnumComponentsExA diff --git a/dll/win32/msi/msi_Fi.rc b/dll/win32/msi/msi_Fi.rc index 7769a8afe76..89841729a73 100644 --- a/dll/win32/msi/msi_Fi.rc +++ b/dll/win32/msi/msi_Fi.rc @@ -24,39 +24,39 @@ LANGUAGE LANG_FINNISH, SUBLANG_DEFAULT STRINGTABLE { - 4 "The specified installation package could not be opened. Please check the file path and try again." + 4 "Annettua asennuspakettia ei voitu avata. Tarkista tiedoston polku ja yrit uudelleen." 5 "Polkua %s ei lydy." 9 "Anna levy %s" 10 "Windows Installer %s\n\n" \ - "Usage:\n" \ - "msiexec command {required parameter} [optional parammeter]\n\n" \ - "Install a product:\n" \ - "\t/i {package|productcode} [property]\n" \ - "\t/package {package|productcode} [property]\n" \ - "\t/a package [property]\n" \ - "Repair an installation:\n" \ - "\t/f[p|o|e|d|c|a|u|m|s|v] {package|productcode}\n" \ - "Uninstall a product:\n" \ - "\t/uninstall {package|productcode} [property]\n" \ - "\t/x {package|productcode} [property]\n" \ - "Advertise a product:\n" \ - "\t/j[u|m] package [/t transform] [/g languageid]\n" \ - "Apply a patch:\n" \ - "\t/p patchpackage [property]\n" \ - "\t/p patchpackage /a package [property]\n" \ - "Log and UI Modifiers for above commands:\n" \ - "\t/l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] logfile\n" \ + "K艙tt:\n" \ + "msiexec komento {pakollinen parametri} [valinnainen parametri]\n\n" \ + "Asenna tuote:\n" \ + "\t/i {paketti|tuotekoodi} [ominaisuus]\n" \ + "\t/package {paketti|tuotekoodi} [ominaisuus]\n" \ + "\t/a {paketti} [ominaisuus]\n" \ + "Korjaa asennus:\n" \ + "\t/f[p|o|e|d|c|a|u|m|s|v] {paketti|tuotekoodi}\n" \ + "Poista tuote:\n" \ + "\t/uninstall {paketti|tuotekoodi} [ominaisuus]\n" \ + "\t/x {paketti|tuotekoodi} [ominaisuus]\n" \ + "Mainosta (advertise) tuotetta:\n" \ + "\t/j[u|m] paketti [/t muunnos] [/g kielitunnus]\n" \ + "Asenna korjaus:\n" \ + "\t/p korjauspaketti [ominaisuus]\n" \ + "\t/p korjauspaketti /a paketti [ominaisuus]\n" \ + "Loki- ja k艙ttliittym臑setukset edellisille komennoille:\n" \ + "\t/l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] lokitiedosto\n" \ "\t/q{|n|b|r|f|n+|b+|b-}\n" \ - "Register MSI Service:\n" \ + "Rekisteri MSI-palvelu:\n" \ "\t/y\n" \ - "Unregister MSI Service:\n" \ + "Peru MSI-palvelun rekisterinti:\n" \ "\t/z\n" \ - "Display this help:\n" \ + "N艙t t舂 ohje:\n" \ "\t/help\n" \ "\t/?\n" - 11 "Anna kansio, joka sis舁t蒿 %s" + 11 "Anna kansio, jossa on %s" 12 "Ominaisuuden asennusl臧de puuttuu." 13 "Ominaisuuden verkkolevy puuttuu." 14 "Ominaisuus:" - 15 "Valitse kansio, joka sis舁t蒿 %s" + 15 "Valitse kansio, jossa on %s" } diff --git a/dll/win32/msi/msipriv.h b/dll/win32/msi/msipriv.h index 344b1450e3a..0361ff42c6e 100644 --- a/dll/win32/msi/msipriv.h +++ b/dll/win32/msi/msipriv.h @@ -29,6 +29,7 @@ #include "fdi.h" #include "msi.h" #include "msiquery.h" +#include "msidefs.h" #include "objbase.h" #include "objidl.h" #include "winnls.h" @@ -36,6 +37,8 @@ #include "wine/list.h" #include "wine/debug.h" +static const BOOL is_64bit = sizeof(void *) > sizeof(int); + #define MSI_DATASIZEMASK 0x00ff #define MSITYPE_VALID 0x0100 #define MSITYPE_LOCALIZABLE 0x200 @@ -45,6 +48,7 @@ #define MSITYPE_TEMPORARY 0x4000 #define MAX_STREAM_NAME_LEN 62 +#define LONG_STR_BYTES 3 /* Install UI level mask for AND operation to exclude flags */ #define INSTALLUILEVEL_MASK 0x0007 @@ -103,6 +107,7 @@ typedef struct tagMSIFIELD union { INT iVal; + INT_PTR pVal; LPWSTR szwVal; IStream *stream; } u; @@ -303,10 +308,21 @@ struct tagMSIVIEW struct msi_dialog_tag; typedef struct msi_dialog_tag msi_dialog; +enum platform +{ + PLATFORM_INTEL, + PLATFORM_INTEL64, + PLATFORM_X64 +}; + typedef struct tagMSIPACKAGE { MSIOBJECTHDR hdr; MSIDATABASE *db; + INT version; + enum platform platform; + UINT num_langids; + LANGID *langids; struct list patches; struct list components; struct list features; @@ -388,7 +404,6 @@ typedef struct tagMSIFEATURE typedef struct tagMSICOMPONENT { struct list entry; - DWORD magic; LPWSTR Component; LPWSTR ComponentId; LPWSTR Directory; @@ -595,9 +610,9 @@ typedef struct tagMSISCRIPT #define MSIHANDLETYPE_PACKAGE 5 #define MSIHANDLETYPE_PREVIEW 6 -#define MSI_MAJORVERSION 3 -#define MSI_MINORVERSION 1 -#define MSI_BUILDNUMBER 4000 +#define MSI_MAJORVERSION 4 +#define MSI_MINORVERSION 5 +#define MSI_BUILDNUMBER 6001 #define GUID_SIZE 39 #define SQUISH_GUID_SIZE 33 @@ -672,7 +687,7 @@ extern VOID msi_destroy_stringtable( string_table *st ); extern const WCHAR *msi_string_lookup_id( const string_table *st, UINT id ); extern HRESULT msi_init_string_table( IStorage *stg ); extern string_table *msi_load_string_table( IStorage *stg, UINT *bytes_per_strref ); -extern UINT msi_save_string_table( const string_table *st, IStorage *storage ); +extern UINT msi_save_string_table( const string_table *st, IStorage *storage, UINT *bytes_per_strref ); extern BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name ); extern MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table ); @@ -709,11 +724,13 @@ extern UINT MSI_RecordGetIStream( MSIRECORD *, UINT, IStream **); extern const WCHAR *MSI_RecordGetString( const MSIRECORD *, UINT ); extern MSIRECORD *MSI_CreateRecord( UINT ); extern UINT MSI_RecordSetInteger( MSIRECORD *, UINT, int ); +extern UINT MSI_RecordSetIntPtr( MSIRECORD *, UINT, INT_PTR ); extern UINT MSI_RecordSetStringW( MSIRECORD *, UINT, LPCWSTR ); extern BOOL MSI_RecordIsNull( MSIRECORD *, UINT ); extern UINT MSI_RecordGetStringW( MSIRECORD * , UINT, LPWSTR, LPDWORD); extern UINT MSI_RecordGetStringA( MSIRECORD *, UINT, LPSTR, LPDWORD); extern int MSI_RecordGetInteger( MSIRECORD *, UINT ); +extern INT_PTR MSI_RecordGetIntPtr( MSIRECORD *, UINT ); extern UINT MSI_RecordReadStream( MSIRECORD *, UINT, char *, LPDWORD); extern UINT MSI_RecordSetStream(MSIRECORD *, UINT, IStream *); extern UINT MSI_RecordGetFieldCount( const MSIRECORD *rec ); @@ -765,7 +782,7 @@ extern UINT msi_package_add_info(MSIPACKAGE *, DWORD, DWORD, LPCWSTR, LPWSTR); extern UINT msi_package_add_media_disk(MSIPACKAGE *, DWORD, DWORD, DWORD, LPWSTR, LPWSTR); extern UINT msi_clone_properties(MSIPACKAGE *); extern UINT msi_set_context(MSIPACKAGE *); -extern void msi_adjust_allusers_property(MSIPACKAGE *); +extern void msi_adjust_privilege_properties(MSIPACKAGE *); extern UINT MSI_GetFeatureCost(MSIPACKAGE *, MSIFEATURE *, MSICOSTTREE, INSTALLSTATE, LPINT); /* for deformating */ @@ -776,8 +793,8 @@ extern BOOL unsquash_guid(LPCWSTR in, LPWSTR out); extern BOOL squash_guid(LPCWSTR in, LPWSTR out); extern BOOL encode_base85_guid(GUID *,LPWSTR); extern BOOL decode_base85_guid(LPCWSTR,GUID*); -extern UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create); -extern UINT MSIREG_DeleteUninstallKey(LPCWSTR szProduct); +extern UINT MSIREG_OpenUninstallKey(MSIPACKAGE *package, HKEY *key, BOOL create); +extern UINT MSIREG_DeleteUninstallKey(MSIPACKAGE *package); extern UINT MSIREG_OpenProductKey(LPCWSTR szProduct, LPCWSTR szUserSid, MSIINSTALLCONTEXT context, HKEY* key, BOOL create); extern UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, MSIINSTALLCONTEXT context, @@ -843,6 +860,7 @@ extern UINT msi_spawn_error_dialog( MSIPACKAGE*, LPWSTR, LPWSTR ); /* summary information */ extern MSISUMMARYINFO *MSI_GetSummaryInformationW( IStorage *stg, UINT uiUpdateCount ); extern LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty ); +extern INT msi_suminfo_get_int32( MSISUMMARYINFO *si, UINT uiProperty ); extern LPWSTR msi_get_suminfo_product( IStorage *stg ); extern UINT msi_add_suminfo( MSIDATABASE *db, LPWSTR **records, int num_records, int num_columns ); @@ -915,6 +933,10 @@ static inline void msi_feature_set_state(MSIPACKAGE *package, feature->ActionRequest = state; feature->Action = state; } + if (feature->Attributes & msidbFeatureAttributesUIDisallowAbsent) + { + feature->Action = INSTALLSTATE_UNKNOWN; + } } static inline void msi_component_set_state(MSIPACKAGE *package, @@ -1123,6 +1145,12 @@ static const WCHAR szMIMEDatabase[] = {'M','I','M','E','\\','D','a','t','a','b', static const WCHAR szLocalPackage[] = {'L','o','c','a','l','P','a','c','k','a','g','e',0}; static const WCHAR szOriginalDatabase[] = {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0}; static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0}; +static const WCHAR szAdminUser[] = {'A','d','m','i','n','U','s','e','r',0}; +static const WCHAR szIntel[] = {'I','n','t','e','l',0}; +static const WCHAR szIntel64[] = {'I','n','t','e','l','6','4',0}; +static const WCHAR szX64[] = {'x','6','4',0}; +static const WCHAR szWow6432NodeCLSID[] = {'W','o','w','6','4','3','2','N','o','d','e','\\','C','L','S','I','D',0}; +static const WCHAR szWow6432Node[] = {'W','o','w','6','4','3','2','N','o','d','e',0}; /* memory allocation macro functions */ static void *msi_alloc( size_t len ) __WINE_ALLOC_SIZE(1); diff --git a/dll/win32/msi/msiquery.c b/dll/win32/msi/msiquery.c index df81a748223..cf6959391d3 100644 --- a/dll/win32/msi/msiquery.c +++ b/dll/win32/msi/msiquery.c @@ -382,7 +382,7 @@ UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec) if (r == ERROR_SUCCESS) { query->row ++; - MSI_RecordSetInteger(*prec, 0, (int)query); + MSI_RecordSetIntPtr(*prec, 0, (INT_PTR)query); } return r; @@ -617,7 +617,7 @@ UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec ) if ( !view || !view->ops->modify) return ERROR_FUNCTION_FAILED; - if ( mode == MSIMODIFY_UPDATE && MSI_RecordGetInteger( rec, 0 ) != (int)query ) + if ( mode == MSIMODIFY_UPDATE && MSI_RecordGetIntPtr( rec, 0 ) != (INT_PTR)query ) return ERROR_FUNCTION_FAILED; r = view->ops->modify( view, mode, rec, query->row ); @@ -901,6 +901,9 @@ UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db, MSIQUERY *query = NULL; UINT r; + if (!TABLE_Exists( db, table )) + return ERROR_INVALID_TABLE; + r = MSI_OpenQuery( db, &query, sql, table ); if( r != ERROR_SUCCESS ) return r; diff --git a/dll/win32/msi/package.c b/dll/win32/msi/package.c index d3321686964..32576a0362c 100644 --- a/dll/win32/msi/package.c +++ b/dll/win32/msi/package.c @@ -280,6 +280,7 @@ static void free_package_structures( MSIPACKAGE *package ) msi_free( package->ProductCode ); msi_free( package->ActionFormat ); msi_free( package->LastAction ); + msi_free( package->langids ); /* cleanup control event subscriptions */ ControlEvent_CleanupSubscriptions( package ); @@ -417,7 +418,7 @@ static UINT set_installed_prop( MSIPACKAGE *package ) HKEY hkey = 0; UINT r; - r = MSIREG_OpenUninstallKey( package->ProductCode, &hkey, FALSE ); + r = MSIREG_OpenUninstallKey( package, &hkey, FALSE ); if (r == ERROR_SUCCESS) { RegCloseKey( hkey ); @@ -618,72 +619,49 @@ static VOID set_installer_properties(MSIPACKAGE *package) SYSTEMTIME systemtime; LANGID langid; - static const WCHAR CFF[] = -{'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0}; - static const WCHAR PFF[] = -{'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0}; - static const WCHAR CADF[] = -{'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0}; - static const WCHAR FaF[] = -{'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0}; - static const WCHAR FoF[] = -{'F','o','n','t','s','F','o','l','d','e','r',0}; - static const WCHAR SendTF[] = -{'S','e','n','d','T','o','F','o','l','d','e','r',0}; - static const WCHAR SMF[] = -{'S','t','a','r','t','M','e','n','u','F','o','l','d','e','r',0}; - static const WCHAR StF[] = -{'S','t','a','r','t','u','p','F','o','l','d','e','r',0}; - static const WCHAR TemplF[] = -{'T','e','m','p','l','a','t','e','F','o','l','d','e','r',0}; - static const WCHAR DF[] = -{'D','e','s','k','t','o','p','F','o','l','d','e','r',0}; - static const WCHAR PMF[] = -{'P','r','o','g','r','a','m','M','e','n','u','F','o','l','d','e','r',0}; - static const WCHAR ATF[] = -{'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0}; - static const WCHAR ADF[] = -{'A','p','p','D','a','t','a','F','o','l','d','e','r',0}; - static const WCHAR SF[] = -{'S','y','s','t','e','m','F','o','l','d','e','r',0}; - static const WCHAR SF16[] = -{'S','y','s','t','e','m','1','6','F','o','l','d','e','r',0}; - static const WCHAR LADF[] = -{'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0}; - static const WCHAR MPF[] = -{'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0}; - static const WCHAR PF[] = -{'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0}; - static const WCHAR WF[] = -{'W','i','n','d','o','w','s','F','o','l','d','e','r',0}; - static const WCHAR WV[] = -{'W','i','n','d','o','w','s','V','o','l','u','m','e',0}; - static const WCHAR TF[]= -{'T','e','m','p','F','o','l','d','e','r',0}; - static const WCHAR szAdminUser[] = -{'A','d','m','i','n','U','s','e','r',0}; - static const WCHAR szPriv[] = -{'P','r','i','v','i','l','e','g','e','d',0}; - static const WCHAR v9x[] = { 'V','e','r','s','i','o','n','9','X',0 }; - static const WCHAR vNT[] = { 'V','e','r','s','i','o','n','N','T',0 }; - static const WCHAR szMsiNTProductType[] = { 'M','s','i','N','T','P','r','o','d','u','c','t','T','y','p','e',0 }; + static const WCHAR szCommonFilesFolder[] = {'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0}; + static const WCHAR szProgramFilesFolder[] = {'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0}; + static const WCHAR szCommonAppDataFolder[] = {'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0}; + static const WCHAR szFavoritesFolder[] = {'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0}; + static const WCHAR szFontsFolder[] = {'F','o','n','t','s','F','o','l','d','e','r',0}; + static const WCHAR szSendToFolder[] = {'S','e','n','d','T','o','F','o','l','d','e','r',0}; + static const WCHAR szStartMenuFolder[] = {'S','t','a','r','t','M','e','n','u','F','o','l','d','e','r',0}; + static const WCHAR szStartupFolder[] = {'S','t','a','r','t','u','p','F','o','l','d','e','r',0}; + static const WCHAR szTemplateFolder[] = {'T','e','m','p','l','a','t','e','F','o','l','d','e','r',0}; + static const WCHAR szDesktopFolder[] = {'D','e','s','k','t','o','p','F','o','l','d','e','r',0}; + static const WCHAR szProgramMenuFolder[] = {'P','r','o','g','r','a','m','M','e','n','u','F','o','l','d','e','r',0}; + static const WCHAR szAdminToolsFolder[] = {'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0}; + static const WCHAR szAppDataFolder[] = {'A','p','p','D','a','t','a','F','o','l','d','e','r',0}; + static const WCHAR szSystemFolder[] = {'S','y','s','t','e','m','F','o','l','d','e','r',0}; + static const WCHAR szSystem16Folder[] = {'S','y','s','t','e','m','1','6','F','o','l','d','e','r',0}; + static const WCHAR szLocalAppDataFolder[] = {'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0}; + static const WCHAR szMyPicturesFolder[] = {'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0}; + static const WCHAR szPersonalFolder[] = {'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0}; + static const WCHAR szWindowsFolder[] = {'W','i','n','d','o','w','s','F','o','l','d','e','r',0}; + static const WCHAR szWindowsVolume[] = {'W','i','n','d','o','w','s','V','o','l','u','m','e',0}; + static const WCHAR szTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0}; + static const WCHAR szPrivileged[] = {'P','r','i','v','i','l','e','g','e','d',0}; + static const WCHAR szVersion9x[] = {'V','e','r','s','i','o','n','9','X',0}; + static const WCHAR szVersionNT[] = {'V','e','r','s','i','o','n','N','T',0}; + static const WCHAR szMsiNTProductType[] = {'M','s','i','N','T','P','r','o','d','u','c','t','T','y','p','e',0}; static const WCHAR szFormat[] = {'%','l','i',0}; - static const WCHAR szWinBuild[] = -{'W','i','n','d','o','w','s','B','u','i','l','d', 0 }; - static const WCHAR szSPL[] = -{'S','e','r','v','i','c','e','P','a','c','k','L','e','v','e','l',0 }; + static const WCHAR szWindowsBuild[] = {'W','i','n','d','o','w','s','B','u','i','l','d',0}; + static const WCHAR szServicePackLevel[] = {'S','e','r','v','i','c','e','P','a','c','k','L','e','v','e','l',0}; static const WCHAR szSix[] = {'6',0 }; - static const WCHAR szVersionMsi[] = { 'V','e','r','s','i','o','n','M','s','i',0 }; static const WCHAR szVersionDatabase[] = { 'V','e','r','s','i','o','n','D','a','t','a','b','a','s','e',0 }; static const WCHAR szPhysicalMemory[] = { 'P','h','y','s','i','c','a','l','M','e','m','o','r','y',0 }; static const WCHAR szFormat2[] = {'%','l','i','.','%','l','i',0}; -/* Screen properties */ static const WCHAR szScreenX[] = {'S','c','r','e','e','n','X',0}; static const WCHAR szScreenY[] = {'S','c','r','e','e','n','Y',0}; static const WCHAR szColorBits[] = {'C','o','l','o','r','B','i','t','s',0}; static const WCHAR szIntFormat[] = {'%','d',0}; - static const WCHAR szIntel[] = { 'I','n','t','e','l',0 }; + static const WCHAR szMsiAMD64[] = { 'M','s','i','A','M','D','6','4',0 }; + static const WCHAR szMsix64[] = { 'M','s','i','x','6','4',0 }; + static const WCHAR szSystem64Folder[] = { 'S','y','s','t','e','m','6','4','F','o','l','d','e','r',0 }; + static const WCHAR szCommonFiles64Folder[] = { 'C','o','m','m','o','n','F','i','l','e','s','6','4','F','o','l','d','e','r',0 }; + static const WCHAR szProgramFiles64Folder[] = { 'P','r','o','g','r','a','m','F','i','l','e','s','6','4','F','o','l','d','e','r',0 }; + static const WCHAR szVersionNT64[] = { 'V','e','r','s','i','o','n','N','T','6','4',0 }; static const WCHAR szUserInfo[] = { 'S','O','F','T','W','A','R','E','\\', 'M','i','c','r','o','s','o','f','t','\\', @@ -699,17 +677,20 @@ static VOID set_installer_properties(MSIPACKAGE *package) 'C','u','r','r','e','n','t','V','e','r','s','i','o','n',0 }; static const WCHAR szRegisteredUser[] = {'R','e','g','i','s','t','e','r','e','d','O','w','n','e','r',0}; - static const WCHAR szRegisteredOrg[] = { + static const WCHAR szRegisteredOrganization[] = { 'R','e','g','i','s','t','e','r','e','d','O','r','g','a','n','i','z','a','t','i','o','n',0 }; static const WCHAR szUSERNAME[] = {'U','S','E','R','N','A','M','E',0}; static const WCHAR szCOMPANYNAME[] = {'C','O','M','P','A','N','Y','N','A','M','E',0}; static const WCHAR szDate[] = {'D','a','t','e',0}; static const WCHAR szTime[] = {'T','i','m','e',0}; - static const WCHAR szUserLangID[] = {'U','s','e','r','L','a','n','g','u','a','g','e','I','D',0}; + static const WCHAR szUserLanguageID[] = {'U','s','e','r','L','a','n','g','u','a','g','e','I','D',0}; static const WCHAR szSystemLangID[] = {'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0}; static const WCHAR szProductState[] = {'P','r','o','d','u','c','t','S','t','a','t','e',0}; static const WCHAR szLogonUser[] = {'L','o','g','o','n','U','s','e','r',0}; + static const WCHAR szNetHoodFolder[] = {'N','e','t','H','o','o','d','F','o','l','d','e','r',0}; + static const WCHAR szPrintHoodFolder[] = {'P','r','i','n','t','H','o','o','d','F','o','l','d','e','r',0}; + static const WCHAR szRecentFolder[] = {'R','e','c','e','n','t','F','o','l','d','e','r',0}; /* * Other things that probably should be set: @@ -720,130 +701,175 @@ static VOID set_installer_properties(MSIPACKAGE *package) * RedirectedDllSupport */ - SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, CFF, pth); + msi_set_property(package->db, szCommonAppDataFolder, pth); - SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_FAVORITES, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, PFF, pth); + msi_set_property(package->db, szFavoritesFolder, pth); - SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, CADF, pth); + msi_set_property(package->db, szFontsFolder, pth); - SHGetFolderPathW(NULL,CSIDL_FAVORITES,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_SENDTO, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, FaF, pth); + msi_set_property(package->db, szSendToFolder, pth); - SHGetFolderPathW(NULL,CSIDL_FONTS,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_STARTMENU, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, FoF, pth); + msi_set_property(package->db, szStartMenuFolder, pth); - SHGetFolderPathW(NULL,CSIDL_SENDTO,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, SendTF, pth); + msi_set_property(package->db, szStartupFolder, pth); - SHGetFolderPathW(NULL,CSIDL_STARTMENU,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_TEMPLATES, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, SMF, pth); + msi_set_property(package->db, szTemplateFolder, pth); - SHGetFolderPathW(NULL,CSIDL_STARTUP,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, StF, pth); + msi_set_property(package->db, szDesktopFolder, pth); - SHGetFolderPathW(NULL,CSIDL_TEMPLATES,NULL,0,pth); + /* FIXME: set to AllUsers profile path if ALLUSERS is set */ + SHGetFolderPathW(NULL, CSIDL_PROGRAMS, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, TemplF, pth); + msi_set_property(package->db, szProgramMenuFolder, pth); - SHGetFolderPathW(NULL,CSIDL_DESKTOP,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_ADMINTOOLS, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, DF, pth); + msi_set_property(package->db, szAdminToolsFolder, pth); - SHGetFolderPathW(NULL,CSIDL_PROGRAMS,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, PMF, pth); + msi_set_property(package->db, szAppDataFolder, pth); - SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, ATF, pth); + msi_set_property(package->db, szSystemFolder, pth); + msi_set_property(package->db, szSystem16Folder, pth); - SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, ADF, pth); + msi_set_property(package->db, szLocalAppDataFolder, pth); - SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_MYPICTURES, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, SF, pth); - msi_set_property(package->db, SF16, pth); + msi_set_property(package->db, szMyPicturesFolder, pth); - SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, LADF, pth); + msi_set_property(package->db, szPersonalFolder, pth); - SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_WINDOWS, NULL, 0, pth); strcatW(pth, szBackSlash); - msi_set_property(package->db, MPF, pth); - - SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth); - strcatW(pth, szBackSlash); - msi_set_property(package->db, PF, pth); - - SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth); - strcatW(pth, szBackSlash); - msi_set_property(package->db, WF, pth); + msi_set_property(package->db, szWindowsFolder, pth); + SHGetFolderPathW(NULL, CSIDL_PRINTHOOD, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szPrintHoodFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_NETHOOD, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szNetHoodFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_RECENT, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szRecentFolder, pth); + /* Physical Memory is specified in MB. Using total amount. */ msex.dwLength = sizeof(msex); GlobalMemoryStatusEx( &msex ); - sprintfW( bufstr, szIntFormat, (int)(msex.ullTotalPhys/1024/1024)); + sprintfW( bufstr, szIntFormat, (int)(msex.ullTotalPhys / 1024 / 1024) ); msi_set_property(package->db, szPhysicalMemory, bufstr); - SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth); + SHGetFolderPathW(NULL, CSIDL_WINDOWS, NULL, 0, pth); ptr = strchrW(pth,'\\'); - if (ptr) - *(ptr+1) = 0; - msi_set_property(package->db, WV, pth); + if (ptr) *(ptr + 1) = 0; + msi_set_property(package->db, szWindowsVolume, pth); GetTempPathW(MAX_PATH,pth); - msi_set_property(package->db, TF, pth); - + msi_set_property(package->db, szTempFolder, pth); /* in a wine environment the user is always admin and privileged */ msi_set_property(package->db, szAdminUser, szOne); - msi_set_property(package->db, szPriv, szOne); + msi_set_property(package->db, szPrivileged, szOne); /* set the os things */ OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); GetVersionExW((OSVERSIONINFOW *)&OSVersion); - verval = OSVersion.dwMinorVersion+OSVersion.dwMajorVersion*100; - sprintfW(verstr,szFormat,verval); + verval = OSVersion.dwMinorVersion + OSVersion.dwMajorVersion * 100; + sprintfW(verstr, szFormat, verval); switch (OSVersion.dwPlatformId) { case VER_PLATFORM_WIN32_WINDOWS: - msi_set_property(package->db, v9x, verstr); + msi_set_property(package->db, szVersion9x, verstr); break; case VER_PLATFORM_WIN32_NT: - msi_set_property(package->db, vNT, verstr); - sprintfW(verstr,szFormat,OSVersion.wProductType); + msi_set_property(package->db, szVersionNT, verstr); + sprintfW(verstr, szFormat,OSVersion.wProductType); msi_set_property(package->db, szMsiNTProductType, verstr); break; } - sprintfW(verstr,szFormat,OSVersion.dwBuildNumber); - msi_set_property(package->db, szWinBuild, verstr); + sprintfW(verstr, szFormat, OSVersion.dwBuildNumber); + msi_set_property(package->db, szWindowsBuild, verstr); /* just fudge this */ - msi_set_property(package->db, szSPL, szSix); + msi_set_property(package->db, szServicePackLevel, szSix); sprintfW( bufstr, szFormat2, MSI_MAJORVERSION, MSI_MINORVERSION); msi_set_property( package->db, szVersionMsi, bufstr ); sprintfW( bufstr, szFormat, MSI_MAJORVERSION * 100); msi_set_property( package->db, szVersionDatabase, bufstr ); - GetSystemInfo( &sys_info ); + GetNativeSystemInfo( &sys_info ); + sprintfW( bufstr, szIntFormat, sys_info.wProcessorLevel ); if (sys_info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) { - sprintfW( bufstr, szIntFormat, sys_info.wProcessorLevel ); msi_set_property( package->db, szIntel, bufstr ); + + GetSystemDirectoryW( pth, MAX_PATH ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szSystemFolder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szProgramFilesFolder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES_COMMON, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szCommonFilesFolder, pth ); + } + else if (sys_info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + { + msi_set_property( package->db, szMsiAMD64, bufstr ); + msi_set_property( package->db, szMsix64, bufstr ); + msi_set_property( package->db, szVersionNT64, verstr ); + + GetSystemDirectoryW( pth, MAX_PATH ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szSystem64Folder, pth ); + + GetSystemWow64DirectoryW( pth, MAX_PATH ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szSystemFolder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szProgramFiles64Folder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILESX86, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szProgramFilesFolder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES_COMMON, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szCommonFiles64Folder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES_COMMONX86, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szCommonFilesFolder, pth ); } /* Screen properties. */ @@ -878,7 +904,7 @@ static VOID set_installer_properties(MSIPACKAGE *package) (username = msi_reg_get_val_str( hkey, szRegisteredUser ))) msi_set_property( package->db, szUSERNAME, username ); if (!companyname && - (companyname = msi_reg_get_val_str( hkey, szRegisteredOrg ))) + (companyname = msi_reg_get_val_str( hkey, szRegisteredOrganization ))) msi_set_property( package->db, szCOMPANYNAME, companyname ); CloseHandle( hkey ); } @@ -908,7 +934,7 @@ static VOID set_installer_properties(MSIPACKAGE *package) langid = GetUserDefaultLangID(); sprintfW(bufstr, szIntFormat, langid); - msi_set_property( package->db, szUserLangID, bufstr ); + msi_set_property( package->db, szUserLanguageID, bufstr ); langid = GetSystemDefaultLangID(); sprintfW(bufstr, szIntFormat, langid); @@ -1042,7 +1068,7 @@ static UINT msi_load_admin_properties(MSIPACKAGE *package) return r; } -void msi_adjust_allusers_property( MSIPACKAGE *package ) +void msi_adjust_privilege_properties( MSIPACKAGE *package ) { /* FIXME: this should depend on the user's privileges */ if (msi_get_property_int( package->db, szAllUsers, 0 ) == 2) @@ -1050,6 +1076,7 @@ void msi_adjust_allusers_property( MSIPACKAGE *package ) TRACE("resetting ALLUSERS property from 2 to 1\n"); msi_set_property( package->db, szAllUsers, szOne ); } + msi_set_property( package->db, szAdminUser, szOne ); } MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *db, LPCWSTR base_url ) @@ -1074,7 +1101,7 @@ MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *db, LPCWSTR base_url ) create_temp_property_table( package ); msi_clone_properties( package ); - msi_adjust_allusers_property( package ); + msi_adjust_privilege_properties( package ); package->ProductCode = msi_dup_property( package->db, szProductCode ); package->script = msi_alloc_zero( sizeof(MSISCRIPT) ); @@ -1119,7 +1146,10 @@ static UINT copy_package_to_temp( LPCWSTR szPackage, LPWSTR filename ) if( !CopyFileW( szPackage, filename, FALSE ) ) { UINT error = GetLastError(); - ERR("failed to copy package %s to %s (%u)\n", debugstr_w(szPackage), debugstr_w(filename), error); + if ( error == ERROR_FILE_NOT_FOUND ) + ERR("can't find %s\n", debugstr_w(szPackage)); + else + ERR("failed to copy package %s to %s (%u)\n", debugstr_w(szPackage), debugstr_w(filename), error); DeleteFileW( filename ); return error; } @@ -1257,6 +1287,92 @@ static UINT apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code ) return r; } +static UINT msi_parse_summary( MSISUMMARYINFO *si, MSIPACKAGE *package ) +{ + WCHAR *template, *p, *q; + DWORD i, count; + + package->version = msi_suminfo_get_int32( si, PID_PAGECOUNT ); + TRACE("version: %d\n", package->version); + + template = msi_suminfo_dup_string( si, PID_TEMPLATE ); + if (!template) + return ERROR_SUCCESS; /* native accepts missing template property */ + + TRACE("template: %s\n", debugstr_w(template)); + + p = strchrW( template, ';' ); + if (!p) + { + WARN("invalid template string %s\n", debugstr_w(template)); + msi_free( template ); + return ERROR_PATCH_PACKAGE_INVALID; + } + *p = 0; + if (!template[0] || !strcmpW( template, szIntel )) + package->platform = PLATFORM_INTEL; + else if (!strcmpW( template, szIntel64 )) + package->platform = PLATFORM_INTEL64; + else if (!strcmpW( template, szX64 )) + package->platform = PLATFORM_X64; + else + { + WARN("unknown platform %s\n", debugstr_w(template)); + msi_free( template ); + return ERROR_INSTALL_PLATFORM_UNSUPPORTED; + } + + count = 1; + for (q = ++p; (q = strchrW( q, ',' )); q++) count++; + + package->langids = msi_alloc( count * sizeof(LANGID) ); + if (!package->langids) + { + msi_free( template ); + return ERROR_OUTOFMEMORY; + } + + i = 0; + while (*p) + { + q = strchrW( p, ',' ); + if (q) *q = 0; + package->langids[i] = atoiW( p ); + if (!q) break; + p = q + 1; + i++; + } + package->num_langids = i + 1; + + msi_free( template ); + return ERROR_SUCCESS; +} + +static UINT validate_package( MSIPACKAGE *package ) +{ + BOOL is_wow64; + UINT i; + + IsWow64Process( GetCurrentProcess(), &is_wow64 ); + if (package->platform == PLATFORM_X64) + { + if (!is_64bit && !is_wow64) + return ERROR_INSTALL_PLATFORM_UNSUPPORTED; + if (package->version < 200) + return ERROR_INSTALL_PACKAGE_INVALID; + } + if (!package->num_langids) + { + return ERROR_SUCCESS; + } + for (i = 0; i < package->num_langids; i++) + { + if (!package->langids[i] || IsValidLocale( package->langids[i], LCID_INSTALLED )) + return ERROR_SUCCESS; + } + return ERROR_INSTALL_LANGUAGE_UNSUPPORTED; +} + UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage) { static const WCHAR Database[] = {'D','A','T','A','B','A','S','E',0}; @@ -1269,6 +1385,7 @@ UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage) WCHAR temppath[MAX_PATH], localfile[MAX_PATH], cachefile[MAX_PATH]; LPCWSTR file = szPackage; DWORD index = 0; + MSISUMMARYINFO *si; TRACE("%s %p\n", debugstr_w(szPackage), pPackage); @@ -1339,7 +1456,7 @@ UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage) * read/write, which is safe because we always create a copy that is thrown * away when we're done. */ - r = MSI_OpenDatabaseW( file, MSIDBOPEN_DIRECT, &db ); + r = MSI_OpenDatabaseW( file, MSIDBOPEN_TRANSACT, &db ); if( r != ERROR_SUCCESS ) { if (file != szPackage) @@ -1368,6 +1485,29 @@ UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage) if( file != szPackage ) track_tempfile( package, file ); + si = MSI_GetSummaryInformationW( db->storage, 0 ); + if (!si) + { + WARN("failed to load summary info %u\n", r); + msiobj_release( &package->hdr ); + return ERROR_INSTALL_PACKAGE_INVALID; + } + + r = msi_parse_summary( si, package ); + msiobj_release( &si->hdr ); + if (r != ERROR_SUCCESS) + { + WARN("failed to parse summary info %u\n", r); + msiobj_release( &package->hdr ); + return r; + } + + r = validate_package( package ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &package->hdr ); + return r; + } msi_set_property( package->db, Database, db->path ); if( UrlIsW( szPackage, URLIS_URL ) ) @@ -1398,7 +1538,7 @@ UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage) if (r != ERROR_SUCCESS) { ERR("registered patch failed to apply %u\n", r); - MSI_FreePackage( (MSIOBJECTHDR *)package ); + msiobj_release( &package->hdr ); return r; } @@ -1408,7 +1548,7 @@ UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage) if (index) { msi_clone_properties( package ); - msi_adjust_allusers_property( package ); + msi_adjust_privilege_properties( package ); } *pPackage = package; diff --git a/dll/win32/msi/record.c b/dll/win32/msi/record.c index 45adbac8471..7bc82814b2e 100644 --- a/dll/win32/msi/record.c +++ b/dll/win32/msi/record.c @@ -45,6 +45,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(msidb); #define MSIFIELD_INT 1 #define MSIFIELD_WSTR 3 #define MSIFIELD_STREAM 4 +#define MSIFIELD_INTPTR 5 static void MSI_FreeField( MSIFIELD *field ) { @@ -52,6 +53,7 @@ static void MSI_FreeField( MSIFIELD *field ) { case MSIFIELD_NULL: case MSIFIELD_INT: + case MSIFIELD_INTPTR: break; case MSIFIELD_WSTR: msi_free( field->u.szwVal); @@ -177,6 +179,9 @@ UINT MSI_RecordCopyField( MSIRECORD *in_rec, UINT in_n, case MSIFIELD_INT: out->u.iVal = in->u.iVal; break; + case MSIFIELD_INTPTR: + out->u.pVal = in->u.pVal; + break; case MSIFIELD_WSTR: str = strdupW( in->u.szwVal ); if ( !str ) @@ -200,6 +205,32 @@ UINT MSI_RecordCopyField( MSIRECORD *in_rec, UINT in_n, return r; } +INT_PTR MSI_RecordGetIntPtr( MSIRECORD *rec, UINT iField ) +{ + int ret; + + TRACE( "%p %d\n", rec, iField ); + + if( iField > rec->count ) + return MININT_PTR; + + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + return rec->fields[iField].u.iVal; + case MSIFIELD_INTPTR: + return rec->fields[iField].u.pVal; + case MSIFIELD_WSTR: + if( string2intW( rec->fields[iField].u.szwVal, &ret ) ) + return ret; + return MININT_PTR; + default: + break; + } + + return MININT_PTR; +} + int MSI_RecordGetInteger( MSIRECORD *rec, UINT iField) { int ret = 0; @@ -213,6 +244,8 @@ int MSI_RecordGetInteger( MSIRECORD *rec, UINT iField) { case MSIFIELD_INT: return rec->fields[iField].u.iVal; + case MSIFIELD_INTPTR: + return rec->fields[iField].u.pVal; case MSIFIELD_WSTR: if( string2intW( rec->fields[iField].u.szwVal, &ret ) ) return ret; @@ -267,6 +300,20 @@ UINT WINAPI MsiRecordClearData( MSIHANDLE handle ) return ERROR_SUCCESS; } +UINT MSI_RecordSetIntPtr( MSIRECORD *rec, UINT iField, INT_PTR pVal ) +{ + TRACE("%p %u %ld\n", rec, iField, pVal); + + if( iField > rec->count ) + return ERROR_INVALID_PARAMETER; + + MSI_FreeField( &rec->fields[iField] ); + rec->fields[iField].type = MSIFIELD_INTPTR; + rec->fields[iField].u.pVal = pVal; + + return ERROR_SUCCESS; +} + UINT MSI_RecordSetInteger( MSIRECORD *rec, UINT iField, int iVal ) { TRACE("%p %u %d\n", rec, iField, iVal); diff --git a/dll/win32/msi/registry.c b/dll/win32/msi/registry.c index 2a84e11be61..e83490f5e36 100644 --- a/dll/win32/msi/registry.c +++ b/dll/win32/msi/registry.c @@ -40,7 +40,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(msi); - /* * This module will be all the helper functions for registry access by the * installer bits. @@ -103,6 +102,15 @@ static const WCHAR szUninstall_fmt[] = { 'U','n','i','n','s','t','a','l','l','\\', '%','s',0 }; +static const WCHAR szUninstall_32node_fmt[] = { +'S','o','f','t','w','a','r','e','\\', +'W','o','w','6','4','3','2','N','o','d','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'W','i','n','d','o','w','s','\\', +'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', +'U','n','i','n','s','t','a','l','l','\\', +'%','s',0 }; + static const WCHAR szUserProduct[] = { 'S','o','f','t','w','a','r','e','\\', 'M','i','c','r','o','s','o','f','t','\\', @@ -509,28 +517,36 @@ static UINT get_user_sid(LPWSTR *usersid) return ERROR_SUCCESS; } -UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create) +UINT MSIREG_OpenUninstallKey(MSIPACKAGE *package, HKEY *key, BOOL create) { UINT rc; WCHAR keypath[0x200]; - TRACE("%s\n",debugstr_w(szProduct)); - sprintfW(keypath,szUninstall_fmt,szProduct); + TRACE("%s\n", debugstr_w(package->ProductCode)); + + if (is_64bit && package->platform == PLATFORM_INTEL) + sprintfW(keypath, szUninstall_32node_fmt, package->ProductCode); + else + sprintfW(keypath, szUninstall_fmt, package->ProductCode); if (create) - rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key); + rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, KEY_ALL_ACCESS, NULL, key, NULL); else - rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key); + rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, KEY_ALL_ACCESS, key); return rc; } -UINT MSIREG_DeleteUninstallKey(LPCWSTR szProduct) +UINT MSIREG_DeleteUninstallKey(MSIPACKAGE *package) { WCHAR keypath[0x200]; - TRACE("%s\n",debugstr_w(szProduct)); - sprintfW(keypath,szUninstall_fmt,szProduct); + TRACE("%s\n", debugstr_w(package->ProductCode)); + + if (is_64bit && package->platform == PLATFORM_INTEL) + sprintfW(keypath, szUninstall_32node_fmt, package->ProductCode); + else + sprintfW(keypath, szUninstall_fmt, package->ProductCode); return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath); } diff --git a/dll/win32/msi/storages.c b/dll/win32/msi/storages.c index 3e8887437a7..56f252fa37f 100644 --- a/dll/win32/msi/storages.c +++ b/dll/win32/msi/storages.c @@ -334,10 +334,12 @@ static UINT STORAGES_get_column_info(struct tagMSIVIEW *view, UINT n, static UINT storages_find_row(MSISTORAGESVIEW *sv, MSIRECORD *rec, UINT *row) { LPCWSTR str; - UINT i, id, data; + UINT r, i, id, data; str = MSI_RecordGetString(rec, 1); - msi_string2idW(sv->db->strings, str, &id); + r = msi_string2idW(sv->db->strings, str, &id); + if (r != ERROR_SUCCESS) + return r; for (i = 0; i < sv->num_rows; i++) { diff --git a/dll/win32/msi/string.c b/dll/win32/msi/string.c index 21b1db2a405..5644749392f 100644 --- a/dll/win32/msi/string.c +++ b/dll/win32/msi/string.c @@ -3,6 +3,7 @@ * * Copyright 2002-2004, Mike McCormack for CodeWeavers * Copyright 2007 Robert Shearman for CodeWeavers + * Copyright 2010 Hans Leidekker for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -40,8 +41,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(msidb); -#define LONG_STR_BYTES 3 - typedef struct _msistring { USHORT persistent_refcount; @@ -564,7 +563,7 @@ end: return st; } -UINT msi_save_string_table( const string_table *st, IStorage *storage ) +UINT msi_save_string_table( const string_table *st, IStorage *storage, UINT *bytes_per_strref ) { UINT i, datasize = 0, poolsize = 0, sz, used, r, codepage, n; UINT ret = ERROR_FUNCTION_FAILED; @@ -593,8 +592,16 @@ UINT msi_save_string_table( const string_table *st, IStorage *storage ) used = 0; codepage = st->codepage; - pool[0]=codepage&0xffff; - pool[1]=(codepage>>16); + pool[0] = codepage & 0xffff; + pool[1] = codepage >> 16; + if (st->maxcount > 0xffff) + { + pool[1] |= 0x8000; + *bytes_per_strref = LONG_STR_BYTES; + } + else + *bytes_per_strref = sizeof(USHORT); + n = 1; for( i=1; imaxcount; i++ ) { diff --git a/dll/win32/msi/suminfo.c b/dll/win32/msi/suminfo.c index 902a61f6544..2a9cd713001 100644 --- a/dll/win32/msi/suminfo.c +++ b/dll/win32/msi/suminfo.c @@ -469,7 +469,7 @@ UINT WINAPI MsiGetSummaryInformationW( MSIHANDLE hDatabase, if( !pHandle ) return ERROR_INVALID_PARAMETER; - if( szDatabase ) + if( szDatabase && szDatabase[0] ) { LPCWSTR persist = uiUpdateCount ? MSIDBOPEN_TRANSACT : MSIDBOPEN_READONLY; @@ -644,6 +644,18 @@ LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty ) return strdupAtoW( prop->u.pszVal ); } +INT msi_suminfo_get_int32( MSISUMMARYINFO *si, UINT uiProperty ) +{ + PROPVARIANT *prop; + + if ( uiProperty >= MSI_MAX_PROPS ) + return -1; + prop = &si->property[uiProperty]; + if( prop->vt != VT_I4 ) + return -1; + return prop->u.lVal; +} + LPWSTR msi_get_suminfo_product( IStorage *stg ) { MSISUMMARYINFO *si; diff --git a/dll/win32/msi/table.c b/dll/win32/msi/table.c index 72d1f2103a5..3e456ed56c4 100644 --- a/dll/win32/msi/table.c +++ b/dll/win32/msi/table.c @@ -42,7 +42,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(msidb); #define MSITABLE_HASH_TABLE_SIZE 37 -#define LONG_STR_BYTES 3 typedef struct tagMSICOLUMNHASHENTRY { @@ -117,13 +116,13 @@ static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz); static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count ); -static inline UINT bytes_per_column( MSIDATABASE *db, const MSICOLUMNINFO *col ) +static inline UINT bytes_per_column( MSIDATABASE *db, const MSICOLUMNINFO *col, UINT bytes_per_strref ) { if( MSITYPE_IS_BINARY(col->type) ) return 2; if( col->type & MSITYPE_STRING ) - return db->bytes_per_strref; + return bytes_per_strref; if( (col->type & 0xff) <= 2) return 2; @@ -399,24 +398,33 @@ static void free_table( MSITABLE *table ) msi_free( table ); } -static UINT msi_table_get_row_size( MSIDATABASE *db,const MSICOLUMNINFO *cols, - UINT count ) +static UINT msi_table_get_row_size( MSIDATABASE *db, const MSICOLUMNINFO *cols, UINT count, UINT bytes_per_strref ) { - const MSICOLUMNINFO *last_col = &cols[count-1]; + const MSICOLUMNINFO *last_col; + if (!count) return 0; - return last_col->offset + bytes_per_column( db, last_col ); + + if (bytes_per_strref != LONG_STR_BYTES) + { + UINT i, size = 0; + for (i = 0; i < count; i++) size += bytes_per_column( db, &cols[i], bytes_per_strref ); + return size; + } + last_col = &cols[count - 1]; + return last_col->offset + bytes_per_column( db, last_col, bytes_per_strref ); } /* add this table to the list of cached tables in the database */ static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg ) { BYTE *rawdata = NULL; - UINT rawsize = 0, i, j, row_size = 0; + UINT rawsize = 0, i, j, row_size, row_size_mem; TRACE("%s\n",debugstr_w(t->name)); - row_size = msi_table_get_row_size( db, t->colinfo, t->col_count ); + row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, db->bytes_per_strref ); + row_size_mem = msi_table_get_row_size( db, t->colinfo, t->col_count, LONG_STR_BYTES ); /* if we can't read the table, just assume that it's empty */ read_stream_data( stg, t->name, TRUE, &rawdata, &rawsize ); @@ -441,17 +449,19 @@ static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg /* transpose all the data */ TRACE("Transposing data from %d rows\n", t->row_count ); - for( i=0; irow_count; i++ ) + for (i = 0; i < t->row_count; i++) { - t->data[i] = msi_alloc( row_size ); + UINT ofs = 0, ofs_mem = 0; + + t->data[i] = msi_alloc( row_size_mem ); if( !t->data[i] ) goto err; t->data_persistent[i] = TRUE; - for( j=0; jcol_count; j++ ) + for (j = 0; j < t->col_count; j++) { - UINT ofs = t->colinfo[j].offset; - UINT n = bytes_per_column( db, &t->colinfo[j] ); + UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES ); + UINT n = bytes_per_column( db, &t->colinfo[j], db->bytes_per_strref ); UINT k; if ( n != 2 && n != 3 && n != 4 ) @@ -459,9 +469,23 @@ static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg ERR("oops - unknown column width %d\n", n); goto err; } - - for ( k = 0; k < n; k++ ) - t->data[i][ofs + k] = rawdata[ofs*t->row_count + i * n + k]; + if (t->colinfo[j].type & MSITYPE_STRING && n < m) + { + for (k = 0; k < m; k++) + { + if (k < n) + t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k]; + else + t->data[i][ofs_mem + k] = 0; + } + } + else + { + for (k = 0; k < n; k++) + t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k]; + } + ofs_mem += m; + ofs += n; } } @@ -729,10 +753,20 @@ static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret ) return ERROR_SUCCESS; } -static UINT save_table( MSIDATABASE *db, const MSITABLE *t ) +static UINT read_table_int(BYTE *const *data, UINT row, UINT col, UINT bytes) { - BYTE *rawdata = NULL, *p; - UINT rawsize, r, i, j, row_size; + UINT ret = 0, i; + + for (i = 0; i < bytes; i++) + ret += data[row][col + i] << i * 8; + + return ret; +} + +static UINT save_table( MSIDATABASE *db, const MSITABLE *t, UINT bytes_per_strref ) +{ + BYTE *rawdata = NULL; + UINT rawsize, r, i, j, row_size, row_count; /* Nothing to do for non-persistent tables */ if( t->persistent == MSICONDITION_FALSE ) @@ -740,9 +774,17 @@ static UINT save_table( MSIDATABASE *db, const MSITABLE *t ) TRACE("Saving %s\n", debugstr_w( t->name ) ); - row_size = msi_table_get_row_size( db, t->colinfo, t->col_count ); - - rawsize = t->row_count * row_size; + row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, bytes_per_strref ); + row_count = t->row_count; + for (i = 0; i < t->row_count; i++) + { + if (!t->data_persistent[i]) + { + row_count = 1; /* yes, this is bizarre */ + break; + } + } + rawsize = row_count * row_size; rawdata = msi_alloc_zero( rawsize ); if( !rawdata ) { @@ -751,25 +793,41 @@ static UINT save_table( MSIDATABASE *db, const MSITABLE *t ) } rawsize = 0; - p = rawdata; - for( i=0; icol_count; i++ ) + for (i = 0; i < t->row_count; i++) { - for( j=0; jrow_count; j++ ) + UINT ofs = 0, ofs_mem = 0; + + if (!t->data_persistent[i]) break; + + for (j = 0; j < t->col_count; j++) { - UINT offset = t->colinfo[i].offset; + UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES ); + UINT n = bytes_per_column( db, &t->colinfo[j], bytes_per_strref ); + UINT k; - if (!t->data_persistent[j]) continue; - if (i == 0) - rawsize += row_size; - - *p++ = t->data[j][offset]; - *p++ = t->data[j][offset + 1]; - if( 4 == bytes_per_column( db, &t->colinfo[i] ) ) + if (n != 2 && n != 3 && n != 4) { - *p++ = t->data[j][offset + 2]; - *p++ = t->data[j][offset + 3]; + ERR("oops - unknown column width %d\n", n); + goto err; } + if (t->colinfo[j].type & MSITYPE_STRING && n < m) + { + UINT id = read_table_int( t->data, i, ofs_mem, LONG_STR_BYTES ); + if (id > 1 << bytes_per_strref * 8) + { + ERR("string id %u out of range\n", id); + r = ERROR_FUNCTION_FAILED; + goto err; + } + } + for (k = 0; k < n; k++) + { + rawdata[ofs * row_count + i * n + k] = t->data[i][ofs_mem + k]; + } + ofs_mem += m; + ofs += n; } + rawsize += row_size; } TRACE("writing %d bytes\n", rawsize); @@ -777,12 +835,10 @@ static UINT save_table( MSIDATABASE *db, const MSITABLE *t ) err: msi_free( rawdata ); - return r; } -static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo, - DWORD count ) +static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo, DWORD count ) { DWORD i; @@ -791,7 +847,7 @@ static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo, assert( (i+1) == colinfo[ i ].number ); if (i) colinfo[i].offset = colinfo[ i - 1 ].offset - + bytes_per_column( db, &colinfo[ i - 1 ] ); + + bytes_per_column( db, &colinfo[ i - 1 ], LONG_STR_BYTES ); else colinfo[i].offset = 0; TRACE("column %d is [%s] with type %08x ofs %d\n", @@ -855,16 +911,6 @@ static LPWSTR msi_makestring( const MSIDATABASE *db, UINT stringid) return strdupW(msi_string_lookup_id( db->strings, stringid )); } -static UINT read_table_int(BYTE *const *data, UINT row, UINT col, UINT bytes) -{ - UINT ret = 0, i; - - for (i = 0; i < bytes; i++) - ret += (data[row][col + i] << i * 8); - - return ret; -} - static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz) { @@ -902,11 +948,11 @@ static UINT get_tablecolumns( MSIDATABASE *db, count = table->row_count; for( i=0; idata, i, 0, db->bytes_per_strref) != table_id ) + if( read_table_int(table->data, i, 0, LONG_STR_BYTES) != table_id ) continue; if( colinfo ) { - UINT id = read_table_int(table->data, i, table->colinfo[2].offset, db->bytes_per_strref); + UINT id = read_table_int(table->data, i, table->colinfo[2].offset, LONG_STR_BYTES); UINT col = read_table_int(table->data, i, table->colinfo[1].offset, sizeof(USHORT)) - (1<<15); /* check the column number is in range */ @@ -971,7 +1017,7 @@ static void msi_update_table_columns( MSIDATABASE *db, LPCWSTR name ) if (!table->col_count) goto done; - size = msi_table_get_row_size( db, table->colinfo, table->col_count ); + size = msi_table_get_row_size( db, table->colinfo, table->col_count, LONG_STR_BYTES ); offset = table->colinfo[table->col_count - 1].offset; for ( n = 0; n < table->row_count; n++ ) @@ -988,8 +1034,8 @@ done: /* try to find the table name in the _Tables table */ BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name ) { - UINT r, table_id = 0, i, count; - MSITABLE *table = NULL; + UINT r, table_id, i; + MSITABLE *table; static const WCHAR szStreams[] = {'_','S','t','r','e','a','m','s',0}; static const WCHAR szStorages[] = {'_','S','t','o','r','a','g','e','s',0}; @@ -1012,10 +1058,11 @@ BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name ) return FALSE; } - count = table->row_count; - for( i=0; idata[ i ][ 0 ] == table_id ) + for( i = 0; i < table->row_count; i++ ) + { + if( read_table_int( table->data, i, 0, LONG_STR_BYTES ) == table_id ) return TRUE; + } return FALSE; } @@ -1059,7 +1106,7 @@ static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT * if (tv->order) row = tv->order->reorder[row]; - n = bytes_per_column( tv->db, &tv->columns[col-1] ); + n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES ); if (n != 2 && n != 3 && n != 4) { ERR("oops! what is %d bytes per column?\n", n ); @@ -1116,7 +1163,7 @@ static UINT msi_stream_name( const MSITABLEVIEW *tv, UINT row, LPWSTR *pstname ) { static const WCHAR fmt[] = { '%','d',0 }; WCHAR number[0x20]; - UINT n = bytes_per_column( tv->db, &tv->columns[i] ); + UINT n = bytes_per_column( tv->db, &tv->columns[i], LONG_STR_BYTES ); switch( n ) { @@ -1213,7 +1260,7 @@ static UINT TABLE_set_int( MSITABLEVIEW *tv, UINT row, UINT col, UINT val ) msi_free( tv->columns[col-1].hash_table ); tv->columns[col-1].hash_table = NULL; - n = bytes_per_column( tv->db, &tv->columns[col-1] ); + n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES ); if ( n != 2 && n != 3 && n != 4 ) { ERR("oops! what is %d bytes per column?\n", n ); @@ -1304,7 +1351,7 @@ static UINT get_table_value_from_record( MSITABLEVIEW *tv, MSIRECORD *rec, UINT if (r != ERROR_SUCCESS) return ERROR_NOT_FOUND; } - else if ( 2 == bytes_per_column( tv->db, &columninfo ) ) + else if ( bytes_per_column( tv->db, &columninfo, LONG_STR_BYTES ) == 2 ) { *pvalue = 0x8000 + MSI_RecordGetInteger( rec, iField ); if ( *pvalue & 0xffff0000 ) @@ -1577,39 +1624,60 @@ static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec ) return ERROR_SUCCESS; } -static UINT find_insert_index( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *pidx ) +static int compare_record( MSITABLEVIEW *tv, UINT row, MSIRECORD *rec ) { - UINT r, idx, j, ivalue, x; + UINT r, i, ivalue, x; - TRACE("%p %p %p\n", tv, rec, pidx); - - for (idx = 0; idx < tv->table->row_count; idx++) + for (i = 0; i < tv->num_cols; i++ ) { - for (j = 0; j < tv->num_cols; j++ ) + r = get_table_value_from_record( tv, rec, i + 1, &ivalue ); + if (r != ERROR_SUCCESS) + return 1; + + r = TABLE_fetch_int( &tv->view, row, i + 1, &x ); + if (r != ERROR_SUCCESS) { - r = get_table_value_from_record (tv, rec, j+1, &ivalue); - if (r != ERROR_SUCCESS) - break; + WARN("TABLE_fetch_int should not fail here %u\n", r); + return -1; + } + if (ivalue > x) + { + return 1; + } + else if (ivalue == x) + { + if (i < tv->num_cols - 1) continue; + return 0; + } + else + return -1; + } + return 1; +} - r = TABLE_fetch_int(&tv->view, idx, j + 1, &x); - if (r != ERROR_SUCCESS) - return r; +static int find_insert_index( MSITABLEVIEW *tv, MSIRECORD *rec ) +{ + int idx, c, low = 0, high = tv->table->row_count - 1; - if (ivalue > x) - break; - else if (ivalue == x) - continue; - else { - TRACE("Found %d.\n", idx); - *pidx = idx; - return ERROR_SUCCESS; - } + TRACE("%p %p\n", tv, rec); + + while (low <= high) + { + idx = (low + high) / 2; + c = compare_record( tv, idx, rec ); + + if (c < 0) + high = idx - 1; + else if (c > 0) + low = idx + 1; + else + { + TRACE("found %u\n", idx); + return idx; } } - - TRACE("Found %d.\n", idx); - *pidx = idx; - return ERROR_SUCCESS; + TRACE("found %u\n", high + 1); + return high + 1; } static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary ) @@ -1625,11 +1693,7 @@ static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, return ERROR_FUNCTION_FAILED; if (row == -1) - { - r = find_insert_index(tv, rec, &row); - if( r != ERROR_SUCCESS ) - return ERROR_FUNCTION_FAILED; - } + row = find_insert_index( tv, rec ); r = table_create_new_row( view, &row, temporary ); TRACE("insert_row returned %08x\n", r); @@ -2306,7 +2370,7 @@ UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ) tv->db = db; tv->columns = tv->table->colinfo; tv->num_cols = tv->table->col_count; - tv->row_size = msi_table_get_row_size( db, tv->table->colinfo, tv->table->col_count ); + tv->row_size = msi_table_get_row_size( db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES ); TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size ); @@ -2318,12 +2382,13 @@ UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ) UINT MSI_CommitTables( MSIDATABASE *db ) { - UINT r; + UINT r, bytes_per_strref; + HRESULT hr; MSITABLE *table = NULL; TRACE("%p\n",db); - r = msi_save_string_table( db->strings, db->storage ); + r = msi_save_string_table( db->strings, db->storage, &bytes_per_strref ); if( r != ERROR_SUCCESS ) { WARN("failed to save string table r=%08x\n",r); @@ -2332,7 +2397,7 @@ UINT MSI_CommitTables( MSIDATABASE *db ) LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry ) { - r = save_table( db, table ); + r = save_table( db, table, bytes_per_strref ); if( r != ERROR_SUCCESS ) { WARN("failed to save table %s (r=%08x)\n", @@ -2344,7 +2409,13 @@ UINT MSI_CommitTables( MSIDATABASE *db ) /* force everything to reload next time */ free_cached_tables( db ); - return ERROR_SUCCESS; + hr = IStorage_Commit( db->storage, 0 ); + if (FAILED( hr )) + { + WARN("failed to commit changes 0x%08x\n", hr); + r = ERROR_FUNCTION_FAILED; + } + return r; } MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table ) @@ -2463,7 +2534,7 @@ static MSIRECORD *msi_get_transform_record( const MSITABLEVIEW *tv, const string IStream *stm = NULL; UINT r; - ofs += bytes_per_column( tv->db, &columns[i] ); + ofs += bytes_per_column( tv->db, &columns[i], bytes_per_strref ); r = msi_record_encoded_stream_name( tv, rec, &encname ); if ( r != ERROR_SUCCESS ) @@ -2490,7 +2561,7 @@ static MSIRECORD *msi_get_transform_record( const MSITABLEVIEW *tv, const string } else { - UINT n = bytes_per_column( tv->db, &columns[i] ); + UINT n = bytes_per_column( tv->db, &columns[i], bytes_per_strref ); switch( n ) { case 2: @@ -2704,7 +2775,7 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg, ! MSITYPE_IS_BINARY(tv->columns[i].type) ) sz += bytes_per_strref; else - sz += bytes_per_column( tv->db, &tv->columns[i] ); + sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref ); } } else @@ -2726,7 +2797,7 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg, ! MSITYPE_IS_BINARY(tv->columns[i].type) ) sz += bytes_per_strref; else - sz += bytes_per_column( tv->db, &tv->columns[i] ); + sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref ); } } } diff --git a/dll/win32/msi/version.rc b/dll/win32/msi/version.rc index 4fe3d9173e9..ab027f409a2 100644 --- a/dll/win32/msi/version.rc +++ b/dll/win32/msi/version.rc @@ -18,9 +18,9 @@ #define WINE_FILEDESCRIPTION_STR "Wine MSI dll" #define WINE_FILENAME_STR "msi.dll" -#define WINE_FILEVERSION 3,1,4000,2435 -#define WINE_FILEVERSION_STR "3.1.4000.2435" -#define WINE_PRODUCTVERSION 3,1,4000,2435 -#define WINE_PRODUCTVERSION_STR "3.1.4000.2435" +#define WINE_FILEVERSION 4,5,6001,22159 +#define WINE_FILEVERSION_STR "4.5.6001.22159" +#define WINE_PRODUCTVERSION 4,5,6001,22159 +#define WINE_PRODUCTVERSION_STR "4.5.6001.22159" #include "wine/wine_common_ver.rc" diff --git a/dll/win32/newdev/lang/ja-JP.rc b/dll/win32/newdev/lang/ja-JP.rc index e0600cf8ac3..ea51d295694 100644 --- a/dll/win32/newdev/lang/ja-JP.rc +++ b/dll/win32/newdev/lang/ja-JP.rc @@ -8,10 +8,10 @@ FONT 9, "MS UI Gothic" BEGIN LTEXT "ようこそ",IDC_WELCOMETITLE,120,8,195,24 LTEXT "このウィザードでは、以下の新しいデバイス ドライバをインストールします:", - IDC_STATIC,120,21,195,16 + IDC_STATIC,120,21,195,30 LTEXT "続行するには、[次へ] をクリックしてください。",IDC_STATIC,120,169,195,17 - LTEXT "不明なデバイス",IDC_DEVICE,134,36,164,11 - LTEXT "警告! カスタム デバイスをインストールすると、コンピュータがフリーズしたり、起動不能\になる可能\性があります!",IDC_STATIC,120,59,195,16 + LTEXT "不明なデバイス",IDC_DEVICE,134,52,164,11 + LTEXT "警告! カスタム デバイスをインストールすると、コンピュータがフリーズしたり、起動不能\になる可能\性があります!",IDC_STATIC,120,67,195,34 CONTROL "ドライバを自動的にインストールする",IDC_RADIO_AUTO,"Button", BS_AUTORADIOBUTTON,120,112,178,13 CONTROL "ドライバを特定の場所からインストールする",IDC_RADIO_MANUAL, diff --git a/dll/win32/rpcrt4/ndr_marshall.c b/dll/win32/rpcrt4/ndr_marshall.c index 9946bd13d57..839a4cad882 100644 --- a/dll/win32/rpcrt4/ndr_marshall.c +++ b/dll/win32/rpcrt4/ndr_marshall.c @@ -6032,6 +6032,7 @@ static LONG unmarshall_discriminant(PMIDL_STUB_MESSAGE pStubMsg, case RPC_FC_WCHAR: case RPC_FC_SHORT: case RPC_FC_USHORT: + case RPC_FC_ENUM16: { USHORT d; ALIGN_POINTER(pStubMsg->Buffer, sizeof(USHORT)); diff --git a/dll/win32/rpcrt4/rpcrt4_ros.diff b/dll/win32/rpcrt4/rpcrt4_ros.diff index 548e18aacdb..a3309f0b565 100644 --- a/dll/win32/rpcrt4/rpcrt4_ros.diff +++ b/dll/win32/rpcrt4/rpcrt4_ros.diff @@ -15,6 +15,18 @@ Index: epm_towers.h #define EPM_PROTOCOL_OSI_TP4 0x05 #define EPM_PROTOCOL_OSI_CLNS 0x06 #define EPM_PROTOCOL_TCP 0x07 +Index: ndr_marshall.c +=================================================================== +--- ndr_marshall.c (working copy) ++++ ndr_marshall.c (working copy) +@@ -6032,6 +6032,7 @@ + case RPC_FC_WCHAR: + case RPC_FC_SHORT: + case RPC_FC_USHORT: ++ case RPC_FC_ENUM16: + { + USHORT d; + ALIGN_POINTER(pStubMsg->Buffer, sizeof(USHORT)); Index: rpc_epmap.c =================================================================== --- rpc_epmap.c (working copy) diff --git a/dll/win32/syssetup/install.c b/dll/win32/syssetup/install.c index b5c226fd755..adf909ada4c 100644 --- a/dll/win32/syssetup/install.c +++ b/dll/win32/syssetup/install.c @@ -810,7 +810,7 @@ CreateShortcuts(VOID) if (CreateShortcutFolder(CSIDL_PROGRAMS, IDS_GAMES, szFolder, sizeof(szFolder)/sizeof(szFolder[0]))) { CreateShortcut(CSIDL_PROGRAMS, szFolder, IDS_SHORT_SOLITAIRE, _T("%SystemRoot%\\system32\\sol.exe"), IDS_CMT_SOLITAIRE, TRUE); - CreateShortcut(CSIDL_PROGRAMS, szFolder, IDS_SHORT_WINEMINE, _T("%SystemRoot%\\system32\\winemine.exe"), IDS_CMT_WINEMINE, TRUE); + CreateShortcut(CSIDL_PROGRAMS, szFolder, IDS_SHORT_WINEMINE, _T("%SystemRoot%\\system32\\winmine.exe"), IDS_CMT_WINEMINE, TRUE); CreateShortcut(CSIDL_PROGRAMS, szFolder, IDS_SHORT_SPIDER, _T("%SystemRoot%\\system32\\spider.exe"), IDS_CMT_SPIDER, TRUE); } diff --git a/dll/win32/syssetup/lang/ja-JP.rc b/dll/win32/syssetup/lang/ja-JP.rc index 242f54e6ccb..ebe36a432d3 100644 --- a/dll/win32/syssetup/lang/ja-JP.rc +++ b/dll/win32/syssetup/lang/ja-JP.rc @@ -102,14 +102,14 @@ BEGIN "", IDC_STATIC, 53, 7, 253, 20 LTEXT "", IDC_LOCALETEXT, 53, 29, 250, 16 LTEXT "システム、ユーザーロケール設定変更には[カスタマイズ]をクリックします。", - IDC_STATIC, 53, 60, 196, 8 - PUSHBUTTON "カスタマイズ(&C)...", IDC_CUSTOMLOCALE, 250, 57, 50, 14 + IDC_STATIC, 53, 50, 166, 25 + PUSHBUTTON "カスタマイズ(&C)...", IDC_CUSTOMLOCALE, 220, 52, 80, 14 LTEXT "キーボード レイアウトは、キーを押したときにどの文字が入力されるかを制御します。", - IDC_STATIC, 53, 86, 253, 8 - LTEXT "", IDC_LAYOUTTEXT, 53, 100, 250, 16 + IDC_STATIC, 53, 81, 253, 20 + LTEXT "", IDC_LAYOUTTEXT, 53, 106, 250, 14 LTEXT "キーボードレイアウト変更には[カスタマイズ]をクリックします。", - IDC_STATIC, 53, 126, 184, 8 - PUSHBUTTON "カスタマイズ(&U)...", IDC_CUSTOMLAYOUT, 250, 122, 50, 14 + IDC_STATIC, 53, 120, 166, 25 + PUSHBUTTON "カスタマイズ(&U)...", IDC_CUSTOMLAYOUT, 220, 122, 80, 14 END diff --git a/dll/win32/user32/include/user32.h b/dll/win32/user32/include/user32.h index ba2a8364656..c6dbbc7279c 100644 --- a/dll/win32/user32/include/user32.h +++ b/dll/win32/user32/include/user32.h @@ -43,7 +43,8 @@ #include #define HOOKID_TO_FLAG(HookId) (1 << ((HookId) + 1)) -#define ISITHOOKED(HookId) (GetWin32ClientInfo()->fsHooks & HOOKID_TO_FLAG(HookId)) +#define ISITHOOKED(HookId) (GetWin32ClientInfo()->fsHooks & HOOKID_TO_FLAG(HookId) ||\ + (GetWin32ClientInfo()->pDeskInfo && GetWin32ClientInfo()->pDeskInfo->fsHooks & HOOKID_TO_FLAG(HookId))) /* Temporarily in here for now. */ typedef struct _USERAPIHOOKINFO @@ -194,3 +195,4 @@ VOID FASTCALL GetConnected(VOID); BOOL FASTCALL DefSetText(HWND hWnd, PCWSTR String, BOOL Ansi); BOOL FASTCALL TestWindowProcess(PWND); VOID UserGetWindowBorders(DWORD, DWORD, SIZE *, BOOL); +VOID FASTCALL IntNotifyWinEvent(DWORD, HWND, LONG, LONG, DWORD); diff --git a/dll/win32/user32/include/user32p.h b/dll/win32/user32/include/user32p.h index 0227e8edce3..7bcf68cf777 100644 --- a/dll/win32/user32/include/user32p.h +++ b/dll/win32/user32/include/user32p.h @@ -83,9 +83,6 @@ #define NtUserGetCursorPos(lpPoint) \ (BOOL)NtUserCallOneParam((DWORD_PTR)lpPoint, ONEPARAM_ROUTINE_GETCURSORPOSITION) -#define NtUserIsWindowInDestroy(hWnd) \ - (BOOL)NtUserCallOneParam((DWORD_PTR)hWnd, ONEPARAM_ROUTINE_ISWINDOWINDESTROY) - #define NtUserEnableProcessWindowGhosting(bEnable) \ NtUserCallOneParam((DWORD_PTR)bEnable, ONEPARAM_ROUTINE_ENABLEPROCWNDGHSTING) diff --git a/dll/win32/user32/misc/misc.c b/dll/win32/user32/misc/misc.c index 76aaf462a31..e7b7672cfdc 100644 --- a/dll/win32/user32/misc/misc.c +++ b/dll/win32/user32/misc/misc.c @@ -304,13 +304,13 @@ GetUser32Handle(HANDLE handle) static const BOOL g_ObjectHeapTypeShared[VALIDATE_TYPE_EVENT + 1] = { FALSE, /* VALIDATE_TYPE_FREE (not used) */ - FALSE, /* VALIDATE_TYPE_WIN FALSE */ + FALSE, /* VALIDATE_TYPE_WIN */ TRUE, /* VALIDATE_TYPE_MENU FALSE */ TRUE, /* VALIDATE_TYPE_CURSOR */ TRUE, /* VALIDATE_TYPE_MWPOS */ - TRUE, /* VALIDATE_TYPE_HOOK FALSE */ + FALSE, /* VALIDATE_TYPE_HOOK */ FALSE, /* (not used) */ - TRUE, /* VALIDATE_TYPE_CALLPROC FALSE */ + FALSE, /* VALIDATE_TYPE_CALLPROC */ TRUE, /* VALIDATE_TYPE_ACCEL */ FALSE, /* (not used) */ FALSE, /* (not used) */ diff --git a/dll/win32/user32/windows/defwnd.c b/dll/win32/user32/windows/defwnd.c index 628940de1d6..76b34be0e9f 100644 --- a/dll/win32/user32/windows/defwnd.c +++ b/dll/win32/user32/windows/defwnd.c @@ -103,6 +103,7 @@ BOOL FASTCALL DefSetText(HWND hWnd, PCWSTR String, BOOL Ansi) { + BOOL Ret; LARGE_STRING lsString; if ( String ) @@ -112,7 +113,12 @@ DefSetText(HWND hWnd, PCWSTR String, BOOL Ansi) else RtlInitLargeUnicodeString((PLARGE_UNICODE_STRING)&lsString, String, 0); } - return NtUserDefSetText(hWnd, (String ? &lsString : NULL)); + Ret = NtUserDefSetText(hWnd, (String ? &lsString : NULL)); + + if (Ret) + IntNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hWnd, OBJID_WINDOW, CHILDID_SELF, 0); + + return Ret; } void @@ -1969,7 +1975,6 @@ RealDefWindowProcA(HWND hWnd, { DefWndNCPaint(hWnd, (HRGN)1, -1); } - Result = 1; break; } diff --git a/dll/win32/user32/windows/hook.c b/dll/win32/user32/windows/hook.c index 304f9320a96..f79bcbe7080 100644 --- a/dll/win32/user32/windows/hook.c +++ b/dll/win32/user32/windows/hook.c @@ -34,6 +34,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(user32); +typedef struct _NOTIFYEVENT +{ + DWORD event; + LONG idObject; + LONG idChild; + DWORD flags; +} NOTIFYEVENT, *PNOTIFYEVENT; + /* PRIVATE FUNCTIONS *********************************************************/ static @@ -99,6 +107,29 @@ IntSetWindowsHook( return NtUserSetWindowsHookEx(hMod, &USModuleName, dwThreadId, idHook, lpfn, bAnsi); } +/* + Since ReactOS uses User32 as the main message source this was needed. + Base on the funny rules from the wine tests it left it with this option. + 8^( + */ +VOID +FASTCALL +IntNotifyWinEvent( + DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild, + DWORD flags + ) +{ + NOTIFYEVENT ne; + ne.event = event; + ne.idObject = idObject; + ne.idChild = idChild; + ne.flags = flags; + if (gpsi->dwInstalledEventHooks & GetMaskFromEvent(event)) + NtUserCallHwndParam(hwnd, (DWORD)&ne, HWNDPARAM_ROUTINE_ROS_NOTIFYWINEVENT); +} /* FUNCTIONS *****************************************************************/ @@ -195,7 +226,7 @@ CallNextHookEx( { PCLIENTINFO ClientInfo; DWORD Flags, Save; - PHOOK pHook; + PHOOK pHook, phkNext; LRESULT lResult = 0; GetConnected(); @@ -204,9 +235,14 @@ CallNextHookEx( if (!ClientInfo->phkCurrent) return 0; - pHook = SharedPtrToUser(ClientInfo->phkCurrent); + pHook = DesktopPtrToUser(ClientInfo->phkCurrent); - if (pHook->HookId == WH_CALLWNDPROC || pHook->HookId == WH_CALLWNDPROCRET) + if (!pHook->phkNext) return 0; // Nothing to do.... + + phkNext = DesktopPtrToUser(pHook->phkNext); + + if ( phkNext->HookId == WH_CALLWNDPROC || + phkNext->HookId == WH_CALLWNDPROCRET) { Save = ClientInfo->dwHookData; Flags = ClientInfo->CI_flags & CI_CURTHPRHOOK; @@ -215,7 +251,7 @@ CallNextHookEx( if (wParam) ClientInfo->CI_flags |= CI_CURTHPRHOOK; else ClientInfo->CI_flags &= ~CI_CURTHPRHOOK; - if (pHook->HookId == WH_CALLWNDPROC) + if (phkNext->HookId == WH_CALLWNDPROC) { PCWPSTRUCT pCWP = (PCWPSTRUCT)lParam; @@ -225,7 +261,7 @@ CallNextHookEx( pCWP->lParam, (ULONG_PTR)&lResult, FNID_CALLWNDPROC, - pHook->Ansi); + phkNext->Ansi); } else { @@ -239,7 +275,7 @@ CallNextHookEx( pCWPR->lParam, (ULONG_PTR)&lResult, FNID_CALLWNDPROCRET, - pHook->Ansi); + phkNext->Ansi); } ClientInfo->CI_flags ^= ((ClientInfo->CI_flags ^ Flags) & CI_CURTHPRHOOK); ClientInfo->dwHookData = Save; @@ -252,23 +288,27 @@ CallNextHookEx( /* - * @unimplemented + * @implemented */ HHOOK WINAPI SetWindowsHookW(int idHook, HOOKPROC lpfn) { - return IntSetWindowsHook(idHook, lpfn, NULL, 0, FALSE); + DWORD ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread); + return IntSetWindowsHook(idHook, lpfn, NULL, ThreadId, FALSE); +// return NtUserSetWindowsHookAW(idHook, lpfn, FALSE); } /* - * @unimplemented + * @implemented */ HHOOK WINAPI SetWindowsHookA(int idHook, HOOKPROC lpfn) { - return IntSetWindowsHook(idHook, lpfn, NULL, 0, TRUE); + DWORD ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread); + return IntSetWindowsHook(idHook, lpfn, NULL, ThreadId, TRUE); +// return NtUserSetWindowsHookAW(idHook, lpfn, TRUE); } /* @@ -377,7 +417,7 @@ IsWinEventHookInstalled( } /* - * @unimplemented + * @implemented */ HHOOK WINAPI @@ -392,7 +432,7 @@ SetWindowsHookExA( /* - * @unimplemented + * @implemented */ HHOOK WINAPI @@ -417,14 +457,15 @@ User32CallHookProcFromKernel(PVOID Arguments, ULONG ArgumentLength) PHOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS CbtCreatewndExtra = NULL; WPARAM wParam = 0; LPARAM lParam = 0; - PKBDLLHOOKSTRUCT KeyboardLlData; - PMSLLHOOKSTRUCT MouseLlData; - PMSG Msg; - PMOUSEHOOKSTRUCT MHook; - PCWPSTRUCT CWP; - PCWPRETSTRUCT CWPR; + PKBDLLHOOKSTRUCT pKeyboardLlData; + PMSLLHOOKSTRUCT pMouseLlData; + PMSG pMsg; + PMOUSEHOOKSTRUCT pMHook; + PCWPSTRUCT pCWP; + PCWPRETSTRUCT pCWPR; PRECTL prl; LPCBTACTIVATESTRUCT pcbtas; + BOOL Hit = FALSE; Common = (PHOOKPROC_CALLBACK_ARGUMENTS) Arguments; @@ -464,8 +505,8 @@ User32CallHookProcFromKernel(PVOID Arguments, ULONG ArgumentLength) } break; case HCBT_CLICKSKIPPED: - MHook = (PMOUSEHOOKSTRUCT)((PCHAR) Common + Common->lParam); - lParam = (LPARAM) MHook; + pMHook = (PMOUSEHOOKSTRUCT)((PCHAR) Common + Common->lParam); + lParam = (LPARAM) pMHook; break; case HCBT_MOVESIZE: prl = (PRECTL)((PCHAR) Common + Common->lParam); @@ -475,7 +516,7 @@ User32CallHookProcFromKernel(PVOID Arguments, ULONG ArgumentLength) pcbtas = (LPCBTACTIVATESTRUCT)((PCHAR) Common + Common->lParam); lParam = (LPARAM) pcbtas; break; - case HCBT_KEYSKIPPED: + case HCBT_KEYSKIPPED: /* The rest SEH support */ case HCBT_MINMAX: case HCBT_SETFOCUS: case HCBT_SYSCOMMAND: @@ -490,7 +531,17 @@ User32CallHookProcFromKernel(PVOID Arguments, ULONG ArgumentLength) } if (Common->Proc) - Result = Common->Proc(Common->Code, wParam, lParam); + { + _SEH2_TRY + { + Result = Common->Proc(Common->Code, wParam, lParam); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Hit = TRUE; + } + _SEH2_END; + } else { ERR("Common = 0x%x, Proc = 0x%x\n",Common,Common->Proc); @@ -504,41 +555,67 @@ User32CallHookProcFromKernel(PVOID Arguments, ULONG ArgumentLength) break; } case WH_KEYBOARD_LL: - KeyboardLlData = (PKBDLLHOOKSTRUCT)((PCHAR) Common + Common->lParam); - Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) KeyboardLlData); + pKeyboardLlData = (PKBDLLHOOKSTRUCT)((PCHAR) Common + Common->lParam); + Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) pKeyboardLlData); break; case WH_MOUSE_LL: - MouseLlData = (PMSLLHOOKSTRUCT)((PCHAR) Common + Common->lParam); - Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) MouseLlData); + pMouseLlData = (PMSLLHOOKSTRUCT)((PCHAR) Common + Common->lParam); + Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) pMouseLlData); break; - case WH_MOUSE: - MHook = (PMOUSEHOOKSTRUCT)((PCHAR) Common + Common->lParam); - Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) MHook); + case WH_MOUSE: /* SEH support */ + pMHook = (PMOUSEHOOKSTRUCT)((PCHAR) Common + Common->lParam); + _SEH2_TRY + { + Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) pMHook); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Hit = TRUE; + } + _SEH2_END; break; case WH_CALLWNDPROC: - CWP = (PCWPSTRUCT)((PCHAR) Common + Common->lParam); - Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) CWP); + pCWP = (PCWPSTRUCT)((PCHAR) Common + Common->lParam); + Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) pCWP); break; case WH_CALLWNDPROCRET: - CWPR = (PCWPRETSTRUCT)((PCHAR) Common + Common->lParam); - Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) CWPR); + pCWPR = (PCWPRETSTRUCT)((PCHAR) Common + Common->lParam); + Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) pCWPR); break; - case WH_MSGFILTER: + case WH_MSGFILTER: /* All SEH support */ case WH_SYSMSGFILTER: case WH_GETMESSAGE: - Msg = (PMSG)((PCHAR) Common + Common->lParam); -// FIXME("UHOOK Memory: %x: %x\n",Common, Msg); - Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) Msg); + pMsg = (PMSG)((PCHAR) Common + Common->lParam); + _SEH2_TRY + { + Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) pMsg); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Hit = TRUE; + } + _SEH2_END; break; - case WH_FOREGROUNDIDLE: + case WH_FOREGROUNDIDLE: /* <-- SEH support */ case WH_KEYBOARD: case WH_SHELL: - Result = Common->Proc(Common->Code, Common->wParam, Common->lParam); + _SEH2_TRY + { + Result = Common->Proc(Common->Code, Common->wParam, Common->lParam); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Hit = TRUE; + } + _SEH2_END; break; default: return ZwCallbackReturn(NULL, 0, STATUS_NOT_SUPPORTED); } - + if (Hit) + { + ERR("Hook Exception! Id: %d, Code %d, Proc 0x%x\n",Common->HookId,Common->Code,Common->Proc); + } return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS); } diff --git a/dll/win32/user32/windows/input.c b/dll/win32/user32/windows/input.c index aa9a997f070..08e457bf5a1 100644 --- a/dll/win32/user32/windows/input.c +++ b/dll/win32/user32/windows/input.c @@ -138,6 +138,7 @@ EnableWindow(HWND hWnd, if (Update) { + IntNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hWnd, OBJID_WINDOW, CHILDID_SELF, 0); SendMessageW(hWnd, WM_ENABLE, (LPARAM)bEnable, 0); } // Return nonzero if it was disabled, or zero if it wasn't: diff --git a/dll/win32/user32/windows/menu.c b/dll/win32/user32/windows/menu.c index dd8f1ff1780..c9e87de47de 100644 --- a/dll/win32/user32/windows/menu.c +++ b/dll/win32/user32/windows/menu.c @@ -1636,6 +1636,8 @@ static BOOL FASTCALL MenuShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, UINT fl top_popup_hmenu = hmenu; } + IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0); + /* Display the window */ SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, 0, 0, 0, 0, @@ -3445,6 +3447,7 @@ static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y, if (MenuInfo.Flags & MF_POPUP) { + IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0); DestroyWindow(MenuInfo.Wnd); MenuInfo.Wnd = NULL; @@ -3518,7 +3521,11 @@ static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT MenuInfo.Wnd = hWnd; MenuSetRosMenuInfo(&MenuInfo); } - + + IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART, + hWnd, + MenuInfo.Flags & MF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU, + CHILDID_SELF, 0); return TRUE; } /*********************************************************************** @@ -3528,6 +3535,7 @@ static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup) { TRACE("hwnd=%p\n", hWnd); + IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0); SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 ); ShowCaret(0); top_popup = 0; @@ -3645,7 +3653,7 @@ BOOL WINAPI TrackPopupMenuEx( HMENU Menu, UINT Flags, int x, int y, { BOOL ret = FALSE; - if (!IsMenu(Menu)) + if (!IsMenu(Menu)) { SetLastError( ERROR_INVALID_MENU_HANDLE ); return FALSE; diff --git a/dll/win32/user32/windows/message.c b/dll/win32/user32/windows/message.c index 2c89b7863df..2645c87f234 100644 --- a/dll/win32/user32/windows/message.c +++ b/dll/win32/user32/windows/message.c @@ -2064,13 +2064,18 @@ SendMessageW(HWND Wnd, if (Wnd != HWND_BROADCAST && (Msg < WM_DDE_FIRST || Msg > WM_DDE_LAST)) { - if (Window != NULL && Window->head.pti == ti && !IsThreadHooked(GetWin32ClientInfo())) + if ( Window != NULL && + Window->head.pti == ti && + !IsThreadHooked(GetWin32ClientInfo()) && + !(Window->state & WNDS_SERVERSIDEWINDOWPROC) ) { /* NOTE: We can directly send messages to the window procedure if *all* the following conditions are met: * Window belongs to calling thread * The calling thread is not being hooked + * Not calling a server side proc: + Desktop, Switch, ScrollBar, Menu, IconTitle, or hWndMessage */ return IntCallMessageProc(Window, Wnd, Msg, wParam, lParam, FALSE); @@ -2130,13 +2135,18 @@ SendMessageA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) if (Wnd != HWND_BROADCAST && (Msg < WM_DDE_FIRST || Msg > WM_DDE_LAST)) { - if (Window != NULL && Window->head.pti == ti && !IsThreadHooked(GetWin32ClientInfo())) + if ( Window != NULL && + Window->head.pti == ti && + !IsThreadHooked(GetWin32ClientInfo()) && + !(Window->state & WNDS_SERVERSIDEWINDOWPROC) ) { /* NOTE: We can directly send messages to the window procedure if *all* the following conditions are met: * Window belongs to calling thread * The calling thread is not being hooked + * Not calling a server side proc: + Desktop, Switch, ScrollBar, Menu, IconTitle, or hWndMessage */ return IntCallMessageProc(Window, Wnd, Msg, wParam, lParam, TRUE); diff --git a/dll/win32/user32/windows/window.c b/dll/win32/user32/windows/window.c index 468536ee290..1b1a0894e15 100644 --- a/dll/win32/user32/windows/window.c +++ b/dll/win32/user32/windows/window.c @@ -2060,7 +2060,11 @@ AnyPopup(VOID) BOOL WINAPI IsWindowInDestroy(HWND hWnd) { - return NtUserIsWindowInDestroy(hWnd); + PWND pwnd; + pwnd = ValidateHwnd(hWnd); + if (!pwnd) + return FALSE; + return ((pwnd->state2 & WNDS2_INDESTROY) == WNDS2_INDESTROY); } /* diff --git a/dll/win32/wdmaud.drv/legacy.c b/dll/win32/wdmaud.drv/legacy.c index d569d0ba00c..cce93e8994a 100644 --- a/dll/win32/wdmaud.drv/legacy.c +++ b/dll/win32/wdmaud.drv/legacy.c @@ -282,6 +282,25 @@ WdmAudCloseSoundDeviceByLegacy( /* First stop the stream */ if (DeviceType != MIXER_DEVICE_TYPE) { + DeviceInfo.u.State = KSSTATE_PAUSE; + SyncOverlappedDeviceIoControl(KernelHandle, + IOCTL_SETDEVICE_STATE, + (LPVOID) &DeviceInfo, + sizeof(WDMAUD_DEVICE_INFO), + (LPVOID) &DeviceInfo, + sizeof(WDMAUD_DEVICE_INFO), + NULL); + + DeviceInfo.u.State = KSSTATE_ACQUIRE; + SyncOverlappedDeviceIoControl(KernelHandle, + IOCTL_SETDEVICE_STATE, + (LPVOID) &DeviceInfo, + sizeof(WDMAUD_DEVICE_INFO), + (LPVOID) &DeviceInfo, + sizeof(WDMAUD_DEVICE_INFO), + NULL); + + DeviceInfo.u.State = KSSTATE_STOP; SyncOverlappedDeviceIoControl(KernelHandle, IOCTL_SETDEVICE_STATE, diff --git a/drivers/ksfilter/ks/connectivity.c b/drivers/ksfilter/ks/connectivity.c index b20263af07f..2825221c4d1 100644 --- a/drivers/ksfilter/ks/connectivity.c +++ b/drivers/ksfilter/ks/connectivity.c @@ -330,6 +330,7 @@ KspPinPropertyHandler( NTSTATUS Status = STATUS_NOT_SUPPORTED; ULONG Count; const PKSDATARANGE* DataRanges; + LPGUID Guid; IoStack = IoGetCurrentIrpStackLocation(Irp); Buffer = Irp->UserBuffer; @@ -509,48 +510,77 @@ KspPinPropertyHandler( case KSPROPERTY_PIN_CATEGORY: + if (!Descriptor->Category) + { + /* no pin category */ + return STATUS_NOT_FOUND; + } + + /* check size */ Size = sizeof(GUID); if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < Size) { + /* buffer too small */ Irp->IoStatus.Information = Size; Status = STATUS_BUFFER_TOO_SMALL; break; } - if (Descriptor->Category) - { - RtlMoveMemory(Buffer, Descriptor->Category, sizeof(GUID)); - } + /* copy category guid */ + RtlMoveMemory(Buffer, Descriptor->Category, sizeof(GUID)); + + /* save result */ Status = STATUS_SUCCESS; Irp->IoStatus.Information = Size; break; case KSPROPERTY_PIN_NAME: - if (!Descriptor->Name) + + if (Descriptor->Name) { - Irp->IoStatus.Information = 0; - Status = STATUS_SUCCESS; - break; + /* use pin name */ + Guid = (LPGUID)Descriptor->Name; + } + else + { + /* use pin category as fallback */ + Guid = (LPGUID)Descriptor->Category; } - Status = KspReadMediaCategory((LPGUID)Descriptor->Name, &KeyInfo); + if (!Guid) + { + /* no friendly name available */ + return STATUS_NOT_FOUND; + } + + /* read friendly name category name */ + Status = KspReadMediaCategory(Guid, &KeyInfo); if (!NT_SUCCESS(Status)) { + /* failed to read category */ Irp->IoStatus.Information = 0; break; } + /* store required length */ Irp->IoStatus.Information = KeyInfo->DataLength + sizeof(WCHAR); + /* check if buffer is too small */ if (KeyInfo->DataLength + sizeof(WCHAR) > IoStack->Parameters.DeviceIoControl.OutputBufferLength) { + /* buffer too small */ Status = STATUS_BUFFER_OVERFLOW; FreeItem(KeyInfo); break; } + /* copy result */ RtlMoveMemory(Irp->UserBuffer, &KeyInfo->Data, KeyInfo->DataLength); + + /* null terminate name */ ((LPWSTR)Irp->UserBuffer)[KeyInfo->DataLength / sizeof(WCHAR)] = L'\0'; + + /* free key info */ FreeItem(KeyInfo); break; case KSPROPERTY_PIN_PROPOSEDATAFORMAT: diff --git a/drivers/ksfilter/ks/property.c b/drivers/ksfilter/ks/property.c index b1a17e3ed09..b7f93fe2ac7 100644 --- a/drivers/ksfilter/ks/property.c +++ b/drivers/ksfilter/ks/property.c @@ -47,6 +47,25 @@ FindPropertyHandler( IoStatus->Information = PropertySet[Index].PropertyItem[ItemIndex].MinData; return STATUS_MORE_ENTRIES; } + + /* store property set */ + *Set = (PKSPROPERTY_SET)&PropertySet[Index]; + + if (Property->Flags & KSPROPERTY_TYPE_SET) + { + /* store property handler */ + *PropertyHandler = PropertySet[Index].PropertyItem[ItemIndex].SetPropertyHandler; + return STATUS_SUCCESS; + } + + if (Property->Flags & KSPROPERTY_TYPE_GET) + { + /* store property handler */ + *PropertyHandler = PropertySet[Index].PropertyItem[ItemIndex].GetPropertyHandler; + return STATUS_SUCCESS; + } + + if (Property->Flags & KSPROPERTY_TYPE_BASICSUPPORT) { PULONG Flags; @@ -64,14 +83,21 @@ FindPropertyHandler( /* clear flags */ *Flags = 0; + IoStatus->Information = sizeof(ULONG); + + if (PropertySet[Index].PropertyItem[ItemIndex].SupportHandler) + { + /* use support handler from driver */ + *PropertyHandler = PropertySet[Index].PropertyItem[ItemIndex].SupportHandler; + return STATUS_SUCCESS; + } + if (PropertySet[Index].PropertyItem[ItemIndex].GetSupported) *Flags |= KSPROPERTY_TYPE_GET; if (PropertySet[Index].PropertyItem[ItemIndex].SetSupported) *Flags |= KSPROPERTY_TYPE_SET; - IoStatus->Information = sizeof(ULONG); - if (OutputBufferLength >= sizeof(KSPROPERTY_DESCRIPTION)) { /* get output buffer */ @@ -89,14 +115,6 @@ FindPropertyHandler( } return STATUS_SUCCESS; } - if (Property->Flags & KSPROPERTY_TYPE_SET) - *PropertyHandler = PropertySet[Index].PropertyItem[ItemIndex].SetPropertyHandler; - - if (Property->Flags & KSPROPERTY_TYPE_GET) - *PropertyHandler = PropertySet[Index].PropertyItem[ItemIndex].GetPropertyHandler; - - *Set = (PKSPROPERTY_SET)&PropertySet[Index]; - return STATUS_SUCCESS; } } } @@ -171,7 +189,7 @@ KspPropertyHandler( } } } - else if (IsEqualGUIDAligned(&Property->Set, &GUID_NULL) && Property->Id == 0 && Property->Flags == KSPROPERTY_TYPE_SETSUPPORT) + else if (IsEqualGUIDAligned(&Property->Set, &GUID_NULL) && Property->Id == 0 && (Property->Flags & KSPROPERTY_TYPE_SETSUPPORT) == KSPROPERTY_TYPE_SETSUPPORT) { // store output size Irp->IoStatus.Information = sizeof(GUID) * PropertySetsCount; @@ -189,7 +207,7 @@ KspPropertyHandler( { RtlMoveMemory(&Guid[Index], PropertySet[Index].Set, sizeof(GUID)); } - return STATUS_SUCCESS; + Status = STATUS_SUCCESS; } /* done */ diff --git a/drivers/ksfilter/ks/topology.c b/drivers/ksfilter/ks/topology.c index 096d36cfe2f..321f0ba59ed 100644 --- a/drivers/ksfilter/ks/topology.c +++ b/drivers/ksfilter/ks/topology.c @@ -151,6 +151,7 @@ KsTopologyPropertyHandler( PIO_STACK_LOCATION IoStack; NTSTATUS Status; PKEY_VALUE_PARTIAL_INFORMATION KeyInfo; + LPGUID Guid; IoStack = IoGetCurrentIrpStackLocation(Irp); @@ -177,37 +178,60 @@ KsTopologyPropertyHandler( case KSPROPERTY_TOPOLOGY_NAME: Node = (KSP_NODE*)Property; + /* check for invalid node id */ if (Node->NodeId >= Topology->TopologyNodesCount) { + /* invalid node id */ Irp->IoStatus.Information = 0; Status = STATUS_INVALID_PARAMETER; break; } - Status = KspReadMediaCategory((LPGUID)&Topology->TopologyNodesNames[Node->NodeId], &KeyInfo); + /* check if there is a name supplied */ + if (!IsEqualGUIDAligned(&Topology->TopologyNodesNames[Node->NodeId], &GUID_NULL)) + { + /* node name has been supplied */ + Guid = (LPGUID)&Topology->TopologyNodesNames[Node->NodeId]; + } + else + { + /* fallback to topology node type */ + Guid = (LPGUID)&Topology->TopologyNodes[Node->NodeId]; + } + + /* read topology node name */ + Status = KspReadMediaCategory(Guid, &KeyInfo); if (!NT_SUCCESS(Status)) { Irp->IoStatus.Information = 0; break; } + /* store result size */ Irp->IoStatus.Information = KeyInfo->DataLength + sizeof(WCHAR); + /* check for buffer overflow */ if (KeyInfo->DataLength + sizeof(WCHAR) > IoStack->Parameters.DeviceIoControl.OutputBufferLength) { + /* buffer too small */ Status = STATUS_BUFFER_OVERFLOW; FreeItem(KeyInfo); break; } + /* copy result buffer */ RtlMoveMemory(Irp->UserBuffer, &KeyInfo->Data, KeyInfo->DataLength); + + /* zero terminate it */ ((LPWSTR)Irp->UserBuffer)[KeyInfo->DataLength / sizeof(WCHAR)] = L'\0'; + + /* free key info */ FreeItem(KeyInfo); break; default: Irp->IoStatus.Information = 0; - Status = STATUS_NOT_IMPLEMENTED; + Status = STATUS_NOT_FOUND; } diff --git a/drivers/storage/class/cdrom_new/cdrom.c b/drivers/storage/class/cdrom_new/cdrom.c new file mode 100644 index 00000000000..aaaa8ee9c7d --- /dev/null +++ b/drivers/storage/class/cdrom_new/cdrom.c @@ -0,0 +1,7136 @@ +/*-- + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + cdrom.c + +Abstract: + + The CDROM class driver tranlates IRPs to SRBs with embedded CDBs + and sends them to its devices through the port driver. + +Environment: + + kernel mode only + +Notes: + + SCSI Tape, CDRom and Disk class drivers share common routines + that can be found in the CLASS directory (..\ntos\dd\class). + +Revision History: + +--*/ + +#include "stddef.h" +#include "string.h" + +#include "ntddk.h" +#include "initguid.h" + +#include "ntddcdvd.h" +#include "classpnp.h" + +#include "ntddstor.h" +#include "cdrom.h" + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, DriverEntry) + +#pragma alloc_text(PAGE, CdRomUnload) +#pragma alloc_text(PAGE, CdRomAddDevice) +#pragma alloc_text(PAGE, CdRomCreateDeviceObject) +#pragma alloc_text(PAGE, CdRomStartDevice) +#pragma alloc_text(PAGE, ScanForSpecial) +#pragma alloc_text(PAGE, ScanForSpecialHandler) +#pragma alloc_text(PAGE, CdRomRemoveDevice) +#pragma alloc_text(PAGE, CdRomGetDeviceType) +#pragma alloc_text(PAGE, CdRomReadWriteVerification) +#pragma alloc_text(PAGE, CdRomGetDeviceParameter) +#pragma alloc_text(PAGE, CdRomSetDeviceParameter) +#pragma alloc_text(PAGE, CdRomPickDvdRegion) +#pragma alloc_text(PAGE, CdRomIsPlayActive) + +#pragma alloc_text(PAGEHITA, HitachiProcessError) +#pragma alloc_text(PAGEHIT2, HitachiProcessErrorGD2000) + +#pragma alloc_text(PAGETOSH, ToshibaProcessErrorCompletion) +#pragma alloc_text(PAGETOSH, ToshibaProcessError) + +#endif + +#define IS_WRITE_REQUEST(irpStack) \ + (irpStack->MajorFunction == IRP_MJ_WRITE) + +#define IS_READ_WRITE_REQUEST(irpStack) \ +((irpStack->MajorFunction == IRP_MJ_READ) || \ + (irpStack->MajorFunction == IRP_MJ_WRITE) || \ + ((irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) && \ + (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_RAW_READ))) + + + + +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine initializes the cdrom class driver. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + RegistryPath - Pointer to the name of the services node for this driver. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + CLASS_INIT_DATA InitializationData; + PCDROM_DRIVER_EXTENSION driverExtension; + NTSTATUS status; + + PAGED_CODE(); + + WPP_INIT_TRACING(DriverObject, RegistryPath); + + TraceLog((CdromDebugTrace, + "CDROM.SYS DriverObject %p loading\n", DriverObject)); + + status = IoAllocateDriverObjectExtension(DriverObject, + CDROM_DRIVER_EXTENSION_ID, + sizeof(CDROM_DRIVER_EXTENSION), + &driverExtension); + + if (!NT_SUCCESS(status)) { + TraceLog((CdromDebugWarning, + "DriverEntry !! no DriverObjectExtension %x\n", status)); + return status; + } + + // + // always zero the memory, since we are now reloading the driver. + // + + RtlZeroMemory(driverExtension, sizeof(CDROM_DRIVER_EXTENSION)); + + // + // Zero InitData + // + + RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA)); + + // + // Set sizes + // + + InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA); + + InitializationData.FdoData.DeviceExtensionSize = DEVICE_EXTENSION_SIZE; + + InitializationData.FdoData.DeviceType = FILE_DEVICE_CD_ROM; + InitializationData.FdoData.DeviceCharacteristics = + FILE_REMOVABLE_MEDIA | FILE_DEVICE_SECURE_OPEN; + + // + // Set entry points + // + + InitializationData.FdoData.ClassError = CdRomErrorHandler; + InitializationData.FdoData.ClassInitDevice = CdRomInitDevice; + InitializationData.FdoData.ClassStartDevice = CdRomStartDevice; + InitializationData.FdoData.ClassStopDevice = CdRomStopDevice; + InitializationData.FdoData.ClassRemoveDevice = CdRomRemoveDevice; + + InitializationData.FdoData.ClassReadWriteVerification = CdRomReadWriteVerification; + InitializationData.FdoData.ClassDeviceControl = CdRomDeviceControlDispatch; + + InitializationData.FdoData.ClassPowerDevice = ClassSpinDownPowerHandler; + InitializationData.FdoData.ClassShutdownFlush = CdRomShutdownFlush; + InitializationData.FdoData.ClassCreateClose = NULL; + + InitializationData.ClassStartIo = CdRomStartIo; + InitializationData.ClassAddDevice = CdRomAddDevice; + + InitializationData.ClassTick = CdRomTickHandler; + InitializationData.ClassUnload = CdRomUnload; + + // + // Call the class init routine + // + return ClassInitialize( DriverObject, RegistryPath, &InitializationData); + +} // end DriverEntry() + + +VOID +CdRomUnload( + IN PDRIVER_OBJECT DriverObject + ) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(DriverObject); + TraceLog((CdromDebugTrace, + "CDROM.SYS DriverObject %p unloading\n", DriverObject)); + WPP_CLEANUP(DriverObject); + return; +} // end CdRomUnload() + + +NTSTATUS +CdRomAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject + ) + +/*++ + +Routine Description: + + This routine creates and initializes a new FDO for the corresponding + PDO. It may perform property queries on the FDO but cannot do any + media access operations. + +Arguments: + + DriverObject - CDROM class driver object. + + Pdo - the physical device object we are being added to + +Return Value: + + status + +--*/ + +{ + NTSTATUS status; + + PAGED_CODE(); + + // + // Get the address of the count of the number of cdroms already initialized. + // + DbgPrint("add device\n"); + + status = CdRomCreateDeviceObject(DriverObject, + PhysicalDeviceObject); + + // + // Note: this always increments driver extension counter + // it will eventually wrap, and fail additions + // if an existing cdrom has the given number. + // so unlikely that we won't even bother considering + // this case, since the cure is quite likely worse + // than the symptoms. + // + + if(NT_SUCCESS(status)) { + + // + // keep track of the total number of active cdroms in IoGet(), + // as some programs use this to determine when they have found + // all the cdroms in the system. + // + + TraceLog((CdromDebugTrace, "CDROM.SYS Add succeeded\n")); + IoGetConfigurationInformation()->CdRomCount++; + + } else { + + TraceLog((CdromDebugWarning, + "CDROM.SYS Add failed! %x\n", status)); + + } + + return status; +} + + +NTSTATUS +CdRomCreateDeviceObject( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject + ) + +/*++ + +Routine Description: + + This routine creates an object for the device and then calls the + SCSI port driver for media capacity and sector size. + +Arguments: + + DriverObject - Pointer to driver object created by system. + PortDeviceObject - to connect to SCSI port driver. + DeviceCount - Number of previously installed CDROMs. + PortCapabilities - Pointer to structure returned by SCSI port + driver describing adapter capabilites (and limitations). + LunInfo - Pointer to configuration information for this device. + +Return Value: + + NTSTATUS + +--*/ +{ + UCHAR ntNameBuffer[64]; + STRING ntNameString; + NTSTATUS status; + + PDEVICE_OBJECT lowerDevice = NULL; + PDEVICE_OBJECT deviceObject = NULL; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL; + PCDROM_DATA cdData = NULL; + PCDROM_DRIVER_EXTENSION driverExtension = NULL; + ULONG deviceNumber; + + CCHAR dosNameBuffer[64]; + CCHAR deviceNameBuffer[64]; + STRING deviceNameString; + STRING dosString; + UNICODE_STRING dosUnicodeString; + UNICODE_STRING unicodeString; + + PAGED_CODE(); + + // + // Claim the device. Note that any errors after this + // will goto the generic handler, where the device will + // be released. + // + + lowerDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject); + + status = ClassClaimDevice(lowerDevice, FALSE); + + if(!NT_SUCCESS(status)) { + + // + // Someone already had this device - we're in trouble + // + + ObDereferenceObject(lowerDevice); + return status; + } + + // + // Create device object for this device by first getting a unique name + // for the device and then creating it. + // + + driverExtension = IoGetDriverObjectExtension(DriverObject, + CDROM_DRIVER_EXTENSION_ID); + ASSERT(driverExtension != NULL); + + // + // InterlockedCdRomCounter is biased by 1. + // + + deviceNumber = InterlockedIncrement(&driverExtension->InterlockedCdRomCounter) - 1; + sprintf(ntNameBuffer, "\\Device\\CdRom%d", deviceNumber); + + + status = ClassCreateDeviceObject(DriverObject, + ntNameBuffer, + PhysicalDeviceObject, + TRUE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + TraceLog((CdromDebugWarning, + "CreateCdRomDeviceObjects: Can not create device %s\n", + ntNameBuffer)); + + goto CreateCdRomDeviceObjectExit; + } + + // + // Indicate that IRPs should include MDLs. + // + + SET_FLAG(deviceObject->Flags, DO_DIRECT_IO); + + fdoExtension = deviceObject->DeviceExtension; + + // + // Back pointer to device object. + // + + fdoExtension->CommonExtension.DeviceObject = deviceObject; + + // + // This is the physical device. + // + + fdoExtension->CommonExtension.PartitionZeroExtension = fdoExtension; + + // + // Initialize lock count to zero. The lock count is used to + // disable the ejection mechanism when media is mounted. + // + + fdoExtension->LockCount = 0; + + // + // Save system cdrom number + // + + fdoExtension->DeviceNumber = deviceNumber; + + // + // Set the alignment requirements for the device based on the + // host adapter requirements + // + + if (lowerDevice->AlignmentRequirement > deviceObject->AlignmentRequirement) { + deviceObject->AlignmentRequirement = lowerDevice->AlignmentRequirement; + } + + // + // Save the device descriptors + // + + fdoExtension->AdapterDescriptor = NULL; + + fdoExtension->DeviceDescriptor = NULL; + + // + // Clear the SrbFlags and disable synchronous transfers + // + + fdoExtension->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // Finally, attach to the PDO + // + + fdoExtension->LowerPdo = PhysicalDeviceObject; + + fdoExtension->CommonExtension.LowerDeviceObject = + IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject); + + if(fdoExtension->CommonExtension.LowerDeviceObject == NULL) { + + // + // Uh - oh, we couldn't attach + // cleanup and return + // + + status = STATUS_UNSUCCESSFUL; + goto CreateCdRomDeviceObjectExit; + } + + // + // CdRom uses an extra stack location for synchronizing it's start io + // routine + // + + deviceObject->StackSize++; + + // + // cdData is used a few times below + // + + cdData = fdoExtension->CommonExtension.DriverData; + + // + // For NTMS to be able to easily determine drives-drv. letter matches. + // + + status = CdRomCreateWellKnownName( deviceObject ); + + if (!NT_SUCCESS(status)) { + TraceLog((CdromDebugWarning, + "CdromCreateDeviceObjects: unable to create symbolic " + "link for device %wZ\n", &fdoExtension->CommonExtension.DeviceName)); + TraceLog((CdromDebugWarning, + "CdromCreateDeviceObjects: (non-fatal error)\n")); + } + + ClassUpdateInformationInRegistry(deviceObject, "CdRom", + fdoExtension->DeviceNumber, NULL, 0); + + // + // from above IoGetAttachedDeviceReference + // + + ObDereferenceObject(lowerDevice); + + // + // need to init timerlist here in case a remove occurs + // without a start, since we check the list is empty on remove. + // + + cdData->DelayedRetryIrp = NULL; + cdData->DelayedRetryInterval = 0; + + // + // need this to be initialized for RPC Phase 1 drives (rpc0) + // + + KeInitializeMutex(&cdData->Rpc0RegionMutex, 0); + + // + // The device is initialized properly - mark it as such. + // + + CLEAR_FLAG(deviceObject->Flags, DO_DEVICE_INITIALIZING); + + return(STATUS_SUCCESS); + +CreateCdRomDeviceObjectExit: + + // + // Release the device since an error occured. + // + + // ClassClaimDevice(PortDeviceObject, + // LunInfo, + // TRUE, + // NULL); + + // + // from above IoGetAttachedDeviceReference + // + + ObDereferenceObject(lowerDevice); + + if (deviceObject != NULL) { + IoDeleteDevice(deviceObject); + } + + return status; + +} // end CreateCdRomDeviceObject() + + +NTSTATUS +CdRomInitDevice( + IN PDEVICE_OBJECT Fdo + ) + +/*++ + +Routine Description: + + This routine will complete the cd-rom initialization. This includes + allocating sense info buffers and srb s-lists, reading drive capacity + and setting up Media Change Notification (autorun). + + This routine will not clean up allocate resources if it fails - that + is left for device stop/removal + +Arguments: + + Fdo - a pointer to the functional device object for this device + +Return Value: + + status + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension( + Fdo->DriverObject); + + PVOID senseData = NULL; + + ULONG timeOut; + PCDROM_DATA cddata; + + BOOLEAN changerDevice; + BOOLEAN isMmcDevice = FALSE; + + ULONG bps; + ULONG lastBit; + + + NTSTATUS status; + + PAGED_CODE(); + + // + // Build the lookaside list for srb's for the physical disk. Should only + // need a couple. + // + + ClassInitializeSrbLookasideList(&(fdoExtension->CommonExtension), + CDROM_SRB_LIST_SIZE); + + // + // Allocate request sense buffer. + // + + senseData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + SENSE_BUFFER_SIZE, + CDROM_TAG_SENSE_INFO); + + if (senseData == NULL) { + + // + // The buffer cannot be allocated. + // + + status = STATUS_INSUFFICIENT_RESOURCES; + goto CdRomInitDeviceExit; + } + + // + // Set the sense data pointer in the device extension. + // + + fdoExtension->SenseData = senseData; + + // + // CDROMs are not partitionable so starting offset is 0. + // + + commonExtension->StartingOffset.LowPart = 0; + commonExtension->StartingOffset.HighPart = 0; + + // + // Set timeout value in seconds. + // + + timeOut = ClassQueryTimeOutRegistryValue(Fdo); + if (timeOut) { + fdoExtension->TimeOutValue = timeOut; + } else { + fdoExtension->TimeOutValue = SCSI_CDROM_TIMEOUT; + } + + cddata = (PCDROM_DATA)(commonExtension->DriverData); + + // + // Set up media change support defaults. + // + + KeInitializeSpinLock(&cddata->DelayedRetrySpinLock); + + cddata->DelayedRetryIrp = NULL; + cddata->DelayedRetryInterval = 0; + cddata->Mmc.WriteAllowed = FALSE; + + // + // Scan for controllers that require special processing. + // + + ScanForSpecial(Fdo); + + // + // Determine if the drive is MMC-Capable + // + + CdRomIsDeviceMmcDevice(Fdo, &isMmcDevice); + + if (!isMmcDevice) { + + SET_FLAG(Fdo->Characteristics, FILE_READ_ONLY_DEVICE); + + } else { + + // + // the drive supports at least a subset of MMC commands + // (and therefore supports READ_CD, etc...) + // + + cddata->Mmc.IsMmc = TRUE; + + // + // allocate a buffer for all the capabilities and such + // + + status = CdRomAllocateMmcResources(Fdo); + if (!NT_SUCCESS(status)) { + goto CdRomInitDeviceExit; + } + + +#if 0 + // + // determine all the various media types from the profiles feature + // + { + PFEATURE_DATA_PROFILE_LIST profileHeader; + ULONG mediaTypes = 0; + ULONG i; + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError, + "Checking all profiles for media types supported.\n" + )); + + profileHeader = CdRomFindFeaturePage(cddata->Mmc.CapabilitiesBuffer, + cddata->Mmc.CapabilitiesBufferSize, + FeatureProfileList); + if (profileHeader == NULL) { + + // + // if profiles don't exist, there is something seriously + // wrong with this command -- it's either not a cdrom or + // one that hasn't implemented the spec correctly. exit + // now while we have the chance to do so safely. + // + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError, + "CdromDevice supports GET_CONFIGURATION, but " + "doesn't provide profiles for PDO %p!\n", + fdoExtension->LowerPdo)); + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto CdRomInitDeviceExit; + + } + + for (i = 0; i < MAX_CDROM_MEDIA_TYPES; i++) { + + BOOLEAN profileFound; + CdRomFindProfileInProfiles(profileHeader, + MediaProfileMatch[i].Profile, + &profileFound); + if (profileFound) { + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError, + "CdromInit -> Found Profile %x => Media %x " + "(%x total)\n", + MediaProfileMatch[i].Profile, + MediaProfileMatch[i].Media, + mediaTypes + 1 + )); + + cddata->Mmc.MediaProfileMatches[mediaTypes] = + MediaProfileMatch[i]; + mediaTypes++; + + } + + } + + if (mediaTypes == 0) { + + // + // if profiles don't exist, there is something seriously + // wrong with this command -- it's either not a cdrom or + // one that hasn't implemented the spec correctly. exit + // now while we have the chance to do so safely. + // + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError, + "CdromDevice supports GET_CONFIGURATION, but " + "doesn't support any of the standard profiles " + "for PDO %p!\n", fdoExtension->LowerPdo)); + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto CdRomInitDeviceExit; + + } + + cddata->Mmc.MediaTypes = mediaTypes; + + + } +#endif // media checks, and all failure paths due to bad firmware. + + // + // if the drive supports target defect management and sector-addressable + // writes, then we should allow writes to the media. + // + + if (CdRomFindFeaturePage(cddata->Mmc.CapabilitiesBuffer, + cddata->Mmc.CapabilitiesBufferSize, + FeatureDefectManagement) && + CdRomFindFeaturePage(cddata->Mmc.CapabilitiesBuffer, + cddata->Mmc.CapabilitiesBufferSize, + FeatureRandomWritable)) { + + // + // the drive is target defect managed, and supports random writes + // on sector-aligment. allow writes to occur by setting the error + // handler to point to a private media change detection handler. + // + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "Found a WRITE capable device: %p\n", Fdo)); + + // + // the write specific pages have been found -- + // set the error handler and set it to require an update! + // + + cddata->Mmc.UpdateState = CdromMmcUpdateRequired; + cddata->ErrorHandler = CdRomMmcErrorHandler; + + } + + // + // ISSUE-2000/4/4-henrygab - mmc-compliant compliant drives should + // be initialized based upon reported + // capabilities, such as CSS, Analogue Audio, + // READ_CD capabilities, and (possibly) even + // drive capacity information. + // + + TraceLog((CdromDebugWarning, + "Defaulting to READ_CD because device %p is MMC compliant\n", + Fdo)); + SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT); + SET_FLAG(cddata->XAFlags, XA_USE_READ_CD); + + } + + + // + // Set the default geometry for the cdrom to match what NT 4 used. + // Classpnp will use these values to compute the cylinder count rather + // than using it's NT 5.0 defaults. + // + + fdoExtension->DiskGeometry.TracksPerCylinder = 0x40; + fdoExtension->DiskGeometry.SectorsPerTrack = 0x20; + + // + // Do READ CAPACITY. This SCSI command returns the last sector address + // on the device and the bytes per sector. These are used to calculate + // the drive capacity in bytes. + // + // NOTE: This should be change to send the Srb synchronously, then + // call CdRomInterpretReadCapacity() to properly setup the defaults. + // + + status = ClassReadDriveCapacity(Fdo); + + bps = fdoExtension->DiskGeometry.BytesPerSector; + + if (!NT_SUCCESS(status) || !bps) { + + TraceLog((CdromDebugWarning, + "CdRomStartDevice: Can't read capacity for device %wZ\n", + &(fdoExtension->CommonExtension.DeviceName))); + + // + // Set disk geometry to default values (per ISO 9660). + // + + bps = 2048; + fdoExtension->SectorShift = 11; + commonExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff); + + } else { + + // + // Insure that bytes per sector is a power of 2 + // This corrects a problem with the HP 4020i CDR where it + // returns an incorrect number for bytes per sector. + // + + lastBit = (ULONG) -1; + while (bps) { + lastBit++; + bps = bps >> 1; + } + + bps = 1 << lastBit; + } + fdoExtension->DiskGeometry.BytesPerSector = bps; + TraceLog((CdromDebugTrace, "CdRomInitDevice: Calc'd bps = %x\n", bps)); + + + ClassInitializeMediaChangeDetection(fdoExtension, "CdRom"); + + + // + // test for audio read capabilities + // + + TraceLog((CdromDebugWarning, + "Detecting XA_READ capabilities\n")); + + if (CdRomGetDeviceType(Fdo) == FILE_DEVICE_DVD) { + + TraceLog((CdromDebugWarning, + "CdRomInitDevice: DVD Devices require START_UNIT\n")); + + + // + // all DVD devices must support the READ_CD command + // + + TraceLog((CdromDebugWarning, + "CdRomDetermineRawReadCapabilities: DVD devices " + "support READ_CD command for FDO %p\n", Fdo)); + SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT); + SET_FLAG(cddata->XAFlags, XA_USE_READ_CD); + + + status = STATUS_SUCCESS; + + } else if ((fdoExtension->DeviceDescriptor->BusType != BusTypeScsi) && + (fdoExtension->DeviceDescriptor->BusType != BusTypeAta) && + (fdoExtension->DeviceDescriptor->BusType != BusTypeAtapi) && + (fdoExtension->DeviceDescriptor->BusType != BusTypeUnknown) + ) { + + // + // devices on the newer busses must support READ_CD command + // + + TraceLog((CdromDebugWarning, + "CdRomDetermineRawReadCapabilities: Devices for newer " + "busses must support READ_CD command for FDO %p, Bus %x\n", + Fdo, fdoExtension->DeviceDescriptor->BusType)); + SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT); + SET_FLAG(cddata->XAFlags, XA_USE_READ_CD); + + } + + // + // now clear all our READ_CD flags if the drive should have supported + // it, but we are not sure it actually does. we still won't query + // the drive more than one time if it supports the command. + // + + if (TEST_FLAG(cddata->HackFlags, CDROM_HACK_FORCE_READ_CD_DETECTION)) { + + TraceLog((CdromDebugWarning, + "Forcing detection of READ_CD for FDO %p because " + "testing showed some firmware did not properly support it\n", + Fdo)); + CLEAR_FLAG(cddata->XAFlags, XA_USE_READ_CD); + + } + + + // + // read our READ_CD support in the registry if it was seeded. + // + { + ULONG readCdSupported = 0; + + ClassGetDeviceParameter(fdoExtension, + CDROM_SUBKEY_NAME, + CDROM_READ_CD_NAME, + &readCdSupported + ); + + if (readCdSupported != 0) { + + TraceLog((CdromDebugWarning, + "Defaulting to READ_CD because previously detected " + "that the device supports it for Fdo %p.\n", + Fdo + )); + SET_FLAG(cddata->XAFlags, XA_USE_READ_CD); + + } + + } + + + // + // backwards-compatible hackish attempt to determine if the drive + // supports any method of reading digital audio from the disc. + // + + if (!TEST_FLAG(cddata->XAFlags, XA_USE_READ_CD)) { + + SCSI_REQUEST_BLOCK srb; + PCDB cdb; + ULONG length; + PUCHAR buffer = NULL; + ULONG count; + + // + // ISSUE-2000/07/05-henrygab - use the mode page to determine + // READ_CD support, then fall back on the below + // (unreliable?) hack. + // + + // + // Build the MODE SENSE CDB. The data returned will be kept in the + // device extension and used to set block size. + // + + length = max(sizeof(ERROR_RECOVERY_DATA),sizeof(ERROR_RECOVERY_DATA10)); + + buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + length, + CDROM_TAG_MODE_DATA); + + if (!buffer) { + TraceLog((CdromDebugWarning, + "CdRomDetermineRawReadCapabilities: cannot allocate " + "buffer, so leaving for FDO %p\n", Fdo)); + status = STATUS_INSUFFICIENT_RESOURCES; + goto CdRomInitDeviceExit; + } + + for (count = 0; count < 2; count++) { + + if (count == 0) { + length = sizeof(ERROR_RECOVERY_DATA); + } else { + length = sizeof(ERROR_RECOVERY_DATA10); + } + + RtlZeroMemory(buffer, length); + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + cdb = (PCDB)srb.Cdb; + + srb.TimeOutValue = fdoExtension->TimeOutValue; + + if (count == 0) { + srb.CdbLength = 6; + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = 0x1; + // note: not setting DBD in order to get the block descriptor! + cdb->MODE_SENSE.AllocationLength = (UCHAR)length; + } else { + srb.CdbLength = 10; + cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; + cdb->MODE_SENSE10.PageCode = 0x1; + // note: not setting DBD in order to get the block descriptor! + cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(length >> 8); + cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(length & 0xFF); + } + + status = ClassSendSrbSynchronous(Fdo, + &srb, + buffer, + length, + FALSE); + + + if (NT_SUCCESS(status) || (status == STATUS_DATA_OVERRUN)) { + + // + // STATUS_DATA_OVERRUN means it's a newer drive with more info + // to tell us, so it's probably able to support READ_CD + // + + RtlZeroMemory(cdb, CDB12GENERIC_LENGTH); + + srb.CdbLength = 12; + cdb->READ_CD.OperationCode = SCSIOP_READ_CD; + + status = ClassSendSrbSynchronous(Fdo, + &srb, + NULL, + 0, + FALSE); + + if (NT_SUCCESS(status) || + (status == STATUS_NO_MEDIA_IN_DEVICE) || + (status == STATUS_NONEXISTENT_SECTOR) || + (status == STATUS_UNRECOGNIZED_MEDIA) + ) { + + // + // READ_CD works + // + + TraceLog((CdromDebugWarning, + "CdRomDetermineRawReadCapabilities: Using " + "READ_CD for FDO %p due to status %x\n", + Fdo, + status)); + SET_FLAG(cddata->XAFlags, XA_USE_READ_CD); + + // + // ignore errors in saving this info + // + + ClassSetDeviceParameter(fdoExtension, + CDROM_SUBKEY_NAME, + CDROM_READ_CD_NAME, + 1 + ); + + + break; // out of the for loop + + } + + TraceLog((CdromDebugWarning, + "CdRomDetermineRawReadCapabilities: Using " + "%s-byte mode switching for FDO %p due to status " + "%x returned for READ_CD\n", + ((count == 0) ? "6" : "10"), Fdo, status)); + + if (count == 0) { + SET_FLAG(cddata->XAFlags, XA_USE_6_BYTE); + RtlCopyMemory(&cddata->Header, + buffer, + sizeof(ERROR_RECOVERY_DATA)); + cddata->Header.ModeDataLength = 0; + } else { + SET_FLAG(cddata->XAFlags, XA_USE_10_BYTE); + RtlCopyMemory(&cddata->Header10, + buffer, + sizeof(ERROR_RECOVERY_DATA10)); + cddata->Header10.ModeDataLength[0] = 0; + cddata->Header10.ModeDataLength[1] = 0; + } + break; // out of for loop + + } + TraceLog((CdromDebugWarning, + "FDO %p failed %x byte mode sense, status %x\n", + Fdo, + ((count == 0) ? 6 : 10), + status + )); + + // + // mode sense failed + // + + } // end of for loop to try 6 and 10-byte mode sense + + if (count == 2) { + + // + // nothing worked. we probably cannot support digital + // audio extraction from this drive + // + + TraceLog((CdromDebugWarning, + "CdRomDetermineRawReadCapabilities: FDO %p " + "cannot support READ_CD\n", Fdo)); + CLEAR_FLAG(cddata->XAFlags, XA_PLEXTOR_CDDA); + CLEAR_FLAG(cddata->XAFlags, XA_NEC_CDDA); + SET_FLAG(cddata->XAFlags, XA_NOT_SUPPORTED); + + } // end of count == 2 + + // + // free our resources + // + + ExFreePool(buffer); + + // + // set a successful status + // (in case someone later checks this) + // + + status = STATUS_SUCCESS; + + } + + // + // Register interfaces for this device. + // + + { + UNICODE_STRING interfaceName; + + RtlInitUnicodeString(&interfaceName, NULL); + + status = IoRegisterDeviceInterface(fdoExtension->LowerPdo, + (LPGUID) &CdRomClassGuid, + NULL, + &interfaceName); + + if(NT_SUCCESS(status)) { + + cddata->CdromInterfaceString = interfaceName; + + status = IoSetDeviceInterfaceState( + &interfaceName, + TRUE); + + if(!NT_SUCCESS(status)) { + + TraceLog((CdromDebugWarning, + "CdromInitDevice: Unable to register cdrom " + "DCA for fdo %p [%lx]\n", + Fdo, status)); + } + } + } + + return(STATUS_SUCCESS); + +CdRomInitDeviceExit: + + CdRomDeAllocateMmcResources(Fdo); + RtlZeroMemory(&(cddata->Mmc), sizeof(CDROM_MMC_EXTENSION)); + + return status; + +} + + +NTSTATUS +CdRomStartDevice( + IN PDEVICE_OBJECT Fdo + ) +/*++ + +Routine Description: + + This routine starts the timer for the cdrom + +Arguments: + + Fdo - a pointer to the functional device object for this device + +Return Value: + + status + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData); + PDVD_COPY_PROTECT_KEY copyProtectKey; + PDVD_RPC_KEY rpcKey; + IO_STATUS_BLOCK ioStatus; + ULONG bufferLen; + + // CdRomCreateWellKnownName(Fdo); + + // + // if we have a DVD-ROM + // if we have a rpc0 device + // fake a rpc2 device + // if device does not have a dvd region set + // select a dvd region for the user + // + + cddata->DvdRpc0Device = FALSE; + + // + // since StartIo() will call IoStartNextPacket() on error, allowing + // StartIo() to be non-recursive prevents stack overflow bugchecks in + // severe error cases (such as fault-injection in the verifier). + // + // the only difference is that the thread context may be different + // in StartIo() than in the caller of IoStartNextPacket(). + // + + IoSetStartIoAttributes(Fdo, TRUE, TRUE); + + // + // check to see if we have a DVD device + // + + if (CdRomGetDeviceType(Fdo) != FILE_DEVICE_DVD) { + return STATUS_SUCCESS; + } + + // + // we got a DVD drive. + // now, figure out if we have a RPC0 device + // + + bufferLen = DVD_RPC_KEY_LENGTH; + copyProtectKey = + (PDVD_COPY_PROTECT_KEY)ExAllocatePoolWithTag(PagedPool, + bufferLen, + DVD_TAG_RPC2_CHECK); + + if (copyProtectKey == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get the device region + // + RtlZeroMemory (copyProtectKey, bufferLen); + copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH; + copyProtectKey->KeyType = DvdGetRpcKey; + + // + // Build a request for READ_KEY + // + ClassSendDeviceIoControlSynchronous( + IOCTL_DVD_READ_KEY, + Fdo, + copyProtectKey, + DVD_RPC_KEY_LENGTH, + DVD_RPC_KEY_LENGTH, + FALSE, + &ioStatus + ); + + if (!NT_SUCCESS(ioStatus.Status)) { + + // + // we have a rpc0 device + // + // NOTE: THIS MODIFIES THE BEHAVIOR OF THE IOCTL + // + + cddata->DvdRpc0Device = TRUE; + + TraceLog((CdromDebugWarning, + "CdromStartDevice (%p): RPC Phase 1 drive detected\n", + Fdo)); + + // + // note: we could force this chosen now, but it's better to reduce + // the number of code paths that could be taken. always delay to + // increase the percentage code coverage. + // + + TraceLog((CdromDebugWarning, + "CdromStartDevice (%p): Delay DVD Region Selection\n", + Fdo)); + + cddata->Rpc0SystemRegion = 0xff; + cddata->Rpc0SystemRegionResetCount = DVD_MAX_REGION_RESET_COUNT; + cddata->PickDvdRegion = 1; + cddata->Rpc0RetryRegistryCallback = 1; + ExFreePool(copyProtectKey); + return STATUS_SUCCESS; + + } else { + + rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData; + + // + // TypeCode of zero means that no region has been set. + // + + if (rpcKey->TypeCode == 0) { + TraceLog((CdromDebugWarning, + "CdromStartDevice (%p): must choose DVD region\n", + Fdo)); + cddata->PickDvdRegion = 1; + CdRomPickDvdRegion(Fdo); + } + } + + ExFreePool (copyProtectKey); + + return STATUS_SUCCESS; +} + + +NTSTATUS +CdRomStopDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ) +{ + return STATUS_SUCCESS; +} + + +VOID +CdRomStartIo( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp + ) +{ + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + PIO_STACK_LOCATION irpStack; + + PIRP irp2 = NULL; + + ULONG transferPages; + ULONG transferByteCount = currentIrpStack->Parameters.Read.Length; + LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset; + PCDROM_DATA cdData; + PSCSI_REQUEST_BLOCK srb = NULL; + PCDB cdb; + PUCHAR senseBuffer = NULL; + PVOID dataBuffer; + NTSTATUS status; + BOOLEAN use6Byte; + + // + // Mark IRP with status pending. + // + + IoMarkIrpPending(Irp); + + cdData = (PCDROM_DATA)(fdoExtension->CommonExtension.DriverData); + use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE); + + // + // if this test is true, then we will exit the routine within this + // code block, queueing the irp for later completion. + // + + if ((cdData->Mmc.IsMmc) && + (cdData->Mmc.UpdateState != CdromMmcUpdateComplete) + ) { + + USHORT queueDepth; + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdRomStartIo: [%p] Device needs to update capabilities\n", + Irp)); + ASSERT(cdData->Mmc.IsMmc); + ASSERT(cdData->Mmc.CapabilitiesIrp != NULL); + ASSERT(cdData->Mmc.CapabilitiesIrp != Irp); + + // + // NOTE - REF #0002 + // + // the state was either UpdateRequired (which means we will + // have to start the work item) or UpdateStarted (which means + // we have already started the work item at least once -- may + // transparently change to UpdateComplete). + // + // if it's update required, we just queue it, change to UpdateStarted, + // start the workitem, and start the next packet. + // + // else, we must queue the item and check the queue depth. if the + // queue depth is equal to 1, that means the worker item from the + // previous attempt has already de-queued the items, so we should + // call this routine again (retry) as an optimization rather than + // re-add it this irp to the queue. since this is tail recursion, + // it won't take much/any stack to do this. + // + // NOTE: This presumes the following items are true: + // + // we only add to the list from CdRomStartIo(), which is serialized. + // we only set to UpdateStarted from CdRomStartIo(), and only if + // the state was UpdateRequired. + // we only set to UpdateRequired from CdRomMmcErrorHandler(), and + // only if the state was UpdateComplete. + // we only set to UpdateComplete from the workitem, and assert the + // state was UpdateStarted. + // we flush the entire queue in one atomic operation in the workitem, + // except in the special case described above when we dequeue + // the request immediately. + // + // order of operations is vitally important: queue, then test the depth + // this will prevent lost irps. + // + + ExInterlockedPushEntrySList(&(cdData->Mmc.DelayedIrps), + (PSINGLE_LIST_ENTRY)&(Irp->Tail.Overlay.DriverContext[0]), + &(cdData->Mmc.DelayedLock)); + + queueDepth = ExQueryDepthSList(&(cdData->Mmc.DelayedIrps)); + if (queueDepth == 1) { + + if (cdData->Mmc.UpdateState == CdromMmcUpdateRequired) { + LONG oldState; + + // + // should free any old partition list info that + // we've previously saved away and then start the WorkItem + // + + oldState = InterlockedExchange(&cdData->Mmc.UpdateState, + CdromMmcUpdateStarted); + ASSERT(oldState == CdromMmcUpdateRequired); + + IoQueueWorkItem(cdData->Mmc.CapabilitiesWorkItem, + CdRomUpdateMmcDriveCapabilities, + DelayedWorkQueue, + NULL); + + } else { + + // + // they *just* finished updating, so we should flush the list + // back onto the StartIo queue and start the next packet. + // + + CdRompFlushDelayedList(Fdo, &(cdData->Mmc), STATUS_SUCCESS, FALSE); + + } + + } + + // + // start the next packet so we don't deadlock.... + // + + IoStartNextPacket(Fdo, FALSE); + return; + + } + + // + // If the flag is set in the device object + // force a verify for READ, WRITE and RAW_READ requests + // Note that ioctls are passed through.... + // + + if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME) && + IS_READ_WRITE_REQUEST(currentIrpStack)) { + + TraceLog((CdromDebugTrace, + "CdRomStartIo: [%p] Volume needs verified\n", Irp)); + + if (!(currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) { + + if (Irp->Tail.Overlay.Thread) { + IoSetHardErrorOrVerifyDevice(Irp, Fdo); + } + + Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED; + + TraceLog((CdromDebugTrace, + "CdRomStartIo: [%p] Calling UpdateCapcity - " + "ioctl event = %p\n", + Irp, + nextIrpStack->Parameters.Others.Argument1 + )); + + // + // our device control dispatch routine stores an event in the next + // stack location to signal when startio has completed. We need to + // pass this in so that the update capacity completion routine can + // set it rather than completing the Irp. + // + + status = CdRomUpdateCapacity(fdoExtension, + Irp, + nextIrpStack->Parameters.Others.Argument1 + ); + + TraceLog((CdromDebugTrace, + "CdRomStartIo: [%p] UpdateCapacity returned %lx\n", + Irp, status)); + return; + } + } + + // + // fail writes if they are not allowed... + // + + if ((currentIrpStack->MajorFunction == IRP_MJ_WRITE) && + !(cdData->Mmc.WriteAllowed)) { + + TraceLog((CdromDebugError, + "CdRomStartIo: [%p] Device %p failing write request\n", + Irp, Fdo)); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + if (currentIrpStack->MajorFunction == IRP_MJ_READ || + currentIrpStack->MajorFunction == IRP_MJ_WRITE ) { + + ULONG maximumTransferLength = fdoExtension->AdapterDescriptor->MaximumTransferLength; + + // + // Add partition byte offset to make starting byte relative to + // beginning of disk. + // + + currentIrpStack->Parameters.Read.ByteOffset.QuadPart += + (fdoExtension->CommonExtension.StartingOffset.QuadPart); + + // + // Calculate number of pages in this transfer. + // + + transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress), + currentIrpStack->Parameters.Read.Length); + + // + // Check if request length is greater than the maximum number of + // bytes that the hardware can transfer. + // + + if (cdData->RawAccess) { + + // + // a writable device must be MMC compliant, which supports + // READ_CD commands. + // + + ASSERT(currentIrpStack->MajorFunction != IRP_MJ_WRITE); + + ASSERT(!TEST_FLAG(cdData->XAFlags, XA_USE_READ_CD)); + + // + // Fire off a mode select to switch back to cooked sectors. + // + + irp2 = IoAllocateIrp((CCHAR)(Fdo->StackSize+1), FALSE); + + if (!irp2) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + srb = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SCSI_REQUEST_BLOCK), + CDROM_TAG_SRB); + if (!srb) { + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + cdb = (PCDB)srb->Cdb; + + // + // Allocate sense buffer. + // + + senseBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + SENSE_BUFFER_SIZE, + CDROM_TAG_SENSE_INFO); + + if (!senseBuffer) { + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + // + // Set up the irp. + // + + IoSetNextIrpStackLocation(irp2); + irp2->IoStatus.Status = STATUS_SUCCESS; + irp2->IoStatus.Information = 0; + irp2->Flags = 0; + irp2->UserBuffer = NULL; + + // + // Save the device object and irp in a private stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(irp2); + irpStack->DeviceObject = Fdo; + irpStack->Parameters.Others.Argument2 = (PVOID) Irp; + + // + // The retry count will be in the real Irp, as the retry logic will + // recreate our private irp. + // + + if (!(nextIrpStack->Parameters.Others.Argument1)) { + + // + // Only jam this in if it doesn't exist. The completion routines can + // call StartIo directly in the case of retries and resetting it will + // cause infinite loops. + // + + nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES; + } + + // + // Construct the IRP stack for the lower level driver. + // + + irpStack = IoGetNextIrpStackLocation(irp2); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; + irpStack->Parameters.Scsi.Srb = srb; + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->OriginalRequest = irp2; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + srb->SenseInfoBuffer = senseBuffer; + + transferByteCount = (use6Byte) ? sizeof(ERROR_RECOVERY_DATA) : sizeof(ERROR_RECOVERY_DATA10); + + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + transferByteCount, + CDROM_TAG_RAW); + + if (!dataBuffer) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + transferByteCount, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + srb->DataBuffer = dataBuffer; + + // + // Set the new block size in the descriptor. + // + + if (use6Byte) { + cdData->BlockDescriptor.BlockLength[0] = (UCHAR)(COOKED_SECTOR_SIZE >> 16) & 0xFF; + cdData->BlockDescriptor.BlockLength[1] = (UCHAR)(COOKED_SECTOR_SIZE >> 8) & 0xFF; + cdData->BlockDescriptor.BlockLength[2] = (UCHAR)(COOKED_SECTOR_SIZE & 0xFF); + } else { + cdData->BlockDescriptor10.BlockLength[0] = (UCHAR)(COOKED_SECTOR_SIZE >> 16) & 0xFF; + cdData->BlockDescriptor10.BlockLength[1] = (UCHAR)(COOKED_SECTOR_SIZE >> 8) & 0xFF; + cdData->BlockDescriptor10.BlockLength[2] = (UCHAR)(COOKED_SECTOR_SIZE & 0xFF); + } + + // + // Move error page into dataBuffer. + // + + RtlCopyMemory(srb->DataBuffer, &cdData->Header, transferByteCount); + + // + // Build and send a mode select to switch into raw mode. + // + + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT); + srb->DataTransferLength = transferByteCount; + srb->TimeOutValue = fdoExtension->TimeOutValue * 2; + + if (use6Byte) { + srb->CdbLength = 6; + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = (UCHAR)transferByteCount; + } else { + srb->CdbLength = 10; + cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; + cdb->MODE_SELECT10.PFBit = 1; + cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR)(transferByteCount >> 8); + cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR)(transferByteCount & 0xFF); + } + + // + // Update completion routine. + // + + IoSetCompletionRoutine(irp2, + CdRomSwitchModeCompletion, + srb, + TRUE, + TRUE, + TRUE); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + + // + // Request needs to be split. Completion of each portion of the + // request will fire off the next portion. The final request will + // signal Io to send a new request. + // + + transferPages = + fdoExtension->AdapterDescriptor->MaximumPhysicalPages - 1; + + if(maximumTransferLength > (transferPages << PAGE_SHIFT)) { + maximumTransferLength = transferPages << PAGE_SHIFT; + } + + // + // Check that the maximum transfer size is not zero + // + + if(maximumTransferLength == 0) { + maximumTransferLength = PAGE_SIZE; + } + + ClassSplitRequest(Fdo, Irp, maximumTransferLength); + return; + + } else if (currentIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { + + // + // Allocate an irp, srb and associated structures. + // + + irp2 = IoAllocateIrp((CCHAR)(Fdo->StackSize+1), + FALSE); + + if (!irp2) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + srb = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SCSI_REQUEST_BLOCK), + CDROM_TAG_SRB); + if (!srb) { + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + cdb = (PCDB)srb->Cdb; + + // + // Allocate sense buffer. + // + + senseBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + SENSE_BUFFER_SIZE, + CDROM_TAG_SENSE_INFO); + + if (!senseBuffer) { + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + RtlZeroMemory(senseBuffer, SENSE_BUFFER_SIZE); + + // + // Set up the irp. + // + + IoSetNextIrpStackLocation(irp2); + irp2->IoStatus.Status = STATUS_SUCCESS; + irp2->IoStatus.Information = 0; + irp2->Flags = 0; + irp2->UserBuffer = NULL; + + // + // Save the device object and irp in a private stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(irp2); + irpStack->DeviceObject = Fdo; + irpStack->Parameters.Others.Argument2 = (PVOID) Irp; + + // + // The retry count will be in the real Irp, as the retry logic will + // recreate our private irp. + // + + if (!(nextIrpStack->Parameters.Others.Argument1)) { + + // + // Only jam this in if it doesn't exist. The completion routines can + // call StartIo directly in the case of retries and resetting it will + // cause infinite loops. + // + + nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES; + } + + // + // keep track of the new irp as Argument3 + // + + nextIrpStack->Parameters.Others.Argument3 = irp2; + + + // + // Construct the IRP stack for the lower level driver. + // + + irpStack = IoGetNextIrpStackLocation(irp2); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; + irpStack->Parameters.Scsi.Srb = srb; + + IoSetCompletionRoutine(irp2, + CdRomDeviceControlCompletion, + srb, + TRUE, + TRUE, + TRUE); + // + // Setup those fields that are generic to all requests. + // + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->OriginalRequest = irp2; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + srb->SenseInfoBuffer = senseBuffer; + + switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) { + + + case IOCTL_CDROM_RAW_READ: { + + // + // Determine whether the drive is currently in raw or cooked mode, + // and which command to use to read the data. + // + + if (!TEST_FLAG(cdData->XAFlags, XA_USE_READ_CD)) { + + PRAW_READ_INFO rawReadInfo = + (PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer; + ULONG maximumTransferLength; + ULONG transferPages; + + if (cdData->RawAccess) { + + ULONG startingSector; + UCHAR min, sec, frame; + + // + // Free the recently allocated irp, as we don't need it. + // + + IoFreeIrp(irp2); + + cdb = (PCDB)srb->Cdb; + RtlZeroMemory(cdb, CDB12GENERIC_LENGTH); + + // + // Calculate starting offset. + // + + startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> fdoExtension->SectorShift); + transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE; + maximumTransferLength = fdoExtension->AdapterDescriptor->MaximumTransferLength; + transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress), + transferByteCount); + + // + // Determine if request is within limits imposed by miniport. + // + if (transferByteCount > maximumTransferLength || + transferPages > fdoExtension->AdapterDescriptor->MaximumPhysicalPages) { + + // + // The claim is that this won't happen, and is backed up by + // ActiveMovie usage, which does unbuffered XA reads of 0x18000, yet + // we get only 4 sector requests. + // + + ExFreePool(senseBuffer); + ExFreePool(srb); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + + } + + srb->OriginalRequest = Irp; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->CdbLength = 10; + srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); + + if (rawReadInfo->TrackMode == CDDA) { + if (TEST_FLAG(cdData->XAFlags, XA_PLEXTOR_CDDA)) { + + srb->CdbLength = 12; + + cdb->PLXTR_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->PLXTR_READ_CDDA.TransferBlockByte3 = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + cdb->PLXTR_READ_CDDA.TransferBlockByte2 = (UCHAR) (rawReadInfo->SectorCount >> 8); + cdb->PLXTR_READ_CDDA.TransferBlockByte1 = 0; + cdb->PLXTR_READ_CDDA.TransferBlockByte0 = 0; + + cdb->PLXTR_READ_CDDA.SubCode = 0; + cdb->PLXTR_READ_CDDA.OperationCode = 0xD8; + + } else if (TEST_FLAG(cdData->XAFlags, XA_NEC_CDDA)) { + + cdb->NEC_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->NEC_READ_CDDA.TransferBlockByte1 = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + cdb->NEC_READ_CDDA.TransferBlockByte0 = (UCHAR) (rawReadInfo->SectorCount >> 8); + + cdb->NEC_READ_CDDA.OperationCode = 0xD4; + } + } else { + + cdb->CDB10.TransferBlocksMsb = (UCHAR) (rawReadInfo->SectorCount >> 8); + cdb->CDB10.TransferBlocksLsb = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + + cdb->CDB10.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->CDB10.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->CDB10.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->CDB10.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->CDB10.OperationCode = SCSIOP_READ; + } + + srb->SrbStatus = srb->ScsiStatus = 0; + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + nextIrpStack->Parameters.Scsi.Srb = srb; + + // HACKHACK - REF #0001 + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(Irp, + CdRomXACompletion, + srb, + TRUE, + TRUE, + TRUE); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp); + return; + + } else { + + transferByteCount = (use6Byte) ? sizeof(ERROR_RECOVERY_DATA) : sizeof(ERROR_RECOVERY_DATA10); + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + transferByteCount, + CDROM_TAG_RAW ); + if (!dataBuffer) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + transferByteCount, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + srb->DataBuffer = dataBuffer; + + // + // Set the new block size in the descriptor. + // This will set the block read size to RAW_SECTOR_SIZE + // TODO: Set density code, based on operation + // + + if (use6Byte) { + cdData->BlockDescriptor.BlockLength[0] = (UCHAR)(RAW_SECTOR_SIZE >> 16) & 0xFF; + cdData->BlockDescriptor.BlockLength[1] = (UCHAR)(RAW_SECTOR_SIZE >> 8) & 0xFF; + cdData->BlockDescriptor.BlockLength[2] = (UCHAR)(RAW_SECTOR_SIZE & 0xFF); + cdData->BlockDescriptor.DensityCode = 0; + } else { + cdData->BlockDescriptor10.BlockLength[0] = (UCHAR)(RAW_SECTOR_SIZE >> 16) & 0xFF; + cdData->BlockDescriptor10.BlockLength[1] = (UCHAR)(RAW_SECTOR_SIZE >> 8) & 0xFF; + cdData->BlockDescriptor10.BlockLength[2] = (UCHAR)(RAW_SECTOR_SIZE & 0xFF); + cdData->BlockDescriptor10.DensityCode = 0; + } + + // + // Move error page into dataBuffer. + // + + RtlCopyMemory(srb->DataBuffer, &cdData->Header, transferByteCount); + + + // + // Build and send a mode select to switch into raw mode. + // + + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT); + srb->DataTransferLength = transferByteCount; + srb->TimeOutValue = fdoExtension->TimeOutValue * 2; + + if (use6Byte) { + srb->CdbLength = 6; + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = (UCHAR)transferByteCount; + } else { + + srb->CdbLength = 10; + cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; + cdb->MODE_SELECT10.PFBit = 1; + cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR)(transferByteCount >> 8); + cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR)(transferByteCount & 0xFF); + } + + // + // Update completion routine. + // + + IoSetCompletionRoutine(irp2, + CdRomSwitchModeCompletion, + srb, + TRUE, + TRUE, + TRUE); + + } + + } else { + + PRAW_READ_INFO rawReadInfo = + (PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer; + ULONG startingSector; + + // + // Free the recently allocated irp, as we don't need it. + // + + IoFreeIrp(irp2); + + cdb = (PCDB)srb->Cdb; + RtlZeroMemory(cdb, CDB12GENERIC_LENGTH); + + + // + // Calculate starting offset. + // + + startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> fdoExtension->SectorShift); + transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE; + + srb->OriginalRequest = Irp; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); + srb->CdbLength = 12; + srb->SrbStatus = srb->ScsiStatus = 0; + + // + // Fill in CDB fields. + // + + cdb = (PCDB)srb->Cdb; + + + cdb->READ_CD.TransferBlocks[2] = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + cdb->READ_CD.TransferBlocks[1] = (UCHAR) (rawReadInfo->SectorCount >> 8 ); + cdb->READ_CD.TransferBlocks[0] = (UCHAR) (rawReadInfo->SectorCount >> 16); + + + cdb->READ_CD.StartingLBA[3] = (UCHAR) (startingSector & 0xFF); + cdb->READ_CD.StartingLBA[2] = (UCHAR) ((startingSector >> 8)); + cdb->READ_CD.StartingLBA[1] = (UCHAR) ((startingSector >> 16)); + cdb->READ_CD.StartingLBA[0] = (UCHAR) ((startingSector >> 24)); + + // + // Setup cdb depending upon the sector type we want. + // + + switch (rawReadInfo->TrackMode) { + case CDDA: + + cdb->READ_CD.ExpectedSectorType = CD_DA_SECTOR; + cdb->READ_CD.IncludeUserData = 1; + cdb->READ_CD.HeaderCode = 3; + cdb->READ_CD.IncludeSyncData = 1; + break; + + case YellowMode2: + + cdb->READ_CD.ExpectedSectorType = YELLOW_MODE2_SECTOR; + cdb->READ_CD.IncludeUserData = 1; + cdb->READ_CD.HeaderCode = 1; + cdb->READ_CD.IncludeSyncData = 1; + break; + + case XAForm2: + + cdb->READ_CD.ExpectedSectorType = FORM2_MODE2_SECTOR; + cdb->READ_CD.IncludeUserData = 1; + cdb->READ_CD.HeaderCode = 3; + cdb->READ_CD.IncludeSyncData = 1; + break; + + default: + ExFreePool(senseBuffer); + ExFreePool(srb); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + cdb->READ_CD.OperationCode = SCSIOP_READ_CD; + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + nextIrpStack->Parameters.Scsi.Srb = srb; + + // HACKHACK - REF #0001 + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(Irp, + CdRomXACompletion, + srb, + TRUE, + TRUE, + TRUE); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp); + return; + + } + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + // + // the _EX version does the same thing on the front end + // + + case IOCTL_DISK_GET_LENGTH_INFO: + case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: + case IOCTL_DISK_GET_DRIVE_GEOMETRY: + case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX: + case IOCTL_CDROM_GET_DRIVE_GEOMETRY: { + + // + // Issue ReadCapacity to update device extension + // with information for current media. + // + + TraceLog((CdromDebugError, + "CdRomStartIo: Get drive geometry/length " + "info (%p)\n", Irp)); + + // + // setup remaining srb and cdb parameters. + // + + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + srb->DataTransferLength = sizeof(READ_CAPACITY_DATA); + srb->CdbLength = 10; + srb->TimeOutValue = fdoExtension->TimeOutValue; + + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + sizeof(READ_CAPACITY_DATA), + CDROM_TAG_READ_CAP); + if (!dataBuffer) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + sizeof(READ_CAPACITY_DATA), + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + srb->DataBuffer = dataBuffer; + cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_GET_CONFIGURATION: { + + PGET_CONFIGURATION_IOCTL_INPUT inputBuffer; + + TraceLog((CdromDebugError, + "CdRomStartIo: Get configuration (%p)\n", Irp)); + + if (!cdData->Mmc.IsMmc) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + transferByteCount = currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + transferByteCount, + CDROM_TAG_GET_CONFIG); + if (!dataBuffer) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + transferByteCount, + FALSE, + FALSE, + (PIRP) NULL); + if (!irp2->MdlAddress) { + ExFreePool(dataBuffer); + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + // + // setup remaining srb and cdb parameters + // + + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->CdbLength = 10; + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->DataBuffer = dataBuffer; + + cdb->GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION; + cdb->GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(transferByteCount >> 8); + cdb->GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(transferByteCount & 0xff); + + inputBuffer = (PGET_CONFIGURATION_IOCTL_INPUT)Irp->AssociatedIrp.SystemBuffer; + cdb->GET_CONFIGURATION.StartingFeature[0] = (UCHAR)(inputBuffer->Feature >> 8); + cdb->GET_CONFIGURATION.StartingFeature[1] = (UCHAR)(inputBuffer->Feature & 0xff); + cdb->GET_CONFIGURATION.RequestType = (UCHAR)(inputBuffer->RequestType); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + case IOCTL_DISK_VERIFY: { + + PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer; + LARGE_INTEGER byteOffset; + ULONG sectorOffset; + USHORT sectorCount; + + if (!cdData->Mmc.WriteAllowed) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_MEDIA_WRITE_PROTECTED; + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + // + // Verify sectors + // + + srb->CdbLength = 10; + + cdb->CDB10.OperationCode = SCSIOP_VERIFY; + + // + // Add disk offset to starting sector. + // + + byteOffset.QuadPart = commonExtension->StartingOffset.QuadPart + + verifyInfo->StartingOffset.QuadPart; + + // + // Convert byte offset to sector offset. + // + + sectorOffset = (ULONG)(byteOffset.QuadPart >> fdoExtension->SectorShift); + + // + // Convert ULONG byte count to USHORT sector count. + // + + sectorCount = (USHORT)(verifyInfo->Length >> fdoExtension->SectorShift); + + // + // Move little endian values into CDB in big endian format. + // + + cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)§orOffset)->Byte3; + cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)§orOffset)->Byte2; + cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)§orOffset)->Byte1; + cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)§orOffset)->Byte0; + + cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)§orCount)->Byte1; + cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)§orCount)->Byte0; + + // + // The verify command is used by the NT FORMAT utility and + // requests are sent down for 5% of the volume size. The + // request timeout value is calculated based on the number of + // sectors verified. + // + + srb->TimeOutValue = ((sectorCount + 0x7F) >> 7) * + fdoExtension->TimeOutValue; + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + case IOCTL_STORAGE_CHECK_VERIFY: + case IOCTL_DISK_CHECK_VERIFY: + case IOCTL_CDROM_CHECK_VERIFY: { + + // + // Since a test unit ready is about to be performed, reset the + // timer value to decrease the opportunities for it to race with + // this code. + // + + ClassResetMediaChangeTimer(fdoExtension); + + // + // Set up the SRB/CDB + // + + srb->CdbLength = 6; + cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; + srb->TimeOutValue = fdoExtension->TimeOutValue * 2; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); + + + TraceLog((CdromDebugTrace, + "CdRomStartIo: [%p] Sending CHECK_VERIFY irp %p\n", + Irp, irp2)); + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + case IOCTL_DVD_READ_STRUCTURE: { + + CdRomDeviceControlDvdReadStructure(Fdo, Irp, irp2, srb); + return; + + } + + case IOCTL_DVD_END_SESSION: { + CdRomDeviceControlDvdEndSession(Fdo, Irp, irp2, srb); + return; + } + + case IOCTL_DVD_START_SESSION: + case IOCTL_DVD_READ_KEY: { + + CdRomDeviceControlDvdStartSessionReadKey(Fdo, Irp, irp2, srb); + return; + + } + + + case IOCTL_DVD_SEND_KEY: + case IOCTL_DVD_SEND_KEY2: { + + CdRomDeviceControlDvdSendKey (Fdo, Irp, irp2, srb); + return; + + + } + + case IOCTL_CDROM_READ_TOC_EX: { + + PCDROM_READ_TOC_EX inputBuffer = Irp->AssociatedIrp.SystemBuffer; + + transferByteCount = currentIrpStack->Parameters.Read.Length; + + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + transferByteCount, + CDROM_TAG_TOC); + if (!dataBuffer) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + transferByteCount, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + // + // setup the request per user request + // do validity checking in devctl dispatch, not here + // + + cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC; + cdb->READ_TOC.Msf = inputBuffer->Msf; + cdb->READ_TOC.Format2 = inputBuffer->Format; + cdb->READ_TOC.StartingTrack = inputBuffer->SessionTrack; + cdb->READ_TOC.AllocationLength[0] = (UCHAR)(transferByteCount >> 8); + cdb->READ_TOC.AllocationLength[1] = (UCHAR)(transferByteCount & 0xff); + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + // + // do the standard stuff.... + // + + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->CdbLength = 10; + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->DataBuffer = dataBuffer; + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_GET_LAST_SESSION: + case IOCTL_CDROM_READ_TOC: { + + if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == + IOCTL_CDROM_GET_LAST_SESSION) { + + // + // Set format to return first and last session numbers. + // + + cdb->READ_TOC.Format = CDROM_READ_TOC_EX_FORMAT_SESSION; + + } else { + + // + // Use MSF addressing + // + + cdb->READ_TOC.Msf = 1; + + } + + + transferByteCount = + currentIrpStack->Parameters.Read.Length > + sizeof(CDROM_TOC) ? sizeof(CDROM_TOC): + currentIrpStack->Parameters.Read.Length; + + // + // Set size of TOC structure. + // + + cdb->READ_TOC.AllocationLength[0] = (UCHAR) (transferByteCount >> 8); + cdb->READ_TOC.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF); + + // + // setup remaining srb and cdb parameters. + // + + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->CdbLength = 10; + srb->TimeOutValue = fdoExtension->TimeOutValue; + + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + transferByteCount, + CDROM_TAG_TOC); + if (!dataBuffer) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + transferByteCount, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + srb->DataBuffer = dataBuffer; + cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC; + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + + } + + case IOCTL_CDROM_PLAY_AUDIO_MSF: { + + PCDROM_PLAY_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Set up the SRB/CDB + // + + srb->CdbLength = 10; + cdb->PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF; + + cdb->PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM; + cdb->PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS; + cdb->PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF; + + cdb->PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM; + cdb->PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS; + cdb->PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF; + + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + + } + + case IOCTL_CDROM_READ_Q_CHANNEL: { + + PSUB_Q_CHANNEL_DATA userChannelData = + Irp->AssociatedIrp.SystemBuffer; + PCDROM_SUB_Q_DATA_FORMAT inputBuffer = + Irp->AssociatedIrp.SystemBuffer; + + // + // Allocate buffer for subq channel information. + // + + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + sizeof(SUB_Q_CHANNEL_DATA), + CDROM_TAG_SUB_Q); + + if (!dataBuffer) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + sizeof(SUB_Q_CHANNEL_DATA), + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + srb->DataBuffer = dataBuffer; + + // + // Always logical unit 0, but only use MSF addressing + // for IOCTL_CDROM_CURRENT_POSITION + // + + if (inputBuffer->Format==IOCTL_CDROM_CURRENT_POSITION) + cdb->SUBCHANNEL.Msf = CDB_USE_MSF; + + // + // Return subchannel data + // + + cdb->SUBCHANNEL.SubQ = CDB_SUBCHANNEL_BLOCK; + + // + // Specify format of informatin to return + // + + cdb->SUBCHANNEL.Format = inputBuffer->Format; + + // + // Specify which track to access (only used by Track ISRC reads) + // + + if (inputBuffer->Format==IOCTL_CDROM_TRACK_ISRC) { + cdb->SUBCHANNEL.TrackNumber = inputBuffer->Track; + } + + // + // Set size of channel data -- however, this is dependent on + // what information we are requesting (which Format) + // + + switch( inputBuffer->Format ) { + + case IOCTL_CDROM_CURRENT_POSITION: + transferByteCount = sizeof(SUB_Q_CURRENT_POSITION); + break; + + case IOCTL_CDROM_MEDIA_CATALOG: + transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER); + break; + + case IOCTL_CDROM_TRACK_ISRC: + transferByteCount = sizeof(SUB_Q_TRACK_ISRC); + break; + } + + cdb->SUBCHANNEL.AllocationLength[0] = (UCHAR) (transferByteCount >> 8); + cdb->SUBCHANNEL.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF); + cdb->SUBCHANNEL.OperationCode = SCSIOP_READ_SUB_CHANNEL; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->CdbLength = 10; + srb->TimeOutValue = fdoExtension->TimeOutValue; + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + + } + + case IOCTL_CDROM_PAUSE_AUDIO: { + + cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME; + cdb->PAUSE_RESUME.Action = CDB_AUDIO_PAUSE; + + srb->CdbLength = 10; + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_RESUME_AUDIO: { + + cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME; + cdb->PAUSE_RESUME.Action = CDB_AUDIO_RESUME; + + srb->CdbLength = 10; + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_SEEK_AUDIO_MSF: { + + PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer; + ULONG logicalBlockAddress; + + logicalBlockAddress = MSF_TO_LBA(inputBuffer->M, inputBuffer->S, inputBuffer->F); + + cdb->SEEK.OperationCode = SCSIOP_SEEK; + cdb->SEEK.LogicalBlockAddress[0] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3; + cdb->SEEK.LogicalBlockAddress[1] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2; + cdb->SEEK.LogicalBlockAddress[2] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1; + cdb->SEEK.LogicalBlockAddress[3] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0; + + srb->CdbLength = 10; + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + + } + + case IOCTL_CDROM_STOP_AUDIO: { + + cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; + cdb->START_STOP.Immediate = 1; + cdb->START_STOP.Start = 0; + cdb->START_STOP.LoadEject = 0; + + srb->CdbLength = 6; + srb->TimeOutValue = fdoExtension->TimeOutValue; + + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_GET_CONTROL: { + + PAUDIO_OUTPUT audioOutput; + PCDROM_AUDIO_CONTROL audioControl = Irp->AssociatedIrp.SystemBuffer; + + // + // Allocate buffer for volume control information. + // + + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + MODE_DATA_SIZE, + CDROM_TAG_VOLUME); + + if (!dataBuffer) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + MODE_DATA_SIZE, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + srb->DataBuffer = dataBuffer; + + RtlZeroMemory(dataBuffer, MODE_DATA_SIZE); + + // + // Setup for either 6 or 10 byte CDBs. + // + + if (use6Byte) { + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE; + cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE; + + // + // Disable block descriptors. + // + + cdb->MODE_SENSE.Dbd = TRUE; + + srb->CdbLength = 6; + } else { + + cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; + cdb->MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE; + cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8); + cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF); + + // + // Disable block descriptors. + // + + cdb->MODE_SENSE10.Dbd = TRUE; + + srb->CdbLength = 10; + } + + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->DataTransferLength = MODE_DATA_SIZE; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + + } + + case IOCTL_CDROM_GET_VOLUME: + case IOCTL_CDROM_SET_VOLUME: { + + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + MODE_DATA_SIZE, + CDROM_TAG_VOLUME); + + if (!dataBuffer) { + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + MODE_DATA_SIZE, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(Irp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + srb->DataBuffer = dataBuffer; + + RtlZeroMemory(dataBuffer, MODE_DATA_SIZE); + + + if (use6Byte) { + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE; + cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE; + + srb->CdbLength = 6; + + } else { + + cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; + cdb->MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE; + cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8); + cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF); + + srb->CdbLength = 10; + } + + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->DataTransferLength = MODE_DATA_SIZE; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + + if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_SET_VOLUME) { + + // + // Setup a different completion routine as the mode sense data is needed in order + // to send the mode select. + // + + IoSetCompletionRoutine(irp2, + CdRomSetVolumeIntermediateCompletion, + srb, + TRUE, + TRUE, + TRUE); + + } + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + + } + + case IOCTL_STORAGE_SET_READ_AHEAD: { + + PSTORAGE_SET_READ_AHEAD readAhead = Irp->AssociatedIrp.SystemBuffer; + + ULONG blockAddress; + PFOUR_BYTE fourByte = (PFOUR_BYTE) &blockAddress; + + // + // setup the SRB for a set readahead command + // + + cdb->SET_READ_AHEAD.OperationCode = SCSIOP_SET_READ_AHEAD; + + blockAddress = (ULONG) (readAhead->TriggerAddress.QuadPart >> + fdoExtension->SectorShift); + + cdb->SET_READ_AHEAD.TriggerLBA[0] = fourByte->Byte3; + cdb->SET_READ_AHEAD.TriggerLBA[1] = fourByte->Byte2; + cdb->SET_READ_AHEAD.TriggerLBA[2] = fourByte->Byte1; + cdb->SET_READ_AHEAD.TriggerLBA[3] = fourByte->Byte0; + + blockAddress = (ULONG) (readAhead->TargetAddress.QuadPart >> + fdoExtension->SectorShift); + + cdb->SET_READ_AHEAD.ReadAheadLBA[0] = fourByte->Byte3; + cdb->SET_READ_AHEAD.ReadAheadLBA[1] = fourByte->Byte2; + cdb->SET_READ_AHEAD.ReadAheadLBA[2] = fourByte->Byte1; + cdb->SET_READ_AHEAD.ReadAheadLBA[3] = fourByte->Byte0; + + srb->CdbLength = 12; + srb->TimeOutValue = fdoExtension->TimeOutValue; + + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2); + return; + } + + case IOCTL_DISK_GET_DRIVE_LAYOUT: + case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: + case IOCTL_DISK_GET_PARTITION_INFO: + case IOCTL_DISK_GET_PARTITION_INFO_EX: { + + ASSERT(irp2); + ASSERT(senseBuffer); + ASSERT(srb); + + ExFreePool(srb); + ExFreePool(senseBuffer); + IoFreeIrp(irp2); + + // + // NOTE: should probably update the media's capacity first... + // + + CdromFakePartitionInfo(commonExtension, Irp); + return; + } + + case IOCTL_DISK_IS_WRITABLE: { + + TraceLog((CdromDebugWarning, + "CdRomStartIo: DiskIsWritable (%p) - returning %s\n", + Irp, (cdData->Mmc.WriteAllowed ? "TRUE" : "false"))); + + ASSERT(irp2); + ASSERT(senseBuffer); + ASSERT(srb); + + ExFreePool(srb); + ExFreePool(senseBuffer); + IoFreeIrp(irp2); + + Irp->IoStatus.Information = 0; + if (cdData->Mmc.WriteAllowed) { + Irp->IoStatus.Status = STATUS_SUCCESS; + } else { + Irp->IoStatus.Status = STATUS_MEDIA_WRITE_PROTECTED; + } + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp); + return; + } + + default: { + + UCHAR uniqueAddress; + + // + // Just complete the request - CdRomClassIoctlCompletion will take + // care of it for us + // + // NOTE: THIS IS A SYNCHRONIZATION METHOD!!! + // + + // + // Acquire a new copy of the lock so that ClassCompleteRequest + // doesn't get confused when we complete the other request while + // holding the lock. + // + + // + // NOTE: CdRomDeviceControlDispatch/CdRomDeviceControlCompletion + // wait for the event and eventually calls + // IoStartNextPacket() + // + + ASSERT(irp2); + ASSERT(senseBuffer); + ASSERT(srb); + + ExFreePool(srb); + ExFreePool(senseBuffer); + IoFreeIrp(irp2); + + + + ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddress); + ClassReleaseRemoveLock(Fdo, Irp); + ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT); + ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddress); + return; + } + + } // end switch() + } else if (currentIrpStack->MajorFunction == IRP_MJ_SHUTDOWN || + currentIrpStack->MajorFunction == IRP_MJ_FLUSH_BUFFERS) { + + currentIrpStack->Parameters.Others.Argument1 = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + CdRomShutdownFlushCompletion(Fdo, NULL, Irp); + return; + + } + + // + // If a read or an unhandled IRP_MJ_XX, end up here. The unhandled IRP_MJ's + // are expected and composed of AutoRun Irps, at present. + // + + IoCallDriver(commonExtension->LowerDeviceObject, Irp); + return; +} + + +NTSTATUS +CdRomReadWriteVerification( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the entry called by the I/O system for read requests. + It builds the SRB and sends it to the port driver. + +Arguments: + + DeviceObject - the system object for the device. + Irp - IRP involved. + +Return Value: + + NT Status + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG transferByteCount = currentIrpStack->Parameters.Read.Length; + LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset; + + PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData); + + SCSI_REQUEST_BLOCK srb; + PCDB cdb = (PCDB)srb.Cdb; + NTSTATUS status; + + PAGED_CODE(); + + // + // note: we are no longer failing write commands immediately + // they are now failed in StartIo based upon media ability + // + + // + // If the cd is playing music then reject this request. + // + + if (PLAY_ACTIVE(fdoExtension)) { + Irp->IoStatus.Status = STATUS_DEVICE_BUSY; + return STATUS_DEVICE_BUSY; + } + + // + // Verify parameters of this request. + // Check that ending sector is on disc and + // that number of bytes to transfer is a multiple of + // the sector size. + // + + startingOffset.QuadPart = currentIrpStack->Parameters.Read.ByteOffset.QuadPart + + transferByteCount; + + if (!fdoExtension->DiskGeometry.BytesPerSector) { + fdoExtension->DiskGeometry.BytesPerSector = 2048; + } + + if ((startingOffset.QuadPart > commonExtension->PartitionLength.QuadPart) || + (transferByteCount & fdoExtension->DiskGeometry.BytesPerSector - 1)) { + + // + // Fail request with status of invalid parameters. + // + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + return STATUS_INVALID_PARAMETER; + } + + + return STATUS_SUCCESS; + +} // end CdRomReadWriteVerification() + + +NTSTATUS +CdRomSwitchModeCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData); + BOOLEAN use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE); + PIO_STACK_LOCATION realIrpStack; + PIO_STACK_LOCATION realIrpNextStack; + PSCSI_REQUEST_BLOCK srb = Context; + PIRP realIrp = NULL; + NTSTATUS status; + BOOLEAN retry; + + // + // Extract the 'real' irp from the irpstack. + // + + realIrp = (PIRP) irpStack->Parameters.Others.Argument2; + realIrpStack = IoGetCurrentIrpStackLocation(realIrp); + realIrpNextStack = IoGetNextIrpStackLocation(realIrp); + + // + // Check SRB status for success of completing request. + // + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + ULONG retryInterval; + + TraceLog((CdromDebugTrace, + "CdRomSetVolumeIntermediateCompletion: Irp %p, Srb %p, Real Irp %p\n", + Irp, + srb, + realIrp)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ClassReleaseQueue(DeviceObject); + } + + + retry = ClassInterpretSenseInfo(DeviceObject, + srb, + irpStack->MajorFunction, + irpStack->Parameters.DeviceIoControl.IoControlCode, + MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1), + &status, + &retryInterval); + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && realIrpNextStack->Parameters.Others.Argument1--) { + + if (((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)) { + + // + // Retry request. + // + + TraceLog((CdromDebugWarning, + "Retry request %p - Calling StartIo\n", Irp)); + + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + // + // Call StartIo directly since IoStartNextPacket hasn't been called, + // the serialisation is still intact. + // + + CdRomRetryRequest(fdoExtension, + realIrp, + retryInterval, + FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; + + } + + // + // Exhausted retries. Fall through and complete the request with the appropriate status. + // + } + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; + + } + + if (NT_SUCCESS(status)) { + + ULONG sectorSize, startingSector, transferByteCount; + PCDB cdb; + + // + // Update device ext. to show which mode we are currently using. + // + + sectorSize = cdData->BlockDescriptor.BlockLength[0] << 16; + sectorSize |= (cdData->BlockDescriptor.BlockLength[1] << 8); + sectorSize |= (cdData->BlockDescriptor.BlockLength[2]); + + cdData->RawAccess = (sectorSize == RAW_SECTOR_SIZE) ? TRUE : FALSE; + + // + // Free the old data buffer, mdl. + // reuse the SenseInfoBuffer and Srb + // + + ExFreePool(srb->DataBuffer); + IoFreeMdl(Irp->MdlAddress); + IoFreeIrp(Irp); + + // + // rebuild the srb. + // + + cdb = (PCDB)srb->Cdb; + RtlZeroMemory(cdb, CDB12GENERIC_LENGTH); + + + if (cdData->RawAccess) { + + PRAW_READ_INFO rawReadInfo = + (PRAW_READ_INFO)realIrpStack->Parameters.DeviceIoControl.Type3InputBuffer; + + ULONG maximumTransferLength; + ULONG transferPages; + UCHAR min, sec, frame; + + // + // Calculate starting offset. + // + + startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> fdoExtension->SectorShift); + transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE; + maximumTransferLength = fdoExtension->AdapterDescriptor->MaximumTransferLength; + transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(realIrp->MdlAddress), + transferByteCount); + + // + // Determine if request is within limits imposed by miniport. + // If the request is larger than the miniport's capabilities, split it. + // + + if (transferByteCount > maximumTransferLength || + transferPages > fdoExtension->AdapterDescriptor->MaximumPhysicalPages) { + + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb); + realIrp->IoStatus.Information = 0; + realIrp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + BAIL_OUT(realIrp); + CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, realIrp); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + srb->OriginalRequest = realIrp; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + + srb->DataTransferLength = transferByteCount; + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->CdbLength = 10; + srb->DataBuffer = MmGetMdlVirtualAddress(realIrp->MdlAddress); + + if (rawReadInfo->TrackMode == CDDA) { + if (TEST_FLAG(cdData->XAFlags, XA_PLEXTOR_CDDA)) { + + srb->CdbLength = 12; + + cdb->PLXTR_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->PLXTR_READ_CDDA.TransferBlockByte3 = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + cdb->PLXTR_READ_CDDA.TransferBlockByte2 = (UCHAR) (rawReadInfo->SectorCount >> 8); + cdb->PLXTR_READ_CDDA.TransferBlockByte1 = 0; + cdb->PLXTR_READ_CDDA.TransferBlockByte0 = 0; + + cdb->PLXTR_READ_CDDA.SubCode = 0; + cdb->PLXTR_READ_CDDA.OperationCode = 0xD8; + + } else if (TEST_FLAG(cdData->XAFlags, XA_NEC_CDDA)) { + + cdb->NEC_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->NEC_READ_CDDA.TransferBlockByte1 = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + cdb->NEC_READ_CDDA.TransferBlockByte0 = (UCHAR) (rawReadInfo->SectorCount >> 8); + + cdb->NEC_READ_CDDA.OperationCode = 0xD4; + } + } else { + cdb->CDB10.TransferBlocksMsb = (UCHAR) (rawReadInfo->SectorCount >> 8); + cdb->CDB10.TransferBlocksLsb = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + + cdb->CDB10.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->CDB10.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->CDB10.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->CDB10.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->CDB10.OperationCode = SCSIOP_READ; + } + + srb->SrbStatus = srb->ScsiStatus = 0; + + + irpStack = IoGetNextIrpStackLocation(realIrp); + irpStack->MajorFunction = IRP_MJ_SCSI; + irpStack->Parameters.Scsi.Srb = srb; + + if (!(irpStack->Parameters.Others.Argument1)) { + + // + // Only jam this in if it doesn't exist. The completion routines can + // call StartIo directly in the case of retries and resetting it will + // cause infinite loops. + // + + irpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES; + } + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(realIrp, + CdRomXACompletion, + srb, + TRUE, + TRUE, + TRUE); + } else { + + PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor; + ULONG maximumTransferLength; + ULONG transferPages; + + // + // a writable device must be MMC compliant, which supports + // READ_CD commands, so writes and mode switching should + // never occur on the same device. + // + + ASSERT(realIrpStack->MajorFunction != IRP_MJ_WRITE); + + // + // free the SRB and SenseInfoBuffer since they aren't used + // by either ClassBuildRequest() nor ClassSplitRequest(). + // + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb); + + // + // Back to cooked sectors. Build and send a normal read. + // The real work for setting offsets was done in startio. + // + + adapterDescriptor = + commonExtension->PartitionZeroExtension->AdapterDescriptor; + maximumTransferLength = adapterDescriptor->MaximumTransferLength; + transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES( + MmGetMdlVirtualAddress(realIrp->MdlAddress), + realIrpStack->Parameters.Read.Length); + + if ((realIrpStack->Parameters.Read.Length > maximumTransferLength) || + (transferPages > adapterDescriptor->MaximumPhysicalPages)) { + + ULONG maxPages = adapterDescriptor->MaximumPhysicalPages; + + if (maxPages != 0) { + maxPages --; // to account for page boundaries + } + + TraceLog((CdromDebugTrace, + "CdromSwitchModeCompletion: Request greater than " + " maximum\n")); + TraceLog((CdromDebugTrace, + "CdromSwitchModeCompletion: Maximum is %lx\n", + maximumTransferLength)); + TraceLog((CdromDebugTrace, + "CdromSwitchModeCompletion: Byte count is %lx\n", + realIrpStack->Parameters.Read.Length)); + + // + // Check that the maximum transfer length fits within + // the maximum number of pages the device can handle. + // + + if (maximumTransferLength > maxPages << PAGE_SHIFT) { + maximumTransferLength = maxPages << PAGE_SHIFT; + } + + // + // Check that maximum transfer size is not zero + // + + if (maximumTransferLength == 0) { + maximumTransferLength = PAGE_SIZE; + } + + // + // Request needs to be split. Completion of each portion + // of the request will fire off the next portion. The final + // request will signal Io to send a new request. + // + + ClassSplitRequest(DeviceObject, realIrp, maximumTransferLength); + return STATUS_MORE_PROCESSING_REQUIRED; + + } else { + + // + // Build SRB and CDB for this IRP. + // + + ClassBuildRequest(DeviceObject, realIrp); + + } + } + + // + // Call the port driver. + // + + IoCallDriver(commonExtension->LowerDeviceObject, realIrp); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // Update device Extension flags to indicate that XA isn't supported. + // + + TraceLog((CdromDebugWarning, + "Device Cannot Support CDDA (but tested positive) " + "Now Clearing CDDA flags for FDO %p\n", DeviceObject)); + SET_FLAG(cdData->XAFlags, XA_NOT_SUPPORTED); + CLEAR_FLAG(cdData->XAFlags, XA_PLEXTOR_CDDA); + CLEAR_FLAG(cdData->XAFlags, XA_NEC_CDDA); + + // + // Deallocate srb and sense buffer. + // + + if (srb) { + if (srb->DataBuffer) { + ExFreePool(srb->DataBuffer); + } + if (srb->SenseInfoBuffer) { + ExFreePool(srb->SenseInfoBuffer); + } + ExFreePool(srb); + } + + if (Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + if (realIrp->PendingReturned) { + IoMarkIrpPending(realIrp); + } + + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + // + // Set status in completing IRP. + // + + realIrp->IoStatus.Status = status; + + // + // Set the hard error if necessary. + // + + if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) { + + // + // Store DeviceObject for filesystem, and clear + // in IoStatus.Information field. + // + + if (realIrp->Tail.Overlay.Thread) { + IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject); + } + realIrp->IoStatus.Information = 0; + } + + CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, realIrp); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +VOID +ScanForSpecialHandler( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + ULONG_PTR HackFlags + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension; + PCDROM_DATA cdData; + + PAGED_CODE(); + + CLEAR_FLAG(HackFlags, CDROM_HACK_INVALID_FLAGS); + + commonExtension = &(FdoExtension->CommonExtension); + cdData = (PCDROM_DATA)(commonExtension->DriverData); + cdData->HackFlags = HackFlags; + + return; +} + +VOID +ScanForSpecial( + PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This function checks to see if an SCSI logical unit requires an special + initialization or error processing. + +Arguments: + + DeviceObject - Supplies the device object to be tested. + + InquiryData - Supplies the inquiry data returned by the device of interest. + + PortCapabilities - Supplies the capabilities of the device object. + +Return Value: + + None. + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PCOMMON_DEVICE_EXTENSION commonExtension; + PCDROM_DATA cdData; + + PAGED_CODE(); + + fdoExtension = DeviceObject->DeviceExtension; + commonExtension = DeviceObject->DeviceExtension; + cdData = (PCDROM_DATA)(commonExtension->DriverData); + + + // + // set our hack flags + // + + ClassScanForSpecial(fdoExtension, CdromHackItems, ScanForSpecialHandler); + + // + // All CDRom's can ignore the queue lock failure for power operations + // and do not require handling the SpinUp case (unknown result of sending + // a cdrom a START_UNIT command -- may eject disks?) + // + // We send the stop command mostly to stop outstanding asynch operations + // (like audio playback) from running when the system is powered off. + // Because of this and the unlikely chance that a PLAY command will be + // sent in the window between the STOP and the time the machine powers down + // we don't require queue locks. This is important because without them + // classpnp's power routines will send the START_STOP_UNIT command to the + // device whether or not it supports locking (atapi does not support locking + // and if we requested them we would end up not stopping audio on atapi + // devices). + // + + SET_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_SPIN_UP); + SET_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_NO_QUEUE_LOCK); + + if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_HITACHI_1750) + && ( fdoExtension->AdapterDescriptor->AdapterUsesPio ) + ) { + + // + // Read-ahead must be disabled in order to get this cdrom drive + // to work on scsi adapters that use PIO. + // + + + TraceLog((CdromDebugWarning, + "CdRom ScanForSpecial: Found Hitachi CDR-1750S.\n")); + + // + // Setup an error handler to reinitialize the cd rom after it is reset. + // + + cdData->ErrorHandler = HitachiProcessError; + + // + // Lock down the hitachi error processing code. + // + + MmLockPagableCodeSection(HitachiProcessError); + SET_FLAG(cdData->HackFlags, CDROM_HACK_LOCKED_PAGES); + + + } else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_TOSHIBA_SD_W1101)) { + + TraceLog((CdromDebugError, + "CdRom ScanForSpecial: Found Toshiba SD-W1101 DVD-RAM " + "-- This drive will *NOT* support DVD-ROM playback.\n")); + + } else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_HITACHI_GD_2000)) { + + TraceLog((CdromDebugWarning, + "CdRom ScanForSpecial: Found Hitachi GD-2000\n")); + + // + // Setup an error handler to spin up the drive when it idles out + // since it seems to like to fail to spin itself back up on its + // own for a REPORT_KEY command. It may also lose the AGIDs that + // it has given, which will result in DVD playback failures. + // This routine will just do what it can... + // + + cdData->ErrorHandler = HitachiProcessErrorGD2000; + + // + // this drive may require START_UNIT commands to spin + // the drive up when it's spun itself down. + // + + SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT); + + // + // Lock down the hitachi error processing code. + // + + MmLockPagableCodeSection(HitachiProcessErrorGD2000); + SET_FLAG(cdData->HackFlags, CDROM_HACK_LOCKED_PAGES); + + } else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_FUJITSU_FMCD_10x)) { + + // + // When Read command is issued to FMCD-101 or FMCD-102 and there is a music + // cd in it. It takes longer time than SCSI_CDROM_TIMEOUT before returning + // error status. + // + + fdoExtension->TimeOutValue = 20; + + } else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_DEC_RRD)) { + + PMODE_PARM_READ_WRITE_DATA modeParameters; + SCSI_REQUEST_BLOCK srb; + PCDB cdb; + NTSTATUS status; + + + TraceLog((CdromDebugWarning, + "CdRom ScanForSpecial: Found DEC RRD.\n")); + + cdData->IsDecRrd = TRUE; + + // + // Setup an error handler to reinitialize the cd rom after it is reset? + // + //commonExtension->DevInfo->ClassError = DecRrdProcessError; + + // + // Found a DEC RRD cd-rom. These devices do not pass MS HCT + // multi-media tests because the DEC firmware modifieds the block + // from the PC-standard 2K to 512. Change the block transfer size + // back to the PC-standard 2K by using a mode select command. + // + + modeParameters = ExAllocatePoolWithTag(NonPagedPool, + sizeof(MODE_PARM_READ_WRITE_DATA), + CDROM_TAG_MODE_DATA + ); + if (modeParameters == NULL) { + return; + } + + RtlZeroMemory(modeParameters, sizeof(MODE_PARM_READ_WRITE_DATA)); + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Set the block length to 2K. + // + + modeParameters->ParameterListHeader.BlockDescriptorLength = + sizeof(MODE_PARAMETER_BLOCK); + + // + // Set block length to 2K (0x0800) in Parameter Block. + // + + modeParameters->ParameterListBlock.BlockLength[0] = 0x00; //MSB + modeParameters->ParameterListBlock.BlockLength[1] = 0x08; + modeParameters->ParameterListBlock.BlockLength[2] = 0x00; //LSB + + // + // Build the mode select CDB. + // + + srb.CdbLength = 6; + srb.TimeOutValue = fdoExtension->TimeOutValue; + + cdb = (PCDB)srb.Cdb; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.ParameterListLength = HITACHI_MODE_DATA_SIZE; + + // + // Send the request to the device. + // + + status = ClassSendSrbSynchronous(DeviceObject, + &srb, + modeParameters, + sizeof(MODE_PARM_READ_WRITE_DATA), + TRUE); + + if (!NT_SUCCESS(status)) { + TraceLog((CdromDebugWarning, + "CdRom ScanForSpecial: Setting DEC RRD to 2K block" + "size failed [%x]\n", status)); + } + ExFreePool(modeParameters); + + } else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_TOSHIBA_XM_3xx)) { + + SCSI_REQUEST_BLOCK srb; + PCDB cdb; + ULONG length; + PUCHAR buffer; + NTSTATUS status; + + // + // Set the density code and the error handler. + // + + length = (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH); + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Build the MODE SENSE CDB. + // + + srb.CdbLength = 6; + cdb = (PCDB)srb.Cdb; + + // + // Set timeout value from device extension. + // + + srb.TimeOutValue = fdoExtension->TimeOutValue; + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = 0x1; + // NOTE: purposely not setting DBD because it is what is needed. + cdb->MODE_SENSE.AllocationLength = (UCHAR)length; + + buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH), + CDROM_TAG_MODE_DATA); + if (!buffer) { + return; + } + + status = ClassSendSrbSynchronous(DeviceObject, + &srb, + buffer, + length, + FALSE); + + ((PERROR_RECOVERY_DATA)buffer)->BlockDescriptor.DensityCode = 0x83; + ((PERROR_RECOVERY_DATA)buffer)->Header.ModeDataLength = 0x0; + + RtlCopyMemory(&cdData->Header, buffer, sizeof(ERROR_RECOVERY_DATA)); + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Build the MODE SENSE CDB. + // + + srb.CdbLength = 6; + cdb = (PCDB)srb.Cdb; + + // + // Set timeout value from device extension. + // + + srb.TimeOutValue = fdoExtension->TimeOutValue; + + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = (UCHAR)length; + + status = ClassSendSrbSynchronous(DeviceObject, + &srb, + buffer, + length, + TRUE); + + if (!NT_SUCCESS(status)) { + TraceLog((CdromDebugWarning, + "Cdrom.ScanForSpecial: Setting density code on Toshiba failed [%x]\n", + status)); + } + + cdData->ErrorHandler = ToshibaProcessError; + + // + // Lock down the toshiba error section. + // + + MmLockPagableCodeSection(ToshibaProcessError); + SET_FLAG(cdData->HackFlags, CDROM_HACK_LOCKED_PAGES); + + ExFreePool(buffer); + + } + + // + // Determine special CD-DA requirements. + // + + if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_READ_CD_SUPPORTED)) { + + SET_FLAG(cdData->XAFlags, XA_USE_READ_CD); + + } else if (!TEST_FLAG(cdData->XAFlags, XA_USE_READ_CD)) { + + if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_PLEXTOR_CDDA)) { + SET_FLAG(cdData->XAFlags, XA_PLEXTOR_CDDA); + } else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_NEC_CDDA)) { + SET_FLAG(cdData->XAFlags, XA_NEC_CDDA); + } + + } + + if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_LOCKED_PAGES)) { + KdPrintEx((DPFLTR_SYSTEM_ID, DPFLTR_ERROR_LEVEL, + "Locking pages for error handler\n")); + } + + + return; +} + + +VOID +HitachiProcessErrorGD2000( + PDEVICE_OBJECT Fdo, + PSCSI_REQUEST_BLOCK OriginalSrb, + NTSTATUS *Status, + BOOLEAN *Retry + ) +/*++ + +Routine Description: + + This routine checks the type of error. If the error suggests that the + drive has spun down and cannot reinitialize itself, send a + START_UNIT or READ to the device. This will force the drive to spin + up. This drive also loses the AGIDs it has granted when it spins down, + which may result in playback failure the first time around. + +Arguments: + + DeviceObject - Supplies a pointer to the device object. + + Srb - Supplies a pointer to the failing Srb. + + Status - return the final status for this command? + + Retry - return if the command should be retried. + +Return Value: + + None. + +--*/ +{ + PSENSE_DATA senseBuffer = OriginalSrb->SenseInfoBuffer; + + UNREFERENCED_PARAMETER(Status); + UNREFERENCED_PARAMETER(Retry); + + if (!TEST_FLAG(OriginalSrb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) { + return; + } + + if (((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_HARDWARE_ERROR) && + (senseBuffer->AdditionalSenseCode == 0x44)) { + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PIRP irp; + PIO_STACK_LOCATION irpStack; + PCOMPLETION_CONTEXT context; + PSCSI_REQUEST_BLOCK newSrb; + PCDB cdb; + + TraceLog((CdromDebugWarning, + "HitachiProcessErrorGD2000 (%p) => Internal Target " + "Failure Detected -- spinning up drive\n", Fdo)); + + // + // the request should be retried because the device isn't ready + // + + *Retry = TRUE; + *Status = STATUS_DEVICE_NOT_READY; + + // + // send a START_STOP unit to spin up the drive + // NOTE: this temporarily violates the StartIo serialization + // mechanism, but the completion routine on this will NOT + // call StartNextPacket(), so it's a temporary disruption + // of the serialization only. + // + + ClassSendStartUnit(Fdo); + + } + + return; +} + + +VOID +HitachiProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ) +/*++ + +Routine Description: + + This routine checks the type of error. If the error indicates CD-ROM the + CD-ROM needs to be reinitialized then a Mode sense command is sent to the + device. This command disables read-ahead for the device. + +Arguments: + + DeviceObject - Supplies a pointer to the device object. + + Srb - Supplies a pointer to the failing Srb. + + Status - Not used. + + Retry - Not used. + +Return Value: + + None. + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer; + LARGE_INTEGER largeInt; + PUCHAR modePage; + PIO_STACK_LOCATION irpStack; + PIRP irp; + PSCSI_REQUEST_BLOCK srb; + PCOMPLETION_CONTEXT context; + PCDB cdb; + ULONG_PTR alignment; + + UNREFERENCED_PARAMETER(Status); + UNREFERENCED_PARAMETER(Retry); + + largeInt.QuadPart = (LONGLONG) 1; + + // + // Check the status. The initialization command only needs to be sent + // if UNIT ATTENTION is returned. + // + + if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) { + + // + // The drive does not require reinitialization. + // + + return; + } + + // + // Found an HITACHI cd-rom that does not work with PIO + // adapters when read-ahead is enabled. Read-ahead is disabled by + // a mode select command. The mode select page code is zero and the + // length is 6 bytes. All of the other bytes should be zero. + // + + if ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) { + + TraceLog((CdromDebugWarning, + "HitachiProcessError: Reinitializing the CD-ROM.\n")); + + // + // Send the special mode select command to disable read-ahead + // on the CD-ROM reader. + // + + alignment = DeviceObject->AlignmentRequirement ? + DeviceObject->AlignmentRequirement : 1; + + context = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(COMPLETION_CONTEXT) + HITACHI_MODE_DATA_SIZE + (ULONG)alignment, + CDROM_TAG_HITACHI_ERROR + ); + + if (context == NULL) { + + // + // If there is not enough memory to fulfill this request, + // simply return. A subsequent retry will fail and another + // chance to start the unit. + // + + return; + } + + context->DeviceObject = DeviceObject; + srb = &context->Srb; + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + // + // Write length to SRB. + // + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set up SCSI bus address. + // + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->TimeOutValue = fdoExtension->TimeOutValue; + + // + // Set the transfer length. + // + + srb->DataTransferLength = HITACHI_MODE_DATA_SIZE; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + + // + // The data buffer must be aligned. + // + + srb->DataBuffer = (PVOID) (((ULONG_PTR) (context + 1) + (alignment - 1)) & + ~(alignment - 1)); + + + // + // Build the HITACHI read-ahead mode select CDB. + // + + srb->CdbLength = 6; + cdb = (PCDB)srb->Cdb; + cdb->MODE_SENSE.LogicalUnitNumber = srb->Lun; + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SENSE.AllocationLength = HITACHI_MODE_DATA_SIZE; + + // + // Initialize the mode sense data. + // + + modePage = srb->DataBuffer; + + RtlZeroMemory(modePage, HITACHI_MODE_DATA_SIZE); + + // + // Set the page length field to 6. + // + + modePage[5] = 6; + + // + // Build the asynchronous request to be sent to the port driver. + // + + irp = IoBuildAsynchronousFsdRequest(IRP_MJ_WRITE, + DeviceObject, + srb->DataBuffer, + srb->DataTransferLength, + &largeInt, + NULL); + + if (irp == NULL) { + + // + // If there is not enough memory to fulfill this request, + // simply return. A subsequent retry will fail and another + // chance to start the unit. + // + + ExFreePool(context); + return; + } + + ClassAcquireRemoveLock(DeviceObject, irp); + + IoSetCompletionRoutine(irp, + (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion, + context, + TRUE, + TRUE, + TRUE); + + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + srb->OriginalRequest = irp; + + // + // Save SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = (PVOID)srb; + + // + // Set up IRP Address. + // + + (VOID)IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp); + + } +} + + +NTSTATUS +ToshibaProcessErrorCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) + +/*++ + +Routine Description: + + Completion routine for the ClassError routine to handle older Toshiba units + that require setting the density code. + +Arguments: + + DeviceObject - Supplies a pointer to the device object. + + Irp - Pointer to irp created to set the density code. + + Context - Supplies a pointer to the Mode Select Srb. + + +Return Value: + + STATUS_MORE_PROCESSING_REQUIRED + +--*/ + +{ + + PSCSI_REQUEST_BLOCK srb = Context; + + // + // Free all of the allocations. + // + + ClassReleaseRemoveLock(DeviceObject, Irp); + + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + IoFreeMdl(Irp->MdlAddress); + IoFreeIrp(Irp); + + // + // Indicate the I/O system should stop processing the Irp completion. + // + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +VOID +ToshibaProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ) + +/*++ + +Routine Description: + + This routine checks the type of error. If the error indicates a unit attention, + the density code needs to be set via a Mode select command. + +Arguments: + + DeviceObject - Supplies a pointer to the device object. + + Srb - Supplies a pointer to the failing Srb. + + Status - Not used. + + Retry - Not used. + +Return Value: + + None. + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData); + PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer; + PIO_STACK_LOCATION irpStack; + PIRP irp; + PSCSI_REQUEST_BLOCK srb; + ULONG length; + PCDB cdb; + PUCHAR dataBuffer; + + + if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) { + return; + } + + // + // The Toshiba's require the density code to be set on power up and media changes. + // + + if ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) { + + + irp = IoAllocateIrp((CCHAR)(DeviceObject->StackSize+1), + FALSE); + + if (!irp) { + return; + } + + srb = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SCSI_REQUEST_BLOCK), + CDROM_TAG_TOSHIBA_ERROR); + if (!srb) { + IoFreeIrp(irp); + return; + } + + + length = sizeof(ERROR_RECOVERY_DATA); + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + length, + CDROM_TAG_TOSHIBA_ERROR); + if (!dataBuffer) { + ExFreePool(srb); + IoFreeIrp(irp); + return; + } + + irp->MdlAddress = IoAllocateMdl(dataBuffer, + length, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp->MdlAddress) { + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp->MdlAddress); + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + srb->DataBuffer = dataBuffer; + cdb = (PCDB)srb->Cdb; + + // + // Set up the irp. + // + + IoSetNextIrpStackLocation(irp); + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = 0; + irp->Flags = 0; + irp->UserBuffer = NULL; + + // + // Save the device object and irp in a private stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(irp); + irpStack->DeviceObject = DeviceObject; + + // + // Construct the IRP stack for the lower level driver. + // + + irpStack = IoGetNextIrpStackLocation(irp); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT; + irpStack->Parameters.Scsi.Srb = srb; + + IoSetCompletionRoutine(irp, + ToshibaProcessErrorCompletion, + srb, + TRUE, + TRUE, + TRUE); + + ClassAcquireRemoveLock(DeviceObject, irp); + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->OriginalRequest = irp; + srb->SenseInfoBufferLength = 0; + + // + // Set the transfer length. + // + + srb->DataTransferLength = length; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + + + srb->CdbLength = 6; + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = (UCHAR)length; + + // + // Copy the Mode page into the databuffer. + // + + RtlCopyMemory(srb->DataBuffer, &cdData->Header, length); + + // + // Set the density code. + // + + ((PERROR_RECOVERY_DATA)srb->DataBuffer)->BlockDescriptor.DensityCode = 0x83; + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp); + } +} + + +BOOLEAN +CdRomIsPlayActive( + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine determines if the cd is currently playing music. + +Arguments: + + DeviceObject - Device object to test. + +Return Value: + + TRUE if the device is playing music. + +--*/ +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + IO_STATUS_BLOCK ioStatus; + PSUB_Q_CURRENT_POSITION currentBuffer; + + PAGED_CODE(); + + // + // if we don't think it is playing audio, don't bother checking. + // + + if (!PLAY_ACTIVE(fdoExtension)) { + return(FALSE); + } + + currentBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + sizeof(SUB_Q_CURRENT_POSITION), + CDROM_TAG_PLAY_ACTIVE); + + if (currentBuffer == NULL) { + return(FALSE); + } + + ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION; + ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0; + + // + // Build the synchronous request to be sent to ourself + // to perform the request. + // + + ClassSendDeviceIoControlSynchronous( + IOCTL_CDROM_READ_Q_CHANNEL, + DeviceObject, + currentBuffer, + sizeof(CDROM_SUB_Q_DATA_FORMAT), + sizeof(SUB_Q_CURRENT_POSITION), + FALSE, + &ioStatus); + + if (!NT_SUCCESS(ioStatus.Status)) { + ExFreePool(currentBuffer); + return FALSE; + } + + // + // should update the playactive flag here. + // + + if (currentBuffer->Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) { + PLAY_ACTIVE(fdoExtension) = TRUE; + } else { + PLAY_ACTIVE(fdoExtension) = FALSE; + } + + ExFreePool(currentBuffer); + + return(PLAY_ACTIVE(fdoExtension)); + +} + + +VOID +CdRomTickHandler( + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine handles the once per second timer provided by the + Io subsystem. It is used to do delayed retries for cdroms. + +Arguments: + + DeviceObject - what to check. + +Return Value: + + None. + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + ULONG isRemoved; + + KIRQL oldIrql; + + PIRP irp; + PIRP heldIrpList; + PIRP nextIrp; + PLIST_ENTRY listEntry; + PCDROM_DATA cddata; + PIO_STACK_LOCATION irpStack; + UCHAR uniqueAddress; + + isRemoved = ClassAcquireRemoveLock(DeviceObject, (PIRP) &uniqueAddress); + + // + // We stop the timer before deleting the device. It's safe to keep going + // if the flag value is REMOVE_PENDING because the removal thread will be + // blocked trying to stop the timer. + // + + ASSERT(isRemoved != REMOVE_COMPLETE); + + // + // This routine is reasonably safe even if the device object has a pending + // remove + + cddata = commonExtension->DriverData; + + // + // Since cdrom is completely synchronized there can never be more than one + // irp delayed for retry at any time. + // + + KeAcquireSpinLock(&(cddata->DelayedRetrySpinLock), &oldIrql); + + if(cddata->DelayedRetryIrp != NULL) { + + PIRP irp = cddata->DelayedRetryIrp; + + // + // If we've got a delayed retry at this point then there had beter + // be an interval for it. + // + + ASSERT(cddata->DelayedRetryInterval != 0); + cddata->DelayedRetryInterval--; + + if(isRemoved) { + + // + // This device is removed - flush the timer queue + // + + cddata->DelayedRetryIrp = NULL; + cddata->DelayedRetryInterval = 0; + + KeReleaseSpinLock(&(cddata->DelayedRetrySpinLock), oldIrql); + + ClassReleaseRemoveLock(DeviceObject, irp); + ClassCompleteRequest(DeviceObject, irp, IO_CD_ROM_INCREMENT); + + } else if (cddata->DelayedRetryInterval == 0) { + + // + // Submit this IRP to the lower driver. This IRP does not + // need to be remembered here. It will be handled again when + // it completes. + // + + cddata->DelayedRetryIrp = NULL; + + KeReleaseSpinLock(&(cddata->DelayedRetrySpinLock), oldIrql); + + TraceLog((CdromDebugWarning, + "CdRomTickHandler: Reissuing request %p (thread = %p)\n", + irp, + irp->Tail.Overlay.Thread)); + + // + // feed this to the appropriate port driver + // + + CdRomRerunRequest(fdoExtension, irp, cddata->DelayedRetryResend); + } else { + KeReleaseSpinLock(&(cddata->DelayedRetrySpinLock), oldIrql); + } + } else { + KeReleaseSpinLock(&(cddata->DelayedRetrySpinLock), oldIrql); + } + + ClassReleaseRemoveLock(DeviceObject, (PIRP) &uniqueAddress); +} + + +NTSTATUS +CdRomUpdateGeometryCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine andles the completion of the test unit ready irps + used to determine if the media has changed. If the media has + changed, this code signals the named event to wake up other + system services that react to media change (aka AutoPlay). + +Arguments: + + DeviceObject - the object for the completion + Irp - the IRP being completed + Context - the SRB from the IRP + +Return Value: + + NTSTATUS + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PCOMMON_DEVICE_EXTENSION commonExtension; + + PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK) Context; + PREAD_CAPACITY_DATA readCapacityBuffer; + PIO_STACK_LOCATION irpStack; + NTSTATUS status; + BOOLEAN retry; + ULONG retryCount; + ULONG lastSector; + PIRP originalIrp; + PCDROM_DATA cddata; + UCHAR uniqueAddress; + + // + // Get items saved in the private IRP stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(Irp); + retryCount = (ULONG)(ULONG_PTR) irpStack->Parameters.Others.Argument1; + originalIrp = (PIRP) irpStack->Parameters.Others.Argument2; + + if (!DeviceObject) { + DeviceObject = irpStack->DeviceObject; + } + ASSERT(DeviceObject); + + fdoExtension = DeviceObject->DeviceExtension; + commonExtension = DeviceObject->DeviceExtension; + cddata = commonExtension->DriverData; + readCapacityBuffer = srb->DataBuffer; + + if ((NT_SUCCESS(Irp->IoStatus.Status)) && (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)) { + + CdRomInterpretReadCapacity(DeviceObject, readCapacityBuffer); + + } else { + + ULONG retryInterval; + + TraceLog((CdromDebugWarning, + "CdRomUpdateGeometryCompletion: [%p] unsuccessful " + "completion of buddy-irp %p (status - %lx)\n", + originalIrp, Irp, Irp->IoStatus.Status)); + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ClassReleaseQueue(DeviceObject); + } + + retry = ClassInterpretSenseInfo(DeviceObject, + srb, + IRP_MJ_SCSI, + 0, + retryCount, + &status, + &retryInterval); + if (retry) { + retryCount--; + if ((retryCount) && (commonExtension->IsRemoved == NO_REMOVE)) { + PCDB cdb; + + TraceLog((CdromDebugWarning, + "CdRomUpdateGeometryCompletion: [%p] Retrying " + "request %p .. thread is %p\n", + originalIrp, Irp, Irp->Tail.Overlay.Thread)); + + // + // set up a one shot timer to get this process started over + // + + irpStack->Parameters.Others.Argument1 = ULongToPtr( retryCount ); + irpStack->Parameters.Others.Argument2 = (PVOID) originalIrp; + irpStack->Parameters.Others.Argument3 = (PVOID) 2; + + // + // Setup the IRP to be submitted again in the timer routine. + // + + irpStack = IoGetNextIrpStackLocation(Irp); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; + irpStack->Parameters.Scsi.Srb = srb; + IoSetCompletionRoutine(Irp, + CdRomUpdateGeometryCompletion, + srb, + TRUE, + TRUE, + TRUE); + + // + // Set up the SRB for read capacity. + // + + srb->CdbLength = 10; + srb->TimeOutValue = fdoExtension->TimeOutValue; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + srb->DataTransferLength = sizeof(READ_CAPACITY_DATA); + + // + // Set up the CDB + // + + cdb = (PCDB) &srb->Cdb[0]; + cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; + + // + // Requests queued onto this list will be sent to the + // lower level driver during CdRomTickHandler + // + + CdRomRetryRequest(fdoExtension, Irp, retryInterval, TRUE); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + if (commonExtension->IsRemoved != NO_REMOVE) { + + // + // We cannot retry the request. Fail it. + // + + originalIrp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + + } else { + + // + // This has been bounced for a number of times. Error the + // original request. + // + + originalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + RtlZeroMemory(&(fdoExtension->DiskGeometry), + sizeof(DISK_GEOMETRY)); + fdoExtension->DiskGeometry.BytesPerSector = 2048; + fdoExtension->SectorShift = 11; + commonExtension->PartitionLength.QuadPart = + (LONGLONG)(0x7fffffff); + fdoExtension->DiskGeometry.MediaType = RemovableMedia; + } + } else { + + // + // Set up reasonable defaults + // + + RtlZeroMemory(&(fdoExtension->DiskGeometry), + sizeof(DISK_GEOMETRY)); + fdoExtension->DiskGeometry.BytesPerSector = 2048; + fdoExtension->SectorShift = 11; + commonExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff); + fdoExtension->DiskGeometry.MediaType = RemovableMedia; + } + } + + // + // Free resources held. + // + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + IoFreeIrp(Irp); + Irp = NULL; + + if (originalIrp->Tail.Overlay.Thread) { + + TraceLog((CdromDebugTrace, + "CdRomUpdateGeometryCompletion: [%p] completing " + "original IRP\n", originalIrp)); + + } else { + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError, + "CdRomUpdateGeometryCompletion: completing irp %p which has " + "no thread\n", originalIrp)); + + } + + { + // NOTE: should the original irp be sent down to the device object? + // it probably should if the SL_OVERRIDER_VERIFY_VOLUME flag + // is set! + KIRQL oldIrql; + PIO_STACK_LOCATION realIrpStack; + + realIrpStack = IoGetCurrentIrpStackLocation(originalIrp); + oldIrql = KeRaiseIrqlToDpcLevel(); + + if (TEST_FLAG(realIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { + CdRomStartIo(DeviceObject, originalIrp); + } else { + originalIrp->IoStatus.Status = STATUS_VERIFY_REQUIRED; + originalIrp->IoStatus.Information = 0; + CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, originalIrp); + } + KeLowerIrql(oldIrql); + } + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +CdRomUpdateCapacity( + IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension, + IN PIRP IrpToComplete, + IN OPTIONAL PKEVENT IoctlEvent + ) + +/*++ + +Routine Description: + + This routine updates the capacity of the disk as recorded in the device extension. + It also completes the IRP given with STATUS_VERIFY_REQUIRED. This routine is called + when a media change has occurred and it is necessary to determine the capacity of the + new media prior to the next access. + +Arguments: + + DeviceExtension - the device to update + IrpToComplete - the request that needs to be completed when done. + +Return Value: + + NTSTATUS + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION) DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION) DeviceExtension; + + PCDB cdb; + PIRP irp; + PSCSI_REQUEST_BLOCK srb; + PREAD_CAPACITY_DATA capacityBuffer; + PIO_STACK_LOCATION irpStack; + PUCHAR senseBuffer; + NTSTATUS status; + + irp = IoAllocateIrp((CCHAR)(commonExtension->DeviceObject->StackSize+1), + FALSE); + + if (irp) { + + srb = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SCSI_REQUEST_BLOCK), + CDROM_TAG_UPDATE_CAP); + if (srb) { + capacityBuffer = ExAllocatePoolWithTag( + NonPagedPoolCacheAligned, + sizeof(READ_CAPACITY_DATA), + CDROM_TAG_UPDATE_CAP); + + if (capacityBuffer) { + + + senseBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + SENSE_BUFFER_SIZE, + CDROM_TAG_UPDATE_CAP); + + if (senseBuffer) { + + irp->MdlAddress = IoAllocateMdl(capacityBuffer, + sizeof(READ_CAPACITY_DATA), + FALSE, + FALSE, + (PIRP) NULL); + + if (irp->MdlAddress) { + + // + // Have all resources. Set up the IRP to send for the capacity. + // + + IoSetNextIrpStackLocation(irp); + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = 0; + irp->Flags = 0; + irp->UserBuffer = NULL; + + // + // Save the device object and retry count in a private stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(irp); + irpStack->DeviceObject = commonExtension->DeviceObject; + irpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES; + irpStack->Parameters.Others.Argument2 = (PVOID) IrpToComplete; + + // + // Construct the IRP stack for the lower level driver. + // + + irpStack = IoGetNextIrpStackLocation(irp); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; + irpStack->Parameters.Scsi.Srb = srb; + IoSetCompletionRoutine(irp, + CdRomUpdateGeometryCompletion, + srb, + TRUE, + TRUE, + TRUE); + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp->MdlAddress); + + + // + // Set up the SRB for read capacity. + // + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + RtlZeroMemory(senseBuffer, SENSE_BUFFER_SIZE); + srb->CdbLength = 10; + srb->TimeOutValue = DeviceExtension->TimeOutValue; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->SrbFlags = DeviceExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + srb->DataBuffer = capacityBuffer; + srb->DataTransferLength = sizeof(READ_CAPACITY_DATA); + srb->OriginalRequest = irp; + srb->SenseInfoBuffer = senseBuffer; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + // + // Set up the CDB + // + + cdb = (PCDB) &srb->Cdb[0]; + cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; + + // + // Set the return value in the IRP that will be completed + // upon completion of the read capacity. + // + + IrpToComplete->IoStatus.Status = STATUS_IO_DEVICE_ERROR; + IoMarkIrpPending(IrpToComplete); + + IoCallDriver(commonExtension->LowerDeviceObject, irp); + + // + // status is not checked because the completion routine for this + // IRP will always get called and it will free the resources. + // + + return STATUS_PENDING; + + } else { + ExFreePool(senseBuffer); + ExFreePool(capacityBuffer); + ExFreePool(srb); + IoFreeIrp(irp); + } + } else { + ExFreePool(capacityBuffer); + ExFreePool(srb); + IoFreeIrp(irp); + } + } else { + ExFreePool(srb); + IoFreeIrp(irp); + } + } else { + IoFreeIrp(irp); + } + } + + // + // complete the original irp with a failure. + // ISSUE-2000/07/05-henrygab - find a way to avoid failure. + // + + RtlZeroMemory(&(fdoExtension->DiskGeometry), + sizeof(DISK_GEOMETRY)); + fdoExtension->DiskGeometry.BytesPerSector = 2048; + fdoExtension->SectorShift = 11; + commonExtension->PartitionLength.QuadPart = + (LONGLONG)(0x7fffffff); + fdoExtension->DiskGeometry.MediaType = RemovableMedia; + + IrpToComplete->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IrpToComplete->IoStatus.Information = 0; + + BAIL_OUT(IrpToComplete); + CdRomCompleteIrpAndStartNextPacketSafely(commonExtension->DeviceObject, + IrpToComplete); + return STATUS_INSUFFICIENT_RESOURCES; +} + + +NTSTATUS +CdRomRemoveDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ) + +/*++ + +Routine Description: + + This routine is responsible for releasing any resources in use by the + cdrom driver and shutting down it's timer routine. This routine is called + when all outstanding requests have been completed and the device has + disappeared - no requests may be issued to the lower drivers. + +Arguments: + + DeviceObject - the device object being removed + +Return Value: + + none - this routine may not fail + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION deviceExtension = + DeviceObject->DeviceExtension; + + PCDROM_DATA cdData = deviceExtension->CommonExtension.DriverData; + + PAGED_CODE(); + + if((Type == IRP_MN_QUERY_REMOVE_DEVICE) || + (Type == IRP_MN_CANCEL_REMOVE_DEVICE)) { + return STATUS_SUCCESS; + } + + if(cdData->DelayedRetryIrp != NULL) { + cdData->DelayedRetryInterval = 1; + CdRomTickHandler(DeviceObject); + } + + CdRomDeAllocateMmcResources(DeviceObject); + + if (deviceExtension->DeviceDescriptor) { + ExFreePool(deviceExtension->DeviceDescriptor); + deviceExtension->DeviceDescriptor = NULL; + } + + if (deviceExtension->AdapterDescriptor) { + ExFreePool(deviceExtension->AdapterDescriptor); + deviceExtension->AdapterDescriptor = NULL; + } + + if (deviceExtension->SenseData) { + ExFreePool(deviceExtension->SenseData); + deviceExtension->SenseData = NULL; + } + + ClassDeleteSrbLookasideList(&deviceExtension->CommonExtension); + + if(cdData->CdromInterfaceString.Buffer != NULL) { + IoSetDeviceInterfaceState( + &(cdData->CdromInterfaceString), + FALSE); + RtlFreeUnicodeString(&(cdData->CdromInterfaceString)); + RtlInitUnicodeString(&(cdData->CdromInterfaceString), NULL); + } + + if(cdData->VolumeInterfaceString.Buffer != NULL) { + IoSetDeviceInterfaceState( + &(cdData->VolumeInterfaceString), + FALSE); + RtlFreeUnicodeString(&(cdData->VolumeInterfaceString)); + RtlInitUnicodeString(&(cdData->VolumeInterfaceString), NULL); + } + + CdRomDeleteWellKnownName(DeviceObject); + + ASSERT(cdData->DelayedRetryIrp == NULL); + + if(Type == IRP_MN_REMOVE_DEVICE) { + + if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_LOCKED_PAGES)) { + + // + // unlock locked pages by locking (to get Mm pointer) + // and then unlocking twice. + // + + PVOID locked; + + if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_HITACHI_1750)) { + + locked = MmLockPagableCodeSection(HitachiProcessError); + + } else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_HITACHI_GD_2000)) { + + locked = MmLockPagableCodeSection(HitachiProcessErrorGD2000); + + } else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_TOSHIBA_XM_3xx )) { + + locked = MmLockPagableCodeSection(ToshibaProcessError); + + } else { + + // this is a problem! + // workaround by locking this twice, once for us and + // once for the non-existant locker from ScanForSpecial + ASSERT(!"hack flags show locked section, but none exists?"); + locked = MmLockPagableCodeSection(CdRomRemoveDevice); + locked = MmLockPagableCodeSection(CdRomRemoveDevice); + + + } + + MmUnlockPagableImageSection(locked); + MmUnlockPagableImageSection(locked); + + } + + // + // keep the system-wide count accurate, as + // programs use this info to know when they + // have found all the cdroms in a system. + // + + TraceLog((CdromDebugTrace, + "CDROM.SYS Remove device\n")); + IoGetConfigurationInformation()->CdRomCount--; + } + + // + // so long, and thanks for all the fish! + // + + return STATUS_SUCCESS; +} + + +DEVICE_TYPE +CdRomGetDeviceType( + IN PDEVICE_OBJECT DeviceObject + ) +/*++ + +Routine Description: + + This routine figures out the real device type + by checking CDVD_CAPABILITIES_PAGE + +Arguments: + + DeviceObject - + +Return Value: + + FILE_DEVICE_CD_ROM or FILE_DEVICE_DVD + + +--*/ +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PCDROM_DATA cdromExtension; + ULONG bufLength; + SCSI_REQUEST_BLOCK srb; + PCDB cdb; + PMODE_PARAMETER_HEADER10 modePageHeader; + PCDVD_CAPABILITIES_PAGE capPage; + ULONG capPageOffset; + DEVICE_TYPE deviceType; + NTSTATUS status; + BOOLEAN use6Byte; + + PAGED_CODE(); + + // + // NOTE: don't cache this until understand how it affects GetMediaTypes() + // + + // + // default device type + // + + deviceType = FILE_DEVICE_CD_ROM; + + fdoExtension = DeviceObject->DeviceExtension; + + cdromExtension = fdoExtension->CommonExtension.DriverData; + + use6Byte = TEST_FLAG(cdromExtension->XAFlags, XA_USE_6_BYTE); + + RtlZeroMemory(&srb, sizeof(srb)); + cdb = (PCDB)srb.Cdb; + + // + // Build the MODE SENSE CDB. The data returned will be kept in the + // device extension and used to set block size. + // + if (use6Byte) { + + bufLength = sizeof(CDVD_CAPABILITIES_PAGE) + + sizeof(MODE_PARAMETER_HEADER); + + capPageOffset = sizeof(MODE_PARAMETER_HEADER); + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.Dbd = 1; + cdb->MODE_SENSE.PageCode = MODE_PAGE_CAPABILITIES; + cdb->MODE_SENSE.AllocationLength = (UCHAR)bufLength; + srb.CdbLength = 6; + } else { + + bufLength = sizeof(CDVD_CAPABILITIES_PAGE) + + sizeof(MODE_PARAMETER_HEADER10); + + capPageOffset = sizeof(MODE_PARAMETER_HEADER10); + + cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; + cdb->MODE_SENSE10.Dbd = 1; + cdb->MODE_SENSE10.PageCode = MODE_PAGE_CAPABILITIES; + cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufLength >> 8); + cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufLength >> 0); + srb.CdbLength = 10; + } + + // + // Set timeout value from device extension. + // + srb.TimeOutValue = fdoExtension->TimeOutValue; + + modePageHeader = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + bufLength, + CDROM_TAG_MODE_DATA); + if (modePageHeader) { + + RtlZeroMemory(modePageHeader, bufLength); + + status = ClassSendSrbSynchronous( + DeviceObject, + &srb, + modePageHeader, + bufLength, + FALSE); + + if (NT_SUCCESS(status) || + (status == STATUS_DATA_OVERRUN) || + (status == STATUS_BUFFER_OVERFLOW) + ) { + + capPage = (PCDVD_CAPABILITIES_PAGE) (((PUCHAR) modePageHeader) + capPageOffset); + + if ((capPage->PageCode == MODE_PAGE_CAPABILITIES) && + (capPage->DVDROMRead || capPage->DVDRRead || + capPage->DVDRAMRead || capPage->DVDRWrite || + capPage->DVDRAMWrite)) { + + deviceType = FILE_DEVICE_DVD; + } + } + ExFreePool (modePageHeader); + } + + return deviceType; +} + + +NTSTATUS +CdRomCreateWellKnownName( + IN PDEVICE_OBJECT DeviceObject + ) +/*++ + +Routine Description: + + This routine creates a symbolic link to the cdrom device object + under \dosdevices. The number of the cdrom device does not neccessarily + match between \dosdevices and \device, but usually will be the same. + + Saves the buffer + +Arguments: + + DeviceObject - + +Return Value: + + NTSTATUS + +--*/ +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PCDROM_DATA cdromData = commonExtension->DriverData; + + UNICODE_STRING unicodeLinkName; + WCHAR wideLinkName[64]; + PWCHAR savedName; + + LONG cdromNumber = fdoExtension->DeviceNumber; + + NTSTATUS status; + + // + // if already linked, assert then return + // + + if (cdromData->WellKnownName.Buffer != NULL) { + + TraceLog((CdromDebugError, + "CdRomCreateWellKnownName: link already exists %p\n", + cdromData->WellKnownName.Buffer)); + ASSERT(FALSE); + return STATUS_UNSUCCESSFUL; + + } + + // + // find an unused CdRomNN to link to + // + + do { + + swprintf(wideLinkName, L"\\DosDevices\\CdRom%d", cdromNumber); + RtlInitUnicodeString(&unicodeLinkName, wideLinkName); + status = IoCreateSymbolicLink(&unicodeLinkName, + &(commonExtension->DeviceName)); + + cdromNumber++; + + } while((status == STATUS_OBJECT_NAME_COLLISION) || + (status == STATUS_OBJECT_NAME_EXISTS)); + + if (!NT_SUCCESS(status)) { + + TraceLog((CdromDebugWarning, + "CdRomCreateWellKnownName: Error %lx linking %wZ to " + "device %wZ\n", + status, + &unicodeLinkName, + &(commonExtension->DeviceName))); + return status; + + } + + TraceLog((CdromDebugWarning, + "CdRomCreateWellKnownName: successfully linked %wZ " + "to device %wZ\n", + &unicodeLinkName, + &(commonExtension->DeviceName))); + + // + // Save away the symbolic link name in the driver data block. We need + // it so we can delete the link when the device is removed. + // + + savedName = ExAllocatePoolWithTag(PagedPool, + unicodeLinkName.MaximumLength, + CDROM_TAG_STRINGS); + + if (savedName == NULL) { + IoDeleteSymbolicLink(&unicodeLinkName); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(savedName, + unicodeLinkName.Buffer, + unicodeLinkName.MaximumLength); + + RtlInitUnicodeString(&(cdromData->WellKnownName), savedName); + + // + // the name was saved and the link created + // + + return STATUS_SUCCESS; +} + + +VOID +CdRomDeleteWellKnownName( + IN PDEVICE_OBJECT DeviceObject + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PCDROM_DATA cdromData = commonExtension->DriverData; + + if(cdromData->WellKnownName.Buffer != NULL) { + + IoDeleteSymbolicLink(&(cdromData->WellKnownName)); + ExFreePool(cdromData->WellKnownName.Buffer); + cdromData->WellKnownName.Buffer = NULL; + cdromData->WellKnownName.Length = 0; + cdromData->WellKnownName.MaximumLength = 0; + + } + return; +} + + +NTSTATUS +CdRomGetDeviceParameter ( + IN PDEVICE_OBJECT Fdo, + IN PWSTR ParameterName, + IN OUT PULONG ParameterValue + ) +/*++ + +Routine Description: + + retrieve a devnode registry parameter + +Arguments: + + DeviceObject - Cdrom Device Object + + ParameterName - parameter name to look up + + ParameterValuse - default parameter value + +Return Value: + + NT Status + +--*/ +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + NTSTATUS status; + HANDLE deviceParameterHandle; + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + ULONG defaultParameterValue; + + PAGED_CODE(); + + // + // open the given parameter + // + status = IoOpenDeviceRegistryKey(fdoExtension->LowerPdo, + PLUGPLAY_REGKEY_DRIVER, + KEY_READ, + &deviceParameterHandle); + + if(NT_SUCCESS(status)) { + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + defaultParameterValue = *ParameterValue; + + queryTable->Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; + queryTable->Name = ParameterName; + queryTable->EntryContext = ParameterValue; + queryTable->DefaultType = REG_NONE; + queryTable->DefaultData = NULL; + queryTable->DefaultLength = 0; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + (PWSTR) deviceParameterHandle, + queryTable, + NULL, + NULL); + if (!NT_SUCCESS(status)) { + + *ParameterValue = defaultParameterValue; + } + + // + // close what we open + // + ZwClose(deviceParameterHandle); + } + + return status; + +} // CdRomGetDeviceParameter + + +NTSTATUS +CdRomSetDeviceParameter ( + IN PDEVICE_OBJECT Fdo, + IN PWSTR ParameterName, + IN ULONG ParameterValue + ) +/*++ + +Routine Description: + + save a devnode registry parameter + +Arguments: + + DeviceObject - Cdrom Device Object + + ParameterName - parameter name + + ParameterValuse - parameter value + +Return Value: + + NT Status + +--*/ +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + NTSTATUS status; + HANDLE deviceParameterHandle; + + PAGED_CODE(); + + // + // open the given parameter + // + status = IoOpenDeviceRegistryKey(fdoExtension->LowerPdo, + PLUGPLAY_REGKEY_DRIVER, + KEY_READ | KEY_WRITE, + &deviceParameterHandle); + + if(NT_SUCCESS(status)) { + + status = RtlWriteRegistryValue( + RTL_REGISTRY_HANDLE, + (PWSTR) deviceParameterHandle, + ParameterName, + REG_DWORD, + &ParameterValue, + sizeof (ParameterValue)); + + // + // close what we open + // + ZwClose(deviceParameterHandle); + } + + return status; + +} // CdromSetDeviceParameter + + +VOID +CdRomPickDvdRegion( + IN PDEVICE_OBJECT Fdo + ) +/*++ + +Routine Description: + + pick a default dvd region + +Arguments: + + DeviceObject - Cdrom Device Object + +Return Value: + + NT Status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData); + + // + // these five pointers all point to dvdReadStructure or part of + // its data, so don't deallocate them more than once! + // + + PDVD_READ_STRUCTURE dvdReadStructure; + PDVD_COPY_PROTECT_KEY copyProtectKey; + PDVD_COPYRIGHT_DESCRIPTOR dvdCopyRight; + PDVD_RPC_KEY rpcKey; + PDVD_SET_RPC_KEY dvdRpcKey; + + IO_STATUS_BLOCK ioStatus; + ULONG bufferLen; + UCHAR mediaRegion; + ULONG pickDvdRegion; + ULONG defaultDvdRegion; + ULONG dvdRegion; + + PAGED_CODE(); + + if ((pickDvdRegion = InterlockedExchange(&cddata->PickDvdRegion, 0)) == 0) { + + // + // it was non-zero, so either another thread will do this, or + // we no longer need to pick a region + // + + return; + } + + // + // short-circuit if license agreement violated + // + + if (cddata->DvdRpc0LicenseFailure) { + TraceLog((CdromDebugWarning, + "DVD License failure. Refusing to pick a region\n")); + InterlockedExchange(&cddata->PickDvdRegion, 0); + return; + } + + + ULONG a, b; + + a = max(sizeof(DVD_DESCRIPTOR_HEADER) + + sizeof(DVD_COPYRIGHT_DESCRIPTOR), + sizeof(DVD_READ_STRUCTURE) + ); + b = max(DVD_RPC_KEY_LENGTH, + DVD_SET_RPC_KEY_LENGTH + ); + bufferLen = max(a, b); + + dvdReadStructure = (PDVD_READ_STRUCTURE) + ExAllocatePoolWithTag(PagedPool, bufferLen, DVD_TAG_DVD_REGION); + + if (dvdReadStructure == NULL) { + InterlockedExchange(&cddata->PickDvdRegion, pickDvdRegion); + return; + } + + if (cddata->DvdRpc0Device && cddata->Rpc0RetryRegistryCallback) { + + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): now retrying RPC0 callback\n", + Fdo)); + + // + // get the registry settings again + // + + ioStatus.Status = CdRomGetRpc0Settings(Fdo); + + if (ioStatus.Status == STATUS_LICENSE_VIOLATION) { + + // + // if this is the returned error, then + // the routine should have set this! + // + + ASSERT(cddata->DvdRpc0LicenseFailure); + cddata->DvdRpc0LicenseFailure = 1; + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): " + "setting to fail all dvd ioctls due to CSS licensing " + "failure.\n", Fdo)); + + pickDvdRegion = 0; + goto getout; + + } + + // + // get the device region, again + // + + copyProtectKey = (PDVD_COPY_PROTECT_KEY)dvdReadStructure; + RtlZeroMemory(copyProtectKey, bufferLen); + copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH; + copyProtectKey->KeyType = DvdGetRpcKey; + + // + // Build a request for READ_KEY + // + + ClassSendDeviceIoControlSynchronous( + IOCTL_DVD_READ_KEY, + Fdo, + copyProtectKey, + DVD_RPC_KEY_LENGTH, + DVD_RPC_KEY_LENGTH, + FALSE, + &ioStatus); + + if (!NT_SUCCESS(ioStatus.Status)) { + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion: Unable to get " + "device RPC data (%x)\n", ioStatus.Status)); + pickDvdRegion = 0; + goto getout; + } + + // + // now that we have gotten the device's RPC data, + // we have set the device extension to usable data. + // no need to call back into this section of code again + // + + cddata->Rpc0RetryRegistryCallback = 0; + + + rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData; + + // + // TypeCode of zero means that no region has been set. + // + + if (rpcKey->TypeCode != 0) { + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): DVD Region already " + "chosen\n", Fdo)); + pickDvdRegion = 0; + goto getout; + } + + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): must choose initial DVD " + " Region\n", Fdo)); + } + + + + copyProtectKey = (PDVD_COPY_PROTECT_KEY) dvdReadStructure; + + dvdCopyRight = (PDVD_COPYRIGHT_DESCRIPTOR) + ((PDVD_DESCRIPTOR_HEADER) dvdReadStructure)->Data; + + // + // get the media region + // + + RtlZeroMemory (dvdReadStructure, bufferLen); + dvdReadStructure->Format = DvdCopyrightDescriptor; + + // + // Build and send a request for READ_KEY + // + + TraceLog((CdromDebugTrace, + "CdRomPickDvdRegion (%p): Getting Copyright Descriptor\n", + Fdo)); + + ClassSendDeviceIoControlSynchronous( + IOCTL_DVD_READ_STRUCTURE, + Fdo, + dvdReadStructure, + sizeof(DVD_READ_STRUCTURE), + sizeof (DVD_DESCRIPTOR_HEADER) + + sizeof(DVD_COPYRIGHT_DESCRIPTOR), + FALSE, + &ioStatus + ); + TraceLog((CdromDebugTrace, + "CdRomPickDvdRegion (%p): Got Copyright Descriptor %x\n", + Fdo, ioStatus.Status)); + + if ((NT_SUCCESS(ioStatus.Status)) && + (dvdCopyRight->CopyrightProtectionType == 0x01) + ) { + + // + // keep the media region bitmap around + // a 1 means ok to play + // + + if (dvdCopyRight->RegionManagementInformation == 0xff) { + TraceLog((CdromDebugError, + "CdRomPickDvdRegion (%p): RegionManagementInformation " + "is set to dis-allow playback for all regions. This is " + "most likely a poorly authored disc. defaulting to all " + "region disc for purpose of choosing initial region\n", + Fdo)); + dvdCopyRight->RegionManagementInformation = 0; + } + + + mediaRegion = ~dvdCopyRight->RegionManagementInformation; + + } else { + + // + // could be media, can't set the device region + // + + if (!cddata->DvdRpc0Device) { + + // + // can't automatically pick a default region on a rpc2 drive + // without media, so just exit + // + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): failed to auto-choose " + "a region due to status %x getting copyright " + "descriptor\n", Fdo, ioStatus.Status)); + goto getout; + + } else { + + // + // for an RPC0 drive, we can try to pick a region for + // the drive + // + + mediaRegion = 0x0; + } + + } + + // + // get the device region + // + + RtlZeroMemory (copyProtectKey, bufferLen); + copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH; + copyProtectKey->KeyType = DvdGetRpcKey; + + // + // Build and send a request for READ_KEY for RPC key + // + + TraceLog((CdromDebugTrace, + "CdRomPickDvdRegion (%p): Getting RpcKey\n", + Fdo)); + ClassSendDeviceIoControlSynchronous( + IOCTL_DVD_READ_KEY, + Fdo, + copyProtectKey, + DVD_RPC_KEY_LENGTH, + DVD_RPC_KEY_LENGTH, + FALSE, + &ioStatus + ); + TraceLog((CdromDebugTrace, + "CdRomPickDvdRegion (%p): Got RpcKey %x\n", + Fdo, ioStatus.Status)); + + if (!NT_SUCCESS(ioStatus.Status)) { + + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): failed to get RpcKey from " + "a DVD Device\n", Fdo)); + goto getout; + + } + + // + // so we now have what we can get for the media region and the + // drive region. we will not set a region if the drive has one + // set already (mask is not all 1's), nor will we set a region + // if there are no more user resets available. + // + + rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData; + + + if (rpcKey->RegionMask != 0xff) { + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): not picking a region since " + "it is already chosen\n", Fdo)); + goto getout; + } + + if (rpcKey->UserResetsAvailable <= 1) { + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): not picking a region since " + "only one change remains\n", Fdo)); + goto getout; + } + + defaultDvdRegion = 0; + + // + // the proppage dvd class installer sets + // this key based upon the system locale + // + + CdRomGetDeviceParameter ( + Fdo, + DVD_DEFAULT_REGION, + &defaultDvdRegion + ); + + if (defaultDvdRegion > DVD_MAX_REGION) { + + // + // the registry has a bogus default + // + + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): registry has a bogus default " + "region value of %x\n", Fdo, defaultDvdRegion)); + defaultDvdRegion = 0; + + } + + // + // if defaultDvdRegion == 0, it means no default. + // + + // + // we will select the initial dvd region for the user + // + + if ((defaultDvdRegion != 0) && + (mediaRegion & + (1 << (defaultDvdRegion - 1)) + ) + ) { + + // + // first choice: + // the media has region that matches + // the default dvd region. + // + + dvdRegion = (1 << (defaultDvdRegion - 1)); + + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): Choice #1: media matches " + "drive's default, chose region %x\n", Fdo, dvdRegion)); + + + } else if (mediaRegion) { + + // + // second choice: + // pick the lowest region number + // from the media + // + + UCHAR mask; + + mask = 1; + dvdRegion = 0; + while (mediaRegion && !dvdRegion) { + + // + // pick the lowest bit + // + dvdRegion = mediaRegion & mask; + mask <<= 1; + } + + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): Choice #2: choosing lowest " + "media region %x\n", Fdo, dvdRegion)); + + } else if (defaultDvdRegion) { + + // + // third choice: + // default dvd region from the dvd class installer + // + + dvdRegion = (1 << (defaultDvdRegion - 1)); + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): Choice #3: using default " + "region for this install %x\n", Fdo, dvdRegion)); + + } else { + + // + // unable to pick one for the user -- this should rarely + // happen, since the proppage dvd class installer sets + // the key based upon the system locale + // + TraceLog((CdromDebugWarning, + "CdRomPickDvdRegion (%p): Choice #4: failed to choose " + "a media region\n", Fdo)); + goto getout; + + } + + // + // now that we've chosen a region, set the region by sending the + // appropriate request to the drive + // + + RtlZeroMemory (copyProtectKey, bufferLen); + copyProtectKey->KeyLength = DVD_SET_RPC_KEY_LENGTH; + copyProtectKey->KeyType = DvdSetRpcKey; + dvdRpcKey = (PDVD_SET_RPC_KEY) copyProtectKey->KeyData; + dvdRpcKey->PreferredDriveRegionCode = (UCHAR) ~dvdRegion; + + // + // Build and send request for SEND_KEY + // + TraceLog((CdromDebugTrace, + "CdRomPickDvdRegion (%p): Sending new Rpc Key to region %x\n", + Fdo, dvdRegion)); + + ClassSendDeviceIoControlSynchronous( + IOCTL_DVD_SEND_KEY2, + Fdo, + copyProtectKey, + DVD_SET_RPC_KEY_LENGTH, + 0, + FALSE, + &ioStatus); + TraceLog((CdromDebugTrace, + "CdRomPickDvdRegion (%p): Sent new Rpc Key %x\n", + Fdo, ioStatus.Status)); + + if (!NT_SUCCESS(ioStatus.Status)) { + DebugPrint ((1, "CdRomPickDvdRegion (%p): unable to set dvd initial " + " region code (%p)\n", Fdo, ioStatus.Status)); + } else { + DebugPrint ((1, "CdRomPickDvdRegion (%p): Successfully set dvd " + "initial region\n", Fdo)); + pickDvdRegion = 0; + } + +getout: + if (dvdReadStructure) { + ExFreePool (dvdReadStructure); + } + + // + // update the new PickDvdRegion value + // + + InterlockedExchange(&cddata->PickDvdRegion, pickDvdRegion); + + return; +} + + +NTSTATUS +CdRomRetryRequest( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PIRP Irp, + IN ULONG Delay, + IN BOOLEAN ResendIrp + ) +{ + PCDROM_DATA cdData; + KIRQL oldIrql; + + if(Delay == 0) { + return CdRomRerunRequest(FdoExtension, Irp, ResendIrp); + } + + cdData = FdoExtension->CommonExtension.DriverData; + + KeAcquireSpinLock(&(cdData->DelayedRetrySpinLock), &oldIrql); + + ASSERT(cdData->DelayedRetryIrp == NULL); + ASSERT(cdData->DelayedRetryInterval == 0); + + cdData->DelayedRetryIrp = Irp; + cdData->DelayedRetryInterval = Delay; + cdData->DelayedRetryResend = ResendIrp; + + KeReleaseSpinLock(&(cdData->DelayedRetrySpinLock), oldIrql); + + return STATUS_PENDING; +} + + +NTSTATUS +CdRomRerunRequest( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN OPTIONAL PIRP Irp, + IN BOOLEAN ResendIrp + ) +{ + if(ResendIrp) { + return IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, + Irp); + } else { + KIRQL oldIrql; + + oldIrql = KeRaiseIrqlToDpcLevel(); + CdRomStartIo(FdoExtension->DeviceObject, Irp); + KeLowerIrql(oldIrql); + return STATUS_MORE_PROCESSING_REQUIRED; + } +} + + +/*++ + +Routine Description: + + This routine just checks for media change sense/asc/ascq and + also for other events, such as bus resets. this is used to + determine if the device behaviour has changed, to allow for + read and write operations to be allowed and/or disallowed. + +Arguments: + + ISSUE-2000/3/30-henrygab - not fully doc'd + +Return Value: + + NTSTATUS + +--*/ +NTSTATUS +CdRomMmcErrorHandler( + IN PDEVICE_OBJECT Fdo, + IN PSCSI_REQUEST_BLOCK Srb, + OUT PNTSTATUS Status, + OUT PBOOLEAN Retry + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + BOOLEAN queryCapabilities = FALSE; + + if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) { + + PCDROM_DATA cddata = (PCDROM_DATA)commonExtension->DriverData; + PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer; + + // + // the following sense keys could indicate a change in + // capabilities. + // + + // + // we used to expect this to be serialized, and only hit from our + // own routine. we now allow some requests to continue during our + // processing of the capabilities update in order to allow + // IoReadPartitionTable() to succeed. + // + + switch (senseBuffer->SenseKey & 0xf) { + + case SCSI_SENSE_NOT_READY: { + if (senseBuffer->AdditionalSenseCode == + SCSI_ADSENSE_NO_MEDIA_IN_DEVICE) { + + if (cddata->Mmc.WriteAllowed) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromErrorHandler: media removed, writes will be " + "failed until new media detected\n")); + } + + // NOTE - REF #0002 + cddata->Mmc.WriteAllowed = FALSE; + } else + if ((senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) && + (senseBuffer->AdditionalSenseCodeQualifier == + SCSI_SENSEQ_BECOMING_READY)) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromErrorHandler: media becoming ready, " + "SHOULD notify shell of change time by sending " + "GESN request immediately!\n")); + } + break; + } // end SCSI_SENSE_NOT_READY + + case SCSI_SENSE_UNIT_ATTENTION: { + switch (senseBuffer->AdditionalSenseCode) { + case SCSI_ADSENSE_MEDIUM_CHANGED: { + + // + // always update if the medium may have changed + // + + // NOTE - REF #0002 + cddata->Mmc.WriteAllowed = FALSE; + InterlockedCompareExchange(&(cddata->Mmc.UpdateState), + CdromMmcUpdateRequired, + CdromMmcUpdateComplete); + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromErrorHandler: media change detected, need to " + "update drive capabilities\n")); + break; + + } // end SCSI_ADSENSE_MEDIUM_CHANGED + + case SCSI_ADSENSE_BUS_RESET: { + + // NOTE - REF #0002 + cddata->Mmc.WriteAllowed = FALSE; + InterlockedCompareExchange(&(cddata->Mmc.UpdateState), + CdromMmcUpdateRequired, + CdromMmcUpdateComplete); + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromErrorHandler: bus reset detected, need to " + "update drive capabilities\n")); + break; + + } // end SCSI_ADSENSE_BUS_RESET + + case SCSI_ADSENSE_OPERATOR_REQUEST: { + + BOOLEAN b = FALSE; + + switch (senseBuffer->AdditionalSenseCodeQualifier) { + case SCSI_SENSEQ_MEDIUM_REMOVAL: { + + // + // eject notification currently handled by classpnp + // + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromErrorHandler: Eject requested by user\n")); + *Retry = TRUE; + *Status = STATUS_DEVICE_BUSY; + break; + } + + case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: + b = TRUE; + case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: { + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromErrorHandler: Write protect %s requested " + "by user\n", + (b ? "disable" : "enable"))); + *Retry = TRUE; + *Status = STATUS_DEVICE_BUSY; + // NOTE - REF #0002 + cddata->Mmc.WriteAllowed = FALSE; + InterlockedCompareExchange(&(cddata->Mmc.UpdateState), + CdromMmcUpdateRequired, + CdromMmcUpdateComplete); + + } + + } // end of AdditionalSenseCodeQualifier switch + + + break; + + } // end SCSI_ADSENSE_OPERATOR_REQUEST + + default: { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromErrorHandler: Unit attention %02x/%02x\n", + senseBuffer->AdditionalSenseCode, + senseBuffer->AdditionalSenseCodeQualifier)); + break; + } + + } // end of AdditionSenseCode switch + break; + + } // end SCSI_SENSE_UNIT_ATTENTION + + case SCSI_SENSE_ILLEGAL_REQUEST: { + if (senseBuffer->AdditionalSenseCode == + SCSI_ADSENSE_WRITE_PROTECT) { + + if (cddata->Mmc.WriteAllowed) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromErrorHandler: media was writable, but " + "failed request with WRITE_PROTECT error...\n")); + } + // NOTE - REF #0002 + // do not update all the capabilities just because + // we can't write to the disc. + cddata->Mmc.WriteAllowed = FALSE; + } + break; + } // end SCSI_SENSE_ILLEGAL_REQUEST + + } // end of SenseKey switch + + } // end of SRB_STATUS_AUTOSENSE_VALID + + return STATUS_SUCCESS; +} + +/*++ + +Routine Description: + + This routine checks for a device-specific error handler + and calls it if it exists. This allows multiple drives + that require their own error handler to co-exist. + +--*/ +VOID +CdRomErrorHandler( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PCDROM_DATA cddata = (PCDROM_DATA)commonExtension->DriverData; + PSENSE_DATA sense = Srb->SenseInfoBuffer; + + if ((Srb->SenseInfoBufferLength >= + RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA,AdditionalSenseCodeQualifier)) && + TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) { + + // + // Many non-WHQL certified drives (mostly CD-RW) return + // 2/4/0 when they have no media instead of the obvious + // choice of: + // + // SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE + // + // These drives should not pass WHQL certification due + // to this discrepency. + // + // However, we have to retry on 2/4/0 (Not ready, LUN not ready, + // no info) and also 3/2/0 (no seek complete). + // + // These conditions occur when the shell tries to examine an + // injected CD (e.g. for autoplay) before the CD is spun up. + // + // The drive should be returning an ASCQ of SCSI_SENSEQ_BECOMING_READY + // (0x01) in order to comply with WHQL standards. + // + // The default retry timeout of one second is acceptable to balance + // these discrepencies. don't modify the status, though.... + // + + if (((sense->SenseKey & 0xf) == SCSI_SENSE_NOT_READY) && + (sense->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) && + (sense->AdditionalSenseCodeQualifier == SCSI_SENSEQ_CAUSE_NOT_REPORTABLE) + ) { + + *Retry = TRUE; + + } else if (((sense->SenseKey & 0xf) == SCSI_SENSE_MEDIUM_ERROR) && + (sense->AdditionalSenseCode == 0x2) && + (sense->AdditionalSenseCodeQualifier == 0x0) + ) { + + *Retry = TRUE; + + } else if ((sense->AdditionalSenseCode == 0x57) && + (sense->AdditionalSenseCodeQualifier == 0x00) + ) { + + // + // UNABLE_TO_RECOVER_TABLE_OF_CONTENTS + // the Matshita CR-585 returns this for all read commands + // on blank CD-R and CD-RW media, and we need to handle + // this for READ_CD detection ability. + // + + *Retry = FALSE; + *Status = STATUS_UNRECOGNIZED_MEDIA; + + } + + } + + // + // tail recursion in both cases takes no stack + // + + if (cddata->ErrorHandler) { + cddata->ErrorHandler(DeviceObject, Srb, Status, Retry); + } + return; +} + + +/*++ + +Routine Description: + + This routine is called for a shutdown and flush IRPs. + These are sent by the system before it actually shuts + down or when the file system does a flush. + +Arguments: + + DriverObject - Pointer to device object to being shutdown by system. + + Irp - IRP involved. + +Return Value: + + NT Status + +--*/ +NTSTATUS +CdRomShutdownFlush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + +} + +/*++ + +Routine Description: + + This routine is called for intermediate work a shutdown or + flush IRPs would need to do. We just want to free our resources + and return STATUS_MORE_PROCESSING_REQUIRED. + +Arguments: + + DeviceObject - NULL? + + Irp - IRP to free + + Context - NULL + +Return Value: + + NT Status + +--*/ +NTSTATUS +CdRomShutdownFlushCompletion( + IN PDEVICE_OBJECT Fdo, + IN PIRP NewIrp, + IN PIRP OriginalIrp + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PIO_STACK_LOCATION originalIrpStack; + ULONG_PTR iteration; + NTSTATUS status = STATUS_SUCCESS; + + ASSERT(OriginalIrp); + + originalIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp); + + // + // always use a new irp so we can call + // CdRomCompleteIrpAndStartNextPacketSafely() from this routine. + // + + if (NewIrp != NULL) { + status = NewIrp->IoStatus.Status; + IoFreeIrp(NewIrp); + NewIrp = NULL; + } + + if (!NT_SUCCESS(status)) { + BAIL_OUT(OriginalIrp); + goto SafeExit; + } + + // + // the current irpstack saves the counter which states + // what part of the multi-part shutdown or flush we are in. + // + + iteration = (ULONG_PTR)originalIrpStack->Parameters.Others.Argument1; + iteration++; + originalIrpStack->Parameters.Others.Argument1 = (PVOID)iteration; + + switch (iteration) { + case 2: + if (originalIrpStack->MajorFunction != IRP_MJ_SHUTDOWN) { + // + // then we don't want to send the unlock command + // the incrementing of the state was done above. + // return the completion routine's result. + // + return CdRomShutdownFlushCompletion(Fdo, NULL, OriginalIrp); + } + // else fall through.... + + case 1: { + + PIRP newIrp = NULL; + PSCSI_REQUEST_BLOCK newSrb = NULL; + PCDB newCdb = NULL; + PIO_STACK_LOCATION newIrpStack = NULL; + ULONG isRemoved; + + newIrp = IoAllocateIrp((CCHAR)(Fdo->StackSize+1), FALSE); + if (newIrp == NULL) { + BAIL_OUT(OriginalIrp); + status = STATUS_INSUFFICIENT_RESOURCES; + goto SafeExit; + } + newSrb = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SCSI_REQUEST_BLOCK), + CDROM_TAG_SRB); + if (newSrb == NULL) { + IoFreeIrp(newIrp); + BAIL_OUT(OriginalIrp); + status = STATUS_INSUFFICIENT_RESOURCES; + goto SafeExit; + } + + // + // ClassIoComplete will free the SRB, but we need a routine + // that will free the irp. then just call ClassSendAsync, + // and don't care about the return value, since the completion + // routine will be called anyways. + // + + IoSetNextIrpStackLocation(newIrp); + newIrpStack = IoGetCurrentIrpStackLocation(newIrp); + newIrpStack->DeviceObject = Fdo; + IoSetCompletionRoutine(newIrp, + CdRomShutdownFlushCompletion, + OriginalIrp, + TRUE, TRUE, TRUE); + IoSetNextIrpStackLocation(newIrp); + newIrpStack = IoGetCurrentIrpStackLocation(newIrp); + newIrpStack->DeviceObject = Fdo; + + // + // setup the request + // + + RtlZeroMemory(newSrb, sizeof(SCSI_REQUEST_BLOCK)); + newCdb = (PCDB)(newSrb->Cdb); + + newSrb->QueueTag = SP_UNTAGGED; + newSrb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + newSrb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // tell classpnp not to call StartNextPacket() + // + + newSrb->SrbFlags = SRB_FLAGS_DONT_START_NEXT_PACKET; + + if (iteration == 1) { + + // + // first synchronize the cache + // + + newSrb->TimeOutValue = fdoExtension->TimeOutValue * 4; + newSrb->CdbLength = 10; + newCdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE; + + } else if (iteration == 2) { + + // + // then unlock the medium + // + + ASSERT( originalIrpStack->MajorFunction == IRP_MJ_SHUTDOWN ); + + newSrb->TimeOutValue = fdoExtension->TimeOutValue; + newSrb->CdbLength = 6; + newCdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; + newCdb->MEDIA_REMOVAL.Prevent = FALSE; + + } + + + isRemoved = ClassAcquireRemoveLock(Fdo, newIrp); + if (isRemoved) { + IoFreeIrp(newIrp); + ExFreePool(newSrb); + ClassReleaseRemoveLock(Fdo, newIrp); + BAIL_OUT(OriginalIrp); + status = STATUS_DEVICE_DOES_NOT_EXIST; + goto SafeExit; + } + ClassSendSrbAsynchronous(Fdo, newSrb, newIrp, NULL, 0, FALSE); + break; + } + + case 3: { + + PSCSI_REQUEST_BLOCK srb; + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(OriginalIrp); + + // + // forward this request to the device appropriately, + // don't use this completion routine anymore... + // + + srb = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SCSI_REQUEST_BLOCK), + CDROM_TAG_SRB); + if (srb == NULL) { + BAIL_OUT(OriginalIrp); + status = STATUS_INSUFFICIENT_RESOURCES; + goto SafeExit; + } + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->TimeOutValue = fdoExtension->TimeOutValue * 4; + srb->QueueTag = SP_UNTAGGED; + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->SrbFlags = fdoExtension->SrbFlags; + srb->CdbLength = 0; + srb->OriginalRequest = OriginalIrp; + + if (originalIrpStack->MajorFunction == IRP_MJ_SHUTDOWN) { + srb->Function = SRB_FUNCTION_SHUTDOWN; + } else { + srb->Function = SRB_FUNCTION_FLUSH; + } + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(OriginalIrp, + ClassIoComplete, + srb, + TRUE, TRUE, TRUE); + + // + // Set the retry count to zero. + // + + originalIrpStack->Parameters.Others.Argument4 = (PVOID) 0; + + // + // Get next stack location and set major function code. + // + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Set up SRB for execute scsi request. + // Save SRB address in next stack for port driver. + // + + nextIrpStack->Parameters.Scsi.Srb = srb; + + // + // Call the port driver to process the request. + // + + IoCallDriver(commonExtension->LowerDeviceObject, OriginalIrp); + + break; + + } + default: { + ASSERT(FALSE); + break; + } + + } // end switch + + status = STATUS_SUCCESS; + +SafeExit: + + if (!NT_SUCCESS(status)) { + OriginalIrp->IoStatus.Status = status; + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp); + } + + // + // always return STATUS_MORE_PROCESSING_REQUIRED, so noone else tries + // to access the new irp that we free'd.... + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // end CdromShutdownFlush() + + +VOID +CdromFakePartitionInfo( + IN PCOMMON_DEVICE_EXTENSION CommonExtension, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG ioctl = currentIrpStack->Parameters.DeviceIoControl.IoControlCode; + PVOID systemBuffer = Irp->AssociatedIrp.SystemBuffer; + + ASSERT(systemBuffer); + + if ((ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT) && + (ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT_EX) && + (ioctl != IOCTL_DISK_GET_PARTITION_INFO) && + (ioctl != IOCTL_DISK_GET_PARTITION_INFO_EX)) { + TraceLog((CdromDebugError, + "CdromFakePartitionInfo: unhandled ioctl %x\n", ioctl)); + Irp->IoStatus.Status = STATUS_INTERNAL_ERROR; + Irp->IoStatus.Information = 0; + CdRomCompleteIrpAndStartNextPacketSafely(CommonExtension->DeviceObject, + Irp); + return; + } + + // + // nothing to fail from this point on, so set the size appropriately + // and set irp's status to success. + // + + TraceLog((CdromDebugWarning, + "CdromFakePartitionInfo: incoming ioctl %x\n", ioctl)); + + + Irp->IoStatus.Status = STATUS_SUCCESS; + switch (ioctl) { + case IOCTL_DISK_GET_DRIVE_LAYOUT: + Irp->IoStatus.Information = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, + PartitionEntry[1]); + RtlZeroMemory(systemBuffer, FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, + PartitionEntry[1])); + break; + case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: + Irp->IoStatus.Information = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, + PartitionEntry[1]); + RtlZeroMemory(systemBuffer, FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, + PartitionEntry[1])); + break; + case IOCTL_DISK_GET_PARTITION_INFO: + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); + RtlZeroMemory(systemBuffer, sizeof(PARTITION_INFORMATION)); + break; + case IOCTL_DISK_GET_PARTITION_INFO_EX: + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX); + RtlZeroMemory(systemBuffer, sizeof(PARTITION_INFORMATION_EX)); + break; + default: + ASSERT(!"Invalid ioctl should not have reached this point\n"); + break; + } + + // + // if we are getting the drive layout, then we need to start by + // adding some of the non-partition stuff that says we have + // exactly one partition available. + // + + + if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT) { + + PDRIVE_LAYOUT_INFORMATION layout; + layout = (PDRIVE_LAYOUT_INFORMATION)systemBuffer; + layout->PartitionCount = 1; + layout->Signature = 1; + systemBuffer = (PVOID)(layout->PartitionEntry); + ioctl = IOCTL_DISK_GET_PARTITION_INFO; + + } else if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT_EX) { + + PDRIVE_LAYOUT_INFORMATION_EX layoutEx; + layoutEx = (PDRIVE_LAYOUT_INFORMATION_EX)systemBuffer; + layoutEx->PartitionStyle = PARTITION_STYLE_MBR; + layoutEx->PartitionCount = 1; + layoutEx->Mbr.Signature = 1; + systemBuffer = (PVOID)(layoutEx->PartitionEntry); + ioctl = IOCTL_DISK_GET_PARTITION_INFO_EX; + + } + + // + // NOTE: the local var 'ioctl' is now modified to either EX or + // non-EX version. the local var 'systemBuffer' is now pointing + // to the partition information structure. + // + + if (ioctl == IOCTL_DISK_GET_PARTITION_INFO) { + + PPARTITION_INFORMATION partitionInfo; + partitionInfo = (PPARTITION_INFORMATION)systemBuffer; + partitionInfo->RewritePartition = FALSE; + partitionInfo->RecognizedPartition = TRUE; + partitionInfo->PartitionType = PARTITION_FAT32; + partitionInfo->BootIndicator = FALSE; + partitionInfo->HiddenSectors = 0; + partitionInfo->StartingOffset.QuadPart = 0; + partitionInfo->PartitionLength = CommonExtension->PartitionLength; + partitionInfo->PartitionNumber = 0; + + } else { + + PPARTITION_INFORMATION_EX partitionInfo; + partitionInfo = (PPARTITION_INFORMATION_EX)systemBuffer; + partitionInfo->PartitionStyle = PARTITION_STYLE_MBR; + partitionInfo->RewritePartition = FALSE; + partitionInfo->Mbr.RecognizedPartition = TRUE; + partitionInfo->Mbr.PartitionType = PARTITION_FAT32; + partitionInfo->Mbr.BootIndicator = FALSE; + partitionInfo->Mbr.HiddenSectors = 0; + partitionInfo->StartingOffset.QuadPart = 0; + partitionInfo->PartitionLength = CommonExtension->PartitionLength; + partitionInfo->PartitionNumber = 0; + + } + TraceLog((CdromDebugWarning, + "CdromFakePartitionInfo: finishing ioctl %x\n", + currentIrpStack->Parameters.DeviceIoControl.IoControlCode)); + + // + // complete the irp + // + + CdRomCompleteIrpAndStartNextPacketSafely(CommonExtension->DeviceObject, + Irp); + return; + +} + + diff --git a/drivers/storage/class/cdrom_new/cdrom.h b/drivers/storage/class/cdrom_new/cdrom.h new file mode 100644 index 00000000000..7b1a865dcf3 --- /dev/null +++ b/drivers/storage/class/cdrom_new/cdrom.h @@ -0,0 +1,823 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + cdromp.h + +Abstract: + + Private header file for cdrom.sys. This contains private + structure and function declarations as well as constant + values which do not need to be exported. + +Author: + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#ifndef __CDROMP_H__ +#define __CDROMP_H__ + +#include "ntddmmc.h" +#include "trace.h" + +extern CLASSPNP_SCAN_FOR_SPECIAL_INFO CdromHackItems[]; + +typedef enum { + CdromDebugError = 0, // always printed + CdromDebugWarning = 1, // set bit 0x00000001 in nt!kd_cdrom_mask + CdromDebugTrace = 2, // set bit 0x00000002 in nt!kd_cdrom_mask + CdromDebugInfo = 3, // set bit 0x00000004 in nt!kd_cdrom_mask +#if 0 + CdromDebug = z, // set bit 0x00000000 in nt!kd_cdrom_mask + CdromDebug = z, // set bit 0x00000000 in nt!kd_cdrom_mask + CdromDebug = z, // set bit 0x00000000 in nt!kd_cdrom_mask + CdromDebug = z, // set bit 0x00000000 in nt!kd_cdrom_mask +#endif + CdromDebugFeatures = 32 // set bit 0x80000000 in nt!kd_cdrom_mask +}; + +#define CDROM_GET_CONFIGURATION_TIMEOUT (0x4) + +#define CDROM_HACK_DEC_RRD (0x00000001) +#define CDROM_HACK_FUJITSU_FMCD_10x (0x00000002) +#define CDROM_HACK_HITACHI_1750 (0x00000004) +#define CDROM_HACK_HITACHI_GD_2000 (0x00000008) +#define CDROM_HACK_TOSHIBA_SD_W1101 (0x00000010) +#define CDROM_HACK_TOSHIBA_XM_3xx (0x00000020) +#define CDROM_HACK_NEC_CDDA (0x00000040) +#define CDROM_HACK_PLEXTOR_CDDA (0x00000080) +#define CDROM_HACK_BAD_GET_CONFIG_SUPPORT (0x00000100) +#define CDROM_HACK_FORCE_READ_CD_DETECTION (0x00000200) +#define CDROM_HACK_READ_CD_SUPPORTED (0x00000400) +#define CDROM_HACK_LOCKED_PAGES (0x80000000) // not a valid flag to save + +#define CDROM_HACK_VALID_FLAGS (0x000007ff) +#define CDROM_HACK_INVALID_FLAGS (~CDROM_HACK_VALID_FLAGS) + + +typedef struct _XA_CONTEXT { + + // + // Pointer to the device object. + // + + PDEVICE_OBJECT DeviceObject; + + // + // Pointer to the original request when + // a mode select must be sent. + // + + PIRP OriginalRequest; + + // + // Pointer to the mode select srb. + // + + PSCSI_REQUEST_BLOCK Srb; +} XA_CONTEXT, *PXA_CONTEXT; + +typedef struct _ERROR_RECOVERY_DATA { + MODE_PARAMETER_HEADER Header; + MODE_PARAMETER_BLOCK BlockDescriptor; + MODE_READ_RECOVERY_PAGE ReadRecoveryPage; +} ERROR_RECOVERY_DATA, *PERROR_RECOVERY_DATA; + +typedef struct _ERROR_RECOVERY_DATA10 { + MODE_PARAMETER_HEADER10 Header10; + MODE_PARAMETER_BLOCK BlockDescriptor10; + MODE_READ_RECOVERY_PAGE ReadRecoveryPage10; +} ERROR_RECOVERY_DATA10, *PERROR_RECOVERY_DATA10; + +// +// CdRom specific addition to device extension. +// + +typedef struct _CDROM_DRIVER_EXTENSION { + ULONG InterlockedCdRomCounter; + PVOID Reserved[3]; +} CDROM_DRIVER_EXTENSION, *PCDROM_DRIVER_EXTENSION; + +#define CdromMmcUpdateComplete 0 +#define CdromMmcUpdateRequired 1 +#define CdromMmcUpdateStarted 2 + +typedef struct _CDROM_MMC_EXTENSION { + + ULONG IsMmc; // allow quick checks + ULONG WriteAllowed; + + LONG UpdateState; + + SLIST_HEADER DelayedIrps; // irps delayed due to + KSPIN_LOCK DelayedLock; // lock for delayed irps + + PIO_WORKITEM CapabilitiesWorkItem; + PIRP CapabilitiesIrp; + PMDL CapabilitiesMdl; + PGET_CONFIGURATION_HEADER CapabilitiesBuffer; + ULONG CapabilitiesBufferSize; + KEVENT CapabilitiesEvent; + SCSI_REQUEST_BLOCK CapabilitiesSrb; + +} CDROM_MMC_EXTENSION, *PCDROM_MMC_EXTENSION; + + +#define CDROM_DRIVER_EXTENSION_ID CdRomAddDevice + +typedef struct _CDROM_DATA { + + // + // Pointer to the cdrom driver extension + // + + PCDROM_DRIVER_EXTENSION DriverExtension; + + + // + // These bits allow detection of when to requery the + // drive's capabilities. + // + + CDROM_MMC_EXTENSION Mmc; + + // + // hack flags for ScanForSpecial routines + // + + ULONG_PTR HackFlags; + + // + // the error handling routines need to be per-device, + // not per-driver.... + // + + PCLASS_ERROR ErrorHandler; + + // + // Indicates whether an audio play operation + // is currently being performed. + // Only thing this does is prevent reads and + // toc requests while playing audio. + // + + BOOLEAN PlayActive; + + // + // Indicates whether the blocksize used for user data + // is 2048 or 2352. + // + + BOOLEAN RawAccess; + + // + // Indicates that this is a DEC RRD cdrom. + // This drive requires software to fix responses + // from the faulty firmware + // + + BOOLEAN IsDecRrd; + + // + // This points to an irp which needs to be delayed for a bit before a + // retry can be attempted. The interval counter is set by the deferring + // routine and will be decremented to zero in the tick handler. Once + // the counter goes to zero the irp will be issued again. + // DelayedRetryResend controls whether the irp is resent to the lower + // driver (TRUE) or reissued into the startio routine (FALSE) + // + + BOOLEAN DelayedRetryResend; + + PIRP DelayedRetryIrp; + + ULONG DelayedRetryInterval; + + KSPIN_LOCK DelayedRetrySpinLock; + + // + // indicate we need to pick a default dvd region + // for the user if we can + // + + ULONG PickDvdRegion; + + // + // The interface strings registered for this device. + // + + UNICODE_STRING CdromInterfaceString; + UNICODE_STRING VolumeInterfaceString; + + // + // The well known name link for this device. + // + + UNICODE_STRING WellKnownName; + + // + // Indicates whether 6 or 10 bytes mode sense/select + // should be used + // + + ULONG XAFlags; + + // + // keep track of what type of DVD device we are + // + + BOOLEAN DvdRpc0Device; + BOOLEAN DvdRpc0LicenseFailure; + UCHAR Rpc0SystemRegion; // bitmask, one means prevent play + UCHAR Rpc0SystemRegionResetCount; + + ULONG Rpc0RetryRegistryCallback; // one until initial region choosen + + KMUTEX Rpc0RegionMutex; + + // + // Storage for the error recovery page. This is used + // as an easy method to switch block sizes. + // + // NOTE - doubly unnamed structs just aren't very clean looking code - this + // should get cleaned up at some point in the future. + // + + union { + ERROR_RECOVERY_DATA; + ERROR_RECOVERY_DATA10; + }; + +} CDROM_DATA, *PCDROM_DATA; + +#define DEVICE_EXTENSION_SIZE sizeof(FUNCTIONAL_DEVICE_EXTENSION) + sizeof(CDROM_DATA) +#define SCSI_CDROM_TIMEOUT 10 +#define SCSI_CHANGER_BONUS_TIMEOUT 10 +#define HITACHI_MODE_DATA_SIZE 12 +#define MODE_DATA_SIZE 64 +#define RAW_SECTOR_SIZE 2352 +#define COOKED_SECTOR_SIZE 2048 +#define CDROM_SRB_LIST_SIZE 4 + +#define PLAY_ACTIVE(x) (((PCDROM_DATA)(x->CommonExtension.DriverData))->PlayActive) + +#define MSF_TO_LBA(Minutes,Seconds,Frames) \ + (ULONG)((60 * 75 * (Minutes)) + (75 * (Seconds)) + ((Frames) - 150)) + +#define LBA_TO_MSF(Lba,Minutes,Seconds,Frames) \ +{ \ + (Minutes) = (UCHAR)(Lba / (60 * 75)); \ + (Seconds) = (UCHAR)((Lba % (60 * 75)) / 75); \ + (Frames) = (UCHAR)((Lba % (60 * 75)) % 75); \ +} + +#define DEC_TO_BCD(x) (((x / 10) << 4) + (x % 10)) + +// +// Define flags for XA, CDDA, and Mode Select/Sense +// + +#define XA_USE_6_BYTE 0x01 +#define XA_USE_10_BYTE 0x02 + +#define XA_NOT_SUPPORTED 0x10 +#define XA_USE_READ_CD 0x20 +#define XA_PLEXTOR_CDDA 0x40 +#define XA_NEC_CDDA 0x80 + +// +// Sector types for READ_CD +// + +#define ANY_SECTOR 0 +#define CD_DA_SECTOR 1 +#define YELLOW_MODE1_SECTOR 2 +#define YELLOW_MODE2_SECTOR 3 +#define FORM2_MODE1_SECTOR 4 +#define FORM2_MODE2_SECTOR 5 + +#define MAX_COPY_PROTECT_AGID 4 + +#ifdef ExAllocatePool +#undef ExAllocatePool +#define ExAllocatePool #assert(FALSE) +#endif + +#define CDROM_TAG_GET_CONFIG 'cCcS' // "ScCc" - ioctl GET_CONFIGURATION +#define CDROM_TAG_DC_EVENT 'ECcS' // "ScCE" - device control synch event +#define CDROM_TAG_FEATURE 'FCcS' // "ScCF" - allocated by CdRomGetConfiguration(), free'd by caller +#define CDROM_TAG_DISK_GEOM 'GCcS' // "ScCG" - disk geometry buffer +#define CDROM_TAG_HITACHI_ERROR 'HCcS' // "ScCH" - hitachi error buffer +#define CDROM_TAG_SENSE_INFO 'ICcS' // "ScCI" - sense info buffers +#define CDROM_TAG_POWER_IRP 'iCcS' // "ScCi" - irp for power request +#define CDROM_TAG_SRB 'SCcS' // "ScCS" - srb allocation +#define CDROM_TAG_STRINGS 'sCcS' // "ScCs" - assorted string data +#define CDROM_TAG_MODE_DATA 'MCcS' // "ScCM" - mode data buffer +#define CDROM_TAG_READ_CAP 'PCcS' // "ScCP" - read capacity buffer +#define CDROM_TAG_PLAY_ACTIVE 'pCcS' // "ScCp" - play active checks +#define CDROM_TAG_SUB_Q 'QCcS' // "ScCQ" - read sub q buffer +#define CDROM_TAG_RAW 'RCcS' // "ScCR" - raw mode read buffer +#define CDROM_TAG_TOC 'TCcS' // "ScCT" - read toc buffer +#define CDROM_TAG_TOSHIBA_ERROR 'tCcS' // "ScCt" - toshiba error buffer +#define CDROM_TAG_DEC_ERROR 'dCcS' // "ScCt" - DEC error buffer +#define CDROM_TAG_UPDATE_CAP 'UCcS' // "ScCU" - update capacity path +#define CDROM_TAG_VOLUME 'VCcS' // "ScCV" - volume control buffer +#define CDROM_TAG_VOLUME_INT 'vCcS' // "ScCv" - volume control buffer + +#define DVD_TAG_READ_STRUCTURE 'SVcS' // "ScVS" - used for dvd structure reads +#define DVD_TAG_READ_KEY 'kVcS' // "ScVk" - read buffer for dvd key +#define DVD_TAG_SEND_KEY 'KVcS' // "ScVK" - write buffer for dvd key +#define DVD_TAG_RPC2_CHECK 'sVcS' // "ScVs" - read buffer for dvd/rpc2 check +#define DVD_TAG_DVD_REGION 'tVcS' // "ScVt" - read buffer for rpc2 check +#define DVD_TAG_SECURITY 'XVcS' // "ScVX" - security descriptor + + +#define CDROM_SUBKEY_NAME (L"CdRom") // store new settings here +#define CDROM_READ_CD_NAME (L"ReadCD") // READ_CD support previously detected +#define CDROM_NON_MMC_DRIVE_NAME (L"NonMmc") // MMC commands hang +// +// DVD Registry Value Names for RPC0 Device +// +#define DVD_DEFAULT_REGION (L"DefaultDvdRegion") // this is init. by the dvd class installer +#define DVD_CURRENT_REGION (L"DvdR") +#define DVD_REGION_RESET_COUNT (L"DvdRCnt") +#define DVD_MAX_REGION_RESET_COUNT 2 +#define DVD_MAX_REGION 8 + + + +#define BAIL_OUT(Irp) \ + DebugPrint((2, "Cdrom: [%p] Bailing with status " \ + " %lx at line %x file %s\n", \ + (Irp), (Irp)->IoStatus.Status, \ + __LINE__, __FILE__)) + + +/*++ + +Routine Description: + + This routine grabs an extra remove lock using a local variable + for a unique tag. It then completes the irp in question, and + the just-acquired removelock guarantees that it is still safe + to call IoStartNextPacket(). When that finishes, we release + the newly acquired RemoveLock and return. + +Arguments: + + DeviceObject - the device object for the StartIo queue + Irp - the request we are completing + +Return Value: + + None + +Notes: + + This is implemented as an inline function to allow the compiler + to optimize this as either a function call or as actual inline code. + + This routine will not work with IoXxxRemoveLock() calls, as the + behavior is different. ClassXxxRemoveLock() calls succeed until + the remove has completed, while IoXxxRemoveLock() calls fail as + soon as the call to IoReleaseRemoveLockAndWait() has been called. + The Class version allows this routine to work in a safe manner. + + replaces the following two lines: + IoStartNextPacket(DeviceObject, FALSE); + ClassReleaseRemoveLock(DeviceObject, Irp); + and raises irql as needed to call IoStartNextPacket() + +--*/ +static inline +VOID +CdRomCompleteIrpAndStartNextPacketSafely( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + UCHAR uniqueAddress; + KIRQL oldIrql = KeGetCurrentIrql(); + + ClassAcquireRemoveLock(DeviceObject, (PIRP)&uniqueAddress); + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_CD_ROM_INCREMENT); + + if (oldIrql > DISPATCH_LEVEL) { + ASSERT(!"Cannot call IoStartNextPacket at raised IRQL!"); + } else if (oldIrql < DISPATCH_LEVEL) { + KeRaiseIrqlToDpcLevel(); + } else { // (oldIrql == DISPATCH_LEVEL) + NOTHING; + } + + IoStartNextPacket(DeviceObject, FALSE); + + if (oldIrql > DISPATCH_LEVEL) { + ASSERT(!"Cannot call IoStartNextPacket at raised IRQL!"); + } else if (oldIrql < DISPATCH_LEVEL) { + KeLowerIrql(oldIrql); + } else { // (oldIrql == DISPATCH_LEVEL) + NOTHING; + } + + ClassReleaseRemoveLock(DeviceObject, (PIRP)&uniqueAddress); + + + return; +} + +VOID +CdRomDeviceControlDvdReadStructure( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP OriginalIrp, + IN PIRP NewIrp, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +CdRomDeviceControlDvdEndSession( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP OriginalIrp, + IN PIRP NewIrp, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +CdRomDeviceControlDvdStartSessionReadKey( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP OriginalIrp, + IN PIRP NewIrp, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +CdRomDeviceControlDvdSendKey( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP OriginalIrp, + IN PIRP NewIrp, + IN PSCSI_REQUEST_BLOCK Srb + ); + + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +CdRomUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +CdRomAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT Pdo + ); + +NTSTATUS +CdRomOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +CdRomReadWriteVerification( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +CdRomSwitchMode( + IN PDEVICE_OBJECT DeviceObject, + IN ULONG SectorSize, + IN PIRP OriginalRequest + ); + +NTSTATUS +CdRomDeviceControlDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +CdRomDeviceControlCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +CdRomSetVolumeIntermediateCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +CdRomSwitchModeCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +CdRomXACompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +CdRomClassIoctlCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +CdRomStartIo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +CdRomTickHandler( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +CdRomUpdateCapacity( + IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension, + IN PIRP IrpToComplete, + IN OPTIONAL PKEVENT IoctlEvent + ); + +NTSTATUS +CdRomCreateDeviceObject( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT Pdo + ); + +VOID +ScanForSpecialHandler( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + ULONG_PTR HackFlags + ); + +VOID +ScanForSpecial( + PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +CdRomIsPlayActive( + IN PDEVICE_OBJECT DeviceObject + ); + +VOID +CdRomErrorHandler( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +VOID +HitachiProcessErrorGD2000( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +VOID +HitachiProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +VOID +ToshibaProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +NTSTATUS +ToshibaProcessErrorCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ); + +VOID +CdRomCreateNamedEvent( + IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension, + IN ULONG DeviceNumber + ); + +NTSTATUS +CdRomInitDevice( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +CdRomStartDevice( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +CdRomStopDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ); + +NTSTATUS +CdRomRemoveDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ); + +NTSTATUS +CdRomDvdEndAllSessionsCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +CdRomDvdReadDiskKeyCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +DEVICE_TYPE +CdRomGetDeviceType( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +CdRomCreateWellKnownName( + IN PDEVICE_OBJECT DeviceObject + ); + +VOID +CdRomDeleteWellKnownName( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +CdRomGetDeviceParameter ( + IN PDEVICE_OBJECT DeviceObject, + IN PWSTR ParameterName, + IN OUT PULONG ParameterValue + ); + +NTSTATUS +CdRomSetDeviceParameter ( + IN PDEVICE_OBJECT DeviceObject, + IN PWSTR ParameterName, + IN ULONG ParameterValue + ); + +VOID +CdRomPickDvdRegion ( + IN PDEVICE_OBJECT Fdo +); + +NTSTATUS +CdRomRetryRequest( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PIRP Irp, + IN ULONG Delay, + IN BOOLEAN ResendIrp + ); + +NTSTATUS +CdRomRerunRequest( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN OPTIONAL PIRP Irp, + IN BOOLEAN ResendIrp + ); + +NTSTATUS +CdRomGetRpc0Settings( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +CdRomSetRpc0Settings( + IN PDEVICE_OBJECT Fdo, + IN UCHAR NewRegion + ); + +NTSTATUS +CdRomShutdownFlush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +//////////////////////////////////////////////////////////////////////////////// + +VOID +CdRomIsDeviceMmcDevice( + IN PDEVICE_OBJECT Fdo, + OUT PBOOLEAN IsMmc + ); + +NTSTATUS +CdRomMmcErrorHandler( + IN PDEVICE_OBJECT Fdo, + IN PSCSI_REQUEST_BLOCK Srb, + OUT PNTSTATUS Status, + OUT PBOOLEAN Retry + ); + +PVOID +CdRomFindFeaturePage( + IN PGET_CONFIGURATION_HEADER FeatureBuffer, + IN ULONG Length, + IN FEATURE_NUMBER Feature + ); + +NTSTATUS +CdRomGetConfiguration( + IN PDEVICE_OBJECT Fdo, + OUT PGET_CONFIGURATION_HEADER *Buffer, + OUT PULONG BytesReturned, + IN FEATURE_NUMBER StartingFeature, + IN ULONG RequestedType + ); + +VOID +CdRomUpdateMmcDriveCapabilities( + IN PDEVICE_OBJECT Fdo, + IN PVOID Context // RESERVED == NULL + ); + +VOID +CdRomFindProfileInProfiles( + IN PFEATURE_DATA_PROFILE_LIST ProfileHeader, + IN FEATURE_PROFILE_TYPE ProfileToFind, + OUT PBOOLEAN Exists + ); + +NTSTATUS +CdRomAllocateMmcResources( + IN PDEVICE_OBJECT Fdo + ); + +VOID +CdRomDeAllocateMmcResources( + IN PDEVICE_OBJECT Fdo + ); + +VOID +CdromFakePartitionInfo( + IN PCOMMON_DEVICE_EXTENSION CommonExtension, + IN PIRP Irp + ); + +VOID +CdRomInterpretReadCapacity( + IN PDEVICE_OBJECT Fdo, + IN PREAD_CAPACITY_DATA ReadCapacityBuffer + ); + +NTSTATUS +CdRomShutdownFlushCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIRP Context + ); + +VOID +CdRompFlushDelayedList( + IN PDEVICE_OBJECT Fdo, + IN PCDROM_MMC_EXTENSION MmcData, + IN NTSTATUS Status, + IN BOOLEAN CalledFromWorkItem + ); + +#endif // __CDROMP_H__ + + diff --git a/drivers/storage/class/cdrom_new/cdrom.inf b/drivers/storage/class/cdrom_new/cdrom.inf new file mode 100644 index 00000000000..0cf7858e0d3 --- /dev/null +++ b/drivers/storage/class/cdrom_new/cdrom.inf @@ -0,0 +1,344 @@ +; +; cdrom.inf -- INF file for installing CDROM drives +; +; Copyright (c) 1993-1997, Microsoft Corporation + +[Version] +Signature="$WINDOWS NT$" +Class=CDROM +ClassGuid={4D36E965-E325-11CE-BFC1-08002BE10318} +Provider=%DDK_SAMPLE% +DriverVer=03/15/2001,5.1.2462.0 +CatalogFile=ddk_sample.cat + +[cdaudio_copyfiles] +cdaudio.sys + +[changer_copyfiles] +changer.sys + +[cdrom_copyfiles] +cdrom.sys +redbook.sys + +[storprop_copyfiles] +storprop.dll + +[DestinationDirs] +cdrom_copyfiles = 12 +cdaudio_copyfiles = 12 +changer_copyfiles = 12 +storprop_copyfiles = 11 + +[Manufacturer] +%ATAPI_CHGR% = atapi_chgr +%CHINON% = chinon_cdrom +%DENON% = denon_cdrom +%FUJITSU% = fujitsu_cdrom +%HITACHI% = hitachi_cdrom +%HP% = hp_cdrom +%MITSUMI% = mitsumi_cdrom +%NEC% = nec_cdrom +%OTI% = oti_cdrom +%PIONEER% = pioneer_cdrom +%WEARNES% = wearnes_cdrom +%GenManufacturer% = cdrom_device + +[atapi_chgr] +%NecChanger_devdesc% = changer_install,IDE\CdRomNEC_CD-ROM_DRIVE:251____________________ +%NecChanger_devdesc% = changer_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:251 +%NecChanger_devdesc% = changer_install,IDE\CdRomNEC_CD-ROM_DRIVE:253____________________ +%NecChanger_devdesc% = changer_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:253 +%NecChanger_devdesc% = changer_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:252 +%AlpsChanger_devdesc% = changer_install,IDE\CdRomALPS_DC544______________________________ +%TorisanChanger_devdesc% = changer_install,IDE\CdRomTORiSAN_CD-ROM_CDR-C3G__________________ +%TorisanChanger_devdesc% = changer_install,SCSI\CdRomTORiSAN_CD-ROM_CDR-C3G__ +%TorisanChanger_devdesc% = changer_install,IDE\CdRomTORiSAN_CD-ROM_CDR_C36__________________ +%PanasonicChanger_devdesc% = changer_install,IDE\CdRomMATSHITA_RD-DRC001-M____________________ +%PanasonicChanger_devdesc% = changer_install,IDE\CdRomMATSHITA_RD-DRC002-S____________________ +%PanasonicChanger_devdesc% = changer_install,SCSI\CdRomNAKAMICHMJ-5.16_________ + +[fujitsu_cdrom] +%fujitsu_devdesc% = cdaudio_install,SCSI\CdRomFUJITSU_ + +[chinon_cdrom] +%chinon_devdesc% = cdaudio_install,SCSI\CdRomCHINON__ + +[denon_cdrom] +%denon_devdesc% = cdaudio_install,SCSI\CdRomDENON___ + +[hp_cdrom] +%hp_devdesc% = cdaudio_install,SCSI\CdRomHP______C4324/C4325_____ + +[hitachi_cdrom] +%hitachi_devdesc% = cdaudio_install,SCSI\CdRomHITACHI_CDR-3650/1650S__ +%hitachi_devdesc% = cdaudio_install,SCSI\CdRomHITACHI_CDR-1750S_______ + +[mitsumi_cdrom] +%Mitsumi_cdrom_devdesc% = mitsumi_install,IDE\CdRomMITSUMI_CD-ROM________!A________________ + +[nec_cdrom] +%NecMultispin_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:38_ +%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE_4_M +%NecIntersect_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:80_ +%NecIntersect_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:82_ +%NecIntersect_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:83_ +%NecIntersect_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:84_ +%NecMultispin_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:841 +%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:400 +%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:401 +%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:500 +%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:501 +%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:900 + +[oti_cdrom] +%oti_devdesc% = cdaudio_install,IDE\CdRomOTI_DOLPHIN_8001_IDE____________________ + +[pioneer_cdrom] +%pioneer_devdesc% = cdaudio_install,SCSI\CdRomPIONEER_CD-ROM_DRM-600__ +%pioneer_devdesc% = cdaudio_install,SCSI\CdRomPIONEER_CD-ROM_DRM-600x_ + +[wearnes_cdrom] +%wearnes_devdesc% = cdaudio_install,IDE\CdRomWEARNES_ + + +[cdrom_device] +;; +;; if none of the above matched, then only cdrom.sys is required for this drive +;; +%gencdrom_devdesc% = cdrom_install,SCSI\WormPIONEER_CD-WO_DR-R504X__ +%gencdrom_devdesc% = cdrom_install,SCSI\WormSONY____CD-R___CDU920S__ +%gencdrom_devdesc% = cdrom_install,SCSI\WormSONY____CD-R___CDU948S__ +%gencdrom_devdesc% = cdrom_install,GenCdRom + +;; +;; Use to add filter drivers for the device +;; + +[cdaudio_addreg] +HKR,,"UpperFilters",0x00010008,"cdaudio" + +[changer_addreg] +HKR,,"UpperFilters",0x00010008,"changer" + +[mitsumi_addreg] +HKR,,"FriendlyName",,%Mitsumi_Generic_FriendlyName% + +;; +;; more addreg sections +;; + +[dvd_property_provider_AddReg] +HKR,,EnumPropPages32,,"storprop.dll,DvdPropPageProvider" + +[autorun_addreg] +;; +;; The AutoRunAlwaysDisable key is only for use when the hardware cannot +;; accepts TEST_UNIT_READY commands. Disabling 'AutoRun' or including +;; devices in this list will prevent removable media services from being +;; able to properly handle these devices. +;; +HKLM,"System\CurrentControlSet\Services\cdrom","AutoRun",0x00010003,1 +HKLM,"System\CurrentControlSet\Services\cdrom","AutoRunAlwaysDisable",\ + 0x00010000,\ + "NEC MBR-7 ", \ + "NEC MBR-7.4 ", \ + "PIONEER CHANGR DRM-1804X", \ + "PIONEER CD-ROM DRM-6324X", \ + "PIONEER CD-ROM DRM-624X ", \ + "TORiSAN CD-ROM CDR_C36" + +;; +;; Use to disable synchronous transfers to this device. Sync transfers will +;; always be turned off by default in this INF for any cdrom-type device +;; +[nosync_addreg] +HKR,,"DefaultRequestFlags",0x00010001,8 + +;; +;; Installation section for cdaudio. Sets cdrom as the service and adds +;; cdaudio as an upper filter +;; + +[cdaudio_install] +CopyFiles=cdaudio_copyfiles,cdrom_copyfiles,storprop_copyfiles +AddReg=dvd_property_provider_AddReg + +[cdaudio_install.HW] +AddReg=nosync_addreg,cdaudio_addreg + +[cdaudio_install.Services] +AddService=cdrom,0x00000002,cdrom_ServiceInstallSection +AddService=cdaudio,,cdaudio_ServiceInstallSection +AddService=redbook,,redbook_ServiceInstallSection,redbook_InstallEventLogSection + +;; +;; Installation section for changer +;; + +[changer_install] +CopyFiles=changer_copyfiles,cdrom_copyfiles,storprop_copyfiles +AddReg=dvd_property_provider_AddReg + +[changer_install.HW] +AddReg=changer_addreg + +[changer_install.Services] +AddService=cdrom,0x00000002,cdrom_ServiceInstallSection +AddService=changer,,changer_ServiceInstallSection +AddService=redbook,,redbook_ServiceInstallSection,redbook_InstallEventLogSection + +;; +;; Installation section for mitsumi. +;; + +[mitsumi_install] +CopyFiles=cdrom_copyfiles,storprop_copyfiles +AddReg=dvd_property_provider_AddReg + +[mitsumi_install.HW] +AddReg=nosync_addreg,mitsumi_addreg + +[mitsumi_install.Services] +AddService=cdrom,0x00000002,cdrom_ServiceInstallSection +AddService=redbook,,redbook_ServiceInstallSection,redbook_InstallEventLogSection + +;; +;; Installation section for generic cdrom. +;; + +[cdrom_install] +CopyFiles=cdrom_copyfiles,storprop_copyfiles +AddReg=dvd_property_provider_AddReg + +[cdrom_install.HW] +AddReg=nosync_addreg + +[cdrom_install.Services] +AddService=cdrom,0x00000002,cdrom_ServiceInstallSection +AddService=redbook,,redbook_ServiceInstallSection,redbook_InstallEventLogSection + +;; +;; Service install sections for cdrom and cdaudio +;; + +[cdrom_ServiceInstallSection] +DisplayName = %cdrom_ServiceDesc% +ServiceType = 1 +StartType = 1 +ErrorControl = 1 +ServiceBinary = %12%\cdrom.sys +LoadOrderGroup = SCSI CDROM Class +AddReg=autorun_addreg + +[cdaudio_ServiceInstallSection] +DisplayName = %cdaudio_ServiceDesc% +ServiceType = 1 +StartType = 1 +ErrorControl = 1 +ServiceBinary = %12%\cdaudio.sys +LoadOrderGroup = Pnp Filter + +[changer_ServiceInstallSection] +DisplayName = %changer_ServiceDesc% +ServiceType = 1 +StartType = 1 +ErrorControl = 1 +ServiceBinary = %12%\changer.sys +LoadOrderGroup = Pnp Filter + +[redbook_ServiceInstallSection] +DisplayName = %redbook_ServiceDesc% +ServiceType = 1 +StartType = 1 +ErrorControl = 1 +ServiceBinary = %12%\redbook.sys +LoadOrderGroup = Pnp Filter + +[redbook_InstallEventLogSection] +AddReg = redbook_EventLog_addreg + +[redbook_EventLog_addreg] +HKR,,"EventMessageFile",0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll;%%SystemRoot%%\System32\drivers\redbook.sys" +HKR,,"TypesSupported",0x00010001,7 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[Strings] +DDK_SAMPLE="DDK Sample Provider" +CDClassName = "DVD/CD-ROM drives" + +;; Manufacturer specific strings +ATAPI_CHGR = "Atapi 2.5 Changer Devices" +CHINON = "Chinon" +DENON = "Denon" +FUJITSU = "Fujitsu" +HITACHI = "Hitachi" +HP = "Hewlett Packard" +MITSUMI = "Mitsumi" +NEC = "NEC" +OTI = "OTI" +PIONEER = "Pioneer" +WEARNES = "Wearnes" +GenManufacturer = "(Standard CD-ROM drives)" + +;; Descriptions for enumerated brands and models +AlpsChanger_devdesc = "Alps CD-ROM Changer" +chinon_devdesc = "Chinon CD-ROM Drive" +denon_devdesc = "Denon CD-ROM Drive" +fujitsu_devdesc = "Fujitsu CD-ROM Drive" +hp_devdesc = "Hewlett Packard CD-ROM Drive" +hitachi_devdesc = "Hitachi CD-ROM Drive" +Mitsumi_cdrom_devdesc = "Mitsumi CD-ROM Drive" +NecChanger_devdesc = "NEC CD-ROM Changer" +NecIntersect_devdesc = "NEC Intersect CD-ROM Drive" +NecMultispin_devdesc = "NEC Multispin CD-ROM Drive" +NecOem_devdesc = "NEC CD-ROM Drive" +oti_devdesc = "OTI CD-ROM Drive" +PanasonicChanger_devdesc = "Panasonic CD-ROM Changer" +pioneer_devdesc = "Pioneer CD-ROM Drive" +TorisanChanger_devdesc = "Torisan CD-ROM Changer" +wearnes_devdesc = "Wearnes CD-ROM Drive" +gencdrom_devdesc = "CD-ROM Drive" + +;; Mitsumi Friendly name explictly listed +Mitsumi_Generic_FriendlyName = "Mitsumi CD-ROM Drive" + +;; Service descriptions +cdrom_ServiceDesc = "CD-ROM Driver" +cdaudio_ServiceDesc = "CD-Audio Filter Driver" +changer_ServiceDesc = "CD-Changer Filter Driver" +redbook_ServiceDesc = "Digital CD Audio Playback Filter Driver" + + diff --git a/drivers/storage/class/cdrom_new/cdrom_new.rbuild b/drivers/storage/class/cdrom_new/cdrom_new.rbuild new file mode 100644 index 00000000000..3969aedb585 --- /dev/null +++ b/drivers/storage/class/cdrom_new/cdrom_new.rbuild @@ -0,0 +1,20 @@ + + + + + ntoskrnl + hal + classpnp + ../inc + + -mrtd + -fno-builtin + -w + + cdrom.c + data.c + ioctl.c + mmc.c + scsicdrm.rc + sec.c + diff --git a/drivers/storage/class/cdrom_new/data.c b/drivers/storage/class/cdrom_new/data.c new file mode 100644 index 00000000000..02d4239bf95 --- /dev/null +++ b/drivers/storage/class/cdrom_new/data.c @@ -0,0 +1,99 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + +Abstract: + + +Environment: + + +Notes: + +Revision History: + +--*/ + +#include "ntddk.h" +#include "classpnp.h" +#include "trace.h" + +#ifdef ALLOC_DATA_PRAGMA +#pragma data_seg("PAGE") +#endif + +/* + +#define CDROM_HACK_DEC_RRD (0x00000001) +#define CDROM_HACK_FUJITSU_FMCD_10x (0x00000002) +#define CDROM_HACK_HITACHI_1750 (0x00000004) +#define CDROM_HACK_HITACHI_GD_2000 (0x00000008) +#define CDROM_HACK_TOSHIBA_SD_W1101 (0x00000010) +#define CDROM_HACK_TOSHIBA_XM_3xx (0x00000020) +#define CDROM_HACK_NEC_CDDA (0x00000040) +#define CDROM_HACK_PLEXTOR_CDDA (0x00000080) +#define CDROM_HACK_BAD_GET_CONFIG_SUPPORT (0x00000100) +#define CDROM_HACK_FORCE_READ_CD_DETECTION (0x00000200) +#define CDROM_HACK_READ_CD_SUPPORTED (0x00000400) + +*/ + +CLASSPNP_SCAN_FOR_SPECIAL_INFO CdromHackItems[] = { + // digital put out drives using 512 byte block sizes, + // and needed us to send a mode page to set the sector + // size back to 2048. + { "DEC" , "RRD" , NULL, 0x0001 }, + // these fujitsu drives take longer than ten seconds to + // timeout commands when audio discs are placed in them + { "FUJITSU" , "FMCD-101" , NULL, 0x0002 }, + { "FUJITSU" , "FMCD-102" , NULL, 0x0002 }, + // these hitachi drives don't work properly in PIO mode + { "HITACHI ", "CDR-1750S" , NULL, 0x0004 }, + { "HITACHI ", "CDR-3650/1650S" , NULL, 0x0004 }, + // this particular gem doesn't automatcially spin up + // on some media access commands. + { "" , "HITACHI GD-2000" , NULL, 0x0008 }, + { "" , "HITACHI DVD-ROM GD-2000" , NULL, 0x0008 }, + // this particular drive doesn't support DVD playback. + // just print an error message in CHK builds. + { "TOSHIBA ", "SD-W1101 DVD-RAM" , NULL, 0x0010 }, + // not sure what this device's issue was. seems to + // require mode selects at various times. + { "TOSHIBA ", "CD-ROM XM-3" , NULL, 0x0020 }, + // NEC defined a "READ_CD" type command before there was + // a standard, so fall back on this as an option. + { "NEC" , "" , NULL, 0x0040 }, + // plextor defined a "READ_CD" type command before there was + // a standard, so fall back on this as an option. + { "PLEXTOR ", "" , NULL, 0x0080 }, + // this drive times out and sometimes disappears from the bus + // when send GET_CONFIGURATION commands. don't send them. + { "" , "LG DVD-ROM DRD-840B" , NULL, 0x0100 }, + { "" , "SAMSUNG DVD-ROM SD-608" , NULL, 0x0300 }, + // these drives should have supported READ_CD, but at least + // some firmware revisions did not. force READ_CD detection. + { "" , "SAMSUNG DVD-ROM SD-" , NULL, 0x2000 }, + // the mitsumi drive below doesn't follow the block-only spec, + // and we end up hanging when sending it commands it doesn't + // understand. this causes complications later, also. + { "MITSUMI ", "CR-4802TE " , NULL, 0x0100 }, + // some drives return various funky errors (such as 3/2/0 NO_SEEK_COMPLETE) + // during the detection of READ_CD support, resulting in iffy detection. + // since they probably don't support mode switching, which is really old + // legacy stuff anyways, the ability to read digitally is lost when + // these drives return unexpected error codes. note: MMC compliant drives + // are presumed to support READ_CD, as are DVD drives, and anything + // connected to a bus type other than IDE or SCSI, and therefore don't + // need to be here. + { "YAMAHA ", "CRW8424S " , NULL, 0x0400 }, + // and finally, a place to finish the list. :) + { NULL , NULL , NULL, 0x0000 } +}; + +#ifdef ALLOC_DATA_PRAGMA +#pragma data_seg() +#endif + diff --git a/drivers/storage/class/cdrom_new/ioctl.c b/drivers/storage/class/cdrom_new/ioctl.c new file mode 100644 index 00000000000..446828e5294 --- /dev/null +++ b/drivers/storage/class/cdrom_new/ioctl.c @@ -0,0 +1,4054 @@ +/*-- + +Copyright (C) Microsoft Corporation, 1999 - 1999 + +Module Name: + + ioctl.c + +Abstract: + + The CDROM class driver tranlates IRPs to SRBs with embedded CDBs + and sends them to its devices through the port driver. + +Environment: + + kernel mode only + +Notes: + + SCSI Tape, CDRom and Disk class drivers share common routines + that can be found in the CLASS directory (..\ntos\dd\class). + +Revision History: + +--*/ + +#include "stddef.h" +#include "string.h" + +#include "ntddk.h" + +#include "ntddcdvd.h" +#include "classpnp.h" + +#include "initguid.h" +#include "ntddstor.h" +#include "cdrom.h" + + +#if DBG + PUCHAR READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor+1] = { + "Physical", + "Copyright", + "DiskKey", + "BCA", + "Manufacturer", + "Unknown" + }; +#endif // DBG + +#define DEFAULT_CDROM_SECTORS_PER_TRACK 32 +#define DEFAULT_TRACKS_PER_CYLINDER 64 + + + +NTSTATUS +CdRomDeviceControlDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +/*++ + +Routine Description: + + This is the NT device control handler for CDROMs. + +Arguments: + + DeviceObject - for this CDROM + + Irp - IO Request packet + +Return Value: + + NTSTATUS + +--*/ +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextStack; + PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData); + + BOOLEAN use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE); + SCSI_REQUEST_BLOCK srb; + PCDB cdb = (PCDB)srb.Cdb; + PVOID outputBuffer; + ULONG bytesTransferred = 0; + NTSTATUS status; + NTSTATUS status2; + KIRQL irql; + + ULONG ioctlCode; + ULONG baseCode; + ULONG functionCode; + +RetryControl: + + // + // Zero the SRB on stack. + // + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + Irp->IoStatus.Information = 0; + + // + // if this is a class driver ioctl then we need to change the base code + // to IOCTL_CDROM_BASE so that the switch statement can handle it. + // + // WARNING - currently the scsi class ioctl function codes are between + // 0x200 & 0x300. this routine depends on that fact + // + + ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; + baseCode = ioctlCode >> 16; + functionCode = (ioctlCode & (~0xffffc003)) >> 2; + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Ioctl Code = %lx, Base Code = %lx," + " Function Code = %lx\n", + ioctlCode, + baseCode, + functionCode + )); + + if((functionCode >= 0x200) && (functionCode <= 0x300)) { + + ioctlCode = (ioctlCode & 0x0000ffff) | CTL_CODE(IOCTL_CDROM_BASE, 0, 0, 0); + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Class Code - new ioctl code is %lx\n", + ioctlCode)); + + irpStack->Parameters.DeviceIoControl.IoControlCode = ioctlCode; + + } + + switch (ioctlCode) { + + case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: { + + PGET_MEDIA_TYPES mediaTypes = Irp->AssociatedIrp.SystemBuffer; + PDEVICE_MEDIA_INFO mediaInfo = &mediaTypes->MediaInfo[0]; + ULONG sizeNeeded; + + sizeNeeded = sizeof(GET_MEDIA_TYPES); + + // + // IsMmc is static... + // + + if (cdData->Mmc.IsMmc) { + sizeNeeded += sizeof(DEVICE_MEDIA_INFO) * 1; // return two media types + } + + // + // Ensure that buffer is large enough. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeNeeded) { + + // + // Buffer too small. + // + + Irp->IoStatus.Information = sizeNeeded; + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer, sizeNeeded); + + // + // ISSUE-2000/5/11-henrygab - need to update GET_MEDIA_TYPES_EX + // + + mediaTypes->DeviceType = CdRomGetDeviceType(DeviceObject); + + mediaTypes->MediaInfoCount = 1; + mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = CD_ROM; + mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1; + mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_ONLY; + mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart; + mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder; + mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack; + mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector; + + if (cdData->Mmc.IsMmc) { + + // + // also report a removable disk + // + mediaTypes->MediaInfoCount += 1; + + mediaInfo++; + mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia; + mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1; + mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE; + mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart; + mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder; + mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack; + mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector; + mediaInfo--; + + } + + // + // Status will either be success, if media is present, or no media. + // It would be optimal to base from density code and medium type, but not all devices + // have values for these fields. + // + + // + // Send a TUR to determine if media is present. + // + + srb.CdbLength = 6; + cdb = (PCDB)srb.Cdb; + cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; + + // + // Set timeout value. + // + + srb.TimeOutValue = fdoExtension->TimeOutValue; + + status = ClassSendSrbSynchronous(DeviceObject, + &srb, + NULL, + 0, + FALSE); + + + TraceLog((CdromDebugWarning, + "CdRomDeviceControl: GET_MEDIA_TYPES status of TUR - %lx\n", + status)); + + if (NT_SUCCESS(status)) { + + // + // set the disk's media as current if we can write to it. + // + + if (cdData->Mmc.IsMmc && cdData->Mmc.WriteAllowed) { + + mediaInfo++; + SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, + MEDIA_CURRENTLY_MOUNTED); + mediaInfo--; + + + } else { + + SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, + MEDIA_CURRENTLY_MOUNTED); + + } + + } + + Irp->IoStatus.Information = sizeNeeded; + status = STATUS_SUCCESS; + break; + } + + + case IOCTL_CDROM_RAW_READ: { + + LARGE_INTEGER startingOffset; + ULONGLONG transferBytes; + ULONGLONG endOffset; + ULONGLONG mdlBytes; + ULONG startingSector; + PRAW_READ_INFO rawReadInfo = (PRAW_READ_INFO)irpStack->Parameters.DeviceIoControl.Type3InputBuffer; + + // + // Ensure that XA reads are supported. + // + + if (TEST_FLAG(cdData->XAFlags, XA_NOT_SUPPORTED)) { + TraceLog((CdromDebugWarning, + "CdRomDeviceControl: XA Reads not supported. Flags (%x)\n", + cdData->XAFlags)); + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + // + // Check that ending sector is on disc and buffers are there and of + // correct size. + // + + if (rawReadInfo == NULL) { + + // + // Called from user space. Save the userbuffer in the + // Type3InputBuffer so we can reduce the number of code paths. + // + + irpStack->Parameters.DeviceIoControl.Type3InputBuffer = + Irp->AssociatedIrp.SystemBuffer; + + // + // Called from user space. Validate the buffers. + // + + rawReadInfo = (PRAW_READ_INFO)irpStack->Parameters.DeviceIoControl.Type3InputBuffer; + + if (rawReadInfo == NULL) { + + TraceLog((CdromDebugWarning, + "CdRomDeviceControl: Invalid I/O parameters for " + "XA Read (No extent info\n")); + status = STATUS_INVALID_PARAMETER; + break; + + } + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength != + sizeof(RAW_READ_INFO)) { + + TraceLog((CdromDebugWarning, + "CdRomDeviceControl: Invalid I/O parameters for " + "XA Read (Invalid info buffer\n")); + status = STATUS_INVALID_PARAMETER; + break; + + } + } + + // + // if they don't request any data, just fail the request + // + + if (rawReadInfo->SectorCount == 0) { + + status = STATUS_INVALID_PARAMETER; + break; + + } + + startingOffset.QuadPart = rawReadInfo->DiskOffset.QuadPart; + startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> + fdoExtension->SectorShift); + transferBytes = (ULONGLONG)rawReadInfo->SectorCount * RAW_SECTOR_SIZE; + + endOffset = (ULONGLONG)rawReadInfo->SectorCount * COOKED_SECTOR_SIZE; + endOffset += startingOffset.QuadPart; + + // + // check for overflows.... + // + + if (transferBytes < (ULONGLONG)(rawReadInfo->SectorCount)) { + TraceLog((CdromDebugWarning, + "CdRomDeviceControl: Invalid I/O parameters for XA " + "Read (TransferBytes Overflow)\n")); + status = STATUS_INVALID_PARAMETER; + break; + } + if (endOffset < (ULONGLONG)startingOffset.QuadPart) { + TraceLog((CdromDebugWarning, + "CdRomDeviceControl: Invalid I/O parameters for XA " + "Read (EndingOffset Overflow)\n")); + status = STATUS_INVALID_PARAMETER; + break; + } + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + transferBytes) { + TraceLog((CdromDebugWarning, + "CdRomDeviceControl: Invalid I/O parameters for XA " + "Read (Bad buffer size)\n")); + status = STATUS_INVALID_PARAMETER; + break; + } + if (endOffset > (ULONGLONG)commonExtension->PartitionLength.QuadPart) { + TraceLog((CdromDebugWarning, + "CdRomDeviceControl: Invalid I/O parameters for XA " + "Read (Request Out of Bounds)\n")); + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // cannot validate the MdlAddress, since it is not included in any + // other location per the DDK and file system calls. + // + + // + // validate the mdl describes at least the number of bytes + // requested from us. + // + + mdlBytes = (ULONGLONG)MmGetMdlByteCount(Irp->MdlAddress); + if (mdlBytes < transferBytes) { + TraceLog((CdromDebugWarning, + "CdRomDeviceControl: Invalid MDL %s, Irp %p\n", + "size (5)", Irp)); + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // HACKHACK - REF #0001 + // The retry count will be in this irp's IRP_MN function, + // as the new irp was freed, and we therefore cannot use + // this irp's next stack location for this function. + // This may be a good location to store this info for + // when we remove RAW_READ (mode switching), as we will + // no longer have the nextIrpStackLocation to play with + // when that occurs + // + // once XA_READ is removed, then this hack can also be + // removed. + // + irpStack->MinorFunction = MAXIMUM_RETRIES; // HACKHACK - REF #0001 + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: + case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX: { + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Get drive geometryEx\n")); + if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength < + FIELD_OFFSET(DISK_GEOMETRY_EX, Data)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = FIELD_OFFSET(DISK_GEOMETRY_EX, Data); + break; + } + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + } + + case IOCTL_DISK_GET_DRIVE_GEOMETRY: + case IOCTL_CDROM_GET_DRIVE_GEOMETRY: { + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Get drive geometry\n")); + + if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof( DISK_GEOMETRY ) ) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_READ_TOC_EX: { + + PCDROM_READ_TOC_EX inputBuffer; + + if (CdRomIsPlayActive(DeviceObject)) { + status = STATUS_DEVICE_BUSY; + break; + } + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(CDROM_READ_TOC_EX)) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + MINIMUM_CDROM_READ_TOC_EX_SIZE) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = MINIMUM_CDROM_READ_TOC_EX_SIZE; + break; + } + + if (irpStack->Parameters.Read.Length > ((USHORT)-1)) { + status = STATUS_INVALID_PARAMETER; + break; + } + + inputBuffer = Irp->AssociatedIrp.SystemBuffer; + + if ((inputBuffer->Reserved1 != 0) || + (inputBuffer->Reserved2 != 0) || + (inputBuffer->Reserved3 != 0)) { + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // NOTE: when adding new formats, ensure that first two bytes + // specify the amount of additional data available. + // + + if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_TOC ) || + (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_FULL_TOC) || + (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_CDTEXT )) { + + // SessionTrack field is used + + } else + if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_SESSION) || + (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_PMA) || + (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_ATIP)) { + + // SessionTrack field is reserved + + if (inputBuffer->SessionTrack != 0) { + status = STATUS_INVALID_PARAMETER; + break; + } + + } else { + status = STATUS_INVALID_PARAMETER; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + } + + case IOCTL_CDROM_GET_LAST_SESSION: { + + // + // If the cd is playing music then reject this request. + // + + if (CdRomIsPlayActive(DeviceObject)) { + status = STATUS_DEVICE_BUSY; + break; + } + + // + // Make sure the caller is requesting enough data to make this worth + // our while. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(CDROM_TOC_SESSION_DATA)) { + + // + // they didn't request the entire TOC -- use _EX version + // for partial transfers and such. + // + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(CDROM_TOC_SESSION_DATA); + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_READ_TOC: { + + // + // If the cd is playing music then reject this request. + // + + if (CdRomIsPlayActive(DeviceObject)) { + status = STATUS_DEVICE_BUSY; + break; + } + + // + // Make sure the caller is requesting enough data to make this worth + // our while. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(CDROM_TOC)) { + + // + // they didn't request the entire TOC -- use _EX version + // for partial transfers and such. + // + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(CDROM_TOC); + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_PLAY_AUDIO_MSF: { + + // + // Play Audio MSF + // + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Play audio MSF\n")); + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(CDROM_PLAY_AUDIO_MSF)) { + + // + // Indicate unsuccessful status. + // + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_SEEK_AUDIO_MSF: { + + + // + // Seek Audio MSF + // + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Seek audio MSF\n")); + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(CDROM_SEEK_AUDIO_MSF)) { + + // + // Indicate unsuccessful status. + // + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + + } + + case IOCTL_CDROM_PAUSE_AUDIO: { + + // + // Pause audio + // + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Pause audio\n")); + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + + break; + } + + case IOCTL_CDROM_RESUME_AUDIO: { + + // + // Resume audio + // + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Resume audio\n")); + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_READ_Q_CHANNEL: { + + PCDROM_SUB_Q_DATA_FORMAT inputBuffer = + Irp->AssociatedIrp.SystemBuffer; + + if(irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(CDROM_SUB_Q_DATA_FORMAT)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + // + // check for all valid types of request + // + + if (inputBuffer->Format != IOCTL_CDROM_CURRENT_POSITION && + inputBuffer->Format != IOCTL_CDROM_MEDIA_CATALOG && + inputBuffer->Format != IOCTL_CDROM_TRACK_ISRC ) { + status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_GET_CONTROL: { + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Get audio control\n")); + + // + // Verify user buffer is large enough for the data. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(CDROM_AUDIO_CONTROL)) { + + // + // Indicate unsuccessful status and no data transferred. + // + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(CDROM_AUDIO_CONTROL); + break; + + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_GET_VOLUME: { + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Get volume control\n")); + + // + // Verify user buffer is large enough for data. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(VOLUME_CONTROL)) { + + // + // Indicate unsuccessful status and no data transferred. + // + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(VOLUME_CONTROL); + break; + + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_SET_VOLUME: { + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Set volume control\n")); + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(VOLUME_CONTROL)) { + + // + // Indicate unsuccessful status. + // + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_STOP_AUDIO: { + + // + // Stop play. + // + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Stop audio\n")); + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_STORAGE_CHECK_VERIFY: + case IOCTL_DISK_CHECK_VERIFY: + case IOCTL_CDROM_CHECK_VERIFY: { + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: [%p] Check Verify\n", Irp)); + + if((irpStack->Parameters.DeviceIoControl.OutputBufferLength) && + (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))) { + + TraceLog((CdromDebugWarning, + "CdRomDeviceControl: Check Verify: media count " + "buffer too small\n")); + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(ULONG); + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_DVD_READ_STRUCTURE: { + + TraceLog((CdromDebugTrace, + "DvdDeviceControl: [%p] IOCTL_DVD_READ_STRUCTURE\n", Irp)); + + if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { + TraceLog((CdromDebugWarning, + "DvdDeviceControl: License Failure\n")); + status = STATUS_COPY_PROTECTION_FAILURE; + break; + } + + if (cdData->DvdRpc0Device && cdData->Rpc0RetryRegistryCallback) { + // + // if currently in-progress, this will just return. + // prevents looping by doing that interlockedExchange() + // + TraceLog((CdromDebugWarning, + "DvdDeviceControl: PickRegion() from " + "READ_STRUCTURE\n")); + CdRomPickDvdRegion(DeviceObject); + } + + + if(irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(DVD_READ_STRUCTURE)) { + + TraceLog((CdromDebugWarning, + "DvdDeviceControl - READ_STRUCTURE: input buffer " + "length too small (was %d should be %d)\n", + irpStack->Parameters.DeviceIoControl.InputBufferLength, + sizeof(DVD_READ_STRUCTURE))); + status = STATUS_INVALID_PARAMETER; + break; + } + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(READ_DVD_STRUCTURES_HEADER)) { + + TraceLog((CdromDebugWarning, + "DvdDeviceControl - READ_STRUCTURE: output buffer " + "cannot hold header information\n")); + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(READ_DVD_STRUCTURES_HEADER); + break; + } + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength > + MAXUSHORT) { + + // + // key length must fit in two bytes + // + TraceLog((CdromDebugWarning, + "DvdDeviceControl - READ_STRUCTURE: output buffer " + "too large\n")); + status = STATUS_INVALID_PARAMETER; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_DVD_START_SESSION: { + + TraceLog((CdromDebugTrace, + "DvdDeviceControl: [%p] IOCTL_DVD_START_SESSION\n", Irp)); + + if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { + TraceLog((CdromDebugWarning, + "DvdDeviceControl: License Failure\n")); + status = STATUS_COPY_PROTECTION_FAILURE; + break; + } + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(DVD_SESSION_ID)) { + + TraceLog((CdromDebugWarning, + "DvdDeviceControl: DVD_START_SESSION - output " + "buffer too small\n")); + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(DVD_SESSION_ID); + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_DVD_SEND_KEY: + case IOCTL_DVD_SEND_KEY2: { + + PDVD_COPY_PROTECT_KEY key = Irp->AssociatedIrp.SystemBuffer; + ULONG keyLength; + + TraceLog((CdromDebugTrace, + "DvdDeviceControl: [%p] IOCTL_DVD_SEND_KEY\n", Irp)); + + if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { + TraceLog((CdromDebugWarning, + "DvdDeviceControl: License Failure\n")); + status = STATUS_COPY_PROTECTION_FAILURE; + break; + } + + if((irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(DVD_COPY_PROTECT_KEY)) || + (irpStack->Parameters.DeviceIoControl.InputBufferLength != + key->KeyLength)) { + + // + // Key is too small to have a header or the key length doesn't + // match the input buffer length. Key must be invalid + // + + TraceLog((CdromDebugWarning, + "DvdDeviceControl: [%p] IOCTL_DVD_SEND_KEY - " + "key is too small or does not match KeyLength\n", + Irp)); + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // allow only certain key type (non-destructive) to go through + // IOCTL_DVD_SEND_KEY (which only requires READ access to the device + // + if (ioctlCode == IOCTL_DVD_SEND_KEY) { + + if ((key->KeyType != DvdChallengeKey) && + (key->KeyType != DvdBusKey2) && + (key->KeyType != DvdInvalidateAGID)) { + + status = STATUS_INVALID_PARAMETER; + break; + } + } + + if (cdData->DvdRpc0Device) { + + if (key->KeyType == DvdSetRpcKey) { + + PDVD_SET_RPC_KEY rpcKey = (PDVD_SET_RPC_KEY) key->KeyData; + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + DVD_SET_RPC_KEY_LENGTH) { + + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // we have a request to set region code + // on a RPC0 device which doesn't support + // region coding. + // + // we have to fake it. + // + + KeWaitForMutexObject( + &cdData->Rpc0RegionMutex, + UserRequest, + KernelMode, + FALSE, + NULL + ); + + if (cdData->DvdRpc0Device && cdData->Rpc0RetryRegistryCallback) { + // + // if currently in-progress, this will just return. + // prevents looping by doing that interlockedExchange() + // + TraceLog((CdromDebugWarning, + "DvdDeviceControl: PickRegion() from " + "SEND_KEY\n")); + CdRomPickDvdRegion(DeviceObject); + } + + if (cdData->Rpc0SystemRegion == rpcKey->PreferredDriveRegionCode) { + + // + // nothing to change + // + TraceLog((CdromDebugWarning, + "DvdDeviceControl (%p) => not changing " + "regions -- requesting current region\n", + DeviceObject)); + status = STATUS_SUCCESS; + + } else if (cdData->Rpc0SystemRegionResetCount == 0) { + + // + // not allowed to change it again + // + + TraceLog((CdromDebugWarning, + "DvdDeviceControl (%p) => no more region " + "changes are allowed for this device\n", + DeviceObject)); + status = STATUS_CSS_RESETS_EXHAUSTED; + + } else { + + ULONG i; + UCHAR mask; + ULONG bufferLen; + PDVD_READ_STRUCTURE dvdReadStructure; + PDVD_COPYRIGHT_DESCRIPTOR dvdCopyRight; + IO_STATUS_BLOCK ioStatus; + UCHAR mediaRegionData; + + mask = ~rpcKey->PreferredDriveRegionCode; + + if (CountOfSetBitsUChar(mask) != 1) { + + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + // + // this test will always be TRUE except during initial + // automatic selection of the first region. + // + + if (cdData->Rpc0SystemRegion != 0xff) { + + // + // make sure we have a media in the drive with the same + // region code if the drive is already has a region set + // + + TraceLog((CdromDebugTrace, + "DvdDeviceControl (%p) => Checking " + "media region\n", + DeviceObject)); + + bufferLen = max(sizeof(DVD_DESCRIPTOR_HEADER) + + sizeof(DVD_COPYRIGHT_DESCRIPTOR), + sizeof(DVD_READ_STRUCTURE) + ); + + dvdReadStructure = (PDVD_READ_STRUCTURE) + ExAllocatePoolWithTag(PagedPool, + bufferLen, + DVD_TAG_RPC2_CHECK); + + if (dvdReadStructure == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE); + break; + } + + dvdCopyRight = (PDVD_COPYRIGHT_DESCRIPTOR) + ((PDVD_DESCRIPTOR_HEADER) dvdReadStructure)->Data; + + // + // check to see if we have a DVD device + // + + RtlZeroMemory (dvdReadStructure, bufferLen); + dvdReadStructure->Format = DvdCopyrightDescriptor; + + // + // Build a request for READ_KEY + // + ClassSendDeviceIoControlSynchronous( + IOCTL_DVD_READ_STRUCTURE, + DeviceObject, + dvdReadStructure, + sizeof(DVD_READ_STRUCTURE), + sizeof(DVD_DESCRIPTOR_HEADER) + + sizeof(DVD_COPYRIGHT_DESCRIPTOR), + FALSE, + &ioStatus); + + // + // this is just to prevent bugs from creeping in + // if status is not set later in development + // + + status = ioStatus.Status; + + // + // handle errors + // + + if (!NT_SUCCESS(status)) { + KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE); + ExFreePool(dvdReadStructure); + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + // + // save the mediaRegionData before freeing the + // allocated memory + // + + mediaRegionData = + dvdCopyRight->RegionManagementInformation; + ExFreePool(dvdReadStructure); + + TraceLog((CdromDebugWarning, + "DvdDeviceControl (%p) => new mask is %x" + " MediaRegionData is %x\n", DeviceObject, + rpcKey->PreferredDriveRegionCode, + mediaRegionData)); + + // + // the media region must match the requested region + // for RPC0 drives for initial region selection + // + + if (((UCHAR)~(mediaRegionData | rpcKey->PreferredDriveRegionCode)) == 0) { + KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE); + status = STATUS_CSS_REGION_MISMATCH; + break; + } + + } + + // + // now try to set the region + // + + TraceLog((CdromDebugTrace, + "DvdDeviceControl (%p) => Soft-Setting " + "region of RPC1 device to %x\n", + DeviceObject, + rpcKey->PreferredDriveRegionCode + )); + + status = CdRomSetRpc0Settings(DeviceObject, + rpcKey->PreferredDriveRegionCode); + + if (!NT_SUCCESS(status)) { + TraceLog((CdromDebugWarning, + "DvdDeviceControl (%p) => Could not " + "set region code (%x)\n", + DeviceObject, status + )); + } else { + + TraceLog((CdromDebugTrace, + "DvdDeviceControl (%p) => New region set " + " for RPC1 drive\n", DeviceObject)); + + // + // if it worked, our extension is already updated. + // release the mutex + // + + DebugPrint ((4, "DvdDeviceControl (%p) => DVD current " + "region bitmap 0x%x\n", DeviceObject, + cdData->Rpc0SystemRegion)); + DebugPrint ((4, "DvdDeviceControl (%p) => DVD region " + " reset Count 0x%x\n", DeviceObject, + cdData->Rpc0SystemRegionResetCount)); + } + + } + + KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE); + break; + } // end of key->KeyType == DvdSetRpcKey + } // end of Rpc0Device hacks + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + break; + } + + case IOCTL_DVD_READ_KEY: { + + PDVD_COPY_PROTECT_KEY keyParameters = Irp->AssociatedIrp.SystemBuffer; + ULONG keyLength; + + TraceLog((CdromDebugTrace, + "DvdDeviceControl: [%p] IOCTL_DVD_READ_KEY\n", Irp)); + + if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { + TraceLog((CdromDebugWarning, + "DvdDeviceControl: License Failure\n")); + status = STATUS_COPY_PROTECTION_FAILURE; + break; + } + + if (cdData->DvdRpc0Device && cdData->Rpc0RetryRegistryCallback) { + TraceLog((CdromDebugWarning, + "DvdDeviceControl: PickRegion() from READ_KEY\n")); + CdRomPickDvdRegion(DeviceObject); + } + + + if(irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(DVD_COPY_PROTECT_KEY)) { + + TraceLog((CdromDebugWarning, + "DvdDeviceControl: EstablishDriveKey - challenge " + "key buffer too small\n")); + + status = STATUS_INVALID_PARAMETER; + break; + + } + + + switch(keyParameters->KeyType) { + + case DvdChallengeKey: + keyLength = DVD_CHALLENGE_KEY_LENGTH; + break; + + case DvdBusKey1: + case DvdBusKey2: + + keyLength = DVD_BUS_KEY_LENGTH; + break; + + case DvdTitleKey: + keyLength = DVD_TITLE_KEY_LENGTH; + break; + + case DvdAsf: + keyLength = DVD_ASF_LENGTH; + break; + + case DvdDiskKey: + keyLength = DVD_DISK_KEY_LENGTH; + break; + + case DvdGetRpcKey: + keyLength = DVD_RPC_KEY_LENGTH; + break; + + default: + keyLength = sizeof(DVD_COPY_PROTECT_KEY); + break; + } + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + keyLength) { + + TraceLog((CdromDebugWarning, + "DvdDeviceControl: EstablishDriveKey - output " + "buffer too small\n")); + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = keyLength; + break; + } + + if (keyParameters->KeyType == DvdGetRpcKey) { + + CdRomPickDvdRegion(DeviceObject); + } + + if ((keyParameters->KeyType == DvdGetRpcKey) && + (cdData->DvdRpc0Device)) { + + PDVD_RPC_KEY rpcKey; + rpcKey = (PDVD_RPC_KEY)keyParameters->KeyData; + RtlZeroMemory (rpcKey, sizeof (*rpcKey)); + + KeWaitForMutexObject( + &cdData->Rpc0RegionMutex, + UserRequest, + KernelMode, + FALSE, + NULL + ); + + // + // make up the data + // + rpcKey->UserResetsAvailable = cdData->Rpc0SystemRegionResetCount; + rpcKey->ManufacturerResetsAvailable = 0; + if (cdData->Rpc0SystemRegion == 0xff) { + rpcKey->TypeCode = 0; + } else { + rpcKey->TypeCode = 1; + } + rpcKey->RegionMask = (UCHAR) cdData->Rpc0SystemRegion; + rpcKey->RpcScheme = 1; + + KeReleaseMutex( + &cdData->Rpc0RegionMutex, + FALSE + ); + + Irp->IoStatus.Information = DVD_RPC_KEY_LENGTH; + status = STATUS_SUCCESS; + break; + + } else if (keyParameters->KeyType == DvdDiskKey) { + + PDVD_COPY_PROTECT_KEY keyHeader; + PDVD_READ_STRUCTURE readStructureRequest; + + // + // Special case - build a request to get the dvd structure + // so we can get the disk key. + // + + // + // save the key header so we can restore the interesting + // parts later + // + + keyHeader = ExAllocatePoolWithTag(NonPagedPool, + sizeof(DVD_COPY_PROTECT_KEY), + DVD_TAG_READ_KEY); + + if(keyHeader == NULL) { + + // + // Can't save the context so return an error + // + + TraceLog((CdromDebugWarning, + "DvdDeviceControl - READ_KEY: unable to " + "allocate context\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlCopyMemory(keyHeader, + Irp->AssociatedIrp.SystemBuffer, + sizeof(DVD_COPY_PROTECT_KEY)); + + IoCopyCurrentIrpStackLocationToNext(Irp); + + nextStack = IoGetNextIrpStackLocation(Irp); + + nextStack->Parameters.DeviceIoControl.IoControlCode = + IOCTL_DVD_READ_STRUCTURE; + + readStructureRequest = Irp->AssociatedIrp.SystemBuffer; + readStructureRequest->Format = DvdDiskKeyDescriptor; + readStructureRequest->BlockByteOffset.QuadPart = 0; + readStructureRequest->LayerNumber = 0; + readStructureRequest->SessionId = keyHeader->SessionId; + + nextStack->Parameters.DeviceIoControl.InputBufferLength = + sizeof(DVD_READ_STRUCTURE); + + nextStack->Parameters.DeviceIoControl.OutputBufferLength = + sizeof(READ_DVD_STRUCTURES_HEADER) + sizeof(DVD_DISK_KEY_DESCRIPTOR); + + IoSetCompletionRoutine(Irp, + CdRomDvdReadDiskKeyCompletion, + (PVOID) keyHeader, + TRUE, + TRUE, + TRUE); + + { + UCHAR uniqueAddress; + ClassAcquireRemoveLock(DeviceObject, (PIRP)&uniqueAddress); + ClassReleaseRemoveLock(DeviceObject, Irp); + + IoMarkIrpPending(Irp); + IoCallDriver(commonExtension->DeviceObject, Irp); + status = STATUS_PENDING; + + ClassReleaseRemoveLock(DeviceObject, (PIRP)&uniqueAddress); + } + + return STATUS_PENDING; + + } else { + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + } + return STATUS_PENDING; + } + + case IOCTL_DVD_END_SESSION: { + + PDVD_SESSION_ID sessionId = Irp->AssociatedIrp.SystemBuffer; + + TraceLog((CdromDebugTrace, + "DvdDeviceControl: [%p] END_SESSION\n", Irp)); + + if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { + TraceLog((CdromDebugWarning, + "DvdDeviceControl: License Failure\n")); + status = STATUS_COPY_PROTECTION_FAILURE; + break; + } + + if(irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(DVD_SESSION_ID)) { + + TraceLog((CdromDebugWarning, + "DvdDeviceControl: EndSession - input buffer too " + "small\n")); + status = STATUS_INVALID_PARAMETER; + break; + } + + IoMarkIrpPending(Irp); + + if(*sessionId == DVD_END_ALL_SESSIONS) { + + status = CdRomDvdEndAllSessionsCompletion(DeviceObject, Irp, NULL); + + if(status == STATUS_SUCCESS) { + + // + // Just complete the request - it was never issued to the + // lower device + // + + break; + + } else { + + return STATUS_PENDING; + + } + } + + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_DVD_GET_REGION: { + + PDVD_COPY_PROTECT_KEY copyProtectKey; + ULONG keyLength; + IO_STATUS_BLOCK ioStatus; + PDVD_DESCRIPTOR_HEADER dvdHeader; + PDVD_COPYRIGHT_DESCRIPTOR copyRightDescriptor; + PDVD_REGION dvdRegion; + PDVD_READ_STRUCTURE readStructure; + PDVD_RPC_KEY rpcKey; + + TraceLog((CdromDebugTrace, + "DvdDeviceControl: [%p] IOCTL_DVD_GET_REGION\n", Irp)); + + if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { + TraceLog((CdromDebugWarning, + "DvdDeviceControl: License Failure\n")); + status = STATUS_COPY_PROTECTION_FAILURE; + break; + } + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(DVD_REGION)) { + + TraceLog((CdromDebugWarning, + "DvdDeviceControl: output buffer DVD_REGION too small\n")); + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // figure out how much data buffer we need + // + + keyLength = max(sizeof(DVD_DESCRIPTOR_HEADER) + + sizeof(DVD_COPYRIGHT_DESCRIPTOR), + sizeof(DVD_READ_STRUCTURE) + ); + keyLength = max(keyLength, + DVD_RPC_KEY_LENGTH + ); + + // + // round the size to nearest ULONGLONG -- why? + // + + keyLength += sizeof(ULONGLONG) - (keyLength & (sizeof(ULONGLONG) - 1)); + + readStructure = ExAllocatePoolWithTag(NonPagedPool, + keyLength, + DVD_TAG_READ_KEY); + if (readStructure == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlZeroMemory (readStructure, keyLength); + readStructure->Format = DvdCopyrightDescriptor; + + // + // Build a request for READ_STRUCTURE + // + + ClassSendDeviceIoControlSynchronous( + IOCTL_DVD_READ_STRUCTURE, + DeviceObject, + readStructure, + keyLength, + sizeof(DVD_DESCRIPTOR_HEADER) + + sizeof(DVD_COPYRIGHT_DESCRIPTOR), + FALSE, + &ioStatus); + + status = ioStatus.Status; + + if (!NT_SUCCESS(status)) { + TraceLog((CdromDebugWarning, + "CdRomDvdGetRegion => read structure failed %x\n", + status)); + ExFreePool(readStructure); + break; + } + + // + // we got the copyright descriptor, so now get the region if possible + // + + dvdHeader = (PDVD_DESCRIPTOR_HEADER) readStructure; + copyRightDescriptor = (PDVD_COPYRIGHT_DESCRIPTOR) dvdHeader->Data; + + // + // the original irp's systembuffer has a copy of the info that + // should be passed down in the request + // + + dvdRegion = Irp->AssociatedIrp.SystemBuffer; + + dvdRegion->CopySystem = copyRightDescriptor->CopyrightProtectionType; + dvdRegion->RegionData = copyRightDescriptor->RegionManagementInformation; + + // + // now reuse the buffer to request the copy protection info + // + + copyProtectKey = (PDVD_COPY_PROTECT_KEY) readStructure; + RtlZeroMemory (copyProtectKey, DVD_RPC_KEY_LENGTH); + copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH; + copyProtectKey->KeyType = DvdGetRpcKey; + + // + // send a request for READ_KEY + // + + ClassSendDeviceIoControlSynchronous( + IOCTL_DVD_READ_KEY, + DeviceObject, + copyProtectKey, + DVD_RPC_KEY_LENGTH, + DVD_RPC_KEY_LENGTH, + FALSE, + &ioStatus); + status = ioStatus.Status; + + if (!NT_SUCCESS(status)) { + TraceLog((CdromDebugWarning, + "CdRomDvdGetRegion => read key failed %x\n", + status)); + ExFreePool(readStructure); + break; + } + + // + // the request succeeded. if a supported scheme is returned, + // then return the information to the caller + // + + rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData; + + if (rpcKey->RpcScheme == 1) { + + if (rpcKey->TypeCode) { + + dvdRegion->SystemRegion = ~rpcKey->RegionMask; + dvdRegion->ResetCount = rpcKey->UserResetsAvailable; + + } else { + + // + // the drive has not been set for any region + // + + dvdRegion->SystemRegion = 0; + dvdRegion->ResetCount = rpcKey->UserResetsAvailable; + } + Irp->IoStatus.Information = sizeof(DVD_REGION); + + } else { + + TraceLog((CdromDebugWarning, + "CdRomDvdGetRegion => rpcKey->RpcScheme != 1\n")); + status = STATUS_INVALID_DEVICE_REQUEST; + } + + ExFreePool(readStructure); + break; + } + + + case IOCTL_STORAGE_SET_READ_AHEAD: { + + if(irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(STORAGE_SET_READ_AHEAD)) { + + TraceLog((CdromDebugWarning, + "DvdDeviceControl: SetReadAhead buffer too small\n")); + status = STATUS_INVALID_PARAMETER; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_DISK_IS_WRITABLE: { + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + + } + + case IOCTL_DISK_GET_DRIVE_LAYOUT: { + + ULONG size; + + // + // we always fake zero or one partitions, and one partition + // structure is included in DRIVE_LAYOUT_INFORMATION + // + + size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[1]); + + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Get drive layout\n")); + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < size) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = size; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + + + } + case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: { + + ULONG size; + + // + // we always fake zero or one partitions, and one partition + // structure is included in DRIVE_LAYOUT_INFORMATION_EX + // + + size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]); + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Get drive layout ex\n")); + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < size) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = size; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + + } + + + case IOCTL_DISK_GET_PARTITION_INFO: { + + // + // Check that the buffer is large enough. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(PARTITION_INFORMATION)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + + } + case IOCTL_DISK_GET_PARTITION_INFO_EX: { + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(PARTITION_INFORMATION_EX)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX); + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + } + + case IOCTL_DISK_VERIFY: { + + TraceLog((CdromDebugTrace, + "IOCTL_DISK_VERIFY to device %p through irp %p\n", + DeviceObject, Irp)); + + // + // Validate buffer length. + // + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(VERIFY_INFORMATION)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + } + + case IOCTL_DISK_GET_LENGTH_INFO: { + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(GET_LENGTH_INFORMATION)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); + break; + } + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + } + + case IOCTL_CDROM_GET_CONFIGURATION: { + + PGET_CONFIGURATION_IOCTL_INPUT inputBuffer; + + TraceLog((CdromDebugTrace, + "IOCTL_CDROM_GET_CONFIGURATION to via irp %p\n", Irp)); + + // + // Validate buffer length. + // + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength != + sizeof(GET_CONFIGURATION_IOCTL_INPUT)) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(GET_CONFIGURATION_HEADER)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(GET_CONFIGURATION_HEADER); + break; + } + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength > 0xffff) { + // output buffer is too large + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + // + // also verify the arguments are reasonable. + // + + inputBuffer = Irp->AssociatedIrp.SystemBuffer; + if (inputBuffer->Feature > 0xffff) { + status = STATUS_INVALID_PARAMETER; + break; + } + if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) && + (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) && + (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL)) { + status = STATUS_INVALID_PARAMETER; + break; + } + if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1]) { + status = STATUS_INVALID_PARAMETER; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + return STATUS_PENDING; + + } + + default: { + + BOOLEAN synchronize = (KeGetCurrentIrql() == PASSIVE_LEVEL); + PKEVENT deviceControlEvent; + + // + // If the ioctl has come in at passive level then we will synchronize + // with our start-io routine when sending the ioctl. If the ioctl + // has come in at a higher interrupt level and it was not handled + // above then it's unlikely to be a request for the class DLL - however + // we'll still use it's common code to forward the request through. + // + + if (synchronize) { + + deviceControlEvent = ExAllocatePoolWithTag(NonPagedPool, + sizeof(KEVENT), + CDROM_TAG_DC_EVENT); + + if (deviceControlEvent == NULL) { + + // + // must complete this irp unsuccessful here + // + status = STATUS_INSUFFICIENT_RESOURCES; + break; + + } else { + + PIO_STACK_LOCATION currentStack; + + KeInitializeEvent(deviceControlEvent, NotificationEvent, FALSE); + + currentStack = IoGetCurrentIrpStackLocation(Irp); + nextStack = IoGetNextIrpStackLocation(Irp); + + // + // Copy the stack down a notch + // + + IoCopyCurrentIrpStackLocationToNext(Irp); + + IoSetCompletionRoutine( + Irp, + CdRomClassIoctlCompletion, + deviceControlEvent, + TRUE, + TRUE, + TRUE + ); + + IoSetNextIrpStackLocation(Irp); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + // + // Override volume verifies on this stack location so that we + // will be forced through the synchronization. Once this + // location goes away we get the old value back + // + + SET_FLAG(nextStack->Flags, SL_OVERRIDE_VERIFY_VOLUME); + + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + // + // Wait for CdRomClassIoctlCompletion to set the event. This + // ensures serialization remains intact for these unhandled device + // controls. + // + + KeWaitForSingleObject( + deviceControlEvent, + Executive, + KernelMode, + FALSE, + NULL); + + ExFreePool(deviceControlEvent); + + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: irp %p synchronized\n", Irp)); + + status = Irp->IoStatus.Status; + } + + } else { + status = STATUS_SUCCESS; + } + + // + // If an error occured then propagate that back up - we are no longer + // guaranteed synchronization and the upper layers will have to + // retry. + // + // If no error occured, call down to the class driver directly + // then start up the next request. + // + + if (NT_SUCCESS(status)) { + + UCHAR uniqueAddress; + + // + // The class device control routine will release the remove + // lock for this Irp. We need to make sure we have one + // available so that it's safe to call IoStartNextPacket + // + + if(synchronize) { + + ClassAcquireRemoveLock(DeviceObject, (PIRP)&uniqueAddress); + + } + + status = ClassDeviceControl(DeviceObject, Irp); + + if(synchronize) { + KeRaiseIrql(DISPATCH_LEVEL, &irql); + IoStartNextPacket(DeviceObject, FALSE); + KeLowerIrql(irql); + ClassReleaseRemoveLock(DeviceObject, (PIRP)&uniqueAddress); + } + return status; + + } + + // + // an error occurred (either STATUS_INSUFFICIENT_RESOURCES from + // attempting to synchronize or StartIo() error'd this one + // out), so we need to finish the irp, which is + // done at the end of this routine. + // + break; + + } // end default case + + } // end switch() + + if (status == STATUS_VERIFY_REQUIRED) { + + // + // If the status is verified required and this request + // should bypass verify required then retry the request. + // + + if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) { + + status = STATUS_IO_DEVICE_ERROR; + goto RetryControl; + + } + } + + if (IoIsErrorUserInduced(status)) { + + if (Irp->Tail.Overlay.Thread) { + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); + } + + } + + // + // Update IRP with completion status. + // + + Irp->IoStatus.Status = status; + + // + // Complete the request. + // + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT); + TraceLog((CdromDebugTrace, + "CdRomDeviceControl: Status is %lx\n", status)); + return status; + +} // end CdRomDeviceControl() + + +NTSTATUS +CdRomClassIoctlCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine signals the event used by CdRomDeviceControl to synchronize + class driver (and lower level driver) ioctls with cdrom's startio routine. + The irp completion is short-circuited so that CdRomDeviceControlDispatch + can reissue it once it wakes up. + +Arguments: + + DeviceObject - the device object + Irp - the request we are synchronizing + Context - a PKEVENT that we need to signal + +Return Value: + + NTSTATUS + +--*/ +{ + PKEVENT syncEvent = (PKEVENT) Context; + + TraceLog((CdromDebugTrace, + "CdRomClassIoctlCompletion: setting event for irp %p\n", Irp)); + + // + // We released the lock when we completed this request. Reacquire it. + // + + ClassAcquireRemoveLock(DeviceObject, Irp); + + KeSetEvent(syncEvent, IO_DISK_INCREMENT, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +CdRomDeviceControlCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData); + BOOLEAN use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE); + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION realIrpStack; + PIO_STACK_LOCATION realIrpNextStack; + + PSCSI_REQUEST_BLOCK srb = Context; + + PIRP realIrp = NULL; + + NTSTATUS status; + BOOLEAN retry; + + // + // Extract the 'real' irp from the irpstack. + // + + realIrp = (PIRP) irpStack->Parameters.Others.Argument2; + realIrpStack = IoGetCurrentIrpStackLocation(realIrp); + realIrpNextStack = IoGetNextIrpStackLocation(realIrp); + + // + // check that we've really got the correct irp + // + + ASSERT(realIrpNextStack->Parameters.Others.Argument3 == Irp); + + // + // Check SRB status for success of completing request. + // + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + ULONG retryInterval; + + TraceLog((CdromDebugTrace, + "CdRomDeviceControlCompletion: Irp %p, Srb %p Real Irp %p Status %lx\n", + Irp, + srb, + realIrp, + srb->SrbStatus)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + TraceLog((CdromDebugTrace, + "CdRomDeviceControlCompletion: Releasing Queue\n")); + ClassReleaseQueue(DeviceObject); + } + + + retry = ClassInterpretSenseInfo(DeviceObject, + srb, + irpStack->MajorFunction, + irpStack->Parameters.DeviceIoControl.IoControlCode, + MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1), + &status, + &retryInterval); + + TraceLog((CdromDebugTrace, + "CdRomDeviceControlCompletion: IRP will %sbe retried\n", + (retry ? "" : "not "))); + + // + // Some of the Device Controls need special cases on non-Success status's. + // + + if (realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { + if ((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_LAST_SESSION) || + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) || + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC_EX) || + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_CONTROL) || + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_VOLUME)) { + + if (status == STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; + retry = FALSE; + } + } + + if (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_Q_CHANNEL) { + PLAY_ACTIVE(fdoExtension) = FALSE; + } + } + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + // note: status gets overwritten here + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + + if (((realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) || + (realIrpStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) + ) && + ((realIrpStack->Parameters.DeviceIoControl.IoControlCode == + IOCTL_CDROM_CHECK_VERIFY) || + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == + IOCTL_STORAGE_CHECK_VERIFY) || + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == + IOCTL_STORAGE_CHECK_VERIFY2) || + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == + IOCTL_DISK_CHECK_VERIFY) + ) + ) { + + // + // Update the geometry information, as the media could have + // changed. The completion routine for this will complete + // the real irp and start the next packet. + // + + if (srb) { + if (srb->SenseInfoBuffer) { + ExFreePool(srb->SenseInfoBuffer); + } + if (srb->DataBuffer) { + ExFreePool(srb->DataBuffer); + } + ExFreePool(srb); + srb = NULL; + } + + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + Irp->MdlAddress = NULL; + } + + IoFreeIrp(Irp); + Irp = NULL; + + status = CdRomUpdateCapacity(fdoExtension, realIrp, NULL); + TraceLog((CdromDebugTrace, + "CdRomDeviceControlCompletion: [%p] " + "CdRomUpdateCapacity completed with status %lx\n", + realIrp, status)); + + // + // needed to update the capacity. + // the irp's already handed off to CdRomUpdateCapacity(). + // we've already free'd the current irp. + // nothing left to do in this code path. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + + } // end of ioctls to update capacity + + } + + if (retry && realIrpNextStack->Parameters.Others.Argument1--) { + + if (((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)) { + + // + // Retry request. + // + + TraceLog((CdromDebugWarning, + "Retry request %p - Calling StartIo\n", Irp)); + + + ExFreePool(srb->SenseInfoBuffer); + if (srb->DataBuffer) { + ExFreePool(srb->DataBuffer); + } + ExFreePool(srb); + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + realIrpNextStack->Parameters.Others.Argument3 = (PVOID)-1; + IoFreeIrp(Irp); + + CdRomRetryRequest(fdoExtension, realIrp, retryInterval, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // Exhausted retries. Fall through and complete the request with + // the appropriate status. + // + + } + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; + + } + + + if (NT_SUCCESS(status)) { + + BOOLEAN b = FALSE; + + + switch (realIrpStack->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_CDROM_GET_CONFIGURATION: { + RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer, + srb->DataBuffer, + srb->DataTransferLength); + realIrp->IoStatus.Information = srb->DataTransferLength; + break; + } + + case IOCTL_DISK_GET_LENGTH_INFO: { + + PGET_LENGTH_INFORMATION lengthInfo; + + CdRomInterpretReadCapacity(DeviceObject, + (PREAD_CAPACITY_DATA)srb->DataBuffer); + + lengthInfo = (PGET_LENGTH_INFORMATION)realIrp->AssociatedIrp.SystemBuffer; + lengthInfo->Length = commonExtension->PartitionLength; + realIrp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); + status = STATUS_SUCCESS; + break; + } + + case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: + case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX: { + + PDISK_GEOMETRY_EX geometryEx; + + CdRomInterpretReadCapacity(DeviceObject, + (PREAD_CAPACITY_DATA)srb->DataBuffer); + + geometryEx = (PDISK_GEOMETRY_EX)(realIrp->AssociatedIrp.SystemBuffer); + geometryEx->DiskSize = commonExtension->PartitionLength; + geometryEx->Geometry = fdoExtension->DiskGeometry; + realIrp->IoStatus.Information = + FIELD_OFFSET(DISK_GEOMETRY_EX, Data); + break; + } + + case IOCTL_DISK_GET_DRIVE_GEOMETRY: + case IOCTL_CDROM_GET_DRIVE_GEOMETRY: { + + PDISK_GEOMETRY geometry; + + CdRomInterpretReadCapacity(DeviceObject, + (PREAD_CAPACITY_DATA)srb->DataBuffer); + + geometry = (PDISK_GEOMETRY)(realIrp->AssociatedIrp.SystemBuffer); + *geometry = fdoExtension->DiskGeometry; + realIrp->IoStatus.Information = sizeof(DISK_GEOMETRY); + break; + } + + case IOCTL_DISK_VERIFY: { + // + // nothing to do but return the status... + // + break; + } + + case IOCTL_DISK_CHECK_VERIFY: + case IOCTL_STORAGE_CHECK_VERIFY: + case IOCTL_CDROM_CHECK_VERIFY: { + + if((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_CHECK_VERIFY) && + (realIrpStack->Parameters.DeviceIoControl.OutputBufferLength)) { + + *((PULONG)realIrp->AssociatedIrp.SystemBuffer) = + commonExtension->PartitionZeroExtension->MediaChangeCount; + + realIrp->IoStatus.Information = sizeof(ULONG); + } else { + realIrp->IoStatus.Information = 0; + } + + TraceLog((CdromDebugTrace, + "CdRomDeviceControlCompletion: [%p] completing " + "CHECK_VERIFY buddy irp %p\n", realIrp, Irp)); + break; + } + + case IOCTL_CDROM_READ_TOC_EX: { + + if (srb->DataTransferLength < MINIMUM_CDROM_READ_TOC_EX_SIZE) { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + // + // Copy the returned info into the user buffer. + // + + RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer, + srb->DataBuffer, + srb->DataTransferLength); + + // + // update information field. + // + + realIrp->IoStatus.Information = srb->DataTransferLength; + break; + } + + + case IOCTL_CDROM_GET_LAST_SESSION: + case IOCTL_CDROM_READ_TOC: { + + // + // Copy the returned info into the user buffer. + // + + RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer, + srb->DataBuffer, + srb->DataTransferLength); + + // + // update information field. + // + + realIrp->IoStatus.Information = srb->DataTransferLength; + break; + } + + case IOCTL_DVD_READ_STRUCTURE: { + + DVD_STRUCTURE_FORMAT format = ((PDVD_READ_STRUCTURE) realIrp->AssociatedIrp.SystemBuffer)->Format; + + PDVD_DESCRIPTOR_HEADER header = realIrp->AssociatedIrp.SystemBuffer; + + FOUR_BYTE fourByte; + PTWO_BYTE twoByte; + UCHAR tmp; + + TraceLog((CdromDebugTrace, + "DvdDeviceControlCompletion - IOCTL_DVD_READ_STRUCTURE: completing irp %p (buddy %p)\n", + Irp, + realIrp)); + + TraceLog((CdromDebugTrace, + "DvdDCCompletion - READ_STRUCTURE: descriptor format of %d\n", format)); + + RtlMoveMemory(header, + srb->DataBuffer, + srb->DataTransferLength); + + // + // Cook the data. There are a number of fields that really + // should be byte-swapped for the caller. + // + + TraceLog((CdromDebugInfo, + "DvdDCCompletion - READ_STRUCTURE:\n" + "\tHeader at %p\n" + "\tDvdDCCompletion - READ_STRUCTURE: data at %p\n" + "\tDataBuffer was at %p\n" + "\tDataTransferLength was %lx\n", + header, + header->Data, + srb->DataBuffer, + srb->DataTransferLength)); + + // + // First the fields in the header + // + + TraceLog((CdromDebugInfo, "READ_STRUCTURE: header->Length %lx -> ", + header->Length)); + REVERSE_SHORT(&header->Length); + TraceLog((CdromDebugInfo, "%lx\n", header->Length)); + + // + // Now the fields in the descriptor + // + + if(format == DvdPhysicalDescriptor) { + + PDVD_LAYER_DESCRIPTOR layer = (PDVD_LAYER_DESCRIPTOR) &(header->Data[0]); + + TraceLog((CdromDebugInfo, "READ_STRUCTURE: StartingDataSector %lx -> ", + layer->StartingDataSector)); + REVERSE_LONG(&(layer->StartingDataSector)); + TraceLog((CdromDebugInfo, "%lx\n", layer->StartingDataSector)); + + TraceLog((CdromDebugInfo, "READ_STRUCTURE: EndDataSector %lx -> ", + layer->EndDataSector)); + REVERSE_LONG(&(layer->EndDataSector)); + TraceLog((CdromDebugInfo, "%lx\n", layer->EndDataSector)); + + TraceLog((CdromDebugInfo, "READ_STRUCTURE: EndLayerZeroSector %lx -> ", + layer->EndLayerZeroSector)); + REVERSE_LONG(&(layer->EndLayerZeroSector)); + TraceLog((CdromDebugInfo, "%lx\n", layer->EndLayerZeroSector)); + } + + TraceLog((CdromDebugTrace, "Status is %lx\n", Irp->IoStatus.Status)); + TraceLog((CdromDebugTrace, "DvdDeviceControlCompletion - " + "IOCTL_DVD_READ_STRUCTURE: data transfer length of %d\n", + srb->DataTransferLength)); + + realIrp->IoStatus.Information = srb->DataTransferLength; + break; + } + + case IOCTL_DVD_READ_KEY: { + + PDVD_COPY_PROTECT_KEY copyProtectKey = realIrp->AssociatedIrp.SystemBuffer; + + PCDVD_KEY_HEADER keyHeader = srb->DataBuffer; + ULONG dataLength; + + ULONG transferLength = + srb->DataTransferLength - + FIELD_OFFSET(CDVD_KEY_HEADER, Data); + + // + // Adjust the data length to ignore the two reserved bytes in the + // header. + // + + dataLength = (keyHeader->DataLength[0] << 8) + + keyHeader->DataLength[1]; + dataLength -= 2; + + // + // take the minimum of the transferred length and the + // length as specified in the header. + // + + if(dataLength < transferLength) { + transferLength = dataLength; + } + + TraceLog((CdromDebugTrace, + "DvdDeviceControlCompletion: [%p] - READ_KEY with " + "transfer length of (%d or %d) bytes\n", + Irp, + dataLength, + srb->DataTransferLength - 2)); + + // + // Copy the key data into the return buffer + // + if(copyProtectKey->KeyType == DvdTitleKey) { + + RtlMoveMemory(copyProtectKey->KeyData, + keyHeader->Data + 1, + transferLength - 1); + copyProtectKey->KeyData[transferLength - 1] = 0; + + // + // If this is a title key then we need to copy the CGMS flags + // as well. + // + copyProtectKey->KeyFlags = *(keyHeader->Data); + + } else { + + RtlMoveMemory(copyProtectKey->KeyData, + keyHeader->Data, + transferLength); + } + + copyProtectKey->KeyLength = sizeof(DVD_COPY_PROTECT_KEY); + copyProtectKey->KeyLength += transferLength; + + realIrp->IoStatus.Information = copyProtectKey->KeyLength; + break; + } + + case IOCTL_DVD_START_SESSION: { + + PDVD_SESSION_ID sessionId = realIrp->AssociatedIrp.SystemBuffer; + + PCDVD_KEY_HEADER keyHeader = srb->DataBuffer; + PCDVD_REPORT_AGID_DATA keyData = (PCDVD_REPORT_AGID_DATA) keyHeader->Data; + + *sessionId = keyData->AGID; + + realIrp->IoStatus.Information = sizeof(DVD_SESSION_ID); + + break; + } + + case IOCTL_DVD_END_SESSION: + case IOCTL_DVD_SEND_KEY: + case IOCTL_DVD_SEND_KEY2: + + // + // nothing to return + // + realIrp->IoStatus.Information = 0; + break; + + case IOCTL_CDROM_PLAY_AUDIO_MSF: + + PLAY_ACTIVE(fdoExtension) = TRUE; + + break; + + case IOCTL_CDROM_READ_Q_CHANNEL: { + + PSUB_Q_CHANNEL_DATA userChannelData = realIrp->AssociatedIrp.SystemBuffer; + PCDROM_SUB_Q_DATA_FORMAT inputBuffer = realIrp->AssociatedIrp.SystemBuffer; + PSUB_Q_CHANNEL_DATA subQPtr = srb->DataBuffer; + +#if DBG + switch( inputBuffer->Format ) { + + case IOCTL_CDROM_CURRENT_POSITION: + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus )); + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR )); + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Control = 0x%x\n", subQPtr->CurrentPosition.Control )); + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Track = %u\n", subQPtr->CurrentPosition.TrackNumber )); + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Index = %u\n", subQPtr->CurrentPosition.IndexNumber )); + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Absolute Address = %x\n", *((PULONG)subQPtr->CurrentPosition.AbsoluteAddress) )); + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Relative Address = %x\n", *((PULONG)subQPtr->CurrentPosition.TrackRelativeAddress) )); + break; + + case IOCTL_CDROM_MEDIA_CATALOG: + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus )); + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Mcval is %u\n", subQPtr->MediaCatalog.Mcval )); + break; + + case IOCTL_CDROM_TRACK_ISRC: + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus )); + TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Tcval is %u\n", subQPtr->TrackIsrc.Tcval )); + break; + + } +#endif + + // + // Update the play active status. + // + + if (subQPtr->CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) { + + PLAY_ACTIVE(fdoExtension) = TRUE; + + } else { + + PLAY_ACTIVE(fdoExtension) = FALSE; + + } + + // + // Check if output buffer is large enough to contain + // the data. + // + + if (realIrpStack->Parameters.DeviceIoControl.OutputBufferLength < + srb->DataTransferLength) { + + srb->DataTransferLength = + realIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + } + + // + // Copy our buffer into users. + // + + RtlMoveMemory(userChannelData, + subQPtr, + srb->DataTransferLength); + + realIrp->IoStatus.Information = srb->DataTransferLength; + break; + } + + case IOCTL_CDROM_PAUSE_AUDIO: + + PLAY_ACTIVE(fdoExtension) = FALSE; + realIrp->IoStatus.Information = 0; + break; + + case IOCTL_CDROM_RESUME_AUDIO: + + realIrp->IoStatus.Information = 0; + break; + + case IOCTL_CDROM_SEEK_AUDIO_MSF: + + realIrp->IoStatus.Information = 0; + break; + + case IOCTL_CDROM_STOP_AUDIO: + + PLAY_ACTIVE(fdoExtension) = FALSE; + realIrp->IoStatus.Information = 0; + break; + + case IOCTL_CDROM_GET_CONTROL: { + + PCDROM_AUDIO_CONTROL audioControl = srb->DataBuffer; + PAUDIO_OUTPUT audioOutput; + ULONG bytesTransferred; + + audioOutput = ClassFindModePage((PCHAR)audioControl, + srb->DataTransferLength, + CDROM_AUDIO_CONTROL_PAGE, + use6Byte); + // + // Verify the page is as big as expected. + // + + bytesTransferred = (ULONG)((PCHAR) audioOutput - (PCHAR) audioControl) + + sizeof(AUDIO_OUTPUT); + + if (audioOutput != NULL && + srb->DataTransferLength >= bytesTransferred) { + + audioControl->LbaFormat = audioOutput->LbaFormat; + + audioControl->LogicalBlocksPerSecond = + (audioOutput->LogicalBlocksPerSecond[0] << (UCHAR)8) | + audioOutput->LogicalBlocksPerSecond[1]; + + realIrp->IoStatus.Information = sizeof(CDROM_AUDIO_CONTROL); + + } else { + realIrp->IoStatus.Information = 0; + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + case IOCTL_CDROM_GET_VOLUME: { + + PAUDIO_OUTPUT audioOutput; + PVOLUME_CONTROL volumeControl = srb->DataBuffer; + ULONG i; + ULONG bytesTransferred; + + audioOutput = ClassFindModePage((PCHAR)volumeControl, + srb->DataTransferLength, + CDROM_AUDIO_CONTROL_PAGE, + use6Byte); + + // + // Verify the page is as big as expected. + // + + bytesTransferred = (ULONG)((PCHAR) audioOutput - (PCHAR) volumeControl) + + sizeof(AUDIO_OUTPUT); + + if (audioOutput != NULL && + srb->DataTransferLength >= bytesTransferred) { + + for (i=0; i<4; i++) { + volumeControl->PortVolume[i] = + audioOutput->PortOutput[i].Volume; + } + + // + // Set bytes transferred in IRP. + // + + realIrp->IoStatus.Information = sizeof(VOLUME_CONTROL); + + } else { + realIrp->IoStatus.Information = 0; + status = STATUS_INVALID_DEVICE_REQUEST; + } + + break; + } + + case IOCTL_CDROM_SET_VOLUME: + + realIrp->IoStatus.Information = 0; + break; + + default: + + ASSERT(FALSE); + realIrp->IoStatus.Information = 0; + status = STATUS_INVALID_DEVICE_REQUEST; + + } // end switch() + } + + // + // Deallocate srb and sense buffer. + // + + if (srb) { + if (srb->DataBuffer) { + ExFreePool(srb->DataBuffer); + } + if (srb->SenseInfoBuffer) { + ExFreePool(srb->SenseInfoBuffer); + } + ExFreePool(srb); + } + + if (realIrp->PendingReturned) { + IoMarkIrpPending(realIrp); + } + + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + // + // Set status in completing IRP. + // + + realIrp->IoStatus.Status = status; + + // + // Set the hard error if necessary. + // + + if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) { + + // + // Store DeviceObject for filesystem, and clear + // in IoStatus.Information field. + // + + TraceLog((CdromDebugWarning, + "CdRomDeviceCompletion - Setting Hard Error on realIrp %p\n", + realIrp)); + if (realIrp->Tail.Overlay.Thread) { + IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject); + } + + realIrp->IoStatus.Information = 0; + } + + // + // note: must complete the realIrp, as the completed irp (above) + // was self-allocated. + // + + CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, realIrp); + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +CdRomSetVolumeIntermediateCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData); + BOOLEAN use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE); + PIO_STACK_LOCATION realIrpStack; + PIO_STACK_LOCATION realIrpNextStack; + PSCSI_REQUEST_BLOCK srb = Context; + PIRP realIrp = NULL; + NTSTATUS status; + BOOLEAN retry; + + // + // Extract the 'real' irp from the irpstack. + // + + realIrp = (PIRP) irpStack->Parameters.Others.Argument2; + realIrpStack = IoGetCurrentIrpStackLocation(realIrp); + realIrpNextStack = IoGetNextIrpStackLocation(realIrp); + + // + // Check SRB status for success of completing request. + // + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + ULONG retryInterval; + + TraceLog((CdromDebugTrace, + "CdRomSetVolumeIntermediateCompletion: Irp %p, Srb %p, Real Irp %p\n", + Irp, + srb, + realIrp)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ClassReleaseQueue(DeviceObject); + } + + + retry = ClassInterpretSenseInfo(DeviceObject, + srb, + irpStack->MajorFunction, + irpStack->Parameters.DeviceIoControl.IoControlCode, + MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1), + &status, + &retryInterval); + + if (status == STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; + retry = FALSE; + } + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && realIrpNextStack->Parameters.Others.Argument1--) { + + if (((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)) { + + // + // Retry request. + // + + TraceLog((CdromDebugWarning, + "Retry request %p - Calling StartIo\n", Irp)); + + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + CdRomRetryRequest(deviceExtension, + realIrp, + retryInterval, + FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; + + } + + // + // Exhausted retries. Fall through and complete the request with the appropriate status. + // + + } + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; + + } + + if (NT_SUCCESS(status)) { + + PAUDIO_OUTPUT audioInput = NULL; + PAUDIO_OUTPUT audioOutput; + PVOLUME_CONTROL volumeControl = realIrp->AssociatedIrp.SystemBuffer; + ULONG i,bytesTransferred,headerLength; + PVOID dataBuffer; + PCDB cdb; + + audioInput = ClassFindModePage((PCHAR)srb->DataBuffer, + srb->DataTransferLength, + CDROM_AUDIO_CONTROL_PAGE, + use6Byte); + + // + // Check to make sure the mode sense data is valid before we go on + // + + if(audioInput == NULL) { + + TraceLog((CdromDebugWarning, + "Mode Sense Page %d not found\n", + CDROM_AUDIO_CONTROL_PAGE)); + + realIrp->IoStatus.Information = 0; + realIrp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + goto SafeExit; + } + + if (use6Byte) { + headerLength = sizeof(MODE_PARAMETER_HEADER); + } else { + headerLength = sizeof(MODE_PARAMETER_HEADER10); + } + + bytesTransferred = sizeof(AUDIO_OUTPUT) + headerLength; + + // + // Allocate a new buffer for the mode select. + // + + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + bytesTransferred, + CDROM_TAG_VOLUME_INT); + + if (!dataBuffer) { + realIrp->IoStatus.Information = 0; + realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + goto SafeExit; + } + + RtlZeroMemory(dataBuffer, bytesTransferred); + + // + // Rebuild the data buffer to include the user requested values. + // + + audioOutput = (PAUDIO_OUTPUT) ((PCHAR) dataBuffer + headerLength); + + for (i=0; i<4; i++) { + audioOutput->PortOutput[i].Volume = + volumeControl->PortVolume[i]; + audioOutput->PortOutput[i].ChannelSelection = + audioInput->PortOutput[i].ChannelSelection; + } + + audioOutput->CodePage = CDROM_AUDIO_CONTROL_PAGE; + audioOutput->ParameterLength = sizeof(AUDIO_OUTPUT) - 2; + audioOutput->Immediate = MODE_SELECT_IMMEDIATE; + + // + // Free the old data buffer, mdl. + // + + IoFreeMdl(Irp->MdlAddress); + Irp->MdlAddress = NULL; + ExFreePool(srb->DataBuffer); + + // + // set the data buffer to new allocation, so it can be + // freed in the exit path + // + + srb->DataBuffer = dataBuffer; + + // + // rebuild the srb. + // + + cdb = (PCDB)srb->Cdb; + RtlZeroMemory(cdb, CDB12GENERIC_LENGTH); + + srb->SrbStatus = srb->ScsiStatus = 0; + srb->SrbFlags = deviceExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT); + srb->DataTransferLength = bytesTransferred; + + if (use6Byte) { + + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.ParameterListLength = (UCHAR) bytesTransferred; + cdb->MODE_SELECT.PFBit = 1; + srb->CdbLength = 6; + } else { + + cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; + cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR) (bytesTransferred >> 8); + cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR) (bytesTransferred & 0xFF); + cdb->MODE_SELECT10.PFBit = 1; + srb->CdbLength = 10; + } + + // + // Prepare the MDL + // + + Irp->MdlAddress = IoAllocateMdl(dataBuffer, + bytesTransferred, + FALSE, + FALSE, + (PIRP) NULL); + + if (!Irp->MdlAddress) { + realIrp->IoStatus.Information = 0; + realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + goto SafeExit; + } + + MmBuildMdlForNonPagedPool(Irp->MdlAddress); + + irpStack = IoGetNextIrpStackLocation(Irp); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; + irpStack->Parameters.Scsi.Srb = srb; + + // + // reset the irp completion. + // + + IoSetCompletionRoutine(Irp, + CdRomDeviceControlCompletion, + srb, + TRUE, + TRUE, + TRUE); + // + // Call the port driver. + // + + IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + +SafeExit: + + // + // Deallocate srb and sense buffer. + // + + if (srb) { + if (srb->DataBuffer) { + ExFreePool(srb->DataBuffer); + } + if (srb->SenseInfoBuffer) { + ExFreePool(srb->SenseInfoBuffer); + } + ExFreePool(srb); + } + + if (Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + if (realIrp->PendingReturned) { + IoMarkIrpPending(realIrp); + } + + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + // + // Set status in completing IRP. + // + + realIrp->IoStatus.Status = status; + + // + // Set the hard error if necessary. + // + + if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) { + + // + // Store DeviceObject for filesystem, and clear + // in IoStatus.Information field. + // + + if (realIrp->Tail.Overlay.Thread) { + IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject); + } + realIrp->IoStatus.Information = 0; + } + + CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, realIrp); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +CdRomDvdEndAllSessionsCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine will setup the next stack location to issue an end session + to the device. It will increment the session id in the system buffer + and issue an END_SESSION for that AGID if the AGID is valid. + + When the new AGID is > 3 this routine will complete the request. + +Arguments: + + DeviceObject - the device object for this drive + + Irp - the request + + Context - done + +Return Value: + + STATUS_MORE_PROCESSING_REQUIRED if there is another AGID to clear + status otherwise. + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + + PDVD_SESSION_ID sessionId = Irp->AssociatedIrp.SystemBuffer; + + NTSTATUS status; + + if(++(*sessionId) > MAX_COPY_PROTECT_AGID) { + + // + // We're done here - just return success and let the io system + // continue to complete it. + // + + return STATUS_SUCCESS; + + } + + IoCopyCurrentIrpStackLocationToNext(Irp); + + IoSetCompletionRoutine(Irp, + CdRomDvdEndAllSessionsCompletion, + NULL, + TRUE, + FALSE, + FALSE); + + IoMarkIrpPending(Irp); + + IoCallDriver(fdoExtension->CommonExtension.DeviceObject, Irp); + + // + // At this point we have to assume the irp may have already been + // completed. Ignore the returned status and return. + // + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +CdRomDvdReadDiskKeyCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine handles the completion of a request to obtain the disk + key from the dvd media. It will transform the raw 2K of key data into + a DVD_COPY_PROTECT_KEY structure and copy back the saved key parameters + from the context pointer before returning. + +Arguments: + + DeviceObject - + + Irp - + + Context - a DVD_COPY_PROTECT_KEY pointer which contains the key + parameters handed down by the caller. + +Return Value: + + STATUS_SUCCESS; + +--*/ + +{ + PDVD_COPY_PROTECT_KEY savedKey = Context; + + PREAD_DVD_STRUCTURES_HEADER rawKey = Irp->AssociatedIrp.SystemBuffer; + PDVD_COPY_PROTECT_KEY outputKey = Irp->AssociatedIrp.SystemBuffer; + + if (NT_SUCCESS(Irp->IoStatus.Status)) { + + // + // Shift the data down to its new position. + // + + RtlMoveMemory(outputKey->KeyData, + rawKey->Data, + sizeof(DVD_DISK_KEY_DESCRIPTOR)); + + RtlCopyMemory(outputKey, + savedKey, + sizeof(DVD_COPY_PROTECT_KEY)); + + outputKey->KeyLength = DVD_DISK_KEY_LENGTH; + + Irp->IoStatus.Information = DVD_DISK_KEY_LENGTH; + + } else { + + TraceLog((CdromDebugWarning, + "DiskKey Failed with status %x, %p (%x) bytes\n", + Irp->IoStatus.Status, + (PVOID)Irp->IoStatus.Information, + ((rawKey->Length[0] << 16) | rawKey->Length[1]) + )); + + } + + // + // release the context block + // + + ExFreePool(Context); + + return STATUS_SUCCESS; +} + +NTSTATUS +CdRomXACompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine executes when the port driver has completed a request. + It looks at the SRB status in the completing SRB and if not success + it checks for valid request sense buffer information. If valid, the + info is used to update status with more precise message of type of + error. This routine deallocates the SRB. + +Arguments: + + DeviceObject - Supplies the device object which represents the logical + unit. + + Irp - Supplies the Irp which has completed. + + Context - Supplies a pointer to the SRB. + +Return Value: + + NT status + +--*/ + +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK srb = Context; + PFUNCTIONAL_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + NTSTATUS status; + BOOLEAN retry; + + // + // Check SRB status for success of completing request. + // + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + ULONG retryInterval; + + TraceLog((CdromDebugTrace, "CdromXAComplete: IRP %p SRB %p Status %x\n", + Irp, srb, srb->SrbStatus)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ClassReleaseQueue(DeviceObject); + } + + retry = ClassInterpretSenseInfo( + DeviceObject, + srb, + irpStack->MajorFunction, + irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0, + MAXIMUM_RETRIES - irpStack->MinorFunction, // HACKHACK - REF #0001 + &status, + &retryInterval); + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry) { + + if (irpStack->MinorFunction != 0) { // HACKHACK - REF #0001 + + irpStack->MinorFunction--; // HACKHACK - REF #0001 + + // + // Retry request. + // + + TraceLog((CdromDebugWarning, + "CdRomXACompletion: Retry request %p (%x) - " + "Calling StartIo\n", Irp, irpStack->MinorFunction)); + + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb); + + // + // Call StartIo directly since IoStartNextPacket hasn't been called, + // the serialisation is still intact. + // + + CdRomRetryRequest(deviceExtension, + Irp, + retryInterval, + FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; + + } + + // + // Exhausted retries, fall through and complete the request + // with the appropriate status + // + + TraceLog((CdromDebugWarning, + "CdRomXACompletion: Retries exhausted for irp %p\n", + Irp)); + + } + + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; + + } // end if (SRB_STATUS(srb->SrbStatus) ... + + // + // Return SRB to nonpaged pool. + // + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb); + + // + // Set status in completing IRP. + // + + Irp->IoStatus.Status = status; + + // + // Set the hard error if necessary. + // + + if (!NT_SUCCESS(status) && + IoIsErrorUserInduced(status) && + Irp->Tail.Overlay.Thread != NULL ) { + + // + // Store DeviceObject for filesystem, and clear + // in IoStatus.Information field. + // + + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); + Irp->IoStatus.Information = 0; + } + + // + // If pending has be returned for this irp then mark the current stack as + // pending. + // + + if (Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + { + KIRQL oldIrql = KeRaiseIrqlToDpcLevel(); + IoStartNextPacket(DeviceObject, FALSE); + KeLowerIrql(oldIrql); + } + ClassReleaseRemoveLock(DeviceObject, Irp); + + return status; +} + + +VOID +CdRomDeviceControlDvdReadStructure( + IN PDEVICE_OBJECT Fdo, + IN PIRP OriginalIrp, + IN PIRP NewIrp, + IN PSCSI_REQUEST_BLOCK Srb + ) +{ + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp); + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCDB cdb = (PCDB)Srb->Cdb; + PVOID dataBuffer; + + PDVD_READ_STRUCTURE request; + USHORT dataLength; + ULONG blockNumber; + PFOUR_BYTE fourByte; + + dataLength = + (USHORT)currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + + request = OriginalIrp->AssociatedIrp.SystemBuffer; + blockNumber = + (ULONG)(request->BlockByteOffset.QuadPart >> fdoExtension->SectorShift); + fourByte = (PFOUR_BYTE) &blockNumber; + + Srb->CdbLength = 12; + Srb->TimeOutValue = fdoExtension->TimeOutValue; + Srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN); + + cdb->READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE; + cdb->READ_DVD_STRUCTURE.RMDBlockNumber[0] = fourByte->Byte3; + cdb->READ_DVD_STRUCTURE.RMDBlockNumber[1] = fourByte->Byte2; + cdb->READ_DVD_STRUCTURE.RMDBlockNumber[2] = fourByte->Byte1; + cdb->READ_DVD_STRUCTURE.RMDBlockNumber[3] = fourByte->Byte0; + cdb->READ_DVD_STRUCTURE.LayerNumber = request->LayerNumber; + cdb->READ_DVD_STRUCTURE.Format = (UCHAR)request->Format; + +#if DBG + { + if ((UCHAR)request->Format > DvdMaxDescriptor) { + TraceLog((CdromDebugWarning, + "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n", + (UCHAR)request->Format, + READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor], + dataLength + )); + } else { + TraceLog((CdromDebugWarning, + "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n", + (UCHAR)request->Format, + READ_DVD_STRUCTURE_FORMAT_STRINGS[(UCHAR)request->Format], + dataLength + )); + } + } +#endif // DBG + + if (request->Format == DvdDiskKeyDescriptor) { + + cdb->READ_DVD_STRUCTURE.AGID = (UCHAR) request->SessionId; + + } + + cdb->READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataLength >> 8); + cdb->READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataLength & 0xff); + Srb->DataTransferLength = dataLength; + + + + dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + dataLength, + DVD_TAG_READ_STRUCTURE); + + if (!dataBuffer) { + ExFreePool(Srb->SenseInfoBuffer); + ExFreePool(Srb); + IoFreeIrp(NewIrp); + OriginalIrp->IoStatus.Information = 0; + OriginalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(OriginalIrp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp); + return; + } + RtlZeroMemory(dataBuffer, dataLength); + + NewIrp->MdlAddress = IoAllocateMdl(dataBuffer, + currentIrpStack->Parameters.Read.Length, + FALSE, + FALSE, + (PIRP) NULL); + + if (NewIrp->MdlAddress == NULL) { + ExFreePool(dataBuffer); + ExFreePool(Srb->SenseInfoBuffer); + ExFreePool(Srb); + IoFreeIrp(NewIrp); + OriginalIrp->IoStatus.Information = 0; + OriginalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + BAIL_OUT(OriginalIrp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(NewIrp->MdlAddress); + + Srb->DataBuffer = dataBuffer; + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp); + + return; +} + + +VOID +CdRomDeviceControlDvdEndSession( + IN PDEVICE_OBJECT Fdo, + IN PIRP OriginalIrp, + IN PIRP NewIrp, + IN PSCSI_REQUEST_BLOCK Srb + ) +{ + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp); + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCDB cdb = (PCDB)Srb->Cdb; + + PDVD_SESSION_ID sessionId = OriginalIrp->AssociatedIrp.SystemBuffer; + + Srb->CdbLength = 12; + Srb->TimeOutValue = fdoExtension->TimeOutValue; + Srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); + + cdb->SEND_KEY.OperationCode = SCSIOP_SEND_KEY; + cdb->SEND_KEY.AGID = (UCHAR) (*sessionId); + cdb->SEND_KEY.KeyFormat = DVD_INVALIDATE_AGID; + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp); + return; + +} + + +VOID +CdRomDeviceControlDvdStartSessionReadKey( + IN PDEVICE_OBJECT Fdo, + IN PIRP OriginalIrp, + IN PIRP NewIrp, + IN PSCSI_REQUEST_BLOCK Srb + ) +{ + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp); + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCDB cdb = (PCDB)Srb->Cdb; + NTSTATUS status; + + PDVD_COPY_PROTECT_KEY keyParameters; + PCDVD_KEY_HEADER keyBuffer = NULL; + + ULONG keyLength; + + ULONG allocationLength; + PFOUR_BYTE fourByte; + + // + // Both of these use REPORT_KEY commands. + // Determine the size of the input buffer + // + + if(currentIrpStack->Parameters.DeviceIoControl.IoControlCode == + IOCTL_DVD_READ_KEY) { + + keyParameters = OriginalIrp->AssociatedIrp.SystemBuffer; + + keyLength = sizeof(CDVD_KEY_HEADER) + + (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength - + sizeof(DVD_COPY_PROTECT_KEY)); + } else { + + keyParameters = NULL; + keyLength = sizeof(CDVD_KEY_HEADER) + + sizeof(CDVD_REPORT_AGID_DATA); + } + + TRY { + + keyBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + keyLength, + DVD_TAG_READ_KEY); + + if(keyBuffer == NULL) { + + TraceLog((CdromDebugWarning, + "IOCTL_DVD_READ_KEY - couldn't allocate " + "%d byte buffer for key\n", + keyLength)); + status = STATUS_INSUFFICIENT_RESOURCES; + LEAVE; + } + + + NewIrp->MdlAddress = IoAllocateMdl(keyBuffer, + keyLength, + FALSE, + FALSE, + (PIRP) NULL); + + if(NewIrp->MdlAddress == NULL) { + + TraceLog((CdromDebugWarning, + "IOCTL_DVD_READ_KEY - couldn't create mdl\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + LEAVE; + } + + MmBuildMdlForNonPagedPool(NewIrp->MdlAddress); + + Srb->DataBuffer = keyBuffer; + Srb->CdbLength = 12; + + cdb->REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY; + + allocationLength = keyLength; + fourByte = (PFOUR_BYTE) &allocationLength; + cdb->REPORT_KEY.AllocationLength[0] = fourByte->Byte1; + cdb->REPORT_KEY.AllocationLength[1] = fourByte->Byte0; + + Srb->DataTransferLength = keyLength; + + // + // set the specific parameters.... + // + + if(currentIrpStack->Parameters.DeviceIoControl.IoControlCode == + IOCTL_DVD_READ_KEY) { + + if(keyParameters->KeyType == DvdTitleKey) { + + ULONG logicalBlockAddress; + + logicalBlockAddress = (ULONG) + (keyParameters->Parameters.TitleOffset.QuadPart >> + fdoExtension->SectorShift); + + fourByte = (PFOUR_BYTE) &(logicalBlockAddress); + + cdb->REPORT_KEY.LogicalBlockAddress[0] = fourByte->Byte3; + cdb->REPORT_KEY.LogicalBlockAddress[1] = fourByte->Byte2; + cdb->REPORT_KEY.LogicalBlockAddress[2] = fourByte->Byte1; + cdb->REPORT_KEY.LogicalBlockAddress[3] = fourByte->Byte0; + } + + cdb->REPORT_KEY.KeyFormat = (UCHAR)keyParameters->KeyType; + cdb->REPORT_KEY.AGID = (UCHAR) keyParameters->SessionId; + TraceLog((CdromDebugWarning, + "CdRomDvdReadKey => sending irp %p for irp %p (%s)\n", + NewIrp, OriginalIrp, "READ_KEY")); + + } else { + + cdb->REPORT_KEY.KeyFormat = DVD_REPORT_AGID; + cdb->REPORT_KEY.AGID = 0; + TraceLog((CdromDebugWarning, + "CdRomDvdReadKey => sending irp %p for irp %p (%s)\n", + NewIrp, OriginalIrp, "START_SESSION")); + } + + Srb->TimeOutValue = fdoExtension->TimeOutValue; + Srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp); + + status = STATUS_SUCCESS; + + } FINALLY { + + if (!NT_SUCCESS(status)) { + + // + // An error occured during setup - free resources and + // complete this request. + // + if (NewIrp->MdlAddress != NULL) { + IoFreeMdl(NewIrp->MdlAddress); + } + + if (keyBuffer != NULL) { + ExFreePool(keyBuffer); + } + ExFreePool(Srb->SenseInfoBuffer); + ExFreePool(Srb); + IoFreeIrp(NewIrp); + + OriginalIrp->IoStatus.Information = 0; + OriginalIrp->IoStatus.Status = status; + + BAIL_OUT(OriginalIrp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp); + + } // end !NT_SUCCESS + } + return; +} + + +VOID +CdRomDeviceControlDvdSendKey( + IN PDEVICE_OBJECT Fdo, + IN PIRP OriginalIrp, + IN PIRP NewIrp, + IN PSCSI_REQUEST_BLOCK Srb + ) +{ + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp); + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCDB cdb = (PCDB)Srb->Cdb; + + PDVD_COPY_PROTECT_KEY key; + PCDVD_KEY_HEADER keyBuffer = NULL; + + NTSTATUS status; + ULONG keyLength; + PFOUR_BYTE fourByte; + + key = OriginalIrp->AssociatedIrp.SystemBuffer; + keyLength = (key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY)) + + sizeof(CDVD_KEY_HEADER); + + TRY { + + keyBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + keyLength, + DVD_TAG_SEND_KEY); + + if(keyBuffer == NULL) { + + TraceLog((CdromDebugWarning, + "IOCTL_DVD_SEND_KEY - couldn't allocate " + "%d byte buffer for key\n", + keyLength)); + status = STATUS_INSUFFICIENT_RESOURCES; + LEAVE; + } + + RtlZeroMemory(keyBuffer, keyLength); + + // + // keylength is decremented here by two because the + // datalength does not include the header, which is two + // bytes. keylength is immediately incremented later + // by the same amount. + // + + keyLength -= 2; + fourByte = (PFOUR_BYTE) &keyLength; + keyBuffer->DataLength[0] = fourByte->Byte1; + keyBuffer->DataLength[1] = fourByte->Byte0; + keyLength += 2; + + // + // copy the user's buffer to our own allocated buffer + // + + RtlMoveMemory(keyBuffer->Data, + key->KeyData, + key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY)); + + + NewIrp->MdlAddress = IoAllocateMdl(keyBuffer, + keyLength, + FALSE, + FALSE, + (PIRP) NULL); + + if(NewIrp->MdlAddress == NULL) { + TraceLog((CdromDebugWarning, + "IOCTL_DVD_SEND_KEY - couldn't create mdl\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + LEAVE; + } + + + MmBuildMdlForNonPagedPool(NewIrp->MdlAddress); + + Srb->CdbLength = 12; + Srb->DataBuffer = keyBuffer; + Srb->DataTransferLength = keyLength; + + Srb->TimeOutValue = fdoExtension->TimeOutValue; + Srb->SrbFlags = fdoExtension->SrbFlags; + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT); + + cdb->REPORT_KEY.OperationCode = SCSIOP_SEND_KEY; + + fourByte = (PFOUR_BYTE) &keyLength; + + cdb->SEND_KEY.ParameterListLength[0] = fourByte->Byte1; + cdb->SEND_KEY.ParameterListLength[1] = fourByte->Byte0; + cdb->SEND_KEY.KeyFormat = (UCHAR)key->KeyType; + cdb->SEND_KEY.AGID = (UCHAR) key->SessionId; + + if (key->KeyType == DvdSetRpcKey) { + TraceLog((CdromDebugWarning, + "IOCTL_DVD_SEND_KEY - Setting RPC2 drive region\n")); + } else { + TraceLog((CdromDebugWarning, + "IOCTL_DVD_SEND_KEY - key type %x\n", key->KeyType)); + } + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp); + + status = STATUS_SUCCESS; + + } FINALLY { + + if (!NT_SUCCESS(status)) { + + // + // An error occured during setup - free resources and + // complete this request. + // + + if (NewIrp->MdlAddress != NULL) { + IoFreeMdl(NewIrp->MdlAddress); + } + + if (keyBuffer != NULL) { + ExFreePool(keyBuffer); + } + + ExFreePool(Srb->SenseInfoBuffer); + ExFreePool(Srb); + IoFreeIrp(NewIrp); + + OriginalIrp->IoStatus.Information = 0; + OriginalIrp->IoStatus.Status = status; + + BAIL_OUT(OriginalIrp); + CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp); + + } + } + + return; +} + + +VOID +CdRomInterpretReadCapacity( + IN PDEVICE_OBJECT Fdo, + IN PREAD_CAPACITY_DATA ReadCapacityBuffer + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + ULONG lastSector; + ULONG bps; + ULONG lastBit; + ULONG tmp; + + ASSERT(ReadCapacityBuffer); + ASSERT(commonExtension->IsFdo); + + TraceLog((CdromDebugError, + "CdRomInterpretReadCapacity: Entering\n")); + + // + // Swizzle bytes from Read Capacity and translate into + // the necessary geometry information in the device extension. + // + + tmp = ReadCapacityBuffer->BytesPerBlock; + ((PFOUR_BYTE)&bps)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3; + ((PFOUR_BYTE)&bps)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2; + ((PFOUR_BYTE)&bps)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1; + ((PFOUR_BYTE)&bps)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0; + + // + // Insure that bps is a power of 2. + // This corrects a problem with the HP 4020i CDR where it + // returns an incorrect number for bytes per sector. + // + + if (!bps) { + bps = 2048; + } else { + lastBit = (ULONG) -1; + while (bps) { + lastBit++; + bps = bps >> 1; + } + bps = 1 << lastBit; + } + + fdoExtension->DiskGeometry.BytesPerSector = bps; + + TraceLog((CdromDebugTrace, "CdRomInterpretReadCapacity: Calculated bps %#x\n", + fdoExtension->DiskGeometry.BytesPerSector)); + + // + // Copy last sector in reverse byte order. + // + + tmp = ReadCapacityBuffer->LogicalBlockAddress; + ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3; + ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2; + ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1; + ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0; + + // + // Calculate sector to byte shift. + // + + WHICH_BIT(bps, fdoExtension->SectorShift); + + TraceLog((CdromDebugTrace,"CdRomInterpretReadCapacity: Sector size is %d\n", + fdoExtension->DiskGeometry.BytesPerSector)); + + TraceLog((CdromDebugTrace,"CdRomInterpretReadCapacity: Number of Sectors is %d\n", + lastSector + 1)); + + // + // Calculate media capacity in bytes. + // + + commonExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1); + + // + // we've defaulted to 32/64 forever. don't want to change this now... + // + + fdoExtension->DiskGeometry.TracksPerCylinder = 0x40; + fdoExtension->DiskGeometry.SectorsPerTrack = 0x20; + + // + // Calculate number of cylinders. + // + + fdoExtension->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1) / (32 * 64)); + + commonExtension->PartitionLength.QuadPart = + (commonExtension->PartitionLength.QuadPart << fdoExtension->SectorShift); + + + ASSERT(TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)); + + // + // This device supports removable media. + // + + fdoExtension->DiskGeometry.MediaType = RemovableMedia; + + return; +} + + diff --git a/drivers/storage/class/cdrom_new/mmc.c b/drivers/storage/class/cdrom_new/mmc.c new file mode 100644 index 00000000000..15319bb6c68 --- /dev/null +++ b/drivers/storage/class/cdrom_new/mmc.c @@ -0,0 +1,1422 @@ +/*-- + +Copyright (C) Microsoft Corporation, 2000 + +Module Name: + + mmc.c + +Abstract: + + This file is used to extend cdrom.sys to detect and use mmc-compatible + drives' capabilities more wisely. + +Environment: + + kernel mode only + +Notes: + + SCSI Tape, CDRom and Disk class drivers share common routines + that can be found in the CLASS directory (..\ntos\dd\class). + +Revision History: + +--*/ + +#include "ntddk.h" +#include "classpnp.h" +#include "cdrom.h" + + + +NTSTATUS +CdRomGetConfiguration( + IN PDEVICE_OBJECT Fdo, + OUT PGET_CONFIGURATION_HEADER *Buffer, + OUT PULONG BytesReturned, + IN FEATURE_NUMBER StartingFeature, + IN ULONG RequestedType + ); +VOID +CdRompPrintAllFeaturePages( + IN PGET_CONFIGURATION_HEADER Buffer, + IN ULONG Usable + ); +NTSTATUS +CdRomUpdateMmcDriveCapabilitiesCompletion( + IN PDEVICE_OBJECT Unused, + IN PIRP Irp, + IN PDEVICE_OBJECT Fdo + ); +VOID +CdRomPrepareUpdateCapabilitiesIrp( + PDEVICE_OBJECT Fdo + ); + +/*++ + + NOT DOCUMENTED YET - may be called at up to DISPATCH_LEVEL + if memory is non-paged + PRESUMES ALL DATA IS ACCESSIBLE based on FeatureBuffer + +--*/ +VOID +CdRomFindProfileInProfiles( + IN PFEATURE_DATA_PROFILE_LIST ProfileHeader, + IN FEATURE_PROFILE_TYPE ProfileToFind, + OUT PBOOLEAN Found + ) +{ + PFEATURE_DATA_PROFILE_LIST_EX profile; + ULONG numberOfProfiles; + ULONG i; + + ASSERT((ProfileHeader->Header.AdditionalLength % 4) == 0); + + *Found = FALSE; + + numberOfProfiles = ProfileHeader->Header.AdditionalLength / 4; + profile = ProfileHeader->Profiles; // zero-sized array + + for (i = 0; i < numberOfProfiles; i++) { + + FEATURE_PROFILE_TYPE currentProfile; + + currentProfile = + (profile->ProfileNumber[0] << 8) | + (profile->ProfileNumber[1] & 0xff); + + if (currentProfile == ProfileToFind) { + + *Found = TRUE; + + } + + profile++; + } + return; + +} + + +/*++ + + NOT DOCUMENTED YET - may be called at up to DISPATCH_LEVEL + if memory is non-paged + +--*/ +PVOID +CdRomFindFeaturePage( + IN PGET_CONFIGURATION_HEADER FeatureBuffer, + IN ULONG Length, + IN FEATURE_NUMBER Feature + ) +{ + PUCHAR buffer; + PUCHAR limit; + + if (Length < sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER)) { + return NULL; + } + + // + // set limit to point to first illegal address + // + + limit = (PUCHAR)FeatureBuffer; + limit += Length; + + // + // set buffer to point to first page + // + + buffer = FeatureBuffer->Data; + + // + // loop through each page until we find the requested one, or + // until it's not safe to access the entire feature header + // (if equal, have exactly enough for the feature header) + // + while (buffer + sizeof(FEATURE_HEADER) <= limit) { + + PFEATURE_HEADER header = (PFEATURE_HEADER)buffer; + FEATURE_NUMBER thisFeature; + + thisFeature = + (header->FeatureCode[0] << 8) | + (header->FeatureCode[1]); + + if (thisFeature == Feature) { + + PUCHAR temp; + + // + // if don't have enough memory to safely access all the feature + // information, return NULL + // + temp = buffer; + temp += sizeof(FEATURE_HEADER); + temp += header->AdditionalLength; + + if (temp > limit) { + + // + // this means the transfer was cut-off, an insufficiently + // small buffer was given, or other arbitrary error. since + // it's not safe to view the amount of data (even though + // the header is safe) in this feature, pretend it wasn't + // transferred at all... + // + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "Feature %x exists, but not safe to access all its " + "data. returning NULL\n", Feature)); + return NULL; + } else { + return buffer; + } + } + + if (header->AdditionalLength % 4) { + ASSERT(!"Feature page AdditionalLength field must be integral multiple of 4!\n"); + return NULL; + } + + buffer += sizeof(FEATURE_HEADER); + buffer += header->AdditionalLength; + + } + return NULL; +} + +/*++ + +Private so we can later expose to someone wanting to use a preallocated buffer + +--*/ +NTSTATUS +CdRompGetConfiguration( + IN PDEVICE_OBJECT Fdo, + IN PGET_CONFIGURATION_HEADER Buffer, + IN ULONG BufferSize, + OUT PULONG ValidBytes, + IN FEATURE_NUMBER StartingFeature, + IN ULONG RequestedType + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PCDROM_DATA cdData; + SCSI_REQUEST_BLOCK srb; + PCDB cdb; + ULONG_PTR returned; + NTSTATUS status; + + PAGED_CODE(); + ASSERT(Buffer); + ASSERT(ValidBytes); + + *ValidBytes = 0; + returned = 0; + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + RtlZeroMemory(Buffer, BufferSize); + + fdoExtension = Fdo->DeviceExtension; + cdData = (PCDROM_DATA)(fdoExtension->CommonExtension.DriverData); + + if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_GET_CONFIG_SUPPORT)) { + return STATUS_INVALID_DEVICE_REQUEST; + } + + srb.TimeOutValue = CDROM_GET_CONFIGURATION_TIMEOUT; + srb.CdbLength = 10; + + cdb = (PCDB)srb.Cdb; + cdb->GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION; + cdb->GET_CONFIGURATION.RequestType = (UCHAR)RequestedType; + cdb->GET_CONFIGURATION.StartingFeature[0] = (UCHAR)(StartingFeature >> 8); + cdb->GET_CONFIGURATION.StartingFeature[1] = (UCHAR)(StartingFeature & 0xff); + cdb->GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(BufferSize >> 8); + cdb->GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(BufferSize & 0xff); + + status = ClassSendSrbSynchronous(Fdo, &srb, Buffer, + BufferSize, FALSE); + returned = srb.DataTransferLength; + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: Status was %x\n", status)); + + if (NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) { + + // + // if returned more than can be stored in a ULONG, return false + // + + if (returned > (ULONG)(-1)) { + return STATUS_UNSUCCESSFUL; + } + ASSERT(returned <= BufferSize); + *ValidBytes = (ULONG)returned; + return STATUS_SUCCESS; + + } else { + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: failed %x\n", status)); + return status; + + } + ASSERT(FALSE); + return STATUS_UNSUCCESSFUL; +} + +/*++ + + Allocates buffer with configuration info, returns STATUS_SUCCESS + or an error if one occurred + + NOTE: does not handle case where more than 65000 bytes are returned, + which requires multiple calls with different starting feature + numbers. + +--*/ +NTSTATUS +CdRomGetConfiguration( + IN PDEVICE_OBJECT Fdo, + OUT PGET_CONFIGURATION_HEADER *Buffer, + OUT PULONG BytesReturned, + IN FEATURE_NUMBER StartingFeature, + IN ULONG RequestedType + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + GET_CONFIGURATION_HEADER header; // eight bytes, not a lot + PGET_CONFIGURATION_HEADER buffer; + ULONG returned; + ULONG size; + ULONG i; + NTSTATUS status; + + PAGED_CODE(); + + + fdoExtension = Fdo->DeviceExtension; + *Buffer = NULL; + *BytesReturned = 0; + + buffer = NULL; + returned = 0; + + // + // send the first request down to just get the header + // + + status = CdRompGetConfiguration(Fdo, &header, sizeof(header), + &returned, StartingFeature, RequestedType); + + if (!NT_SUCCESS(status)) { + return status; + } + + // + // now try again, using information returned to allocate + // just enough memory + // + + size = header.DataLength[0] << 24 | + header.DataLength[1] << 16 | + header.DataLength[2] << 8 | + header.DataLength[3] << 0 ; + + + for (i = 0; i < 4; i++) { + + // + // the datalength field is the size *following* + // itself, so adjust accordingly + // + + size += 4*sizeof(UCHAR); + + // + // make sure the size is reasonable + // + + if (size <= sizeof(FEATURE_HEADER)) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: drive reports only %x bytes?\n", + size)); + return STATUS_UNSUCCESSFUL; + } + + // + // allocate the memory + // + + buffer = (PGET_CONFIGURATION_HEADER) + ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + size, + CDROM_TAG_FEATURE); + + if (buffer == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // send the first request down to just get the header + // + + status = CdRompGetConfiguration(Fdo, buffer, size, &returned, + StartingFeature, RequestedType); + + if (!NT_SUCCESS(status)) { + ExFreePool(buffer); + return status; + } + + if (returned > size) { + ExFreePool(buffer); + return STATUS_INTERNAL_ERROR; + } + + returned = buffer->DataLength[0] << 24 | + buffer->DataLength[1] << 16 | + buffer->DataLength[2] << 8 | + buffer->DataLength[3] << 0 ; + returned += 4*sizeof(UCHAR); + + if (returned <= size) { + *Buffer = buffer; + *BytesReturned = size; // amount of 'safe' memory + return STATUS_SUCCESS; + } + + // + // else retry using the new size.... + // + + size = returned; + ExFreePool(buffer); + buffer = NULL; + + } + + // + // it failed after a number of attempts, so just fail. + // + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdRomGetConfiguration: Failed %d attempts to get all feature " + "information\n", i)); + return STATUS_IO_DEVICE_ERROR; +} + +VOID +CdRomIsDeviceMmcDevice( + IN PDEVICE_OBJECT Fdo, + OUT PBOOLEAN IsMmc + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCDROM_DATA cdData = commonExtension->DriverData; + GET_CONFIGURATION_HEADER localHeader; + NTSTATUS status; + ULONG usable; + ULONG size; + ULONG previouslyFailed; + + PAGED_CODE(); + ASSERT( commonExtension->IsFdo ); + + *IsMmc = FALSE; + + // + // read the registry in case the drive failed previously, + // and a timeout is occurring. + // + + previouslyFailed = FALSE; + ClassGetDeviceParameter(fdoExtension, + CDROM_SUBKEY_NAME, + CDROM_NON_MMC_DRIVE_NAME, + &previouslyFailed + ); + + if (previouslyFailed) { + SET_FLAG(cdData->HackFlags, CDROM_HACK_BAD_GET_CONFIG_SUPPORT); + } + + // + // check for the following profiles: + // + // ProfileList + // + + status = CdRompGetConfiguration(Fdo, + &localHeader, + sizeof(localHeader), + &usable, + FeatureProfileList, + SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL); + + if (status == STATUS_INVALID_DEVICE_REQUEST || + status == STATUS_NO_MEDIA_IN_DEVICE || + status == STATUS_IO_DEVICE_ERROR || + status == STATUS_IO_TIMEOUT) { + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "GetConfiguration Failed (%x), device %p not mmc-compliant\n", + status, Fdo + )); + previouslyFailed = TRUE; + ClassSetDeviceParameter(fdoExtension, + CDROM_SUBKEY_NAME, + CDROM_NON_MMC_DRIVE_NAME, + previouslyFailed + ); + return; + + } else if (!NT_SUCCESS(status)) { + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError, + "GetConfiguration Failed, status %x -- defaulting to -ROM\n", + status)); + return; + + } else if (usable < sizeof(GET_CONFIGURATION_HEADER)) { + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "GetConfiguration Failed, returned only %x bytes!\n", usable)); + previouslyFailed = TRUE; + ClassSetDeviceParameter(fdoExtension, + CDROM_SUBKEY_NAME, + CDROM_NON_MMC_DRIVE_NAME, + previouslyFailed + ); + return; + + } + + size = (localHeader.DataLength[0] << 24) | + (localHeader.DataLength[1] << 16) | + (localHeader.DataLength[2] << 8) | + (localHeader.DataLength[3] << 0); + + if(size <= 4) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "GetConfiguration Failed, claims MMC support but doesn't " + "correctly return config length!\n")); + return; + } + + size += 4; // sizeof the datalength fields + +#if DBG + { + PGET_CONFIGURATION_HEADER dbgBuffer; + NTSTATUS dbgStatus; + + dbgBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + (SIZE_T)size, + CDROM_TAG_FEATURE); + if (dbgBuffer != NULL) { + RtlZeroMemory(dbgBuffer, size); + + dbgStatus = CdRompGetConfiguration(Fdo, dbgBuffer, size, + &size, FeatureProfileList, + SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL); + + if (NT_SUCCESS(dbgStatus)) { + CdRompPrintAllFeaturePages(dbgBuffer, usable); + } + ExFreePool(dbgBuffer); + } + } +#endif // DBG + + *IsMmc = TRUE; + return; +} + +VOID +CdRompPrintAllFeaturePages( + IN PGET_CONFIGURATION_HEADER Buffer, + IN ULONG Usable + ) +{ + PFEATURE_HEADER header; + +//////////////////////////////////////////////////////////////////////////////// +// items expected to ALWAYS be current +//////////////////////////////////////////////////////////////////////////////// + header = CdRomFindFeaturePage(Buffer, Usable, FeatureProfileList); + if (header != NULL) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: CurrentProfile %x " + "with %x bytes of data at %p\n", + Buffer->CurrentProfile[0] << 8 | + Buffer->CurrentProfile[1], + Usable, Buffer)); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureCore); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "CORE Features" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureMorphing); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Morphing" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureMultiRead); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Multi-Read" + )); + } + header = CdRomFindFeaturePage(Buffer, Usable, FeatureRemovableMedium); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Removable Medium" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureTimeout); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Timeouts" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeaturePowerManagement); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Power Management" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureEmbeddedChanger); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Embedded Changer" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureLogicalUnitSerialNumber); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "LUN Serial Number" + )); + } + + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureMicrocodeUpgrade); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Microcode Update" + )); + } + +//////////////////////////////////////////////////////////////////////////////// +// items expected not to always be current +//////////////////////////////////////////////////////////////////////////////// + header = CdRomFindFeaturePage(Buffer, Usable, FeatureCDAudioAnalogPlay); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Analogue CD Audio Operations" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureCdRead); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "reading from CD-ROM/R/RW" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureCdMastering); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "CD Recording (Mastering)" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureCdTrackAtOnce); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "CD Recording (Track At Once)" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureDvdCSS); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "DVD CSS" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureDvdRead); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "DVD Structure Reads" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureDvdRecordableWrite); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "DVD Recording (Mastering)" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureDiscControlBlocks); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "DVD Disc Control Blocks" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureFormattable); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Formatting" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureRandomReadable); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Random Reads" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureRandomWritable); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Random Writes" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureRestrictedOverwrite); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Restricted Overwrites." + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureWriteOnce); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Write Once Media" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureSectorErasable); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Sector Erasable Media" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureIncrementalStreamingWritable); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Incremental Streaming Writing" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureRealTimeStreaming); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "Real-time Streaming Reads" + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureSMART); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "S.M.A.R.T." + )); + } + + header = CdRomFindFeaturePage(Buffer, Usable, FeatureDefectManagement); + if (header) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdromGetConfiguration: %s %s\n", + (header->Current ? + "Currently supports" : "Is able to support"), + "defect management" + )); + } + return; +} + +NTSTATUS +CdRomUpdateMmcDriveCapabilitiesCompletion( + IN PDEVICE_OBJECT Unused, + IN PIRP Irp, + IN PDEVICE_OBJECT Fdo + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCDROM_DATA cdData = fdoExtension->CommonExtension.DriverData; + PCDROM_MMC_EXTENSION mmcData = &(cdData->Mmc); + PSCSI_REQUEST_BLOCK srb = &(mmcData->CapabilitiesSrb); + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + NTSTATUS status = STATUS_UNSUCCESSFUL; + PIRP delayedIrp; + + // completion routine should retry as neccessary. + // when success, clear the flag to allow startio to proceed. + // else fail original request when retries are exhausted. + + ASSERT(mmcData->CapabilitiesIrp == Irp); + + // for now, if succeeded, just print the new pages. + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + // + // ISSUE-2000/4/20-henrygab - should we try to reallocate if size + // available became larger than what we + // originally allocated? otherwise, it + // is possible (not probable) that we + // would miss the feature. can check + // that by finding out what the last + // feature is in the current group. + // + + BOOLEAN retry; + ULONG retryInterval; + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ClassReleaseQueue(Fdo); + } + + retry = ClassInterpretSenseInfo( + Fdo, + srb, + irpStack->MajorFunction, + 0, + MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4), + &status, + &retryInterval); + + // + // DATA_OVERRUN is not an error in this case.... + // + + if (status == STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; + } + + // + // override verify_volume based on original irp's settings + // + + if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) && + status == STATUS_VERIFY_REQUIRED) { + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && irpStack->Parameters.Others.Argument4--) { + + LARGE_INTEGER delay; + delay.QuadPart = retryInterval; + delay.QuadPart *= (LONGLONG)1000 * 1000 * 10; + + // + // retry the request + // + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError, + "Not using ClassRetryRequest Yet\n")); + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "Retry update capabilities %p\n", Irp)); + CdRomPrepareUpdateCapabilitiesIrp(Fdo); + + CdRomRetryRequest(fdoExtension, Irp, retryInterval, TRUE); + + // + // ClassRetryRequest(Fdo, Irp, delay); + // + + return STATUS_MORE_PROCESSING_REQUIRED; + + } + + } else { + + status = STATUS_SUCCESS; + + } + + Irp->IoStatus.Status = status; + + KeSetEvent(&mmcData->CapabilitiesEvent, IO_CD_ROM_INCREMENT, FALSE); + + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +VOID +CdRomPrepareUpdateCapabilitiesIrp( + PDEVICE_OBJECT Fdo + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCDROM_DATA cdData = fdoExtension->CommonExtension.DriverData; + PCDROM_MMC_EXTENSION mmcData = &(cdData->Mmc); + PIO_STACK_LOCATION nextStack; + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + ULONG bufferSize; + PIRP irp; + + ASSERT(mmcData->UpdateState); + ASSERT(ExQueryDepthSList(&(mmcData->DelayedIrps)) != 0); + ASSERT(mmcData->CapabilitiesIrp != NULL); + ASSERT(mmcData->CapabilitiesMdl != NULL); + ASSERT(mmcData->CapabilitiesBuffer); + ASSERT(mmcData->CapabilitiesBufferSize != 0); + ASSERT(fdoExtension->SenseData); + + // + // do *NOT* call IoReuseIrp(), since it would zero out our + // current irp stack location, which we really don't want + // to happen. it would also set the current irp stack location + // to one greater than currently exists (to give max irp usage), + // but we don't want that either, since we use the top irp stack. + // + // IoReuseIrp(mmcData->CapabilitiesIrp, STATUS_UNSUCCESSFUL); + // + + irp = mmcData->CapabilitiesIrp; + srb = &(mmcData->CapabilitiesSrb); + cdb = (PCDB)(srb->Cdb); + bufferSize = mmcData->CapabilitiesBufferSize; + + // + // zero stuff out + // + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + RtlZeroMemory(fdoExtension->SenseData, sizeof(SENSE_DATA)); + RtlZeroMemory(mmcData->CapabilitiesBuffer, bufferSize); + + // + // setup the srb + // + + srb->TimeOutValue = CDROM_GET_CONFIGURATION_TIMEOUT; + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + srb->SenseInfoBuffer = fdoExtension->SenseData; + srb->DataBuffer = mmcData->CapabilitiesBuffer; + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->DataTransferLength = mmcData->CapabilitiesBufferSize; + srb->ScsiStatus = 0; + srb->SrbStatus = 0; + srb->NextSrb = NULL; + srb->OriginalRequest = irp; + srb->SrbFlags = fdoExtension->SrbFlags; + srb->CdbLength = 10; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + + // + // setup the cdb + // + + cdb->GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION; + cdb->GET_CONFIGURATION.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT; + cdb->GET_CONFIGURATION.StartingFeature[0] = 0; + cdb->GET_CONFIGURATION.StartingFeature[1] = 0; + cdb->GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(bufferSize >> 8); + cdb->GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(bufferSize & 0xff); + + // + // setup the irp + // + + nextStack = IoGetNextIrpStackLocation(irp); + nextStack->MajorFunction = IRP_MJ_SCSI; + nextStack->Parameters.Scsi.Srb = srb; + irp->MdlAddress = mmcData->CapabilitiesMdl; + irp->AssociatedIrp.SystemBuffer = mmcData->CapabilitiesBuffer; + IoSetCompletionRoutine(irp, CdRomUpdateMmcDriveCapabilitiesCompletion, Fdo, + TRUE, TRUE, TRUE); + + return; + +} + +VOID +CdRomUpdateMmcDriveCapabilities( + IN PDEVICE_OBJECT Fdo, + IN PVOID Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCDROM_DATA cdData = fdoExtension->CommonExtension.DriverData; + PCDROM_MMC_EXTENSION mmcData = &(cdData->Mmc); + PIO_STACK_LOCATION thisStack = IoGetCurrentIrpStackLocation(mmcData->CapabilitiesIrp); + PSCSI_REQUEST_BLOCK srb = &(mmcData->CapabilitiesSrb); + NTSTATUS status; + + + ASSERT(Context == NULL); + + // + // NOTE: a remove lock is unneccessary, since the delayed irp + // will have said lock held for itself, preventing a remove. + // + CdRomPrepareUpdateCapabilitiesIrp(Fdo); + + ASSERT(thisStack->Parameters.Others.Argument1 == Fdo); + ASSERT(thisStack->Parameters.Others.Argument2 == mmcData->CapabilitiesBuffer); + ASSERT(thisStack->Parameters.Others.Argument3 == &(mmcData->CapabilitiesSrb)); + + mmcData->WriteAllowed = FALSE; // default to read-only + + // + // set max retries, and also allow volume verify override based on + // original (delayed) irp + // + + thisStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES; + + // + // send to self... note that SL_OVERRIDE_VERIFY_VOLUME is not required, + // as this is IRP_MJ_INTERNAL_DEVICE_CONTROL + // + + IoCallDriver(commonExtension->LowerDeviceObject, mmcData->CapabilitiesIrp); + + KeWaitForSingleObject(&mmcData->CapabilitiesEvent, + Executive, KernelMode, FALSE, NULL); + + status = mmcData->CapabilitiesIrp->IoStatus.Status; + + if (!NT_SUCCESS(status)) { + + goto FinishDriveUpdate; + + } + + // + // we've updated the feature set, so update whether or not reads and writes + // are allowed or not. + // + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdRomUpdateMmc => Succeeded " + "--------------------" + "--------------------\n")); + + /*++ + + NOTE: It is important to only use srb->DataTransferLength worth + of data at this point, since the bufferSize is what is + *available* to use, not what was *actually* used. + + --*/ + +#if DBG + CdRompPrintAllFeaturePages(mmcData->CapabilitiesBuffer, + srb->DataTransferLength); +#endif // DBG + + // + // update whether or not writes are allowed. this is currently defined + // as requiring TargetDefectManagement and RandomWritable features + // + { + PFEATURE_HEADER defectHeader; + PFEATURE_HEADER writableHeader; + + defectHeader = CdRomFindFeaturePage(mmcData->CapabilitiesBuffer, + srb->DataTransferLength, + FeatureDefectManagement); + writableHeader = CdRomFindFeaturePage(mmcData->CapabilitiesBuffer, + srb->DataTransferLength, + FeatureRandomWritable); + + if ((defectHeader != NULL) && (writableHeader != NULL) && + (defectHeader->Current) && (writableHeader->Current)) { + + // + // this should be the *ONLY* place writes are set to allowed + // + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdRomUpdateMmc => Writes *allowed*\n")); + mmcData->WriteAllowed = TRUE; + + } else { + + if (defectHeader == NULL) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdRomUpdateMmc => No writes - %s = %s\n", + "defect management", "DNE")); + } else { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdRomUpdateMmc => No writes - %s = %s\n", + "defect management", "Not Current")); + } + if (writableHeader == NULL) { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdRomUpdateMmc => No writes - %s = %s\n", + "sector writable", "DNE")); + } else { + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdRomUpdateMmc => No writes - %s = %s\n", + "sector writable", "Not Current")); + } + } // end of feature checking + } // end of check for writability + + // + // update the cached partition table information + // + // NOTE: THIS WILL CURRENTLY CAUSE A DEADLOCK! + // + // ISSUE-2000/06/20-henrygab - partition support not implemented + // IoReadPartitionTable must be done + // at PASSIVE level, requiring a thread + // or worker item or other such method. + // +#if 0 + status = IoReadPartitionTable(Fdo, 1 << fdoExtension->SectorShift, + TRUE, &mmcData->PartitionList); + if (!NT_SUCCESS(status)) { + + goto FinishDriveUpdate; + + } +#endif + + status = STATUS_SUCCESS; + +FinishDriveUpdate: + + CdRompFlushDelayedList(Fdo, mmcData, status, TRUE); + + return; +} + + +VOID +CdRompFlushDelayedList( + IN PDEVICE_OBJECT Fdo, + IN PCDROM_MMC_EXTENSION MmcData, + IN NTSTATUS Status, + IN BOOLEAN CalledFromWorkItem + ) +{ + PSINGLE_LIST_ENTRY list; + PIRP irp; + + // NOTE - REF #0002 + // + // need to set the new state first to prevent deadlocks. + // this is only done from the workitem, to prevent any + // edge cases where we'd "lose" the UpdateRequired + // + // then, must ignore the state, since it's not guaranteed to + // be the same any longer. the only thing left is to handle + // all the delayed irps by flushing the queue and sending them + // back onto the StartIo queue for the device. + // + + if (CalledFromWorkItem) { + + LONG oldState; + LONG newState; + + if (NT_SUCCESS(Status)) { + newState = CdromMmcUpdateComplete; + } else { + newState = CdromMmcUpdateRequired; + } + + oldState = InterlockedCompareExchange(&MmcData->UpdateState, + newState, + CdromMmcUpdateStarted); + ASSERT(oldState == CdromMmcUpdateStarted); + + } else { + + // + // just flushing the queue if not called from the workitem, + // and we don't want to ever fail the queue in those cases. + // + + ASSERT(NT_SUCCESS(Status)); + + } + + list = ExInterlockedFlushSList(&MmcData->DelayedIrps); + + // if this assert fires, it means that we have started + // a workitem when the previous workitem took the delayed + // irp. if this happens, then the logic in HACKHACK #0002 + // is either flawed or the rules set within are not being + // followed. this would require investigation. + + ASSERT(list != NULL); + + // + // now either succeed or fail all the delayed irps, according + // to the update status. + // + + while (list != NULL) { + + irp = (PIRP)( ((PUCHAR)list) - + FIELD_OFFSET(IRP, Tail.Overlay.DriverContext[0]) + ); + list = list->Next; + irp->Tail.Overlay.DriverContext[0] = 0; + irp->Tail.Overlay.DriverContext[1] = 0; + irp->Tail.Overlay.DriverContext[2] = 0; + irp->Tail.Overlay.DriverContext[3] = 0; + + if (NT_SUCCESS(Status)) { + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdRomUpdateMmc => Re-sending delayed irp %p\n", + irp)); + IoStartPacket(Fdo, irp, NULL, NULL); + + } else { + + KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures, + "CdRomUpdateMmc => Failing delayed irp %p with " + " status %x\n", irp, Status)); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = Status; + ClassReleaseRemoveLock(Fdo, irp); + IoCompleteRequest(irp, IO_CD_ROM_INCREMENT); + + } + + } // while (list) + + return; + +} + +VOID +CdRomDeAllocateMmcResources( + IN PDEVICE_OBJECT Fdo + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCDROM_DATA cddata = commonExtension->DriverData; + PCDROM_MMC_EXTENSION mmcData = &cddata->Mmc; + NTSTATUS status; + + if (mmcData->CapabilitiesWorkItem) { + IoFreeWorkItem(mmcData->CapabilitiesWorkItem); + mmcData->CapabilitiesWorkItem = NULL; + } + if (mmcData->CapabilitiesIrp) { + IoFreeIrp(mmcData->CapabilitiesIrp); + mmcData->CapabilitiesIrp = NULL; + } + if (mmcData->CapabilitiesMdl) { + IoFreeMdl(mmcData->CapabilitiesMdl); + mmcData->CapabilitiesMdl = NULL; + } + if (mmcData->CapabilitiesBuffer) { + ExFreePool(mmcData->CapabilitiesBuffer); + mmcData->CapabilitiesBuffer = NULL; + } + mmcData->CapabilitiesBuffer = 0; + mmcData->IsMmc = FALSE; + mmcData->WriteAllowed = FALSE; + + return; +} + +NTSTATUS +CdRomAllocateMmcResources( + IN PDEVICE_OBJECT Fdo + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCDROM_DATA cddata = commonExtension->DriverData; + PCDROM_MMC_EXTENSION mmcData = &cddata->Mmc; + PIO_STACK_LOCATION irpStack; + NTSTATUS status; + + ASSERT(mmcData->CapabilitiesWorkItem == NULL); + ASSERT(mmcData->CapabilitiesIrp == NULL); + ASSERT(mmcData->CapabilitiesMdl == NULL); + ASSERT(mmcData->CapabilitiesBuffer == NULL); + ASSERT(mmcData->CapabilitiesBufferSize == 0); + + status = CdRomGetConfiguration(Fdo, + &mmcData->CapabilitiesBuffer, + &mmcData->CapabilitiesBufferSize, + FeatureProfileList, + SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL); + if (!NT_SUCCESS(status)) { + ASSERT(mmcData->CapabilitiesBuffer == NULL); + ASSERT(mmcData->CapabilitiesBufferSize == 0); + return status; + } + ASSERT(mmcData->CapabilitiesBuffer != NULL); + ASSERT(mmcData->CapabilitiesBufferSize != 0); + + mmcData->CapabilitiesMdl = IoAllocateMdl(mmcData->CapabilitiesBuffer, + mmcData->CapabilitiesBufferSize, + FALSE, FALSE, NULL); + if (mmcData->CapabilitiesMdl == NULL) { + ExFreePool(mmcData->CapabilitiesBuffer); + mmcData->CapabilitiesBufferSize = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + + mmcData->CapabilitiesIrp = IoAllocateIrp(Fdo->StackSize + 2, FALSE); + if (mmcData->CapabilitiesIrp == NULL) { + IoFreeMdl(mmcData->CapabilitiesMdl); + ExFreePool(mmcData->CapabilitiesBuffer); + mmcData->CapabilitiesBufferSize = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + mmcData->CapabilitiesWorkItem = IoAllocateWorkItem(Fdo); + if (mmcData->CapabilitiesWorkItem == NULL) { + IoFreeIrp(mmcData->CapabilitiesIrp); + IoFreeMdl(mmcData->CapabilitiesMdl); + ExFreePool(mmcData->CapabilitiesBuffer); + mmcData->CapabilitiesBufferSize = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // everything has been allocated, so now prepare it all.... + // + + MmBuildMdlForNonPagedPool(mmcData->CapabilitiesMdl); + ExInitializeSListHead(&mmcData->DelayedIrps); + KeInitializeSpinLock(&mmcData->DelayedLock); + + // + // use the extra stack for internal bookkeeping + // + IoSetNextIrpStackLocation(mmcData->CapabilitiesIrp); + irpStack = IoGetCurrentIrpStackLocation(mmcData->CapabilitiesIrp); + irpStack->Parameters.Others.Argument1 = Fdo; + irpStack->Parameters.Others.Argument2 = mmcData->CapabilitiesBuffer; + irpStack->Parameters.Others.Argument3 = &(mmcData->CapabilitiesSrb); + // arg 4 is the retry count + + // + // set the completion event to FALSE for now + // + + KeInitializeEvent(&mmcData->CapabilitiesEvent, + SynchronizationEvent, FALSE); + return STATUS_SUCCESS; + +} + diff --git a/drivers/storage/class/cdrom_new/scsicdrm.rc b/drivers/storage/class/cdrom_new/scsicdrm.rc new file mode 100644 index 00000000000..cac5f53da93 --- /dev/null +++ b/drivers/storage/class/cdrom_new/scsicdrm.rc @@ -0,0 +1,23 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 1996 - 1999 +// +// File: scsicdrm.rc +// +//-------------------------------------------------------------------------- + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "SCSI CD-ROM Driver" +#define VER_INTERNALNAME_STR "cdrom.sys" +#define VER_ORIGINALFILENAME_STR "cdrom.sys" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/drivers/storage/class/cdrom_new/sec.c b/drivers/storage/class/cdrom_new/sec.c new file mode 100644 index 00000000000..60b70768954 --- /dev/null +++ b/drivers/storage/class/cdrom_new/sec.c @@ -0,0 +1,38 @@ +/*-- + +Copyright (C) Microsoft Corporation, 1999 + +--*/ + + +#include "sec.h" + + +NTSTATUS +CdRomGetRpc0Settings( + IN PDEVICE_OBJECT Fdo + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData); + + cddata->Rpc0SystemRegion = (UCHAR)(~1); // region one + cddata->Rpc0SystemRegionResetCount = 0; // no resets + + return STATUS_SUCCESS; +} + + +NTSTATUS +CdRomSetRpc0Settings( + IN PDEVICE_OBJECT Fdo, + IN UCHAR NewRegion + ) +{ + return STATUS_SUCCESS; +} + + + + + diff --git a/drivers/storage/class/cdrom_new/sec.h b/drivers/storage/class/cdrom_new/sec.h new file mode 100644 index 00000000000..ce8b6272dba --- /dev/null +++ b/drivers/storage/class/cdrom_new/sec.h @@ -0,0 +1,11 @@ +/*-- + +Copyright (C) Microsoft Corporation, 1999 + +--*/ + + +#include "ntddk.h" +#include "classpnp.h" +#include "cdrom.h" + diff --git a/drivers/storage/class/cdrom_new/trace.h b/drivers/storage/class/cdrom_new/trace.h new file mode 100644 index 00000000000..2db702cf3f1 --- /dev/null +++ b/drivers/storage/class/cdrom_new/trace.h @@ -0,0 +1,53 @@ +/* + + WPP_DEFINE_CONTROL_GUID specifies the GUID used for this filter. + *** REPLACE THE GUID WITH YOUR OWN UNIQUE ID *** + WPP_DEFINE_BIT allows setting debug bit masks to selectively print. + + everything else can revert to the default? + +*/ + + +#define TraceLogger(x, ...) DbgPrint(__VA_ARGS__) +#define TraceLog(x) TraceLogger x +#define WPP_INIT_TRACING(x, y) +#define WPP_CLEANUP(x) + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(Cdrom,(58db8e03,0537,45cb,b29b,597f6cbebbfd), \ + WPP_DEFINE_BIT(CdromDebugError) /* bit 0 = 0x00000001 */ \ + WPP_DEFINE_BIT(CdromDebugWarning) /* bit 1 = 0x00000002 */ \ + WPP_DEFINE_BIT(CdromDebugTrace) /* bit 2 = 0x00000004 */ \ + WPP_DEFINE_BIT(CdromDebugInfo) /* bit 3 = 0x00000008 */ \ + WPP_DEFINE_BIT(FilterDebugD04) /* bit 4 = 0x00000010 */ \ + WPP_DEFINE_BIT(FilterDebugD05) /* bit 5 = 0x00000020 */ \ + WPP_DEFINE_BIT(FilterDebugD06) /* bit 6 = 0x00000040 */ \ + WPP_DEFINE_BIT(FilterDebugD07) /* bit 7 = 0x00000080 */ \ + WPP_DEFINE_BIT(FilterDebugD08) /* bit 8 = 0x00000100 */ \ + WPP_DEFINE_BIT(FilterDebugD09) /* bit 9 = 0x00000200 */ \ + WPP_DEFINE_BIT(FilterDebugD10) /* bit 10 = 0x00000400 */ \ + WPP_DEFINE_BIT(FilterDebugD11) /* bit 11 = 0x00000800 */ \ + WPP_DEFINE_BIT(FilterDebugD12) /* bit 12 = 0x00001000 */ \ + WPP_DEFINE_BIT(FilterDebugD13) /* bit 13 = 0x00002000 */ \ + WPP_DEFINE_BIT(FilterDebugD14) /* bit 14 = 0x00004000 */ \ + WPP_DEFINE_BIT(FilterDebugD15) /* bit 15 = 0x00008000 */ \ + WPP_DEFINE_BIT(FilterDebugD16) /* bit 16 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD17) /* bit 17 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD18) /* bit 18 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD19) /* bit 19 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD20) /* bit 20 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD21) /* bit 21 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD22) /* bit 22 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD23) /* bit 23 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD24) /* bit 24 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD25) /* bit 25 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD26) /* bit 26 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD27) /* bit 27 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD28) /* bit 28 = 0x00000000 */ \ + WPP_DEFINE_BIT(FilterDebugD29) /* bit 29 = 0x00000000 */ \ + WPP_DEFINE_BIT(CdromSecError) /* bit 30 = 0x00000000 */ \ + WPP_DEFINE_BIT(CdromSecInfo) /* bit 31 = 0x00000000 */ \ + ) + + diff --git a/drivers/storage/class/directory.rbuild b/drivers/storage/class/directory.rbuild index 0cfffe09e30..30dbef9435e 100644 --- a/drivers/storage/class/directory.rbuild +++ b/drivers/storage/class/directory.rbuild @@ -4,6 +4,9 @@ + + + diff --git a/drivers/storage/class/disk/disk.c b/drivers/storage/class/disk/disk.c index bf380bc0b4e..a7085c43631 100644 --- a/drivers/storage/class/disk/disk.c +++ b/drivers/storage/class/disk/disk.c @@ -2262,6 +2262,88 @@ Return Value: break; + case IOCTL_DISK_GET_PARTITION_INFO_EX: + + // + // Return the information about the partition specified by the device + // object. Note that no information is ever returned about the size + // or partition type of the physical disk, as this doesn't make any + // sense. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(PARTITION_INFORMATION_EX)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + + } + else if (diskData->PartitionNumber == 0) { + + // + // Paritition zero is not a partition so this is not a + // reasonable request. + // + + status = STATUS_INVALID_DEVICE_REQUEST; + + } + else { + + PPARTITION_INFORMATION_EX outputBuffer; + + // + // Update the geometry in case it has changed. + // + + status = UpdateRemovableGeometry (DeviceObject, Irp); + + if (!NT_SUCCESS(status)) { + + // + // Note the drive is not ready. + // + + diskData->DriveNotReady = TRUE; + break; + } + + // + // Note the drive is now ready. + // + + diskData->DriveNotReady = FALSE; + + if (diskData->PartitionType == 0 && (diskData->PartitionNumber > 0)) { + + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + outputBuffer = + (PPARTITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer; + + // + // FIXME: hack of the year, assume that partition is MBR + // Thing that can obviously be wrong... + // + + outputBuffer->PartitionStyle = PARTITION_STYLE_MBR; + outputBuffer->Mbr.PartitionType = diskData->PartitionType; + outputBuffer->StartingOffset = deviceExtension->StartingOffset; + outputBuffer->PartitionLength.QuadPart = deviceExtension->PartitionLength.QuadPart; + outputBuffer->Mbr.HiddenSectors = diskData->HiddenSectors; + outputBuffer->PartitionNumber = diskData->PartitionNumber; + outputBuffer->Mbr.BootIndicator = diskData->BootIndicator; + outputBuffer->RewritePartition = FALSE; + outputBuffer->Mbr.RecognizedPartition = + IsRecognizedPartition(diskData->PartitionType); + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX); + } + + break; + case IOCTL_DISK_SET_PARTITION_INFO: if (diskData->PartitionNumber == 0) { diff --git a/drivers/storage/classpnp/autorun.c b/drivers/storage/classpnp/autorun.c new file mode 100644 index 00000000000..0d2f36d4a52 --- /dev/null +++ b/drivers/storage/classpnp/autorun.c @@ -0,0 +1,3611 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + autorun.c + +Abstract: + + Code for support of media change detection in the class driver + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#include "classp.h" +#include "debug.h" + +#define GESN_TIMEOUT_VALUE (0x4) +#define GESN_BUFFER_SIZE (0x8) +#define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20) +#define MCN_REG_SUBKEY_NAME (L"MediaChangeNotification") +#define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN") +#define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME (L"AlwaysEnableMCN") + +GUID StoragePredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID; + +// +// Only send polling irp when device is fully powered up and a +// power down irp is not in progress. +// +// NOTE: This helps close a window in time where a polling irp could cause +// a drive to spin up right after it has powered down. The problem is +// that SCSIPORT, ATAPI and SBP2 will be in the process of powering +// down (which may take a few seconds), but won't know that. It would +// then get a polling irp which will be put into its queue since it +// the disk isn't powered down yet. Once the disk is powered down it +// will find the polling irp in the queue and then power up the +// device to do the poll. They do not want to check if the polling +// irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical +// path and would slow down all I/Os. A better way to fix this +// would be to serialize the polling and power down irps so that +// only one of them is sent to the device at a time. +// +#define ClasspCanSendPollingIrp(fdoExtension) \ + ((fdoExtension->DevicePowerState == PowerDeviceD0) && \ + (! fdoExtension->PowerDownInProgress) ) + +BOOLEAN +ClasspIsMediaChangeDisabledDueToHardwareLimitation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +ClasspMediaChangeDeviceInstanceOverride( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PBOOLEAN Enabled + ); + +BOOLEAN +ClasspIsMediaChangeDisabledForClass( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PUNICODE_STRING RegistryPath + ); + +VOID +ClasspSetMediaChangeStateEx( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN MEDIA_CHANGE_DETECTION_STATE NewState, + IN BOOLEAN Wait, + IN BOOLEAN KnownStateChange // can ignore oldstate == unknown + ); + +NTSTATUS +ClasspMediaChangeRegistryCallBack( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +VOID +ClasspSendMediaStateIrp( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PMEDIA_CHANGE_DETECTION_INFO Info, + IN ULONG CountDown + ); + +VOID +ClasspFailurePredict( + IN PDEVICE_OBJECT DeviceObject, + IN PFAILURE_PREDICTION_INFO Info + ); + +NTSTATUS +ClasspInitializePolling( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN BOOLEAN AllowDriveToSleep + ); + + +#if ALLOC_PRAGMA + +#pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection) +#pragma alloc_text(PAGE, ClassEnableMediaChangeDetection) +#pragma alloc_text(PAGE, ClassDisableMediaChangeDetection) +#pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection) +#pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack) +#pragma alloc_text(PAGE, ClasspInitializePolling) + +#pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation) +#pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride) +#pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass) + +#pragma alloc_text(PAGE, ClassSetFailurePredictionPoll) +#pragma alloc_text(PAGE, ClasspDisableTimer) +#pragma alloc_text(PAGE, ClasspEnableTimer) + +#endif + +// ISSUE -- make this public? +VOID +ClassSendEjectionNotification( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + // + // For post-NT5.1 work, need to move EjectSynchronizationEvent + // to be a MUTEX so we can attempt to grab it here and benefit + // from deadlock detection. This will allow checking if the media + // has been locked by programs before broadcasting these events. + // (what's the point of broadcasting if the media is not locked?) + // + // This would currently only be a slight optimization. For post-NT5.1, + // it would allow us to send a single PERSISTENT_PREVENT to MMC devices, + // thereby cleaning up a lot of the ejection code. Then, when the + // ejection request occured, we could see if any locks for the media + // existed. if locked, broadcast. if not, we send the eject irp. + // + + // + // for now, just always broadcast. make this a public routine, + // so class drivers can add special hacks to broadcast this for their + // non-MMC-compliant devices also from sense codes. + // + + DBGTRACE(ClassDebugTrace, ("ClassSendEjectionNotification: media EJECT_REQUEST")); + ClasspSendNotification(FdoExtension, + &GUID_IO_MEDIA_EJECT_REQUEST, + 0, + NULL); + return; +} + + +VOID +ClasspSendNotification( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN const GUID * Guid, + IN ULONG ExtraDataSize, + IN PVOID ExtraData + ) +{ + PTARGET_DEVICE_CUSTOM_NOTIFICATION notification; + ULONG requiredSize; + + requiredSize = + (sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)) + + ExtraDataSize; + + if (requiredSize > 0x0000ffff) { + // MAX_USHORT, max total size for these events! + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning, + "Error sending event: size too large! (%x)\n", + requiredSize)); + return; + } + + notification = ExAllocatePoolWithTag(NonPagedPool, + requiredSize, + 'oNcS'); + + // + // if none allocated, exit + // + + if (notification == NULL) { + return; + } + + // + // Prepare and send the request! + // + + RtlZeroMemory(notification, requiredSize); + notification->Version = 1; + notification->Size = (USHORT)(requiredSize); + notification->FileObject = NULL; + notification->NameBufferOffset = -1; + notification->Event = *Guid; + RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize); + + IoReportTargetDeviceChangeAsynchronous(FdoExtension->LowerPdo, + notification, + NULL, NULL); + + ExFreePool(notification); + notification = NULL; + return; +} + + + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspInterpretGesnData() + +Routine Description: + + This routine will interpret the data returned for a GESN command, and + (if appropriate) set the media change event, and broadcast the + appropriate events to user mode for applications who care. + +Arguments: + + FdoExtension - the device + + DataBuffer - the resulting data from a GESN event. + requires at least EIGHT valid bytes (header == 4, data == 4) + + ResendImmediately - whether or not to immediately resend the request. + this should be FALSE if there was no event, FALSE if the reported + event was of the DEVICE BUSY class, else true. + +Return Value: + + None + +Notes: + + DataBuffer must be at least four bytes of valid data (header == 4 bytes), + and have at least eight bytes of allocated memory (all events == 4 bytes). + + The call to StartNextPacket may occur before this routine is completed. + the operational change notifications are informational in nature, and + while useful, are not neccessary to ensure proper operation. For example, + if the device morphs to no longer supporting WRITE commands, all further + write commands will fail. There exists a small timing window wherein + IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If + a device supports software write protect, it is expected that the + application can handle such a case. + + NOTE: perhaps setting the updaterequired byte to one should be done here. + if so, it relies upon the setting of a 32-byte value to be an atomic + operation. unfortunately, there is no simple way to notify a class driver + which wants to know that the device behavior requires updating. + + Not ready events may be sent every second. For example, if we were + to minimize the number of asynchronous notifications, an application may + register just after a large busy time was reported. This would then + prevent the application from knowing the device was busy until some + arbitrarily chosen timeout has occurred. Also, the GESN request would + have to still occur, since it checks for non-busy events (such as user + keybutton presses and media change events) as well. The specification + states that the lower-numered events get reported first, so busy events, + while repeating, will only be reported when all other events have been + cleared from the device. + +--*/ +VOID +ClasspInterpretGesnData( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PNOTIFICATION_EVENT_STATUS_HEADER Header, + IN PBOOLEAN ResendImmediately + ) +{ + PMEDIA_CHANGE_DETECTION_INFO info; + LONG dataLength; + LONG requiredLength; + + info = FdoExtension->MediaChangeDetectionInfo; + + // + // note: don't allocate anything in this routine so that we can + // always just 'return'. + // + + *ResendImmediately = FALSE; + + if (Header->NEA) { + return; + } + if (Header->NotificationClass == NOTIFICATION_NO_CLASS_EVENTS) { + return; + } + + // + // HACKHACK - REF #0001 + // This loop is only taken initially, due to the inability to reliably + // auto-detect drives that report events correctly at boot. When we + // detect this behavior during the normal course of running, we will + // disable the hack, allowing more efficient use of the system. This + // should occur "nearly" instantly, as the drive should have multiple + // events queue'd (ie. power, morphing, media). + // + + if (info->Gesn.HackEventMask) { + + // + // all events use the low four bytes of zero to indicate + // that there was no change in status. + // + + UCHAR thisEvent = Header->ClassEventData[0] & 0xf; + UCHAR lowestSetBit; + UCHAR thisEventBit = (1 << Header->NotificationClass); + + ASSERT(TEST_FLAG(info->Gesn.EventMask, thisEventBit)); + + + // + // some bit magic here... this results in the lowest set bit only + // + + lowestSetBit = info->Gesn.EventMask; + lowestSetBit &= (info->Gesn.EventMask - 1); + lowestSetBit ^= (info->Gesn.EventMask); + + if (thisEventBit != lowestSetBit) { + + // + // HACKHACK - REF #0001 + // the first time we ever see an event set that is not the lowest + // set bit in the request (iow, highest priority), we know that the + // hack is no longer required, as the device is ignoring "no change" + // events when a real event is waiting in the other requested queues. + // + + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => GESN::NONE: Compliant drive found, " + "removing GESN hack (%x, %x)\n", + thisEventBit, info->Gesn.EventMask)); + + info->Gesn.HackEventMask = FALSE; + + } else if (thisEvent == 0) { + + // + // HACKHACK - REF #0001 + // note: this hack prevents poorly implemented firmware from constantly + // returning "No Event". we do this by cycling through the + // supported list of events here. + // + + SET_FLAG(info->Gesn.NoChangeEventMask, thisEventBit); + CLEAR_FLAG(info->Gesn.EventMask, thisEventBit); + + // + // if we have cycled through all supported event types, then + // we need to reset the events we are asking about. else we + // want to resend this request immediately in case there was + // another event pending. + // + + if (info->Gesn.EventMask == 0) { + info->Gesn.EventMask = info->Gesn.NoChangeEventMask; + info->Gesn.NoChangeEventMask = 0; + } else { + *ResendImmediately = TRUE; + } + return; + } + + } // end if (info->Gesn.HackEventMask) + + dataLength = + (Header->EventDataLength[0] << 8) | + (Header->EventDataLength[1] & 0xff); + dataLength -= 2; + requiredLength = 4; // all events are four bytes + + if (dataLength < requiredLength) { + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning, + "Classpnp => GESN returned only %x bytes data for fdo %p\n", + dataLength, FdoExtension->DeviceObject)); + return; + } + if (dataLength != requiredLength) { + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning, + "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n", + dataLength, FdoExtension->DeviceObject)); + dataLength = 4; + } + +/* + ClasspSendNotification(FdoExtension, + &GUID_IO_GENERIC_GESN_EVENT, + sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength, + Header) +*/ + + switch (Header->NotificationClass) { + + case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS: { // 0x3 + + PNOTIFICATION_EXTERNAL_STATUS externalInfo = + (PNOTIFICATION_EXTERNAL_STATUS)(Header->ClassEventData); + DEVICE_EVENT_EXTERNAL_REQUEST externalData; + + // + // unfortunately, due to time constraints, we will only notify + // about keys being pressed, and not released. this makes keys + // single-function, but simplifies the code significantly. + // + + if (externalInfo->ExternalEvent != + NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN) { + break; + } + + *ResendImmediately = TRUE; + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => GESN::EXTERNAL: Event: %x Status %x Req %x\n", + externalInfo->ExternalEvent, externalInfo->ExternalStatus, + (externalInfo->Request[0] >> 8) | externalInfo->Request[1] + )); + + RtlZeroMemory(&externalData, sizeof(DEVICE_EVENT_EXTERNAL_REQUEST)); + externalData.Version = 1; + externalData.DeviceClass = 0; + externalData.ButtonStatus = externalInfo->ExternalEvent; + externalData.Request = + (externalInfo->Request[0] << 8) | + (externalInfo->Request[1] & 0xff); + KeQuerySystemTime(&(externalData.SystemTime)); + externalData.SystemTime.QuadPart *= (LONGLONG)KeQueryTimeIncrement(); + + DBGTRACE(ClassDebugTrace, ("ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST")); + ClasspSendNotification(FdoExtension, + &GUID_IO_DEVICE_EXTERNAL_REQUEST, + sizeof(DEVICE_EVENT_EXTERNAL_REQUEST), + &externalData); + return; + } + + case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS: { // 0x4 + + PNOTIFICATION_MEDIA_STATUS mediaInfo = + (PNOTIFICATION_MEDIA_STATUS)(Header->ClassEventData); + + if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NO_CHANGE) { + break; + } + + *ResendImmediately = TRUE; + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => GESN::MEDIA: Event: %x Status %x\n", + mediaInfo->MediaEvent, mediaInfo->MediaStatus)); + + if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) || + (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE)) { + + + if (TEST_FLAG(FdoExtension->DeviceObject->Characteristics, + FILE_REMOVABLE_MEDIA) && + (ClassGetVpb(FdoExtension->DeviceObject) != NULL) && + (ClassGetVpb(FdoExtension->DeviceObject)->Flags & VPB_MOUNTED) + ) { + + SET_FLAG(FdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME); + + } + InterlockedIncrement(&FdoExtension->MediaChangeCount); + ClasspSetMediaChangeStateEx(FdoExtension, + MediaPresent, + FALSE, + TRUE); + + } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL) { + + ClasspSetMediaChangeStateEx(FdoExtension, + MediaNotPresent, + FALSE, + TRUE); + + } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST) { + + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugError, + "Classpnp => GESN Ejection request received!\n")); + ClassSendEjectionNotification(FdoExtension); + + } + break; + + } + + case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS: { // lowest priority events... + + PNOTIFICATION_BUSY_STATUS busyInfo = + (PNOTIFICATION_BUSY_STATUS)(Header->ClassEventData); + DEVICE_EVENT_BECOMING_READY busyData; + + // + // NOTE: we never actually need to immediately retry for these + // events: if one exists, the device is busy, and if not, + // we still don't want to retry. + // + + if (busyInfo->DeviceBusyStatus == NOTIFICATION_BUSY_STATUS_NO_EVENT) { + break; + } + + // + // else we want to report the approximated time till it's ready. + // + + RtlZeroMemory(&busyData, sizeof(DEVICE_EVENT_BECOMING_READY)); + busyData.Version = 1; + busyData.Reason = busyInfo->DeviceBusyStatus; + busyData.Estimated100msToReady = (busyInfo->Time[0] << 8) | + (busyInfo->Time[1] & 0xff); + + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => GESN::BUSY: Event: %x Status %x Time %x\n", + busyInfo->DeviceBusyEvent, busyInfo->DeviceBusyStatus, + busyData.Estimated100msToReady + )); + + DBGTRACE(ClassDebugTrace, ("ClasspInterpretGesnData: media BECOMING_READY")); + ClasspSendNotification(FdoExtension, + &GUID_IO_DEVICE_BECOMING_READY, + sizeof(DEVICE_EVENT_BECOMING_READY), + &busyData); + break; + } + + default: { + + break; + + } + + } // end switch on notification class + return; +} + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspInternalSetMediaChangeState() + +Routine Description: + + This routine will (if appropriate) set the media change event for the + device. The event will be set if the media state is changed and + media change events are enabled. Otherwise the media state will be + tracked but the event will not be set. + + This routine will lock out the other media change routines if possible + but if not a media change notification may be lost after the enable has + been completed. + +Arguments: + + FdoExtension - the device + + MediaPresent - indicates whether the device has media inserted into it + (TRUE) or not (FALSE). + +Return Value: + + none + +--*/ +VOID +ClasspInternalSetMediaChangeState( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN MEDIA_CHANGE_DETECTION_STATE NewState, + IN BOOLEAN KnownStateChange // can ignore oldstate == unknown + ) +{ +#if DBG + PUCHAR states[] = {"Unknown", "Present", "Not Present"}; +#endif + MEDIA_CHANGE_DETECTION_STATE oldMediaState; + PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; + ULONG data; + NTSTATUS status; + + ASSERT((NewState >= MediaUnknown) && (NewState <= MediaNotPresent)); + + if(info == NULL) { + return; + } + + oldMediaState = InterlockedExchange( + (PLONG)(&info->MediaChangeDetectionState), + (LONG)NewState); + + if((oldMediaState == MediaUnknown) && (!KnownStateChange)) { + + // + // The media was in an indeterminate state before - don't notify for + // this change. + // + + DebugPrint((ClassDebugMCN, + "ClassSetMediaChangeState: State was unknown - this may " + "not be a change\n")); + return; + + } else if(oldMediaState == NewState) { + + // + // Media is in the same state it was before. + // + + return; + } + + if(info->MediaChangeDetectionDisableCount != 0) { + + DBGTRACE(ClassDebugMCN, + ("ClassSetMediaChangeState: MCN not enabled, state " + "changed from %s to %s\n", + states[oldMediaState], states[NewState])); + return; + + } + + DBGTRACE(ClassDebugMCN, + ("ClassSetMediaChangeState: State change from %s to %s\n", + states[oldMediaState], states[NewState])); + + // + // make the data useful -- it used to always be zero. + // + data = FdoExtension->MediaChangeCount; + + if (NewState == MediaPresent) { + + DBGTRACE(ClassDebugTrace, ("ClasspInternalSetMediaChangeState: media ARRIVAL")); + ClasspSendNotification(FdoExtension, + &GUID_IO_MEDIA_ARRIVAL, + sizeof(ULONG), + &data); + + } + else if (NewState == MediaNotPresent) { + + DBGTRACE(ClassDebugTrace, ("ClasspInternalSetMediaChangeState: media REMOVAL")); + ClasspSendNotification(FdoExtension, + &GUID_IO_MEDIA_REMOVAL, + sizeof(ULONG), + &data); + + } else { + + // + // Don't notify of changed going to unknown. + // + + return; + } + + return; +} // end ClasspInternalSetMediaChangeState() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassSetMediaChangeState() + +Routine Description: + + This routine will (if appropriate) set the media change event for the + device. The event will be set if the media state is changed and + media change events are enabled. Otherwise the media state will be + tracked but the event will not be set. + + This routine will lock out the other media change routines if possible + but if not a media change notification may be lost after the enable has + been completed. + +Arguments: + + FdoExtension - the device + + MediaPresent - indicates whether the device has media inserted into it + (TRUE) or not (FALSE). + + Wait - indicates whether the function should wait until it can acquire + the synchronization lock or not. + +Return Value: + + none + +--*/ +VOID +ClasspSetMediaChangeStateEx( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN MEDIA_CHANGE_DETECTION_STATE NewState, + IN BOOLEAN Wait, + IN BOOLEAN KnownStateChange // can ignore oldstate == unknown + ) +{ + PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; + LARGE_INTEGER zero; + NTSTATUS status; + + DBGTRACE(ClassDebugMCN, ("> ClasspSetMediaChangeStateEx")); + + // + // Reset SMART status on media removal as the old status may not be + // valid when there is no media in the device or when new media is + // inserted. + // + + if (NewState == MediaNotPresent) { + + FdoExtension->FailurePredicted = FALSE; + FdoExtension->FailureReason = 0; + + } + + + zero.QuadPart = 0; + + if(info == NULL) { + return; + } + + status = KeWaitForMutexObject(&info->MediaChangeMutex, + Executive, + KernelMode, + FALSE, + ((Wait == TRUE) ? NULL : &zero)); + + if(status == STATUS_TIMEOUT) { + + // + // Someone else is in the process of setting the media state + // + + DBGWARN(("ClasspSetMediaChangeStateEx - timed out waiting for mutex")); + return; + } + + // + // Change the media present state and signal an event, if applicable + // + + ClasspInternalSetMediaChangeState(FdoExtension, NewState, KnownStateChange); + + KeReleaseMutex(&info->MediaChangeMutex, FALSE); + + DBGTRACE(ClassDebugMCN, ("< ClasspSetMediaChangeStateEx")); + + return; +} // end ClassSetMediaChangeStateEx() +VOID +ClassSetMediaChangeState( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN MEDIA_CHANGE_DETECTION_STATE NewState, + IN BOOLEAN Wait + ) +{ + ClasspSetMediaChangeStateEx(FdoExtension, NewState, Wait, FALSE); + return; +} + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspMediaChangeDetectionCompletion() + +Routine Description: + + This routine handles the completion of the test unit ready irps used to + determine if the media has changed. If the media has changed, this code + signals the named event to wake up other system services that react to + media change (aka AutoPlay). + +Arguments: + + DeviceObject - the object for the completion + Irp - the IRP being completed + Context - the SRB from the IRP + +Return Value: + + NTSTATUS + +--*/ +NTSTATUS +ClasspMediaChangeDetectionCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PSCSI_REQUEST_BLOCK Srb + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PCLASS_PRIVATE_FDO_DATA fdoData; + PMEDIA_CHANGE_DETECTION_INFO info; + PIO_STACK_LOCATION nextIrpStack; + NTSTATUS status; + BOOLEAN retryImmediately = FALSE; + + // + // Since the class driver created this request, it's completion routine + // will not get a valid device object handed in. Use the one in the + // irp stack instead + // + + DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject; + fdoExtension = DeviceObject->DeviceExtension; + fdoData = fdoExtension->PrivateFdoData; + info = fdoExtension->MediaChangeDetectionInfo; + + ASSERT(info->MediaChangeIrp != NULL); + ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN)); + DBGTRACE(ClassDebugMCN, ("> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject, Irp)); + + /* + * HACK for IoMega 2GB Jaz drive: + * This drive spins down on its own to preserve the media. + * When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?). + * ClassInterpretSenseInfo would then call ClassSendStartUnit to spin the media up, which defeats the + * purpose of the spindown. + * So in this case, make this into a successful TUR. + * This allows the drive to stay spun down until it is actually accessed again. + * (If the media were actually removed, TUR would fail with 2/3a/0 ). + * This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this + * is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list. + */ + if ((SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) && + TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK) && + (Srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode))){ + + PSENSE_DATA senseData = Srb->SenseInfoBuffer; + + if ((senseData->SenseKey == SCSI_SENSE_NOT_READY) && + (senseData->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)){ + Srb->SrbStatus = SRB_STATUS_SUCCESS; + } + } + + + // + // use ClassInterpretSenseInfo() to check for media state, and also + // to call ClassError() with correct parameters. + // + status = STATUS_SUCCESS; + if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.", DBGGETSRBSTATUSSTR(Srb), DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb))); + + ClassInterpretSenseInfo(DeviceObject, + Srb, + IRP_MJ_SCSI, + 0, + 0, + &status, + NULL); + + } + else { + + fdoData->LoggedTURFailureSinceLastIO = FALSE; + + if (!info->Gesn.Supported) { + + DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent.")); + + // + // success != media for GESN case + // + + ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE); + + } + else { + DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - succeeded (GESN supported).")); + } + } + + if (info->Gesn.Supported) { + + if (status == STATUS_DATA_OVERRUN) { + DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - Overrun")); + status = STATUS_SUCCESS; + } + + if (!NT_SUCCESS(status)) { + DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status)); + } else { + + // + // for GESN, need to interpret the results of the data. + // this may also require an immediate retry + // + + if (Irp->IoStatus.Information == 8 ) { + ClasspInterpretGesnData(fdoExtension, + (PVOID)info->Gesn.Buffer, + &retryImmediately); + } + + } // end of NT_SUCCESS(status) + + } // end of Info->Gesn.Supported + + // + // free port-allocated sense buffer, if any. + // + + if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) { + FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb); + } + + // + // Remember the IRP and SRB for use the next time. + // + + ASSERT(IoGetNextIrpStackLocation(Irp)); + IoGetNextIrpStackLocation(Irp)->Parameters.Scsi.Srb = Srb; + + // + // Reset the MCN timer. + // + + ClassResetMediaChangeTimer(fdoExtension); + + // + // run a sanity check to make sure we're not recursing continuously + // + + if (retryImmediately) { + + info->MediaChangeRetryCount++; + if (info->MediaChangeRetryCount > MAXIMUM_IMMEDIATE_MCN_RETRIES) { + ASSERT(!"Recursing too often in MCN?"); + info->MediaChangeRetryCount = 0; + retryImmediately = FALSE; + } + + } else { + + info->MediaChangeRetryCount = 0; + + } + + + // + // release the remove lock.... + // + + { + UCHAR uniqueValue; + ClassAcquireRemoveLock(DeviceObject, (PIRP)(&uniqueValue)); + ClassReleaseRemoveLock(DeviceObject, Irp); + + + // + // set the irp as not in use + // + { + volatile LONG irpWasInUse; + irpWasInUse = InterlockedCompareExchange(&info->MediaChangeIrpInUse, 0, 1); + #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here. + ASSERT(irpWasInUse); + #endif + } + + // + // now send it again before we release our last remove lock + // + + if (retryImmediately) { + ClasspSendMediaStateIrp(fdoExtension, info, 0); + } + else { + DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - not retrying immediately")); + } + + // + // release the temporary remove lock + // + + ClassReleaseRemoveLock(DeviceObject, (PIRP)(&uniqueValue)); + } + + DBGTRACE(ClassDebugMCN, ("< ClasspMediaChangeDetectionCompletion")); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented + +Routine Description: + + This routine + +Arguments: + + DeviceObject - + Irp - + +Return Value: + + +--*/ +PIRP +ClasspPrepareMcnIrp( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PMEDIA_CHANGE_DETECTION_INFO Info, + IN BOOLEAN UseGesn +) +{ + PSCSI_REQUEST_BLOCK srb; + PIO_STACK_LOCATION irpStack; + PIO_STACK_LOCATION nextIrpStack; + NTSTATUS status; + PCDB cdb; + PIRP irp; + PVOID buffer; + + // + // Setup the IRP to perform a test unit ready. + // + + irp = Info->MediaChangeIrp; + + ASSERT(irp); + + if (irp == NULL) { + return NULL; + } + + // + // don't keep sending this if the device is being removed. + // + + status = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp); + if (status == REMOVE_COMPLETE) { + ASSERT(status != REMOVE_COMPLETE); + return NULL; + } + else if (status == REMOVE_PENDING) { + ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp); + return NULL; + } + else { + ASSERT(status == NO_REMOVE); + } + + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = 0; + irp->Flags = 0; + irp->UserBuffer = NULL; + + // + // If the irp is sent down when the volume needs to be + // verified, CdRomUpdateGeometryCompletion won't complete + // it since it's not associated with a thread. Marking + // it to override the verify causes it always be sent + // to the port driver + // + + irpStack = IoGetCurrentIrpStackLocation(irp); + irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME; + + nextIrpStack = IoGetNextIrpStackLocation(irp); + nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + nextIrpStack->Parameters.Scsi.Srb = &(Info->MediaChangeSrb); + + // + // Prepare the SRB for execution. + // + + srb = nextIrpStack->Parameters.Scsi.Srb; + buffer = srb->SenseInfoBuffer; + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + RtlZeroMemory(buffer, SENSE_BUFFER_SIZE); + + + srb->QueueTag = SP_UNTAGGED; + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->SenseInfoBuffer = buffer; + srb->SrbStatus = 0; + srb->ScsiStatus = 0; + srb->OriginalRequest = irp; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + srb->SrbFlags = FdoExtension->SrbFlags; + SET_FLAG(srb->SrbFlags, Info->SrbFlags); + + srb->TimeOutValue = FdoExtension->TimeOutValue * 2; + + if (srb->TimeOutValue == 0) { + + if (FdoExtension->TimeOutValue == 0) { + + KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL, + "ClassSendTestUnitIrp: FdoExtension->TimeOutValue " + "is set to zero?! -- resetting to 10\n")); + srb->TimeOutValue = 10 * 2; // reasonable default + + } else { + + KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL, + "ClassSendTestUnitIrp: Someone set " + "srb->TimeOutValue to zero?! -- resetting to %x\n", + FdoExtension->TimeOutValue * 2)); + srb->TimeOutValue = FdoExtension->TimeOutValue * 2; + + } + + } + + if (!UseGesn) { + + srb->CdbLength = 6; + srb->DataTransferLength = 0; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); + nextIrpStack->Parameters.DeviceIoControl.IoControlCode = + IOCTL_SCSI_EXECUTE_NONE; + srb->DataBuffer = NULL; + srb->DataTransferLength = 0; + irp->MdlAddress = NULL; + + cdb = (PCDB) &srb->Cdb[0]; + cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; + + } else { + + ASSERT(Info->Gesn.Buffer); + + srb->TimeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN + + srb->CdbLength = 10; + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + nextIrpStack->Parameters.DeviceIoControl.IoControlCode = + IOCTL_SCSI_EXECUTE_IN; + srb->DataBuffer = Info->Gesn.Buffer; + srb->DataTransferLength = Info->Gesn.BufferSize; + irp->MdlAddress = Info->Gesn.Mdl; + + cdb = (PCDB) &srb->Cdb[0]; + cdb->GET_EVENT_STATUS_NOTIFICATION.OperationCode = + SCSIOP_GET_EVENT_STATUS; + cdb->GET_EVENT_STATUS_NOTIFICATION.Immediate = 1; + cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[0] = + (UCHAR)((Info->Gesn.BufferSize) >> 8); + cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[1] = + (UCHAR)((Info->Gesn.BufferSize) & 0xff); + cdb->GET_EVENT_STATUS_NOTIFICATION.NotificationClassRequest = + Info->Gesn.EventMask; + + } + + IoSetCompletionRoutine(irp, + ClasspMediaChangeDetectionCompletion, + srb, + TRUE, + TRUE, + TRUE); + + return irp; + +} + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented + +Routine Description: + + This routine + +Arguments: + + DeviceObject - + Irp - + +Return Value: + +--*/ +VOID +ClasspSendMediaStateIrp( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PMEDIA_CHANGE_DETECTION_INFO Info, + IN ULONG CountDown + ) +{ + BOOLEAN requestPending = FALSE; + LONG irpInUse; + LARGE_INTEGER zero; + NTSTATUS status; + + DBGTRACE(ClassDebugMCN, ("> ClasspSendMediaStateIrp")); + + if (((FdoExtension->CommonExtension.CurrentState != IRP_MN_START_DEVICE) || + (FdoExtension->DevicePowerState != PowerDeviceD0) + ) && + (!Info->MediaChangeIrpLost)) { + + // + // the device may be stopped, powered down, or otherwise queueing io, + // so should not timeout the autorun irp (yet) -- set to zero ticks. + // scattered code relies upon this to not prematurely "lose" an + // autoplay irp that was queued. + // + + Info->MediaChangeIrpTimeInUse = 0; + } + + // + // if the irp is not in use, mark it as such. + // + + irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 1, 0); + + if (irpInUse) { + + LONG timeInUse; + + timeInUse = InterlockedIncrement(&Info->MediaChangeIrpTimeInUse); + + DebugPrint((ClassDebugMCN, "ClasspSendMediaStateIrp: irp in use for " + "%x seconds when synchronizing for MCD\n", timeInUse)); + + if (Info->MediaChangeIrpLost == FALSE) { + + if (timeInUse > MEDIA_CHANGE_TIMEOUT_TIME) { + + // + // currently set to five minutes. hard to imagine a drive + // taking that long to spin up. + // + + DebugPrint((ClassDebugError, + "CdRom%d: Media Change Notification has lost " + "it's irp and doesn't know where to find it. " + "Leave it alone and it'll come home dragging " + "it's stack behind it.\n", + FdoExtension->DeviceNumber)); + Info->MediaChangeIrpLost = TRUE; + } + } + + DBGTRACE(ClassDebugMCN, ("< ClasspSendMediaStateIrp - irpInUse")); + return; + + } + + TRY { + + if (Info->MediaChangeDetectionDisableCount != 0) { + DebugPrint((ClassDebugTrace, "ClassCheckMediaState: device %p has " + " detection disabled \n", FdoExtension->DeviceObject)); + LEAVE; + } + + if (FdoExtension->DevicePowerState != PowerDeviceD0) { + + if (TEST_FLAG(Info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE)) { + DebugPrint((ClassDebugMCN, + "ClassCheckMediaState: device %p is powered " + "down and flags are set to let it sleep\n", + FdoExtension->DeviceObject)); + ClassResetMediaChangeTimer(FdoExtension); + LEAVE; + } + + // + // NOTE: we don't increment the time in use until our power state + // changes above. this way, we won't "lose" the autoplay irp. + // it's up to the lower driver to determine if powering up is a + // good idea. + // + + DebugPrint((ClassDebugMCN, + "ClassCheckMediaState: device %p needs to powerup " + "to handle this io (may take a few extra seconds).\n", + FdoExtension->DeviceObject)); + + } + + Info->MediaChangeIrpTimeInUse = 0; + Info->MediaChangeIrpLost = FALSE; + + if (CountDown == 0) { + + PIRP irp; + + DebugPrint((ClassDebugTrace, + "ClassCheckMediaState: timer expired\n")); + + if (Info->MediaChangeDetectionDisableCount != 0) { + DebugPrint((ClassDebugTrace, + "ClassCheckMediaState: detection disabled\n")); + LEAVE; + } + + // + // Prepare the IRP for the test unit ready + // + + irp = ClasspPrepareMcnIrp(FdoExtension, + Info, + Info->Gesn.Supported); + + // + // Issue the request. + // + + DebugPrint((ClassDebugTrace, + "ClasspSendMediaStateIrp: Device %p getting TUR " + " irp %p\n", FdoExtension->DeviceObject, irp)); + + if (irp == NULL) { + LEAVE; + } + + + // + // note: if we send it to the class dispatch routines, there is + // a timing window here (since they grab the remove lock) + // where we'd be removed. ELIMINATE the window by grabbing + // the lock ourselves above and sending it to the lower + // device object directly or to the device's StartIo + // routine (which doesn't acquire the lock). + // + + requestPending = TRUE; + + DBGTRACE(ClassDebugMCN, (" ClasspSendMediaStateIrp - calling IoCallDriver.")); + IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp); + } + + } FINALLY { + + if(requestPending == FALSE) { + irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 0, 1); + #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here. + ASSERT(irpInUse); + #endif + } + + } + + DBGTRACE(ClassDebugMCN, ("< ClasspSendMediaStateIrp")); + + return; +} // end ClasspSendMediaStateIrp() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassCheckMediaState() + +Routine Description: + + This routine is called by the class driver to test for a media change + condition and/or poll for disk failure prediction. It should be called + from the class driver's IO timer routine once per second. + +Arguments: + + FdoExtension - the device extension + +Return Value: + + none + +--*/ +VOID +ClassCheckMediaState( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; + LONG countDown; + + if(info == NULL) { + DebugPrint((ClassDebugTrace, + "ClassCheckMediaState: detection not enabled\n")); + return; + } + + // + // Media change support is active and the IRP is waiting. Decrement the + // timer. There is no MP protection on the timer counter. This code + // is the only code that will manipulate the timer counter and only one + // instance of it should be running at any given time. + // + + countDown = InterlockedDecrement(&(info->MediaChangeCountDown)); + + // + // Try to acquire the media change event. If we can't do it immediately + // then bail out and assume the caller will try again later. + // + ClasspSendMediaStateIrp(FdoExtension, + info, + countDown); + + return; +} // end ClassCheckMediaState() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassResetMediaChangeTimer() + +Routine Description: + + Resets the media change count down timer to the default number of seconds. + +Arguments: + + FdoExtension - the device to reset the timer for + +Return Value: + + None + +--*/ +VOID +ClassResetMediaChangeTimer( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; + + if(info != NULL) { + InterlockedExchange(&(info->MediaChangeCountDown), + MEDIA_CHANGE_DEFAULT_TIME); + } + return; +} // end ClassResetMediaChangeTimer() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented + +Routine Description: + + This routine + +Arguments: + + DeviceObject - + Irp - + +Return Value: + +--*/ +NTSTATUS +ClasspInitializePolling( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN BOOLEAN AllowDriveToSleep + ) +{ + PDEVICE_OBJECT fdo = FdoExtension->DeviceObject; + PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; + + ULONG size; + PMEDIA_CHANGE_DETECTION_INFO info; + PIRP irp; + + PAGED_CODE(); + + if (FdoExtension->MediaChangeDetectionInfo != NULL) { + return STATUS_SUCCESS; + } + + info = ExAllocatePoolWithTag(NonPagedPool, + sizeof(MEDIA_CHANGE_DETECTION_INFO), + CLASS_TAG_MEDIA_CHANGE_DETECTION); + + if(info != NULL) { + RtlZeroMemory(info, sizeof(MEDIA_CHANGE_DETECTION_INFO)); + + FdoExtension->KernelModeMcnContext.FileObject = (PVOID)-1; + FdoExtension->KernelModeMcnContext.DeviceObject = (PVOID)-1; + FdoExtension->KernelModeMcnContext.LockCount = 0; + FdoExtension->KernelModeMcnContext.McnDisableCount = 0; + + /* + * Allocate an IRP to carry the Test-Unit-Ready. + * Allocate an extra IRP stack location + * so we can cache our device object in the top location. + */ + irp = IoAllocateIrp((CCHAR)(fdo->StackSize+1), FALSE); + + if (irp != NULL) { + + PVOID buffer; + + buffer = ExAllocatePoolWithTag( + NonPagedPoolCacheAligned, + SENSE_BUFFER_SIZE, + CLASS_TAG_MEDIA_CHANGE_DETECTION); + + if (buffer != NULL) { + PIO_STACK_LOCATION irpStack; + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + + srb = &(info->MediaChangeSrb); + info->MediaChangeIrp = irp; + info->SenseBuffer = buffer; + + /* + * For the driver that creates an IRP, there is no 'current' stack location. + * Step down one IRP stack location so that the extra top one + * becomes our 'current' one. + */ + IoSetNextIrpStackLocation(irp); + + /* + * Cache our device object in the extra top IRP stack location + * so we have it in our completion routine. + */ + irpStack = IoGetCurrentIrpStackLocation(irp); + irpStack->DeviceObject = fdo; + + /* + * Now start setting up the next IRP stack location for the call like any driver would. + */ + irpStack = IoGetNextIrpStackLocation(irp); + irpStack->Parameters.Scsi.Srb = srb; + info->MediaChangeIrp = irp; + + // + // Initialize the SRB + // + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Initialize and set up the sense information buffer + // + + RtlZeroMemory(buffer, SENSE_BUFFER_SIZE); + srb->SenseInfoBuffer = buffer; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + // + // Set default values for the media change notification + // configuration. + // + + info->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME; + info->MediaChangeDetectionDisableCount = 0; + + // + // Assume that there is initially no media in the device + // only notify upper layers if there is something there + // + + info->MediaChangeDetectionState = MediaUnknown; + + info->MediaChangeIrpTimeInUse = 0; + info->MediaChangeIrpLost = FALSE; + + // + // setup all extra flags we'll be setting for this irp + // + info->SrbFlags = 0; + if (AllowDriveToSleep) { + SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE); + } + SET_FLAG(info->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY); + SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + SET_FLAG(info->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + + KeInitializeMutex(&info->MediaChangeMutex, 0x100); + + // + // It is ok to support media change events on this + // device. + // + + FdoExtension->MediaChangeDetectionInfo = info; + + // + // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even + // when the device supports DVD (no need to + // check for FILE_DEVICE_DVD, as it's not a + // valid check). + // + + if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM){ + + NTSTATUS status; + + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "ClasspInitializePolling: Testing for GESN\n")); + status = ClasspInitializeGesn(FdoExtension, info); + if (NT_SUCCESS(status)) { + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "ClasspInitializePolling: GESN available " + "for %p\n", FdoExtension->DeviceObject)); + ASSERT(info->Gesn.Supported ); + ASSERT(info->Gesn.Buffer != NULL); + ASSERT(info->Gesn.BufferSize != 0); + ASSERT(info->Gesn.EventMask != 0); + // must return here, for ASSERTs to be valid. + return STATUS_SUCCESS; + } + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "ClasspInitializePolling: GESN *NOT* available " + "for %p\n", FdoExtension->DeviceObject)); + } + + ASSERT(info->Gesn.Supported == 0); + ASSERT(info->Gesn.Buffer == NULL); + ASSERT(info->Gesn.BufferSize == 0); + ASSERT(info->Gesn.EventMask == 0); + info->Gesn.Supported = 0; // just in case.... + return STATUS_SUCCESS; + } + + IoFreeIrp(irp); + } + + ExFreePool(info); + } + + // + // nothing to free here + // + return STATUS_INSUFFICIENT_RESOURCES; + +} // end ClasspInitializePolling() + +NTSTATUS +ClasspInitializeGesn( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PMEDIA_CHANGE_DETECTION_INFO Info + ) +{ + PNOTIFICATION_EVENT_STATUS_HEADER header; + CLASS_DETECTION_STATE detectionState = ClassDetectionUnknown; + PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor; + NTSTATUS status = STATUS_NOT_SUPPORTED; + PIRP irp; + KEVENT event; + BOOLEAN retryImmediately; + ULONG i; + ULONG atapiResets; + + + PAGED_CODE(); + ASSERT(Info == FdoExtension->MediaChangeDetectionInfo); + + // + // read if we already know the abilities of the device + // + + ClassGetDeviceParameter(FdoExtension, + CLASSP_REG_SUBKEY_NAME, + CLASSP_REG_MMC_DETECTION_VALUE_NAME, + (PULONG)&detectionState); + + if (detectionState == ClassDetectionUnsupported) { + goto ExitWithError; + } + + // + // check if the device has a hack flag saying never to try this. + // + + if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags, + FDO_HACK_GESN_IS_BAD)) { + + detectionState = ClassDetectionUnsupported; + ClassSetDeviceParameter(FdoExtension, + CLASSP_REG_SUBKEY_NAME, + CLASSP_REG_MMC_DETECTION_VALUE_NAME, + ClassDetectionSupported); + goto ExitWithError; + + } + + + // + // else go through the process since we allocate buffers and + // get all sorts of device settings. + // + + if (Info->Gesn.Buffer == NULL) { + Info->Gesn.Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + GESN_BUFFER_SIZE, + '??cS'); + } + if (Info->Gesn.Buffer == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitWithError; + } + if (Info->Gesn.Mdl != NULL) { + IoFreeMdl(Info->Gesn.Mdl); + } + Info->Gesn.Mdl = IoAllocateMdl(Info->Gesn.Buffer, + GESN_BUFFER_SIZE, + FALSE, FALSE, NULL); + if (Info->Gesn.Mdl == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitWithError; + } + + MmBuildMdlForNonPagedPool(Info->Gesn.Mdl); + Info->Gesn.BufferSize = GESN_BUFFER_SIZE; + Info->Gesn.EventMask = 0; + + // + // all items are prepared to use GESN (except the event mask, so don't + // optimize this part out!). + // + // now see if it really works. we have to loop through this because + // many SAMSUNG (and one COMPAQ) drives timeout when requesting + // NOT_READY events, even when the IMMEDIATE bit is set. :( + // + // using a drive list is cumbersome, so this might fix the problem. + // + + adapterDescriptor = FdoExtension->AdapterDescriptor; + atapiResets = 0; + retryImmediately = TRUE; + for (i = 0; i < 16 && retryImmediately == TRUE; i++) { + + irp = ClasspPrepareMcnIrp(FdoExtension, Info, TRUE); + if (irp == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitWithError; + } + + ASSERT(TEST_FLAG(Info->MediaChangeSrb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE)); + + // + // replace the completion routine with a different one this time... + // + + IoSetCompletionRoutine(irp, + ClassSignalCompletion, + &event, + TRUE, TRUE, TRUE); + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + status = IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp); + + if (status == STATUS_PENDING) { + status = KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + ASSERT(NT_SUCCESS(status)); + } + ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp); + + if (SRB_STATUS(Info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS) { + ClassInterpretSenseInfo(FdoExtension->DeviceObject, + &(Info->MediaChangeSrb), + IRP_MJ_SCSI, + 0, + 0, + &status, + NULL); + } + + if ((adapterDescriptor->BusType == BusTypeAtapi) && + (Info->MediaChangeSrb.SrbStatus == SRB_STATUS_BUS_RESET) + ) { + + // + // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead + // of SRB_STATUS_TIMEOUT, so we cannot differentiate between + // the two. if we get this status four time consecutively, + // stop trying this command. it is too late to change ATAPI + // at this point, so special-case this here. (07/10/2001) + // NOTE: any value more than 4 may cause the device to be + // marked missing. + // + + atapiResets++; + if (atapiResets >= 4) { + status = STATUS_IO_DEVICE_ERROR; + goto ExitWithError; + } + } + + if (status == STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; + } + + if ((status == STATUS_INVALID_DEVICE_REQUEST) || + (status == STATUS_TIMEOUT) || + (status == STATUS_IO_DEVICE_ERROR) || + (status == STATUS_IO_TIMEOUT) + ) { + + // + // with these error codes, we don't ever want to try this command + // again on this device, since it reacts poorly. + // + + ClassSetDeviceParameter(FdoExtension, + CLASSP_REG_SUBKEY_NAME, + CLASSP_REG_MMC_DETECTION_VALUE_NAME, + ClassDetectionUnsupported); + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning, + "Classpnp => GESN test failed %x for fdo %p\n", + status, FdoExtension->DeviceObject)); + goto ExitWithError; + + + } + + if (!NT_SUCCESS(status)) { + + // + // this may be other errors that should not disable GESN + // for all future start_device calls. + // + + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning, + "Classpnp => GESN test failed %x for fdo %p\n", + status, FdoExtension->DeviceObject)); + goto ExitWithError; + } + + if (i == 0) { + + // + // the first time, the request was just retrieving a mask of + // available bits. use this to mask future requests. + // + + header = (PNOTIFICATION_EVENT_STATUS_HEADER)(Info->Gesn.Buffer); + + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => Fdo %p supports event mask %x\n", + FdoExtension->DeviceObject, header->SupportedEventClasses)); + + + if (TEST_FLAG(header->SupportedEventClasses, + NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) { + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => GESN supports MCN\n")); + } + if (TEST_FLAG(header->SupportedEventClasses, + NOTIFICATION_DEVICE_BUSY_CLASS_MASK)) { + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => GESN supports DeviceBusy\n")); + } + Info->Gesn.EventMask = header->SupportedEventClasses; + + // + // realistically, we are only considering the following events: + // EXTERNAL REQUEST - this is being tested for play/stop/etc. + // MEDIA STATUS - autorun and ejection requests. + // DEVICE BUSY - to allow us to predict when media will be ready. + // therefore, we should not bother querying for the other, + // unknown events. clear all but the above flags. + // + + Info->Gesn.EventMask &= + NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK | + NOTIFICATION_MEDIA_STATUS_CLASS_MASK | + NOTIFICATION_DEVICE_BUSY_CLASS_MASK ; + + + // + // HACKHACK - REF #0001 + // Some devices will *never* report an event if we've also requested + // that it report lower-priority events. this is due to a + // misunderstanding in the specification wherein a "No Change" is + // interpreted to be a real event. what should occur is that the + // device should ignore "No Change" events when multiple event types + // are requested unless there are no other events waiting. this + // greatly reduces the number of requests that the host must send + // to determine if an event has occurred. Since we must work on all + // drives, default to enabling the hack until we find evidence of + // proper firmware. + // + + if (CountOfSetBitsUChar(Info->Gesn.EventMask) == 1) { + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => GESN hack %s for FDO %p\n", + "not required", FdoExtension->DeviceObject)); + } else { + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => GESN hack %s for FDO %p\n", + "enabled", FdoExtension->DeviceObject)); + Info->Gesn.HackEventMask = 1; + } + + } else { + + // + // not the first time looping through, so interpret the results. + // + + ClasspInterpretGesnData(FdoExtension, + (PVOID)Info->Gesn.Buffer, + &retryImmediately); + + } + + } // end loop of GESN requests.... + + // + // we can only use this if it can be relied upon for media changes, + // since we are (by definition) no longer going to be polling via + // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION + // for this command (although a filter driver, such as one for burning + // cd's, might still fake those errors). + // + // since we also rely upon NOT_READY events to change the cursor + // into a "wait" cursor, we can't use GESN without NOT_READY support. + // + + if (TEST_FLAG(Info->Gesn.EventMask, + NOTIFICATION_MEDIA_STATUS_CLASS_MASK) && + TEST_FLAG(Info->Gesn.EventMask, + NOTIFICATION_DEVICE_BUSY_CLASS_MASK) + ) { + + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => Enabling GESN support for fdo %p\n", + FdoExtension->DeviceObject)); + Info->Gesn.Supported = TRUE; + + ClassSetDeviceParameter(FdoExtension, + CLASSP_REG_SUBKEY_NAME, + CLASSP_REG_MMC_DETECTION_VALUE_NAME, + ClassDetectionSupported); + + return STATUS_SUCCESS; + + } + + KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, + "Classpnp => GESN available but not enabled for fdo %p\n", + FdoExtension->DeviceObject)); + goto ExitWithError; + + // fall through... + +ExitWithError: + if (Info->Gesn.Mdl) { + IoFreeMdl(Info->Gesn.Mdl); + Info->Gesn.Mdl = NULL; + } + if (Info->Gesn.Buffer) { + ExFreePool(Info->Gesn.Buffer); + Info->Gesn.Buffer = NULL; + } + Info->Gesn.Supported = 0; + Info->Gesn.EventMask = 0; + Info->Gesn.BufferSize = 0; + return STATUS_NOT_SUPPORTED; + +} + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassInitializeTestUnitPolling() + +Routine Description: + + This routine will initialize MCN regardless of the settings stored + in the registry. This should be used with caution, as some devices + react badly to constant io. (i.e. never spin down, continuously cycling + media in changers, ejection of media, etc.) It is highly suggested to + use ClassInitializeMediaChangeDetection() instead. + +Arguments: + + FdoExtension is the device to poll + + AllowDriveToSleep says whether to attempt to allow the drive to sleep + or not. This only affects system-known spin down states, so if a + drive spins itself down, this has no effect until the system spins + it down. + +Return Value: + +--*/ +NTSTATUS +ClassInitializeTestUnitPolling( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN BOOLEAN AllowDriveToSleep + ) +{ + return ClasspInitializePolling(FdoExtension, AllowDriveToSleep); +} // end ClassInitializeTestUnitPolling() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassInitializeMediaChangeDetection() + +Routine Description: + + This routine checks to see if it is safe to initialize MCN (the back end + to autorun) for a given device. It will then check the device-type wide + key "Autorun" in the service key (for legacy reasons), and then look in + the device-specific key to potentially override that setting. + + If MCN is to be enabled, all neccessary structures and memory are + allocated and initialized. + + This routine MUST be called only from the ClassInit() callback. + +Arguments: + + FdoExtension - the device to initialize MCN for, if appropriate + + EventPrefix - unused, legacy argument. Set to zero. + +Return Value: + +--*/ +VOID +ClassInitializeMediaChangeDetection( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PUCHAR EventPrefix + ) +{ + PDEVICE_OBJECT fdo = FdoExtension->DeviceObject; + NTSTATUS status; + + PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension( + fdo->DriverObject); + + BOOLEAN disabledForBadHardware; + BOOLEAN disabled; + BOOLEAN instanceOverride; + + PAGED_CODE(); + + // + // NOTE: This assumes that ClassInitializeMediaChangeDetection is always + // called in the context of the ClassInitDevice callback. If called + // after then this check will have already been made and the + // once a second timer will not have been enabled. + // + + disabledForBadHardware = ClasspIsMediaChangeDisabledDueToHardwareLimitation( + FdoExtension, + &(driverExtension->RegistryPath) + ); + + if (disabledForBadHardware) { + DebugPrint((ClassDebugMCN, + "ClassInitializeMCN: Disabled due to hardware" + "limitations for this device")); + return; + } + + // + // autorun should now be enabled by default for all media types. + // + + disabled = ClasspIsMediaChangeDisabledForClass( + FdoExtension, + &(driverExtension->RegistryPath) + ); + + DebugPrint((ClassDebugMCN, + "ClassInitializeMCN: Class MCN is %s\n", + (disabled ? "disabled" : "enabled"))); + + status = ClasspMediaChangeDeviceInstanceOverride( + FdoExtension, + &instanceOverride); // default value + + if (!NT_SUCCESS(status)) { + DebugPrint((ClassDebugMCN, + "ClassInitializeMCN: Instance using default\n")); + } else { + DebugPrint((ClassDebugMCN, + "ClassInitializeMCN: Instance override: %s MCN\n", + (instanceOverride ? "Enabling" : "Disabling"))); + disabled = !instanceOverride; + } + + DebugPrint((ClassDebugMCN, + "ClassInitializeMCN: Instance MCN is %s\n", + (disabled ? "disabled" : "enabled"))); + + if (disabled) { + return; + } + + // + // if the drive is not a CDROM, allow the drive to sleep + // + if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) { + ClasspInitializePolling(FdoExtension, FALSE); + } else { + ClasspInitializePolling(FdoExtension, TRUE); + } + + return; +} // end ClassInitializeMediaChangeDetection() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspMediaChangeDeviceInstanceOverride() + +Routine Description: + + The user can override the global setting to enable or disable Autorun on a + specific cdrom device via the control panel. This routine checks and/or + sets this value. + +Arguments: + + FdoExtension - the device to set/get the value for + Value - the value to use in a set + SetValue - whether to set the value + +Return Value: + + TRUE - Autorun is disabled + FALSE - Autorun is not disabled (Default) + +--*/ +NTSTATUS +ClasspMediaChangeDeviceInstanceOverride( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PBOOLEAN Enabled + ) +{ + HANDLE deviceParameterHandle; // cdrom instance key + HANDLE driverParameterHandle; // cdrom specific key + RTL_QUERY_REGISTRY_TABLE queryTable[3]; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING subkeyName; + NTSTATUS status; + ULONG alwaysEnable; + ULONG alwaysDisable; + ULONG i; + + + PAGED_CODE(); + + deviceParameterHandle = NULL; + driverParameterHandle = NULL; + status = STATUS_UNSUCCESSFUL; + alwaysEnable = FALSE; + alwaysDisable = FALSE; + + TRY { + + status = IoOpenDeviceRegistryKey( FdoExtension->LowerPdo, + PLUGPLAY_REGKEY_DEVICE, + KEY_ALL_ACCESS, + &deviceParameterHandle + ); + if (!NT_SUCCESS(status)) { + + // + // this can occur when a new device is added to the system + // this is due to cdrom.sys being an 'essential' driver + // + DebugPrint((ClassDebugMCN, + "ClassMediaChangeDeviceInstanceDisabled: " + "Could not open device registry key [%lx]\n", status)); + LEAVE; + } + + RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME); + InitializeObjectAttributes(&objectAttributes, + &subkeyName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + deviceParameterHandle, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwCreateKey(&driverParameterHandle, + KEY_READ, + &objectAttributes, + 0, + (PUNICODE_STRING) NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (!NT_SUCCESS(status)) { + DebugPrint((ClassDebugMCN, + "ClassMediaChangeDeviceInstanceDisabled: " + "subkey could not be created. %lx\n", status)); + LEAVE; + } + + // + // Default to not changing autorun behavior, based upon setting + // registryValue to zero. + // + + for (i=0;i<2;i++) { + + RtlZeroMemory(&queryTable[0], sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + queryTable[0].DefaultType = REG_DWORD; + queryTable[0].DefaultLength = 0; + + if (i==0) { + queryTable[0].Name = MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME; + queryTable[0].EntryContext = &alwaysDisable; + queryTable[0].DefaultData = &alwaysDisable; + } else { + queryTable[0].Name = MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME; + queryTable[0].EntryContext = &alwaysEnable; + queryTable[0].DefaultData = &alwaysEnable; + } + + // + // don't care if it succeeds, since we set defaults above + // + + RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + (PWSTR)driverParameterHandle, + queryTable, + NULL, + NULL); + } + + } FINALLY { + + if (driverParameterHandle) ZwClose(driverParameterHandle); + if (deviceParameterHandle) ZwClose(deviceParameterHandle); + + } + + if (alwaysEnable && alwaysDisable) { + + DebugPrint((ClassDebugMCN, + "ClassMediaChangeDeviceInstanceDisabled: %s selected\n", + "Both Enable and Disable set -- DISABLE")); + ASSERT(NT_SUCCESS(status)); + status = STATUS_SUCCESS; + *Enabled = FALSE; + + } else if (alwaysDisable) { + + DebugPrint((ClassDebugMCN, + "ClassMediaChangeDeviceInstanceDisabled: %s selected\n", + "DISABLE")); + ASSERT(NT_SUCCESS(status)); + status = STATUS_SUCCESS; + *Enabled = FALSE; + + } else if (alwaysEnable) { + + DebugPrint((ClassDebugMCN, + "ClassMediaChangeDeviceInstanceDisabled: %s selected\n", + "ENABLE")); + ASSERT(NT_SUCCESS(status)); + status = STATUS_SUCCESS; + *Enabled = TRUE; + + } else { + + DebugPrint((ClassDebugMCN, + "ClassMediaChangeDeviceInstanceDisabled: %s selected\n", + "DEFAULT")); + status = STATUS_UNSUCCESSFUL; + + } + + return status; + +} // end ClasspMediaChangeDeviceInstanceOverride() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspIsMediaChangeDisabledDueToHardwareLimitation() + +Routine Description: + + The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for + which to never enable MediaChangeNotification. + + The user can override the global setting to enable or disable Autorun on a + specific cdrom device via the control panel. + +Arguments: + + FdoExtension - + RegistryPath - pointer to the unicode string inside + ...\CurrentControlSet\Services\Cdrom + +Return Value: + + TRUE - no autorun. + FALSE - Autorun may be enabled + +--*/ +BOOLEAN +ClasspIsMediaChangeDisabledDueToHardwareLimitation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PUNICODE_STRING RegistryPath + ) +{ + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE serviceKey = NULL; + RTL_QUERY_REGISTRY_TABLE parameters[2]; + + UNICODE_STRING deviceUnicodeString; + ANSI_STRING deviceString; + ULONG mediaChangeNotificationDisabled = FALSE; + + NTSTATUS status; + + + PAGED_CODE(); + + // + // open the service key. + // + + InitializeObjectAttributes(&objectAttributes, + RegistryPath, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + KEY_READ, + &objectAttributes); + + ASSERT(NT_SUCCESS(status)); + + + if(!NT_SUCCESS(status)) { + + // + // always take the safe path. if we can't open the service key, + // disable autorun + // + + return TRUE; + + } + + TRY { + // + // Determine if drive is in a list of those requiring + // autorun to be disabled. this is stored in a REG_MULTI_SZ + // named AutoRunAlwaysDisable. this is required as some autochangers + // must load the disc to reply to ChkVerify request, causing them + // to cycle discs continuously. + // + + PWSTR nullMultiSz; + PUCHAR vendorId; + PUCHAR productId; + PUCHAR revisionId; + ULONG length; + ULONG offset; + + deviceString.Buffer = NULL; + deviceUnicodeString.Buffer = NULL; + + // + // there may be nothing to check against + // + + if ((deviceDescriptor->VendorIdOffset == 0) && + (deviceDescriptor->ProductIdOffset == 0)) { + LEAVE; + } + + length = 0; + + if (deviceDescriptor->VendorIdOffset == 0) { + vendorId = NULL; + } else { + vendorId = (PUCHAR) deviceDescriptor + deviceDescriptor->VendorIdOffset; + length = strlen(vendorId); + } + + if ( deviceDescriptor->ProductIdOffset == 0 ) { + productId = NULL; + } else { + productId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductIdOffset; + length += strlen(productId); + } + + if ( deviceDescriptor->ProductRevisionOffset == 0 ) { + revisionId = NULL; + } else { + revisionId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset; + length += strlen(revisionId); + } + + // + // allocate a buffer for the string + // + + deviceString.Length = (USHORT)( length ); + deviceString.MaximumLength = deviceString.Length + 1; + deviceString.Buffer = (PUCHAR)ExAllocatePoolWithTag( NonPagedPool, + deviceString.MaximumLength, + CLASS_TAG_AUTORUN_DISABLE + ); + if (deviceString.Buffer == NULL) { + DebugPrint((ClassDebugMCN, + "ClassMediaChangeDisabledForHardware: Unable to alloc " + "string buffer\n" )); + LEAVE; + } + + // + // copy strings to the buffer + // + offset = 0; + + if (vendorId != NULL) { + RtlCopyMemory(deviceString.Buffer + offset, + vendorId, + strlen(vendorId)); + offset += strlen(vendorId); + } + + if ( productId != NULL ) { + RtlCopyMemory(deviceString.Buffer + offset, + productId, + strlen(productId)); + offset += strlen(productId); + } + if ( revisionId != NULL ) { + RtlCopyMemory(deviceString.Buffer + offset, + revisionId, + strlen(revisionId)); + offset += strlen(revisionId); + } + + ASSERT(offset == deviceString.Length); + + deviceString.Buffer[deviceString.Length] = '\0'; // Null-terminated + + // + // convert to unicode as registry deals with unicode strings + // + + status = RtlAnsiStringToUnicodeString( &deviceUnicodeString, + &deviceString, + TRUE + ); + if (!NT_SUCCESS(status)) { + DebugPrint((ClassDebugMCN, + "ClassMediaChangeDisabledForHardware: cannot convert " + "to unicode %lx\n", status)); + LEAVE; + } + + // + // query the value, setting valueFound to true if found + // + + RtlZeroMemory(parameters, sizeof(parameters)); + + nullMultiSz = L"\0"; + parameters[0].QueryRoutine = ClasspMediaChangeRegistryCallBack; + parameters[0].Flags = RTL_QUERY_REGISTRY_REQUIRED; + parameters[0].Name = L"AutoRunAlwaysDisable"; + parameters[0].EntryContext = &mediaChangeNotificationDisabled; + parameters[0].DefaultType = REG_MULTI_SZ; + parameters[0].DefaultData = nullMultiSz; + parameters[0].DefaultLength = 0; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + serviceKey, + parameters, + &deviceUnicodeString, + NULL); + + if ( !NT_SUCCESS(status) ) { + LEAVE; + } + + } FINALLY { + + if (deviceString.Buffer != NULL) { + ExFreePool( deviceString.Buffer ); + } + if (deviceUnicodeString.Buffer != NULL) { + RtlFreeUnicodeString( &deviceUnicodeString ); + } + + ZwClose(serviceKey); + } + + if (mediaChangeNotificationDisabled) { + DebugPrint((ClassDebugMCN, "ClassMediaChangeDisabledForHardware: " + "Device is on disable list\n")); + return TRUE; + } + return FALSE; + +} // end ClasspIsMediaChangeDisabledDueToHardwareLimitation() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspIsMediaChangeDisabledForClass() + +Routine Description: + + The user must specify that AutoPlay is to run on the platform + by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\ + Services\\Autorun:REG_DWORD:1. + + The user can override the global setting to enable or disable Autorun on a + specific cdrom device via the control panel. + +Arguments: + + FdoExtension - + RegistryPath - pointer to the unicode string inside + ...\CurrentControlSet\Services\Cdrom + +Return Value: + + TRUE - Autorun is disabled for this class + FALSE - Autorun is enabled for this class + +--*/ +BOOLEAN +ClasspIsMediaChangeDisabledForClass( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PUNICODE_STRING RegistryPath + ) +{ + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor; + + OBJECT_ATTRIBUTES objectAttributes; + HANDLE serviceKey = NULL; + HANDLE parametersKey = NULL; + RTL_QUERY_REGISTRY_TABLE parameters[3]; + + UNICODE_STRING paramStr; + UNICODE_STRING deviceUnicodeString; + ANSI_STRING deviceString; + + // + // Default to ENABLING MediaChangeNotification (!) + // + + ULONG mcnRegistryValue = 1; + + NTSTATUS status; + + + PAGED_CODE(); + + // + // open the service key. + // + + InitializeObjectAttributes(&objectAttributes, + RegistryPath, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + KEY_READ, + &objectAttributes); + + ASSERT(NT_SUCCESS(status)); + + if(!NT_SUCCESS(status)) { + + // + // return the default value, which is the + // inverse of the registry setting default + // since this routine asks if it's disabled + // + + DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: Defaulting to %s\n", + (mcnRegistryValue ? "Enabled" : "Disabled"))); + return (BOOLEAN)(!mcnRegistryValue); + + } + + RtlZeroMemory(parameters, sizeof(parameters)); + + // + // Open the parameters key (if any) beneath the services key. + // + + RtlInitUnicodeString(¶mStr, L"Parameters"); + + InitializeObjectAttributes(&objectAttributes, + ¶mStr, + OBJ_CASE_INSENSITIVE, + serviceKey, + NULL); + + status = ZwOpenKey(¶metersKey, + KEY_READ, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + parametersKey = NULL; + } + + + + // + // Check for the Autorun value. + // + + parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + parameters[0].Name = L"Autorun"; + parameters[0].EntryContext = &mcnRegistryValue; + parameters[0].DefaultType = REG_DWORD; + parameters[0].DefaultData = &mcnRegistryValue; + parameters[0].DefaultLength = sizeof(ULONG); + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL, + serviceKey, + parameters, + NULL, + NULL); + + DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: " + "/Autorun flag = %d\n", mcnRegistryValue)); + + if(parametersKey != NULL) { + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL, + parametersKey, + parameters, + NULL, + NULL); + DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: " + "/Parameters/Autorun flag = %d\n", + mcnRegistryValue)); + ZwClose(parametersKey); + + } + ZwClose(serviceKey); + + DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: " + "Autoplay for device %p is %s\n", + FdoExtension->DeviceObject, + (mcnRegistryValue ? "on" : "off") + )); + + // + // return if it is _disabled_, which is the + // inverse of the registry setting + // + + return (BOOLEAN)(!mcnRegistryValue); +} // end ClasspIsMediaChangeDisabledForClass() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public? +ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented + +Routine Description: + + This routine + +Arguments: + + DeviceObject - + Irp - + +Return Value: + +--*/ +VOID +ClassEnableMediaChangeDetection( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; + LONG oldCount; + + PAGED_CODE(); + + if(info == NULL) { + DebugPrint((ClassDebugMCN, + "ClassEnableMediaChangeDetection: not initialized\n")); + return; + } + + KeWaitForMutexObject(&info->MediaChangeMutex, + UserRequest, + KernelMode, + FALSE, + NULL); + + oldCount = --info->MediaChangeDetectionDisableCount; + + ASSERT(oldCount >= 0); + + DebugPrint((ClassDebugMCN, "ClassEnableMediaChangeDetection: Disable count " + "reduced to %d - ", + info->MediaChangeDetectionDisableCount)); + + if(oldCount == 0) { + + // + // We don't know what state the media is in anymore. + // + + ClasspInternalSetMediaChangeState(FdoExtension, + MediaUnknown, + FALSE + ); + + // + // Reset the MCN timer. + // + + ClassResetMediaChangeTimer(FdoExtension); + + DebugPrint((ClassDebugMCN, "MCD is enabled\n")); + + } else { + + DebugPrint((ClassDebugMCN, "MCD still disabled\n")); + + } + + + // + // Let something else run. + // + + KeReleaseMutex(&info->MediaChangeMutex, FALSE); + + return; +} // end ClassEnableMediaChangeDetection() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public? +ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented + +Routine Description: + + This routine + +Arguments: + + DeviceObject - + Irp - + +Return Value: + +--*/ +ULONG BreakOnMcnDisable = FALSE; + +VOID +ClassDisableMediaChangeDetection( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; + + PAGED_CODE(); + + if(info == NULL) { + return; + } + + KeWaitForMutexObject(&info->MediaChangeMutex, + UserRequest, + KernelMode, + FALSE, + NULL); + + info->MediaChangeDetectionDisableCount++; + + DebugPrint((ClassDebugMCN, "ClassDisableMediaChangeDetection: " + "disable count is %d\n", + info->MediaChangeDetectionDisableCount)); + + KeReleaseMutex(&info->MediaChangeMutex, FALSE); + + return; +} // end ClassDisableMediaChangeDetection() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?! + +Routine Description: + + This routine will cleanup any resources allocated for MCN. It is called + by classpnp during remove device, and therefore is not typically required + by external drivers. + +Arguments: + +Return Value: + +--*/ +VOID +ClassCleanupMediaChangeDetection( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; + + PAGED_CODE() + + if(info == NULL) { + return; + } + + FdoExtension->MediaChangeDetectionInfo = NULL; + + if (info->Gesn.Buffer) { + ExFreePool(info->Gesn.Buffer); + } + IoFreeIrp(info->MediaChangeIrp); + ExFreePool(info->SenseBuffer); + ExFreePool(info); + return; +} // end ClassCleanupMediaChangeDetection() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented + +Routine Description: + + This routine + +Arguments: + + DeviceObject - + Irp - + +Return Value: + +--*/ +NTSTATUS +ClasspMcnControl( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PIRP Irp, + IN PSCSI_REQUEST_BLOCK Srb + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = + (PCOMMON_DEVICE_EXTENSION) FdoExtension; + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PPREVENT_MEDIA_REMOVAL request = Irp->AssociatedIrp.SystemBuffer; + + PFILE_OBJECT fileObject = irpStack->FileObject; + PFILE_OBJECT_EXTENSION fsContext = NULL; + + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Check to make sure we have a file object extension to keep track of this + // request. If not we'll fail it before synchronizing. + // + + TRY { + + if(fileObject != NULL) { + fsContext = ClasspGetFsContext(commonExtension, fileObject); + }else if(Irp->RequestorMode == KernelMode) { // && fileObject == NULL + fsContext = &FdoExtension->KernelModeMcnContext; + } + + if (fsContext == NULL) { + + // + // This handle isn't setup correctly. We can't let the + // operation go. + // + + status = STATUS_INVALID_PARAMETER; + LEAVE; + } + + if(request->PreventMediaRemoval) { + + // + // This is a lock command. Reissue the command in case bus or + // device was reset and the lock was cleared. + // + + ClassDisableMediaChangeDetection(FdoExtension); + InterlockedIncrement(&(fsContext->McnDisableCount)); + + } else { + + if(fsContext->McnDisableCount == 0) { + status = STATUS_INVALID_DEVICE_STATE; + LEAVE; + } + + InterlockedDecrement(&(fsContext->McnDisableCount)); + ClassEnableMediaChangeDetection(FdoExtension); + } + + } FINALLY { + + Irp->IoStatus.Status = status; + + if(Srb) { + ExFreePool(Srb); + } + + ClassReleaseRemoveLock(FdoExtension->DeviceObject, Irp); + ClassCompleteRequest(FdoExtension->DeviceObject, + Irp, + IO_NO_INCREMENT); + } + return status; +} // end ClasspMcnControl( + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspMediaChangeRegistryCallBack() + +Routine Description: + + This callback for a registry SZ or MULTI_SZ is called once for each + SZ in the value. It will attempt to match the data with the + UNICODE_STRING passed in as Context, and modify EntryContext if a + match is found. Written for ClasspCheckRegistryForMediaChangeCompletion + +Arguments: + + ValueName - name of the key that was opened + ValueType - type of data stored in the value (REG_SZ for this routine) + ValueData - data in the registry, in this case a wide string + ValueLength - length of the data including the terminating null + Context - unicode string to compare against ValueData + EntryContext - should be initialized to 0, will be set to 1 if match found + +Return Value: + + STATUS_SUCCESS + EntryContext will be 1 if found + +--*/ +NTSTATUS +ClasspMediaChangeRegistryCallBack( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) +{ + PULONG valueFound; + PUNICODE_STRING deviceString; + PWSTR keyValue; + + PAGED_CODE(); + UNREFERENCED_PARAMETER(ValueName); + + + // + // if we have already set the value to true, exit + // + + valueFound = EntryContext; + if ((*valueFound) != 0) { + DebugPrint((ClassDebugMCN, "ClasspMcnRegCB: already set to true\n")); + return STATUS_SUCCESS; + } + + if (ValueLength == sizeof(WCHAR)) { + DebugPrint((ClassDebugError, "ClasspMcnRegCB: NULL string should " + "never be passed to registry call-back!\n")); + return STATUS_SUCCESS; + } + + + // + // if the data is not a terminated string, exit + // + + if (ValueType != REG_SZ) { + return STATUS_SUCCESS; + } + + deviceString = Context; + keyValue = ValueData; + ValueLength -= sizeof(WCHAR); // ignore the null character + + // + // do not compare more memory than is in deviceString + // + + if (ValueLength > deviceString->Length) { + ValueLength = deviceString->Length; + } + + // + // if the strings match, disable autorun + // + + if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength) { + DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: Match found\n")); + DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: DeviceString at %p\n", + deviceString->Buffer)); + DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: KeyValue at %p\n", + keyValue)); + (*valueFound) = TRUE; + } + + return STATUS_SUCCESS; +} // end ClasspMediaChangeRegistryCallBack() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented + +Routine Description: + + This routine + +Arguments: + + DeviceObject - + Irp - + +Return Value: + +--*/ +VOID +ClasspTimerTick( + PDEVICE_OBJECT DeviceObject, + PVOID Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + ULONG isRemoved; + + ASSERT(commonExtension->IsFdo); + + // + // Do any media change work + // + isRemoved = ClassAcquireRemoveLock(DeviceObject, (PIRP)ClasspTimerTick); + + // + // We stop the timer before deleting the device. It's safe to keep going + // if the flag value is REMOVE_PENDING because the removal thread will be + // blocked trying to stop the timer. + // + + ASSERT(isRemoved != REMOVE_COMPLETE); + + // + // This routine is reasonably safe even if the device object has a pending + // remove + + if(!isRemoved) { + + PFAILURE_PREDICTION_INFO info = fdoExtension->FailurePredictionInfo; + + // + // Do any media change detection work + // + + if (fdoExtension->MediaChangeDetectionInfo != NULL) { + + ClassCheckMediaState(fdoExtension); + + } + + // + // Do any failure prediction work + // + if ((info != NULL) && (info->Method != FailurePredictionNone)) { + + ULONG countDown; + ULONG active; + + if (ClasspCanSendPollingIrp(fdoExtension)) { + + // + // Synchronization is not required here since the Interlocked + // locked instruction guarantees atomicity. Other code that + // resets CountDown uses InterlockedExchange which is also + // atomic. + // + countDown = InterlockedDecrement(&info->CountDown); + if (countDown == 0) { + + DebugPrint((4, "ClasspTimerTick: Send FP irp for %p\n", + DeviceObject)); + + if(info->WorkQueueItem == NULL) { + + info->WorkQueueItem = + IoAllocateWorkItem(fdoExtension->DeviceObject); + + if(info->WorkQueueItem == NULL) { + + // + // Set the countdown to one minute in the future. + // we'll try again then in the hopes there's more + // free memory. + // + + DebugPrint((1, "ClassTimerTick: Couldn't allocate " + "item - try again in one minute\n")); + InterlockedExchange(&info->CountDown, 60); + + } else { + + // + // Grab the remove lock so that removal will block + // until the work item is done. + // + + ClassAcquireRemoveLock(fdoExtension->DeviceObject, + info->WorkQueueItem); + + IoQueueWorkItem(info->WorkQueueItem, + ClasspFailurePredict, + DelayedWorkQueue, + info); + } + + } else { + + DebugPrint((3, "ClasspTimerTick: Failure " + "Prediction work item is " + "already active for device %p\n", + DeviceObject)); + + } + } // end (countdown == 0) + + } else { + // + // If device is sleeping then just rearm polling timer + DebugPrint((4, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n", + DeviceObject)); + } + + } // end failure prediction polling + + // + // Give driver a chance to do its own specific work + // + + if (commonExtension->DriverExtension->InitData.ClassTick != NULL) { + + commonExtension->DriverExtension->InitData.ClassTick(DeviceObject); + + } // end device specific tick handler + } // end check for removed + + ClassReleaseRemoveLock(DeviceObject, (PIRP)ClasspTimerTick); +} // end ClasspTimerTick() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented + +Routine Description: + + This routine + +Arguments: + + DeviceObject - + Irp - + +Return Value: + +--*/ +NTSTATUS +ClasspEnableTimer( + PDEVICE_OBJECT DeviceObject + ) +{ + NTSTATUS status; + + PAGED_CODE(); + + if (DeviceObject->Timer == NULL) { + + status = IoInitializeTimer(DeviceObject, ClasspTimerTick, NULL); + + } else { + + status = STATUS_SUCCESS; + + } + + if (NT_SUCCESS(status)) { + + IoStartTimer(DeviceObject); + DebugPrint((1, "ClasspEnableTimer: Once a second timer enabled " + "for device %p\n", DeviceObject)); + + } + + DebugPrint((1, "ClasspEnableTimer: Device %p, Status %lx " + "initializing timer\n", DeviceObject, status)); + + return status; + +} // end ClasspEnableTimer() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented + +Routine Description: + + This routine + +Arguments: + + DeviceObject - + Irp - + +Return Value: + +--*/ +NTSTATUS +ClasspDisableTimer( + PDEVICE_OBJECT DeviceObject + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PMEDIA_CHANGE_DETECTION_INFO mCDInfo = fdoExtension->MediaChangeDetectionInfo; + PFAILURE_PREDICTION_INFO fPInfo = fdoExtension->FailurePredictionInfo; + NTSTATUS status; + + PAGED_CODE(); + + if (DeviceObject->Timer != NULL) { + + // + // we are only going to stop the actual timer in remove device routine. + // it is the responsibility of the code within the timer routine to + // check if the device is removed and not processing io for the final + // call. + // this keeps the code clean and prevents lots of bugs. + // + + + IoStopTimer(DeviceObject); + DebugPrint((3, "ClasspDisableTimer: Once a second timer disabled " + "for device %p\n", DeviceObject)); + + } else { + + DebugPrint((1, "ClasspDisableTimer: Timer never enabled\n")); + + } + + return STATUS_SUCCESS; +} // end ClasspDisableTimer() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented + +Routine Description: + + This routine + +Arguments: + + DeviceObject - + Irp - + +Return Value: + +Note: this function can be called (via the workitem callback) after the paging device is shut down, + so it must be PAGE LOCKED. +--*/ +VOID +ClasspFailurePredict( + IN PDEVICE_OBJECT DeviceObject, + IN PFAILURE_PREDICTION_INFO Info + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PIO_WORKITEM workItem; + STORAGE_PREDICT_FAILURE checkFailure; + SCSI_ADDRESS scsiAddress; + + NTSTATUS status; + + ASSERT(Info != NULL); + + DebugPrint((1, "ClasspFailurePredict: Polling for failure\n")); + + // + // Mark the work item as inactive and reset the countdown timer. we + // can't risk freeing the work item until we've released the remove-lock + // though - if we do it might get resused as a tag before we can release + // the lock. + // + + InterlockedExchange(&Info->CountDown, Info->Period); + workItem = InterlockedExchangePointer(&(Info->WorkQueueItem), NULL); + + if (ClasspCanSendPollingIrp(fdoExtension)) { + + KEVENT event; + PDEVICE_OBJECT topOfStack; + PIRP irp = NULL; + IO_STATUS_BLOCK ioStatus; + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + topOfStack = IoGetAttachedDeviceReference(DeviceObject); + + // + // Send down irp to see if drive is predicting failure + // + + irp = IoBuildDeviceIoControlRequest( + IOCTL_STORAGE_PREDICT_FAILURE, + topOfStack, + NULL, + 0, + &checkFailure, + sizeof(STORAGE_PREDICT_FAILURE), + FALSE, + &event, + &ioStatus); + + + if (irp != NULL) { + status = IoCallDriver(topOfStack, irp); + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (NT_SUCCESS(status) && (checkFailure.PredictFailure)) { + + checkFailure.PredictFailure = 512; + + // + // Send down irp to get scsi address + // + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + RtlZeroMemory(&scsiAddress, sizeof(SCSI_ADDRESS)); + irp = IoBuildDeviceIoControlRequest( + IOCTL_SCSI_GET_ADDRESS, + topOfStack, + NULL, + 0, + &scsiAddress, + sizeof(SCSI_ADDRESS), + FALSE, + &event, + &ioStatus); + + if (irp != NULL) { + status = IoCallDriver(topOfStack, irp); + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + } + + ClassNotifyFailurePredicted(fdoExtension, + (PUCHAR)&checkFailure, + sizeof(checkFailure), + (BOOLEAN)(fdoExtension->FailurePredicted == FALSE), + 2, + scsiAddress.PathId, + scsiAddress.TargetId, + scsiAddress.Lun); + + fdoExtension->FailurePredicted = TRUE; + + } + + ObDereferenceObject(topOfStack); + } + + ClassReleaseRemoveLock(DeviceObject, (PIRP) workItem); + IoFreeWorkItem(workItem); + return; +} // end ClasspFailurePredict() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented + +Routine Description: + +Arguments: + +Return Value: + +--*/ +VOID +ClassNotifyFailurePredicted( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PUCHAR Buffer, + ULONG BufferSize, + BOOLEAN LogError, + ULONG UniqueErrorValue, + UCHAR PathId, + UCHAR TargetId, + UCHAR Lun + ) +{ + PIO_ERROR_LOG_PACKET logEntry; + + DebugPrint((1, "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension->DeviceObject)); + + // + // Fire off a WMI event + // + ClassWmiFireEvent(FdoExtension->DeviceObject, + &StoragePredictFailureEventGuid, + 0, + BufferSize, + Buffer); + + // + // Log an error into the eventlog + // + + if (LogError) + { + logEntry = IoAllocateErrorLogEntry( + FdoExtension->DeviceObject, + sizeof(IO_ERROR_LOG_PACKET) + (3 * sizeof(ULONG))); + + if (logEntry != NULL) + { + + logEntry->FinalStatus = STATUS_SUCCESS; + logEntry->ErrorCode = IO_WRN_FAILURE_PREDICTED; + logEntry->SequenceNumber = 0; + logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL; + logEntry->IoControlCode = IOCTL_STORAGE_PREDICT_FAILURE; + logEntry->RetryCount = 0; + logEntry->UniqueErrorValue = UniqueErrorValue; + logEntry->DumpDataSize = 3; + + logEntry->DumpData[0] = PathId; + logEntry->DumpData[1] = TargetId; + logEntry->DumpData[2] = Lun; + + // + // Write the error log packet. + // + + IoWriteErrorLogEntry(logEntry); + } + } +} // end ClassNotifyFailurePredicted() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassSetFailurePredictionPoll() + +Routine Description: + + This routine enables polling for failure prediction, setting the timer + to fire every N seconds as specified by the PollingPeriod. + +Arguments: + + FdoExtension - the device to setup failure prediction for. + + FailurePredictionMethod - specific failure prediction method to use + if set to FailurePredictionNone, will disable failure detection + + PollingPeriod - if 0 then no change to current polling timer + +Return Value: + + NT Status + +--*/ +NTSTATUS +ClassSetFailurePredictionPoll( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + FAILURE_PREDICTION_METHOD FailurePredictionMethod, + ULONG PollingPeriod + ) +{ + PFAILURE_PREDICTION_INFO info; + NTSTATUS status; + DEVICE_POWER_STATE powerState; + + PAGED_CODE(); + + if (FdoExtension->FailurePredictionInfo == NULL) { + + if (FailurePredictionMethod != FailurePredictionNone) { + + info = ExAllocatePoolWithTag(NonPagedPool, + sizeof(FAILURE_PREDICTION_INFO), + CLASS_TAG_FAILURE_PREDICT); + + if (info == NULL) { + + return STATUS_INSUFFICIENT_RESOURCES; + + } + + KeInitializeEvent(&info->Event, SynchronizationEvent, TRUE); + + info->WorkQueueItem = NULL; + info->Period = DEFAULT_FAILURE_PREDICTION_PERIOD; + + } else { + + // + // FaultPrediction has not been previously initialized, nor + // is it being initialized now. No need to do anything. + // + return STATUS_SUCCESS; + + } + + FdoExtension->FailurePredictionInfo = info; + + } else { + + info = FdoExtension->FailurePredictionInfo; + + } + + KeWaitForSingleObject(&info->Event, + UserRequest, + UserMode, + FALSE, + NULL); + + + // + // Reset polling period and counter. Setup failure detection type + // + + if (PollingPeriod != 0) { + + InterlockedExchange(&info->Period, PollingPeriod); + + } + + InterlockedExchange(&info->CountDown, info->Period); + + info->Method = FailurePredictionMethod; + if (FailurePredictionMethod != FailurePredictionNone) { + + status = ClasspEnableTimer(FdoExtension->DeviceObject); + + if (NT_SUCCESS(status)) { + DebugPrint((3, "ClassEnableFailurePredictPoll: Enabled for " + "device %p\n", FdoExtension->DeviceObject)); + } + + } else { + + status = ClasspDisableTimer(FdoExtension->DeviceObject); + DebugPrint((3, "ClassEnableFailurePredictPoll: Disabled for " + "device %p\n", FdoExtension->DeviceObject)); + status = STATUS_SUCCESS; + + } + + KeSetEvent(&info->Event, IO_NO_INCREMENT, FALSE); + + return status; +} // end ClassSetFailurePredictionPoll() + diff --git a/drivers/storage/classpnp/class.c b/drivers/storage/classpnp/class.c new file mode 100644 index 00000000000..0a1c28ea9c5 --- /dev/null +++ b/drivers/storage/classpnp/class.c @@ -0,0 +1,9182 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + class.c + +Abstract: + + SCSI class driver routines + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#define CLASS_INIT_GUID 1 +#include "classp.h" +#include "debug.h" + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(INIT, DriverEntry) + #pragma alloc_text(PAGE, ClassAddDevice) + #pragma alloc_text(PAGE, ClassClaimDevice) + #pragma alloc_text(PAGE, ClassCreateDeviceObject) + #pragma alloc_text(PAGE, ClassDispatchPnp) + #pragma alloc_text(PAGE, ClassGetDescriptor) + #pragma alloc_text(PAGE, ClassGetPdoId) + #pragma alloc_text(PAGE, ClassInitialize) + #pragma alloc_text(PAGE, ClassInitializeEx) + #pragma alloc_text(PAGE, ClassInvalidateBusRelations) + #pragma alloc_text(PAGE, ClassMarkChildMissing) + #pragma alloc_text(PAGE, ClassMarkChildrenMissing) + #pragma alloc_text(PAGE, ClassModeSense) + #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations) + #pragma alloc_text(PAGE, ClassPnpStartDevice) + #pragma alloc_text(PAGE, ClassQueryPnpCapabilities) + #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue) + #pragma alloc_text(PAGE, ClassRemoveDevice) + #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations) + #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry) + #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous) + #pragma alloc_text(PAGE, ClassUnload) + #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest) + #pragma alloc_text(PAGE, ClasspFreeReleaseRequest) + #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo) + #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface) + #pragma alloc_text(PAGE, ClasspScanForClassHacks) + #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry) +#endif + +ULONG ClassPnpAllowUnload = TRUE; + + +#define FirstDriveLetter 'C' +#define LastDriveLetter 'Z' + + + +/*++//////////////////////////////////////////////////////////////////////////// + +DriverEntry() + +Routine Description: + + Temporary entry point needed to initialize the class system dll. + It doesn't do anything. + +Arguments: + + DriverObject - Pointer to the driver object created by the system. + +Return Value: + + STATUS_SUCCESS + +--*/ +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +{ + return STATUS_SUCCESS; +} + + + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassInitialize() + +Routine Description: + + This routine is called by a class driver during its + DriverEntry routine to initialize the driver. + +Arguments: + + Argument1 - Driver Object. + Argument2 - Registry Path. + InitializationData - Device-specific driver's initialization data. + +Return Value: + + A valid return code for a DriverEntry routine. + +--*/ +ULONG +ClassInitialize( + IN PVOID Argument1, + IN PVOID Argument2, + IN PCLASS_INIT_DATA InitializationData + ) +{ + PDRIVER_OBJECT DriverObject = Argument1; + PUNICODE_STRING RegistryPath = Argument2; + + PCLASS_DRIVER_EXTENSION driverExtension; + + NTSTATUS status; + + PAGED_CODE(); + + DebugPrint((3,"\n\nSCSI Class Driver\n")); + + ClasspInitializeDebugGlobals(); + + // + // Validate the length of this structure. This is effectively a + // version check. + // + + if (InitializationData->InitializationDataSize != sizeof(CLASS_INIT_DATA)) { + + // + // This DebugPrint is to help third-party driver writers + // + + DebugPrint((0,"ClassInitialize: Class driver wrong version\n")); + return (ULONG) STATUS_REVISION_MISMATCH; + } + + // + // Check that each required entry is not NULL. Note that Shutdown, Flush and Error + // are not required entry points. + // + + if ((!InitializationData->FdoData.ClassDeviceControl) || + (!((InitializationData->FdoData.ClassReadWriteVerification) || + (InitializationData->ClassStartIo))) || + (!InitializationData->ClassAddDevice) || + (!InitializationData->FdoData.ClassStartDevice)) { + + // + // This DebugPrint is to help third-party driver writers + // + + DebugPrint((0, + "ClassInitialize: Class device-specific driver missing required " + "FDO entry\n")); + + return (ULONG) STATUS_REVISION_MISMATCH; + } + + if ((InitializationData->ClassEnumerateDevice) && + ((!InitializationData->PdoData.ClassDeviceControl) || + (!InitializationData->PdoData.ClassStartDevice) || + (!((InitializationData->PdoData.ClassReadWriteVerification) || + (InitializationData->ClassStartIo))))) { + + // + // This DebugPrint is to help third-party driver writers + // + + DebugPrint((0, "ClassInitialize: Class device-specific missing " + "required PDO entry\n")); + + return (ULONG) STATUS_REVISION_MISMATCH; + } + + if((InitializationData->FdoData.ClassStopDevice == NULL) || + ((InitializationData->ClassEnumerateDevice != NULL) && + (InitializationData->PdoData.ClassStopDevice == NULL))) { + + // + // This DebugPrint is to help third-party driver writers + // + + DebugPrint((0, "ClassInitialize: Class device-specific missing " + "required PDO entry\n")); + ASSERT(FALSE); + return (ULONG) STATUS_REVISION_MISMATCH; + } + + // + // Setup the default power handlers if the class driver didn't provide + // any. + // + + if(InitializationData->FdoData.ClassPowerDevice == NULL) { + InitializationData->FdoData.ClassPowerDevice = ClassMinimalPowerHandler; + } + + if((InitializationData->ClassEnumerateDevice != NULL) && + (InitializationData->PdoData.ClassPowerDevice == NULL)) { + InitializationData->PdoData.ClassPowerDevice = ClassMinimalPowerHandler; + } + + // + // warn that unload is not supported + // + // ISSUE-2000/02/03-peterwie + // We should think about making this a fatal error. + // + + if(InitializationData->ClassUnload == NULL) { + + // + // This DebugPrint is to help third-party driver writers + // + + DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n", + RegistryPath)); + } + + // + // Create an extension for the driver object + // + + status = IoAllocateDriverObjectExtension(DriverObject, + CLASS_DRIVER_EXTENSION_KEY, + sizeof(CLASS_DRIVER_EXTENSION), + &driverExtension); + + if(NT_SUCCESS(status)) { + + // + // Copy the registry path into the driver extension so we can use it later + // + + driverExtension->RegistryPath.Length = RegistryPath->Length; + driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength; + + driverExtension->RegistryPath.Buffer = + ExAllocatePoolWithTag(PagedPool, + RegistryPath->MaximumLength, + '1CcS'); + + if(driverExtension->RegistryPath.Buffer == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + return status; + } + + RtlCopyUnicodeString( + &(driverExtension->RegistryPath), + RegistryPath); + + // + // Copy the initialization data into the driver extension so we can reuse + // it during our add device routine + // + + RtlCopyMemory( + &(driverExtension->InitData), + InitializationData, + sizeof(CLASS_INIT_DATA)); + + driverExtension->DeviceCount = 0; + + } else if (status == STATUS_OBJECT_NAME_COLLISION) { + + // + // The extension already exists - get a pointer to it + // + + driverExtension = IoGetDriverObjectExtension(DriverObject, + CLASS_DRIVER_EXTENSION_KEY); + + ASSERT(driverExtension != NULL); + + } else { + + DebugPrint((1, "ClassInitialize: Class driver extension could not be " + "allocated %lx\n", status)); + return status; + } + + // + // Update driver object with entry points. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreateClose; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassCreateClose; + DriverObject->MajorFunction[IRP_MJ_READ] = ClassReadWrite; + DriverObject->MajorFunction[IRP_MJ_WRITE] = ClassReadWrite; + DriverObject->MajorFunction[IRP_MJ_SCSI] = ClassInternalIoControl; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControlDispatch; + DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ClassShutdownFlush; + DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ClassShutdownFlush; + DriverObject->MajorFunction[IRP_MJ_PNP] = ClassDispatchPnp; + DriverObject->MajorFunction[IRP_MJ_POWER] = ClassDispatchPower; + DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ClassSystemControl; + + if (InitializationData->ClassStartIo) { + DriverObject->DriverStartIo = ClasspStartIo; + } + + if ((InitializationData->ClassUnload) && (ClassPnpAllowUnload == TRUE)) { + DriverObject->DriverUnload = ClassUnload; + } else { + DriverObject->DriverUnload = NULL; + } + + DriverObject->DriverExtension->AddDevice = ClassAddDevice; + + DbgPrint("Driver is ready to go\n"); + status = STATUS_SUCCESS; + return status; +} // end ClassInitialize() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassInitializeEx() + +Routine Description: + + This routine is allows the caller to do any extra initialization or + setup that is not done in ClassInitialize. The operation is + controlled by the GUID that is passed and the contents of the Data + parameter is dependent upon the GUID. + + This is the list of supported operations: + + Guid - GUID_CLASSPNP_QUERY_REGINFOEX + Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer + + Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX + callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The + former callback allows the driver to specify the name of the + mof resource. + +Arguments: + + DriverObject + Guid + Data + +Return Value: + + Status Code + +--*/ +ULONG +ClassInitializeEx( + IN PDRIVER_OBJECT DriverObject, + IN LPGUID Guid, + IN PVOID Data + ) +{ + PCLASS_DRIVER_EXTENSION driverExtension; + + NTSTATUS status; + + PAGED_CODE(); + + driverExtension = IoGetDriverObjectExtension( DriverObject, + CLASS_DRIVER_EXTENSION_KEY + ); + if (IsEqualGUID(Guid, &ClassGuidQueryRegInfoEx)) + { + PCLASS_QUERY_WMI_REGINFO_EX_LIST List; + + // + // Indicate the device supports PCLASS_QUERY_REGINFO_EX + // callback instead of PCLASS_QUERY_REGINFO callback. + // + List = (PCLASS_QUERY_WMI_REGINFO_EX_LIST)Data; + + if (List->Size == sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST)) + { + driverExtension->ClassFdoQueryWmiRegInfoEx = List->ClassFdoQueryWmiRegInfoEx; + driverExtension->ClassPdoQueryWmiRegInfoEx = List->ClassPdoQueryWmiRegInfoEx; + status = STATUS_SUCCESS; + } else { + status = STATUS_INVALID_PARAMETER; + } + } else { + status = STATUS_NOT_SUPPORTED; + } + + return(status); + +} // end ClassInitializeEx() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassUnload() + +Routine Description: + + called when there are no more references to the driver. this allows + drivers to be updated without rebooting. + +Arguments: + + DriverObject - a pointer to the driver object that is being unloaded + +Status: + +--*/ +VOID +ClassUnload( + IN PDRIVER_OBJECT DriverObject + ) +{ + PCLASS_DRIVER_EXTENSION driverExtension; + NTSTATUS status; + + PAGED_CODE(); + + ASSERT( DriverObject->DeviceObject == NULL ); + + driverExtension = IoGetDriverObjectExtension( DriverObject, + CLASS_DRIVER_EXTENSION_KEY + ); + + ASSERT(driverExtension != NULL); + ASSERT(driverExtension->RegistryPath.Buffer != NULL); + ASSERT(driverExtension->InitData.ClassUnload != NULL); + + DebugPrint((1, "ClassUnload: driver unloading %wZ\n", + &driverExtension->RegistryPath)); + + // + // attempt to process the driver's unload routine first. + // + + driverExtension->InitData.ClassUnload(DriverObject); + + // + // free own allocated resources and return + // + + ExFreePool( driverExtension->RegistryPath.Buffer ); + driverExtension->RegistryPath.Buffer = NULL; + driverExtension->RegistryPath.Length = 0; + driverExtension->RegistryPath.MaximumLength = 0; + + return; +} // end ClassUnload() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassAddDevice() + +Routine Description: + + SCSI class driver add device routine. This is called by pnp when a new + physical device come into being. + + This routine will call out to the class driver to verify that it should + own this device then will create and attach a device object and then hand + it to the driver to initialize and create symbolic links + +Arguments: + + DriverObject - a pointer to the driver object that this is being created for + PhysicalDeviceObject - a pointer to the physical device object + +Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device + STATUS_SUCCESS if the creation and attachment was successful + status of device creation and initialization + +--*/ +NTSTATUS +ClassAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject + ) +{ + PCLASS_DRIVER_EXTENSION driverExtension = + IoGetDriverObjectExtension(DriverObject, + CLASS_DRIVER_EXTENSION_KEY); + + NTSTATUS status; + + PAGED_CODE(); + + DbgPrint("got a device\n"); + status = driverExtension->InitData.ClassAddDevice(DriverObject, + PhysicalDeviceObject); + return status; +} // end ClassAddDevice() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassDispatchPnp() + +Routine Description: + + Storage class driver pnp routine. This is called by the io system when + a PNP request is sent to the device. + +Arguments: + + DeviceObject - pointer to the device object + + Irp - pointer to the io request packet + +Return Value: + + status + +--*/ +NTSTATUS +ClassDispatchPnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + BOOLEAN isFdo = commonExtension->IsFdo; + + PCLASS_DRIVER_EXTENSION driverExtension; + PCLASS_INIT_DATA initData; + PCLASS_DEV_INFO devInfo; + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + + NTSTATUS status = Irp->IoStatus.Status; + BOOLEAN completeRequest = TRUE; + BOOLEAN lockReleased = FALSE; + + ULONG isRemoved; + + PAGED_CODE(); + + // + // Extract all the useful information out of the driver object + // extension + // + + driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, + CLASS_DRIVER_EXTENSION_KEY); + if (driverExtension){ + + initData = &(driverExtension->InitData); + + if(isFdo) { + devInfo = &(initData->FdoData); + } else { + devInfo = &(initData->PdoData); + } + + isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp); + + DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n", + DeviceObject, Irp, + irpStack->MinorFunction, + isFdo ? "fdo" : "pdo", + DeviceObject)); + DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n", + DeviceObject, Irp, + commonExtension->PreviousState, + commonExtension->CurrentState)); + + switch(irpStack->MinorFunction) { + + case IRP_MN_START_DEVICE: { + + // + // if this is sent to the FDO we should forward it down the + // attachment chain before we start the FDO. + // + + if (isFdo) { + status = ClassForwardIrpSynchronous(commonExtension, Irp); + } + else { + status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(status)){ + status = Irp->IoStatus.Status = ClassPnpStartDevice(DeviceObject); + } + + break; + } + + + case IRP_MN_QUERY_DEVICE_RELATIONS: { + + DEVICE_RELATION_TYPE type = + irpStack->Parameters.QueryDeviceRelations.Type; + + PDEVICE_RELATIONS deviceRelations = NULL; + + if(!isFdo) { + + if(type == TargetDeviceRelation) { + + // + // Device relations has one entry built in to it's size. + // + + status = STATUS_INSUFFICIENT_RESOURCES; + + deviceRelations = ExAllocatePoolWithTag(PagedPool, + sizeof(DEVICE_RELATIONS), + '2CcS'); + + if(deviceRelations != NULL) { + + RtlZeroMemory(deviceRelations, + sizeof(DEVICE_RELATIONS)); + + Irp->IoStatus.Information = (ULONG_PTR) deviceRelations; + + deviceRelations->Count = 1; + deviceRelations->Objects[0] = DeviceObject; + ObReferenceObject(deviceRelations->Objects[0]); + + status = STATUS_SUCCESS; + } + + } else { + // + // PDO's just complete enumeration requests without altering + // the status. + // + + status = Irp->IoStatus.Status; + } + + break; + + } else if (type == BusRelations) { + + ASSERT(commonExtension->IsInitialized); + + // + // Make sure we support enumeration + // + + if(initData->ClassEnumerateDevice == NULL) { + + // + // Just send the request down to the lower driver. Perhaps + // It can enumerate children. + // + + } else { + + // + // Re-enumerate the device + // + + status = ClassPnpQueryFdoRelations(DeviceObject, Irp); + + if(!NT_SUCCESS(status)) { + completeRequest = TRUE; + break; + } + } + } + + IoCopyCurrentIrpStackLocationToNext(Irp); + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + completeRequest = FALSE; + + break; + } + + case IRP_MN_QUERY_ID: { + + BUS_QUERY_ID_TYPE idType = irpStack->Parameters.QueryId.IdType; + UNICODE_STRING unicodeString; + + if(isFdo) { + + // + // FDO's should just forward the query down to the lower + // device objects + // + + IoCopyCurrentIrpStackLocationToNext(Irp); + ClassReleaseRemoveLock(DeviceObject, Irp); + + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + completeRequest = FALSE; + break; + } + + // + // PDO's need to give an answer - this is easy for now + // + + RtlInitUnicodeString(&unicodeString, NULL); + + status = ClassGetPdoId(DeviceObject, + idType, + &unicodeString); + + if(status == STATUS_NOT_IMPLEMENTED) { + // + // The driver doesn't implement this ID (whatever it is). + // Use the status out of the IRP so that we don't mangle a + // response from someone else. + // + + status = Irp->IoStatus.Status; + } else if(NT_SUCCESS(status)) { + Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer; + } else { + Irp->IoStatus.Information = (ULONG_PTR) NULL; + } + + break; + } + + case IRP_MN_QUERY_STOP_DEVICE: + case IRP_MN_QUERY_REMOVE_DEVICE: { + + DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n", + DeviceObject, Irp, + ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ? + "STOP" : "REMOVE"))); + + // + // If this device is in use for some reason (paging, etc...) + // then we need to fail the request. + // + + if(commonExtension->PagingPathCount != 0) { + + DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging " + "path and cannot be removed\n", + DeviceObject, Irp)); + status = STATUS_DEVICE_BUSY; + break; + } + + // + // Check with the class driver to see if the query operation + // can succeed. + // + + if(irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) { + status = devInfo->ClassStopDevice(DeviceObject, + irpStack->MinorFunction); + } else { + status = devInfo->ClassRemoveDevice(DeviceObject, + irpStack->MinorFunction); + } + + if(NT_SUCCESS(status)) { + + // + // ASSERT that we never get two queries in a row, as + // this will severly mess up the state machine + // + ASSERT(commonExtension->CurrentState != irpStack->MinorFunction); + commonExtension->PreviousState = commonExtension->CurrentState; + commonExtension->CurrentState = irpStack->MinorFunction; + + if(isFdo) { + DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_" + "%s irp\n", DeviceObject, Irp, + ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ? + "STOP" : "REMOVE"))); + status = ClassForwardIrpSynchronous(commonExtension, Irp); + } + } + DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n", + DeviceObject, Irp, status)); + + break; + } + + case IRP_MN_CANCEL_STOP_DEVICE: + case IRP_MN_CANCEL_REMOVE_DEVICE: { + + // + // Check with the class driver to see if the query or cancel + // operation can succeed. + // + + if(irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) { + status = devInfo->ClassStopDevice(DeviceObject, + irpStack->MinorFunction); + ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should " + "never be failed\n", NT_SUCCESS(status)); + } else { + status = devInfo->ClassRemoveDevice(DeviceObject, + irpStack->MinorFunction); + ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should " + "never be failed\n", NT_SUCCESS(status)); + } + + Irp->IoStatus.Status = status; + + // + // We got a CANCEL - roll back to the previous state only + // if the current state is the respective QUERY state. + // + + if(((irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) && + (commonExtension->CurrentState == IRP_MN_QUERY_STOP_DEVICE) + ) || + ((irpStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE) && + (commonExtension->CurrentState == IRP_MN_QUERY_REMOVE_DEVICE) + ) + ) { + + commonExtension->CurrentState = + commonExtension->PreviousState; + commonExtension->PreviousState = 0xff; + + } + + if(isFdo) { + IoCopyCurrentIrpStackLocationToNext(Irp); + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + completeRequest = FALSE; + } else { + status = STATUS_SUCCESS; + } + + break; + } + + case IRP_MN_STOP_DEVICE: { + + // + // These all mean nothing to the class driver currently. The + // port driver will handle all queueing when necessary. + // + + DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n", + DeviceObject, Irp, + (isFdo ? "fdo" : "pdo") + )); + + ASSERT(commonExtension->PagingPathCount == 0); + + // + // ISSUE-2000/02/03-peterwie + // if we stop the timer here then it means no class driver can + // do i/o in its ClassStopDevice routine. This is because the + // retry (among other things) is tied into the tick handler + // and disabling retries could cause the class driver to deadlock. + // Currently no class driver we're aware of issues i/o in its + // Stop routine but this is a case we may want to defend ourself + // against. + // + + if (DeviceObject->Timer) { + IoStopTimer(DeviceObject); + } + + status = devInfo->ClassStopDevice(DeviceObject, IRP_MN_STOP_DEVICE); + + ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should " + "never be failed\n", NT_SUCCESS(status)); + + if(isFdo) { + status = ClassForwardIrpSynchronous(commonExtension, Irp); + } + + if(NT_SUCCESS(status)) { + commonExtension->CurrentState = irpStack->MinorFunction; + commonExtension->PreviousState = 0xff; + } + + break; + } + + case IRP_MN_REMOVE_DEVICE: + case IRP_MN_SURPRISE_REMOVAL: { + + PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject; + UCHAR removeType = irpStack->MinorFunction; + + if (commonExtension->PagingPathCount != 0) { + DBGTRACE(ClassDebugWarning, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject, Irp)); + } + + // + // Release the lock for this IRP before calling in. + // + ClassReleaseRemoveLock(DeviceObject, Irp); + lockReleased = TRUE; + + /* + * If a timer was started on the device, stop it. + */ + if (DeviceObject->Timer) { + IoStopTimer(DeviceObject); + } + + /* + * "Fire-and-forget" the remove irp to the lower stack. + * Don't touch the irp (or the irp stack!) after this. + */ + if (isFdo) { + IoCopyCurrentIrpStackLocationToNext(Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + ASSERT(NT_SUCCESS(status)); + completeRequest = FALSE; + } + else { + status = STATUS_SUCCESS; + } + + /* + * Do our own cleanup and call the class driver's remove + * cleanup routine. + * For IRP_MN_REMOVE_DEVICE, this also deletes our device object, + * so don't touch the extension after this. + */ + commonExtension->PreviousState = commonExtension->CurrentState; + commonExtension->CurrentState = removeType; + ClassRemoveDevice(DeviceObject, removeType); + + break; + } + + case IRP_MN_DEVICE_USAGE_NOTIFICATION: { + + switch(irpStack->Parameters.UsageNotification.Type) { + + case DeviceUsageTypePaging: { + + BOOLEAN setPagable; + + if((irpStack->Parameters.UsageNotification.InPath) && + (commonExtension->CurrentState != IRP_MN_START_DEVICE)) { + + // + // Device isn't started. Don't allow adding a + // paging file, but allow a removal of one. + // + + status = STATUS_DEVICE_NOT_READY; + break; + } + + ASSERT(commonExtension->IsInitialized); + + // + // need to synchronize this now... + // + + KeEnterCriticalRegion(); + status = KeWaitForSingleObject(&commonExtension->PathCountEvent, + Executive, KernelMode, + FALSE, NULL); + ASSERT(NT_SUCCESS(status)); + status = STATUS_SUCCESS; + + // + // If the volume is removable we should try to lock it in + // place or unlock it once per paging path count + // + + if (commonExtension->IsFdo){ + status = ClasspEjectionControl( + DeviceObject, + Irp, + InternalMediaLock, + (BOOLEAN)irpStack->Parameters.UsageNotification.InPath); + } + + if (!NT_SUCCESS(status)){ + KeSetEvent(&commonExtension->PathCountEvent, IO_NO_INCREMENT, FALSE); + KeLeaveCriticalRegion(); + break; + } + + // + // if removing last paging device, need to set DO_POWER_PAGABLE + // bit here, and possible re-set it below on failure. + // + + setPagable = FALSE; + + if (!irpStack->Parameters.UsageNotification.InPath && + commonExtension->PagingPathCount == 1 + ) { + + // + // removing last paging file + // must have DO_POWER_PAGABLE bits set, but only + // if noone set the DO_POWER_INRUSH bit + // + + + if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) { + DebugPrint((2, "ClassDispatchPnp (%p,%p): Last " + "paging file removed, but " + "DO_POWER_INRUSH was set, so NOT " + "setting DO_POWER_PAGABLE\n", + DeviceObject, Irp)); + } else { + DebugPrint((2, "ClassDispatchPnp (%p,%p): Last " + "paging file removed, " + "setting DO_POWER_PAGABLE\n", + DeviceObject, Irp)); + SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE); + setPagable = TRUE; + } + + } + + // + // forward the irp before finishing handling the + // special cases + // + + status = ClassForwardIrpSynchronous(commonExtension, Irp); + + // + // now deal with the failure and success cases. + // note that we are not allowed to fail the irp + // once it is sent to the lower drivers. + // + + if (NT_SUCCESS(status)) { + + IoAdjustPagingPathCount( + &commonExtension->PagingPathCount, + irpStack->Parameters.UsageNotification.InPath); + + if (irpStack->Parameters.UsageNotification.InPath) { + if (commonExtension->PagingPathCount == 1) { + DebugPrint((2, "ClassDispatchPnp (%p,%p): " + "Clearing PAGABLE bit\n", + DeviceObject, Irp)); + CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE); + } + } + + } else { + + // + // cleanup the changes done above + // + + if (setPagable == TRUE) { + DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting " + "PAGABLE bit due to irp failure\n", + DeviceObject, Irp)); + CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE); + setPagable = FALSE; + } + + // + // relock or unlock the media if needed. + // + + if (commonExtension->IsFdo) { + + ClasspEjectionControl( + DeviceObject, + Irp, + InternalMediaLock, + (BOOLEAN)!irpStack->Parameters.UsageNotification.InPath); + } + } + + // + // set the event so the next one can occur. + // + + KeSetEvent(&commonExtension->PathCountEvent, + IO_NO_INCREMENT, FALSE); + KeLeaveCriticalRegion(); + break; + } + + case DeviceUsageTypeHibernation: { + + IoAdjustPagingPathCount( + &commonExtension->HibernationPathCount, + irpStack->Parameters.UsageNotification.InPath + ); + status = ClassForwardIrpSynchronous(commonExtension, Irp); + if (!NT_SUCCESS(status)) { + IoAdjustPagingPathCount( + &commonExtension->HibernationPathCount, + !irpStack->Parameters.UsageNotification.InPath + ); + } + + break; + } + + case DeviceUsageTypeDumpFile: { + IoAdjustPagingPathCount( + &commonExtension->DumpPathCount, + irpStack->Parameters.UsageNotification.InPath + ); + status = ClassForwardIrpSynchronous(commonExtension, Irp); + if (!NT_SUCCESS(status)) { + IoAdjustPagingPathCount( + &commonExtension->DumpPathCount, + !irpStack->Parameters.UsageNotification.InPath + ); + } + + break; + } + + default: { + status = STATUS_INVALID_PARAMETER; + break; + } + } + break; + } + + case IRP_MN_QUERY_CAPABILITIES: { + + DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n", + DeviceObject, Irp)); + + if(!isFdo) { + + status = ClassQueryPnpCapabilities( + DeviceObject, + irpStack->Parameters.DeviceCapabilities.Capabilities + ); + + break; + + } else { + + PDEVICE_CAPABILITIES deviceCapabilities; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PCLASS_PRIVATE_FDO_DATA fdoData; + + fdoExtension = DeviceObject->DeviceExtension; + fdoData = fdoExtension->PrivateFdoData; + deviceCapabilities = + irpStack->Parameters.DeviceCapabilities.Capabilities; + + // + // forward the irp before handling the special cases + // + + status = ClassForwardIrpSynchronous(commonExtension, Irp); + if (!NT_SUCCESS(status)) { + break; + } + + // + // we generally want to remove the device from the hotplug + // applet, which requires the SR-OK bit to be set. + // only when the user specifies that they are capable of + // safely removing things do we want to clear this bit + // (saved in WriteCacheEnableOverride) + // + // setting of this bit is done either above, or by the + // lower driver. + // + // note: may not be started, so check we have FDO data first. + // + + if (fdoData && + fdoData->HotplugInfo.WriteCacheEnableOverride) { + if (deviceCapabilities->SurpriseRemovalOK) { + DebugPrint((1, "Classpnp: Clearing SR-OK bit in " + "device capabilities due to hotplug " + "device or media\n")); + } + deviceCapabilities->SurpriseRemovalOK = FALSE; + } + break; + + } // end QUERY_CAPABILITIES for FDOs + + ASSERT(FALSE); + break; + + + } // end QUERY_CAPABILITIES + + default: { + + if (isFdo){ + IoCopyCurrentIrpStackLocationToNext(Irp); + + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + completeRequest = FALSE; + } + + break; + } + } + } + else { + ASSERT(driverExtension); + status = STATUS_INTERNAL_ERROR; + } + + if (completeRequest){ + Irp->IoStatus.Status = status; + + if (!lockReleased){ + ClassReleaseRemoveLock(DeviceObject, Irp); + } + + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject, Irp, commonExtension->PreviousState, commonExtension->CurrentState)); + } + else { + /* + * The irp is already completed so don't touch it. + * This may be a remove so don't touch the device extension. + */ + DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject, Irp)); + } + + return status; +} // end ClassDispatchPnp() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassPnpStartDevice() + +Routine Description: + + Storage class driver routine for IRP_MN_START_DEVICE requests. + This routine kicks off any device specific initialization + +Arguments: + + DeviceObject - a pointer to the device object + + Irp - a pointer to the io request packet + +Return Value: + + none + +--*/ +NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject) +{ + PCLASS_DRIVER_EXTENSION driverExtension; + PCLASS_INIT_DATA initData; + + PCLASS_DEV_INFO devInfo; + + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + BOOLEAN isFdo = commonExtension->IsFdo; + + BOOLEAN isMountedDevice = TRUE; + UNICODE_STRING interfaceName; + + BOOLEAN timerStarted; + + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, + CLASS_DRIVER_EXTENSION_KEY); + + initData = &(driverExtension->InitData); + if(isFdo) { + devInfo = &(initData->FdoData); + } else { + devInfo = &(initData->PdoData); + } + + ASSERT(devInfo->ClassInitDevice != NULL); + ASSERT(devInfo->ClassStartDevice != NULL); + + if (!commonExtension->IsInitialized){ + + // + // perform FDO/PDO specific initialization + // + + if (isFdo){ + STORAGE_PROPERTY_ID propertyId; + + // + // allocate a private extension for class data + // + + if (fdoExtension->PrivateFdoData == NULL) { + fdoExtension->PrivateFdoData = + ExAllocatePoolWithTag(NonPagedPool, + sizeof(CLASS_PRIVATE_FDO_DATA), + CLASS_TAG_PRIVATE_DATA + ); + } + + if (fdoExtension->PrivateFdoData == NULL) { + DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for " + "private fdo data\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize the struct's various fields. + // + + RtlZeroMemory(fdoExtension->PrivateFdoData, + sizeof(CLASS_PRIVATE_FDO_DATA) + ); + KeInitializeTimer(&fdoExtension->PrivateFdoData->Retry.Timer); + KeInitializeDpc(&fdoExtension->PrivateFdoData->Retry.Dpc, + ClasspRetryRequestDpc, + DeviceObject); + KeInitializeSpinLock(&fdoExtension->PrivateFdoData->Retry.Lock); + fdoExtension->PrivateFdoData->Retry.Granularity = + KeQueryTimeIncrement(); + commonExtension->Reserved4 = (ULONG_PTR)(' GPH'); // debug aid + + // + // NOTE: the old interface allowed the class driver to allocate + // this. this was unsafe for low-memory conditions. allocate one + // unconditionally now, and modify our internal functions to use + // our own exclusively as it is the only safe way to do this. + // + + status = ClasspAllocateReleaseQueueIrp(fdoExtension); + if (!NT_SUCCESS(status)) { + DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the " + "private release queue irp\n")); + return status; + } + + // + // Call port driver to get adapter capabilities. + // + + propertyId = StorageAdapterProperty; + + status = ClassGetDescriptor( + commonExtension->LowerDeviceObject, + &propertyId, + &fdoExtension->AdapterDescriptor); + + if(!NT_SUCCESS(status)) { + + // + // This DebugPrint is to help third-party driver writers + // + + DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor " + "[ADAPTER] failed %lx\n", status)); + return status; + } + + // + // Call port driver to get device descriptor. + // + + propertyId = StorageDeviceProperty; + + status = ClassGetDescriptor( + commonExtension->LowerDeviceObject, + &propertyId, + &fdoExtension->DeviceDescriptor); + + if(!NT_SUCCESS(status)) { + + // + // This DebugPrint is to help third-party driver writers + // + + DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor " + "[DEVICE] failed %lx\n", status)); + return status; + } + + ClasspScanForSpecialInRegistry(fdoExtension); + ClassScanForSpecial(fdoExtension, + ClassBadItems, + ClasspScanForClassHacks); + + // + // allow perf to be re-enabled after a given number of failed IOs + // require this number to be at least CLASS_PERF_RESTORE_MINIMUM + // + + { + ULONG t = 0; + ClassGetDeviceParameter(fdoExtension, + CLASSP_REG_SUBKEY_NAME, + CLASSP_REG_PERF_RESTORE_VALUE_NAME, + &t); + if (t >= CLASS_PERF_RESTORE_MINIMUM) { + fdoExtension->PrivateFdoData->Perf.ReEnableThreshhold = t; + } + } + + + // + // compatibility comes first. writable cd media will not + // get a SYNCH_CACHE on power down. + // + + if (fdoExtension->DeviceObject->DeviceType != FILE_DEVICE_DISK) { + SET_FLAG(fdoExtension->PrivateFdoData->HackFlags, + FDO_HACK_NO_SYNC_CACHE); + } + + // + // initialize the hotplug information only after the ScanForSpecial + // routines, as it relies upon the hack flags. + // + + status = ClasspInitializeHotplugInfo(fdoExtension); + + if (!NT_SUCCESS(status)) { + DebugPrint((1, "ClassPnpStartDevice: Could not initialize " + "hotplug information %lx\n", status)); + return status; + } + + /* + * Allocate/initialize TRANSFER_PACKETs and related resources. + */ + status = InitializeTransferPackets(DeviceObject); + } + + // + // ISSUE - drivers need to disable write caching on the media + // if hotplug and !useroverride. perhaps we should + // allow registration of a callback to enable/disable + // write cache instead. + // + + if (NT_SUCCESS(status)){ + status = devInfo->ClassInitDevice(DeviceObject); + } + + } + + if (!NT_SUCCESS(status)){ + + // + // Just bail out - the remove that comes down will clean up the + // initialized scraps. + // + + return status; + } else { + commonExtension->IsInitialized = TRUE; + + if (commonExtension->IsFdo) { + fdoExtension->PrivateFdoData->Perf.OriginalSrbFlags = fdoExtension->SrbFlags; + } + + } + + // + // If device requests autorun functionality or a once a second callback + // then enable the once per second timer. + // + // NOTE: This assumes that ClassInitializeMediaChangeDetection is always + // called in the context of the ClassInitDevice callback. If called + // after then this check will have already been made and the + // once a second timer will not have been enabled. + // + if ((isFdo) && + ((initData->ClassTick != NULL) || + (fdoExtension->MediaChangeDetectionInfo != NULL) || + ((fdoExtension->FailurePredictionInfo != NULL) && + (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone)))) + { + ClasspEnableTimer(DeviceObject); + timerStarted = TRUE; + } else { + timerStarted = FALSE; + } + + // + // NOTE: the timer looks at commonExtension->CurrentState now + // to prevent Media Change Notification code from running + // until the device is started, but allows the device + // specific tick handler to run. therefore it is imperative + // that commonExtension->CurrentState not be updated until + // the device specific startdevice handler has finished. + // + + status = devInfo->ClassStartDevice(DeviceObject); + + if(NT_SUCCESS(status)) { + commonExtension->CurrentState = IRP_MN_START_DEVICE; + + if((isFdo) && (initData->ClassEnumerateDevice != NULL)) { + isMountedDevice = FALSE; + } + + if((DeviceObject->DeviceType != FILE_DEVICE_DISK) && + (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM)) { + + isMountedDevice = FALSE; + } + + + if(isMountedDevice) { + ClasspRegisterMountedDeviceInterface(DeviceObject); + } + + if((commonExtension->IsFdo) && + (devInfo->ClassWmiInfo.GuidRegInfo != NULL)) { + + IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER); + } + } else { + + if (timerStarted) { + ClasspDisableTimer(DeviceObject); + } + } + + return status; +} + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassReadWrite() + +Routine Description: + + This is the system entry point for read and write requests. The + device-specific handler is invoked to perform any validation necessary. + + If the device object is a PDO (partition object) then the request will + simply be adjusted for Partition0 and issued to the lower device driver. + + IF the device object is an FDO (paritition 0 object), the number of bytes + in the request are checked against the maximum byte counts that the adapter + supports and requests are broken up into + smaller sizes if necessary. + +Arguments: + + DeviceObject - a pointer to the device object for this request + + Irp - IO request + +Return Value: + + NT Status + +--*/ +NTSTATUS ClassReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset; + ULONG transferByteCount = currentIrpStack->Parameters.Read.Length; + ULONG isRemoved; + NTSTATUS status; + + /* + * Grab the remove lock. If we can't acquire it, bail out. + */ + isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp); + if (isRemoved) { + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + status = STATUS_DEVICE_DOES_NOT_EXIST; + } + else if (TEST_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME) && + (currentIrpStack->MinorFunction != CLASSP_VOLUME_VERIFY_CHECKED) && + !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)){ + + /* + * DO_VERIFY_VOLUME is set for the device object, + * but this request is not itself a verify request. + * So fail this request. + */ + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); + Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED; + Irp->IoStatus.Information = 0; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, 0); + status = STATUS_VERIFY_REQUIRED; + } + else { + + /* + * Since we've bypassed the verify-required tests we don't need to repeat + * them with this IRP - in particular we don't want to worry about + * hitting them at the partition 0 level if the request has come through + * a non-zero partition. + */ + currentIrpStack->MinorFunction = CLASSP_VOLUME_VERIFY_CHECKED; + + /* + * Call the miniport driver's pre-pass filter to check if we + * should continue with this transfer. + */ + ASSERT(commonExtension->DevInfo->ClassReadWriteVerification); + status = commonExtension->DevInfo->ClassReadWriteVerification(DeviceObject, Irp); + if (!NT_SUCCESS(status)){ + ASSERT(Irp->IoStatus.Status == status); + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest (DeviceObject, Irp, IO_NO_INCREMENT); + } + else if (status == STATUS_PENDING){ + /* + * ClassReadWriteVerification queued this request. + * So don't touch the irp anymore. + */ + } + else { + + if (transferByteCount == 0) { + /* + * Several parts of the code turn 0 into 0xffffffff, + * so don't process a zero-length request any further. + */ + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + status = STATUS_SUCCESS; + } + else { + /* + * If the driver has its own StartIo routine, call it. + */ + if (commonExtension->DriverExtension->InitData.ClassStartIo) { + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + status = STATUS_PENDING; + } + else { + /* + * The driver does not have its own StartIo routine. + * So process this request ourselves. + */ + + /* + * Add partition byte offset to make starting byte relative to + * beginning of disk. + */ + currentIrpStack->Parameters.Read.ByteOffset.QuadPart += + commonExtension->StartingOffset.QuadPart; + + if (commonExtension->IsFdo){ + + /* + * Add in any skew for the disk manager software. + */ + currentIrpStack->Parameters.Read.ByteOffset.QuadPart += + commonExtension->PartitionZeroExtension->DMByteSkew; + + /* + * Perform the actual transfer(s) on the hardware + * to service this request. + */ + ServiceTransferRequest(DeviceObject, Irp); + status = STATUS_PENDING; + } + else { + /* + * This is a child PDO enumerated for our FDO by e.g. disk.sys + * and owned by e.g. partmgr. Send it down to the next device + * and the same irp will come back to us for the FDO. + */ + IoCopyCurrentIrpStackLocationToNext(Irp); + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(lowerDeviceObject, Irp); + } + } + } + } + } + + return status; +} + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassReadDriveCapacity() + +Routine Description: + + This routine sends a READ CAPACITY to the requested device, updates + the geometry information in the device object and returns + when it is complete. This routine is synchronous. + + This routine must be called with the remove lock held or some other + assurance that the Fdo will not be removed while processing. + +Arguments: + + DeviceObject - Supplies a pointer to the device object that represents + the device whose capacity is to be read. + +Return Value: + + Status is returned. + +--*/ +NTSTATUS ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo) +{ + READ_CAPACITY_DATA readCapacityBuffer = {0}; + NTSTATUS status; + PMDL driveCapMdl; + + driveCapMdl = BuildDeviceInputMdl(&readCapacityBuffer, sizeof(READ_CAPACITY_DATA)); + if (driveCapMdl){ + + TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE); + if (pkt){ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; + KEVENT event; + NTSTATUS pktStatus; + IRP pseudoIrp = {0}; + + /* + * Our engine needs an "original irp" to write the status back to + * and to count down packets (one in this case). + * Just use a pretend irp for this. + */ + pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1); + pseudoIrp.IoStatus.Status = STATUS_SUCCESS; + pseudoIrp.IoStatus.Information = 0; + pseudoIrp.MdlAddress = driveCapMdl; + + /* + * Set this up as a SYNCHRONOUS transfer, submit it, + * and wait for the packet to complete. The result + * status will be written to the original irp. + */ + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + SetupDriveCapacityTransferPacket( pkt, + &readCapacityBuffer, + sizeof(READ_CAPACITY_DATA), + &event, + &pseudoIrp); + SubmitTransferPacket(pkt); + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + + status = pseudoIrp.IoStatus.Status; + + /* + * If we got an UNDERRUN, retry exactly once. + * (The transfer_packet engine didn't retry because the result + * status was success). + */ + if (NT_SUCCESS(status) && + (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA))){ + DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG)pseudoIrp.IoStatus.Information, sizeof(READ_CAPACITY_DATA))); + + pkt = DequeueFreeTransferPacket(Fdo, TRUE); + if (pkt){ + pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1); + pseudoIrp.IoStatus.Status = STATUS_SUCCESS; + pseudoIrp.IoStatus.Information = 0; + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + SetupDriveCapacityTransferPacket( pkt, + &readCapacityBuffer, + sizeof(READ_CAPACITY_DATA), + &event, + &pseudoIrp); + SubmitTransferPacket(pkt); + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = pseudoIrp.IoStatus.Status; + if (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA)){ + status = STATUS_DEVICE_BUSY; + } + } + else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + + if (NT_SUCCESS(status)){ + /* + * The request succeeded. + * Read out and store the drive information. + */ + ULONG cylinderSize; + ULONG bytesPerSector; + ULONG tmp; + ULONG lastSector; + + /* + * Read the bytesPerSector value, + * which is big-endian in the returned buffer. + * Default to the standard 512 bytes. + */ + tmp = readCapacityBuffer.BytesPerBlock; + ((PFOUR_BYTE)&bytesPerSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3; + ((PFOUR_BYTE)&bytesPerSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2; + ((PFOUR_BYTE)&bytesPerSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1; + ((PFOUR_BYTE)&bytesPerSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0; + if (bytesPerSector == 0) { + bytesPerSector = 512; + } + else { + /* + * Clear all but the highest set bit. + * That will give us a bytesPerSector value that is a power of 2. + */ + while (bytesPerSector & (bytesPerSector-1)) { + bytesPerSector &= bytesPerSector-1; + } + } + fdoExt->DiskGeometry.BytesPerSector = bytesPerSector; + + // + // Copy last sector in reverse byte order. + // + + tmp = readCapacityBuffer.LogicalBlockAddress; + ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3; + ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2; + ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1; + ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0; + + // + // Calculate sector to byte shift. + // + + WHICH_BIT(fdoExt->DiskGeometry.BytesPerSector, fdoExt->SectorShift); + + DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n", + fdoExt->DiskGeometry.BytesPerSector)); + + DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n", + lastSector + 1)); + + if (fdoExt->DMActive){ + DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n", + fdoExt->DMSkew)); + lastSector -= fdoExt->DMSkew; + } + + /* + * Check to see if we have a geometry we should be using already. + */ + cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder * + fdoExt->DiskGeometry.SectorsPerTrack); + if (cylinderSize == 0){ + DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry " + "values from %#x/%#x to %#x/%#x\n", + fdoExt->DiskGeometry.TracksPerCylinder, + fdoExt->DiskGeometry.SectorsPerTrack, + 0xff, + 0x3f)); + + fdoExt->DiskGeometry.TracksPerCylinder = 0xff; + fdoExt->DiskGeometry.SectorsPerTrack = 0x3f; + + + cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder * + fdoExt->DiskGeometry.SectorsPerTrack); + } + + // + // Calculate number of cylinders. + // + + fdoExt->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/cylinderSize); + + // + // if there are zero cylinders, then the device lied AND it's + // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg) + // this can fit into a single LONGLONG, so create another usable + // geometry, even if it's unusual looking. This allows small, + // non-standard devices, such as Sony's Memory Stick, to show + // up as having a partition. + // + + if (fdoExt->DiskGeometry.Cylinders.QuadPart == (LONGLONG)0) { + fdoExt->DiskGeometry.SectorsPerTrack = 1; + fdoExt->DiskGeometry.TracksPerCylinder = 1; + fdoExt->DiskGeometry.Cylinders.QuadPart = lastSector; + } + + + // + // Calculate media capacity in bytes. + // + + fdoExt->CommonExtension.PartitionLength.QuadPart = + ((LONGLONG)(lastSector + 1)) << fdoExt->SectorShift; + + /* + * Is this removable or fixed media + */ + if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){ + fdoExt->DiskGeometry.MediaType = RemovableMedia; + } + else { + fdoExt->DiskGeometry.MediaType = FixedMedia; + } + } + else { + /* + * The request failed. + */ + + // + // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update + // what happens when the disk's sector size is bigger than + // 512 bytes and we hit this code path? this is untested. + // + // If the read capacity fails, set the geometry to reasonable parameter + // so things don't fail at unexpected places. Zero the geometry + // except for the bytes per sector and sector shift. + // + + /* + * This request can sometimes fail legitimately + * (e.g. when a SCSI device is attached but turned off) + * so this is not necessarily a device/driver bug. + */ + DBGTRACE(ClassDebugWarning, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo, status)); + + /* + * Write in a default disk geometry which we HOPE is right (??). + * BUGBUG !! + */ + RtlZeroMemory(&fdoExt->DiskGeometry, sizeof(DISK_GEOMETRY)); + fdoExt->DiskGeometry.BytesPerSector = 512; + fdoExt->SectorShift = 9; + fdoExt->CommonExtension.PartitionLength.QuadPart = (LONGLONG) 0; + + /* + * Is this removable or fixed media + */ + if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){ + fdoExt->DiskGeometry.MediaType = RemovableMedia; + } + else { + fdoExt->DiskGeometry.MediaType = FixedMedia; + } + } + + } + else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + FreeDeviceInputMdl(driveCapMdl); + } + else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + return status; +} + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassSendStartUnit() + +Routine Description: + + Send command to SCSI unit to start or power up. + Because this command is issued asynchronounsly, that is, without + waiting on it to complete, the IMMEDIATE flag is not set. This + means that the CDB will not return until the drive has powered up. + This should keep subsequent requests from being submitted to the + device before it has completely spun up. + + This routine is called from the InterpretSense routine, when a + request sense returns data indicating that a drive must be + powered up. + + This routine may also be called from a class driver's error handler, + or anytime a non-critical start device should be sent to the device. + +Arguments: + + Fdo - The functional device object for the stopped device. + +Return Value: + + None. + +--*/ +VOID +ClassSendStartUnit( + IN PDEVICE_OBJECT Fdo + ) +{ + PIO_STACK_LOCATION irpStack; + PIRP irp; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PSCSI_REQUEST_BLOCK srb; + PCOMPLETION_CONTEXT context; + PCDB cdb; + + // + // Allocate Srb from nonpaged pool. + // + + context = ExAllocatePoolWithTag(NonPagedPool, + sizeof(COMPLETION_CONTEXT), + '6CcS'); + + if(context == NULL) { + + // + // ISSUE-2000/02/03-peterwie + // This code path was inheritted from the NT 4.0 class2.sys driver. + // It needs to be changed to survive low-memory conditions. + // + + KeBugCheck(SCSI_DISK_DRIVER_INTERNAL); + } + + // + // Save the device object in the context for use by the completion + // routine. + // + + context->DeviceObject = Fdo; + srb = &context->Srb; + + // + // Zero out srb. + // + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Write length to SRB. + // + + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // Set timeout value large enough for drive to spin up. + // + + srb->TimeOutValue = START_UNIT_TIMEOUT; + + // + // Set the transfer length. + // + + srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | + SRB_FLAGS_DISABLE_AUTOSENSE | + SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // Build the start unit CDB. + // + + srb->CdbLength = 6; + cdb = (PCDB)srb->Cdb; + + cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; + cdb->START_STOP.Start = 1; + cdb->START_STOP.Immediate = 0; + cdb->START_STOP.LogicalUnitNumber = srb->Lun; + + // + // Build the asynchronous request to be sent to the port driver. + // Since this routine is called from a DPC the IRP should always be + // available. + // + + irp = IoAllocateIrp(Fdo->StackSize, FALSE); + + if(irp == NULL) { + + // + // ISSUE-2000/02/03-peterwie + // This code path was inheritted from the NT 4.0 class2.sys driver. + // It needs to be changed to survive low-memory conditions. + // + + KeBugCheck(SCSI_DISK_DRIVER_INTERNAL); + + } + + ClassAcquireRemoveLock(Fdo, irp); + + IoSetCompletionRoutine(irp, + (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion, + context, + TRUE, + TRUE, + TRUE); + + irpStack = IoGetNextIrpStackLocation(irp); + irpStack->MajorFunction = IRP_MJ_SCSI; + srb->OriginalRequest = irp; + + // + // Store the SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = srb; + + // + // Call the port driver with the IRP. + // + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp); + + return; + +} // end StartUnit() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?! + +Routine Description: + + This routine is called when an asynchronous I/O request + which was issused by the class driver completes. Examples of such requests + are release queue or START UNIT. This routine releases the queue if + necessary. It then frees the context and the IRP. + +Arguments: + + DeviceObject - The device object for the logical unit; however since this + is the top stack location the value is NULL. + + Irp - Supplies a pointer to the Irp to be processed. + + Context - Supplies the context to be used to process this request. + +Return Value: + + None. + +--*/ +NTSTATUS +ClassAsynchronousCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) +{ + PCOMPLETION_CONTEXT context = Context; + PSCSI_REQUEST_BLOCK srb; + + if(DeviceObject == NULL) { + + DeviceObject = context->DeviceObject; + } + + srb = &context->Srb; + + // + // If this is an execute srb, then check the return status and make sure. + // the queue is not frozen. + // + + if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) { + + // + // Check for a frozen queue. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + + // + // Unfreeze the queue getting the device object from the context. + // + + ClassReleaseQueue(context->DeviceObject); + } + } + + { // free port-allocated sense buffer if we can detect + + if (((PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->IsFdo) { + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { + FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb); + } + + } else { + + ASSERT(!TEST_FLAG(srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)); + + } + } + + + // + // Free the context and the Irp. + // + + if (Irp->MdlAddress != NULL) { + MmUnlockPages(Irp->MdlAddress); + IoFreeMdl(Irp->MdlAddress); + + Irp->MdlAddress = NULL; + } + + ClassReleaseRemoveLock(DeviceObject, Irp); + + ExFreePool(context); + IoFreeIrp(Irp); + + // + // Indicate the I/O system should stop processing the Irp completion. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // end ClassAsynchronousCompletion() + + + +VOID ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp) +{ + PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor; + PIO_STACK_LOCATION currentSp = IoGetCurrentIrpStackLocation(Irp); + ULONG entireXferLen = currentSp->Parameters.Read.Length; + PUCHAR bufPtr = MmGetMdlVirtualAddress(Irp->MdlAddress); + LARGE_INTEGER targetLocation = currentSp->Parameters.Read.ByteOffset; + PTRANSFER_PACKET pkt; + SINGLE_LIST_ENTRY pktList; + PSINGLE_LIST_ENTRY slistEntry; + ULONG numPackets; + KIRQL oldIrql; + ULONG i; + + /* + * Compute the number of hw xfers we'll have to do. + * Calculate this without allowing for an overflow condition. + */ + ASSERT(fdoData->HwMaxXferLen >= PAGE_SIZE); + numPackets = entireXferLen/fdoData->HwMaxXferLen; + if (entireXferLen % fdoData->HwMaxXferLen){ + numPackets++; + } + + /* + * First get all the TRANSFER_PACKETs that we'll need at once. + * Use our 'simple' slist functions since we don't need interlocked. + */ + SimpleInitSlistHdr(&pktList); + for (i = 0; i < numPackets; i++){ + pkt = DequeueFreeTransferPacket(Fdo, TRUE); + if (pkt){ + SimplePushSlist(&pktList, &pkt->SlistEntry); + } + else { + break; + } + } + + if (i == numPackets){ + /* + * Initialize the original IRP's status to success. + * If any of the packets fail, they will set it to an error status. + * The IoStatus.Information field will be incremented to the + * transfer length as the pieces complete. + */ + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + /* + * Store the number of transfer pieces inside the original IRP. + * It will be used to count down the pieces as they complete. + */ + Irp->Tail.Overlay.DriverContext[0] = LongToPtr(numPackets); + + /* + * We are proceeding with the transfer. + * Mark the client IRP pending since it may complete on a different thread. + */ + IoMarkIrpPending(Irp); + + /* + * Transmit the pieces of the transfer. + */ + while (entireXferLen > 0){ + ULONG thisPieceLen = MIN(fdoData->HwMaxXferLen, entireXferLen); + + /* + * Set up a TRANSFER_PACKET for this piece and send it. + */ + slistEntry = SimplePopSlist(&pktList); + ASSERT(slistEntry); + pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry); + SetupReadWriteTransferPacket( pkt, + bufPtr, + thisPieceLen, + targetLocation, + Irp); + SubmitTransferPacket(pkt); + + entireXferLen -= thisPieceLen; + bufPtr += thisPieceLen; + targetLocation.QuadPart += thisPieceLen; + } + ASSERT(SimpleIsSlistEmpty(&pktList)); + } + else if (i >= 1){ + /* + * We were unable to get all the TRANSFER_PACKETs we need, + * but we did get at least one. + * That means that we are in extreme low-memory stress. + * We'll try doing this transfer using a single packet. + * The port driver is certainly also in stress, so use one-page + * transfers. + */ + + /* + * Free all but one of the TRANSFER_PACKETs. + */ + while (i-- > 1){ + slistEntry = SimplePopSlist(&pktList); + ASSERT(slistEntry); + pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry); + EnqueueFreeTransferPacket(Fdo, pkt); + } + + /* + * Get the single TRANSFER_PACKET that we'll be using. + */ + slistEntry = SimplePopSlist(&pktList); + ASSERT(slistEntry); + ASSERT(SimpleIsSlistEmpty(&pktList)); + pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry); + DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt)); + + /* + * Set default status and the number of transfer packets (one) + * inside the original irp. + */ + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1); + + /* + * Mark the client irp pending since it may complete on + * another thread. + */ + IoMarkIrpPending(Irp); + + /* + * Set up the TRANSFER_PACKET for a lowMem transfer and launch. + */ + SetupReadWriteTransferPacket( pkt, + bufPtr, + entireXferLen, + targetLocation, + Irp); + InitLowMemRetry(pkt, bufPtr, entireXferLen, targetLocation); + StepLowMemRetry(pkt); + } + else { + /* + * We were unable to get ANY TRANSFER_PACKETs. + * Defer this client irp until some TRANSFER_PACKETs free up. + */ + DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp)); + IoMarkIrpPending(Irp); + EnqueueDeferredClientIrp(fdoData, Irp); + } + +} + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassIoComplete() + +Routine Description: + + This routine executes when the port driver has completed a request. + It looks at the SRB status in the completing SRB and if not success + it checks for valid request sense buffer information. If valid, the + info is used to update status with more precise message of type of + error. This routine deallocates the SRB. + + This routine should only be placed on the stack location for a class + driver FDO. + +Arguments: + + Fdo - Supplies the device object which represents the logical + unit. + + Irp - Supplies the Irp which has completed. + + Context - Supplies a pointer to the SRB. + +Return Value: + + NT status + +--*/ +NTSTATUS +ClassIoComplete( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp, + IN PVOID Context + ) +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK srb = Context; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData; + NTSTATUS status; + BOOLEAN retry; + BOOLEAN callStartNextPacket; + + ASSERT(fdoExtension->CommonExtension.IsFdo); + + // + // Check SRB status for success of completing request. + // + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + ULONG retryInterval; + + DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp, srb)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ClassReleaseQueue(Fdo); + } + + retry = ClassInterpretSenseInfo( + Fdo, + srb, + irpStack->MajorFunction, + irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? + irpStack->Parameters.DeviceIoControl.IoControlCode : + 0, + MAXIMUM_RETRIES - + ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4), + &status, + &retryInterval); + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) && + status == STATUS_VERIFY_REQUIRED) { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && (irpStack->Parameters.Others.Argument4--)) { + + // + // Retry request. + // + + DebugPrint((1, "Retry request %p\n", Irp)); + + if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { + FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb); + } + + RetryRequest(Fdo, Irp, srb, FALSE, retryInterval); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + } else { + + // + // Set status for successful request + // + fdoData->LoggedTURFailureSinceLastIO = FALSE; + ClasspPerfIncrementSuccessfulIo(fdoExtension); + status = STATUS_SUCCESS; + } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS) + + + // + // ensure we have returned some info, and it matches what the + // original request wanted for PAGING operations only + // + + if ((NT_SUCCESS(status)) && TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) { + ASSERT(Irp->IoStatus.Information != 0); + ASSERT(irpStack->Parameters.Read.Length == Irp->IoStatus.Information); + } + + // + // remember if the caller wanted to skip calling IoStartNextPacket. + // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl + // calls. this setting only affects device objects with StartIo routines. + // + + callStartNextPacket = !TEST_FLAG(srb->SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET); + if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { + callStartNextPacket = FALSE; + } + + // + // Free the srb + // + + if(!TEST_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT)) { + + if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { + FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb); + } + + if (fdoExtension->CommonExtension.IsSrbLookasideListInitialized){ + ClassFreeOrReuseSrb(fdoExtension, srb); + } + else { + DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver.")); + ExFreePool(srb); + } + + } else { + + DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because " + "SRB_CLASS_FLAGS_PERSISTANT set\n", srb)); + if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { + DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p " + " because SRB_CLASS_FLAGS_PERSISTANT set\n", + srb->SenseInfoBuffer)); + } + + } + + // + // Set status in completing IRP. + // + + Irp->IoStatus.Status = status; + + // + // Set the hard error if necessary. + // + + if (!NT_SUCCESS(status) && + IoIsErrorUserInduced(status) && + (Irp->Tail.Overlay.Thread != NULL) + ) { + + // + // Store DeviceObject for filesystem, and clear + // in IoStatus.Information field. + // + + IoSetHardErrorOrVerifyDevice(Irp, Fdo); + Irp->IoStatus.Information = 0; + } + + // + // If pending has be returned for this irp then mark the current stack as + // pending. + // + + if (Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) { + if (callStartNextPacket) { + KIRQL oldIrql; + KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); + IoStartNextPacket(Fdo, FALSE); + KeLowerIrql(oldIrql); + } + } + + ClassReleaseRemoveLock(Fdo, Irp); + + return status; + +} // end ClassIoComplete() + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassSendSrbSynchronous() + +Routine Description: + + This routine is called by SCSI device controls to complete an + SRB and send it to the port driver synchronously (ie wait for + completion). The CDB is already completed along with the SRB CDB + size and request timeout value. + +Arguments: + + Fdo - Supplies the functional device object which represents the target. + + Srb - Supplies a partially initialized SRB. The SRB cannot come from zone. + + BufferAddress - Supplies the address of the buffer. + + BufferLength - Supplies the length in bytes of the buffer. + + WriteToDevice - Indicates the data should be transfer to the device. + +Return Value: + + NTSTATUS indicating the final results of the operation. + + If NT_SUCCESS(), then the amount of usable data is contained in the field + Srb->DataTransferLength + +--*/ +NTSTATUS +ClassSendSrbSynchronous( + PDEVICE_OBJECT Fdo, + PSCSI_REQUEST_BLOCK Srb, + PVOID BufferAddress, + ULONG BufferLength, + BOOLEAN WriteToDevice + ) +{ + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData; + IO_STATUS_BLOCK ioStatus; + ULONG controlType; + PIRP irp; + PIO_STACK_LOCATION irpStack; + KEVENT event; + PUCHAR senseInfoBuffer; + ULONG retryCount = MAXIMUM_RETRIES; + NTSTATUS status; + BOOLEAN retry; + + // + // NOTE: This code is only pagable because we are not freezing + // the queue. Allowing the queue to be frozen from a pagable + // routine could leave the queue frozen as we try to page in + // the code to unfreeze the queue. The result would be a nice + // case of deadlock. Therefore, since we are unfreezing the + // queue regardless of the result, just set the NO_FREEZE_QUEUE + // flag in the SRB. + // + + ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + ASSERT(fdoExtension->CommonExtension.IsFdo); + + // + // Write length to SRB. + // + + Srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + // + // Set SCSI bus address. + // + + Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // Enable auto request sense. + // + + Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + // + // Sense buffer is in aligned nonpaged pool. + // + // + senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + SENSE_BUFFER_SIZE, + '7CcS'); + + if (senseInfoBuffer == NULL) { + + DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense " + "buffer\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + Srb->SenseInfoBuffer = senseInfoBuffer; + Srb->DataBuffer = BufferAddress; + + // + // Start retries here. + // + +retry: + + // + // use fdoextension's flags by default. + // do not move out of loop, as the flag may change due to errors + // sending this command. + // + + Srb->SrbFlags = fdoExtension->SrbFlags; + + if(BufferAddress != NULL) { + if(WriteToDevice) { + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT); + } else { + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN); + } + } + + // + // Initialize the QueueAction field. + // + + Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + + // + // Disable synchronous transfer for these requests. + // Disable freezing the queue, since all we do is unfreeze it anyways. + // + + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + + // + // Set the event object to the unsignaled state. + // It will be used to signal request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + // + // Build device I/O control request with METHOD_NEITHER data transfer. + // We'll queue a completion routine to cleanup the MDL's and such ourself. + // + + irp = IoAllocateIrp( + (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1), + FALSE); + + if(irp == NULL) { + ExFreePool(senseInfoBuffer); + DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Get next stack location. + // + + irpStack = IoGetNextIrpStackLocation(irp); + + // + // Set up SRB for execute scsi request. Save SRB address in next stack + // for the port driver. + // + + irpStack->MajorFunction = IRP_MJ_SCSI; + irpStack->Parameters.Scsi.Srb = Srb; + + IoSetCompletionRoutine(irp, + ClasspSendSynchronousCompletion, + Srb, + TRUE, + TRUE, + TRUE); + + irp->UserIosb = &ioStatus; + irp->UserEvent = &event; + + if(BufferAddress) { + // + // Build an MDL for the data buffer and stick it into the irp. The + // completion routine will unlock the pages and free the MDL. + // + + irp->MdlAddress = IoAllocateMdl( BufferAddress, + BufferLength, + FALSE, + FALSE, + irp ); + if (irp->MdlAddress == NULL) { + ExFreePool(senseInfoBuffer); + Srb->SenseInfoBuffer = NULL; + IoFreeIrp( irp ); + DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + _SEH2_TRY { + + // + // the io manager unlocks these pages upon completion + // + + MmProbeAndLockPages( irp->MdlAddress, + KernelMode, + (WriteToDevice ? IoReadAccess : + IoWriteAccess)); + + } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + status = _SEH2_GetExceptionCode(); + + ExFreePool(senseInfoBuffer); + Srb->SenseInfoBuffer = NULL; + IoFreeMdl(irp->MdlAddress); + IoFreeIrp(irp); + + DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx " + "locking buffer\n", status)); + return status; + } _SEH2_END; + } + + // + // Set the transfer length. + // + + Srb->DataTransferLength = BufferLength; + + // + // Zero out status. + // + + Srb->ScsiStatus = Srb->SrbStatus = 0; + Srb->NextSrb = 0; + + // + // Set up IRP Address. + // + + Srb->OriginalRequest = irp; + + // + // Call the port driver with the request and wait for it to complete. + // + + status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + // + // Check that request completed without error. + // + + if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + ULONG retryInterval; + + DBGTRACE(ClassDebugWarning, ("ClassSendSrbSynchronous - srb %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", Srb, DBGGETSCSIOPSTR(Srb), DBGGETSRBSTATUSSTR(Srb), (ULONG)Srb->SrbStatus, status, DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb))); + + // + // assert that the queue is not frozen + // + + ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN)); + + // + // Update status and determine if request should be retried. + // + + retry = ClassInterpretSenseInfo(Fdo, + Srb, + IRP_MJ_SCSI, + 0, + MAXIMUM_RETRIES - retryCount, + &status, + &retryInterval); + + + if (retry) { + + if ((status == STATUS_DEVICE_NOT_READY && + ((PSENSE_DATA) senseInfoBuffer)->AdditionalSenseCode == + SCSI_ADSENSE_LUN_NOT_READY) || + (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) { + + LARGE_INTEGER delay; + + // + // Delay for at least 2 seconds. + // + + if(retryInterval < 2) { + retryInterval = 2; + } + + delay.QuadPart = (LONGLONG)( - 10 * 1000 * (LONGLONG)1000 * retryInterval); + + // + // Stall for a while to let the device become ready + // + + KeDelayExecutionThread(KernelMode, FALSE, &delay); + + } + + // + // If retries are not exhausted then retry this operation. + // + + if (retryCount--) { + + if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) { + FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb); + } + + goto retry; + } + } + + } else { + fdoData->LoggedTURFailureSinceLastIO = FALSE; + status = STATUS_SUCCESS; + } + + // + // required even though we allocated our own, since the port driver may + // have allocated one also + // + + if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) { + FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb); + } + + Srb->SenseInfoBuffer = NULL; + ExFreePool(senseInfoBuffer); + + return status; +} + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassInterpretSenseInfo() + +Routine Description: + + This routine interprets the data returned from the SCSI + request sense. It determines the status to return in the + IRP and whether this request can be retried. + +Arguments: + + DeviceObject - Supplies the device object associated with this request. + + Srb - Supplies the scsi request block which failed. + + MajorFunctionCode - Supplies the function code to be used for logging. + + IoDeviceCode - Supplies the device code to be used for logging. + + Status - Returns the status for the request. + +Return Value: + + BOOLEAN TRUE: Drivers should retry this request. + FALSE: Drivers should not retry this request. + +--*/ +BOOLEAN +ClassInterpretSenseInfo( + IN PDEVICE_OBJECT Fdo, + IN PSCSI_REQUEST_BLOCK Srb, + IN UCHAR MajorFunctionCode, + IN ULONG IoDeviceCode, + IN ULONG RetryCount, + OUT NTSTATUS *Status, + OUT OPTIONAL ULONG *RetryInterval + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData; + + PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer; + + BOOLEAN retry = TRUE; + BOOLEAN logError = FALSE; + BOOLEAN unhandledError = FALSE; + BOOLEAN incrementErrorCount = FALSE; + + ULONG badSector = 0; + ULONG uniqueId = 0; + + NTSTATUS logStatus; + + ULONG readSector; + ULONG index; + + ULONG retryInterval = 0; + KIRQL oldIrql; + + + logStatus = -1; + + if(TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) { + + // + // Log anything remotely incorrect about paging i/o + // + + logError = TRUE; + uniqueId = 301; + logStatus = IO_WARNING_PAGING_FAILURE; + } + + // + // Check that request sense buffer is valid. + // + + ASSERT(fdoExtension->CommonExtension.IsFdo); + + + // + // must handle the SRB_STATUS_INTERNAL_ERROR case first, + // as it has all the flags set. + // + + if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) { + + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: Internal Error code is %x\n", + Srb->InternalStatus)); + + retry = FALSE; + *Status = Srb->InternalStatus; + + } else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && + (Srb->SenseInfoBufferLength >= + offsetof(SENSE_DATA, CommandSpecificInformation))) { + + // + // Zero the additional sense code and additional sense code qualifier + // if they were not returned by the device. + // + + readSector = senseBuffer->AdditionalSenseLength + + offsetof(SENSE_DATA, AdditionalSenseLength); + + if (readSector > Srb->SenseInfoBufferLength) { + readSector = Srb->SenseInfoBufferLength; + } + + if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCode)) { + senseBuffer->AdditionalSenseCode = 0; + } + + if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCodeQualifier)) { + senseBuffer->AdditionalSenseCodeQualifier = 0; + } + + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: Error code is %x\n", + senseBuffer->ErrorCode)); + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: Sense key is %x\n", + senseBuffer->SenseKey)); + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: Additional sense code is %x\n", + senseBuffer->AdditionalSenseCode)); + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: Additional sense code qualifier " + "is %x\n", + senseBuffer->AdditionalSenseCodeQualifier)); + + + switch (senseBuffer->SenseKey & 0xf) { + + case SCSI_SENSE_NOT_READY: { + + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Device not ready\n")); + *Status = STATUS_DEVICE_NOT_READY; + + switch (senseBuffer->AdditionalSenseCode) { + + case SCSI_ADSENSE_LUN_NOT_READY: { + + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Lun not ready\n")); + + switch (senseBuffer->AdditionalSenseCodeQualifier) { + + case SCSI_SENSEQ_OPERATION_IN_PROGRESS: { + DEVICE_EVENT_BECOMING_READY notReady; + + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Operation In Progress\n")); + retryInterval = NOT_READY_RETRY_INTERVAL; + + RtlZeroMemory(¬Ready, sizeof(DEVICE_EVENT_BECOMING_READY)); + notReady.Version = 1; + notReady.Reason = 2; + notReady.Estimated100msToReady = retryInterval * 10; + ClasspSendNotification(fdoExtension, + &GUID_IO_DEVICE_BECOMING_READY, + sizeof(DEVICE_EVENT_BECOMING_READY), + ¬Ready); + + break; + } + + case SCSI_SENSEQ_BECOMING_READY: { + DEVICE_EVENT_BECOMING_READY notReady; + + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "In process of becoming ready\n")); + retryInterval = NOT_READY_RETRY_INTERVAL; + + RtlZeroMemory(¬Ready, sizeof(DEVICE_EVENT_BECOMING_READY)); + notReady.Version = 1; + notReady.Reason = 1; + notReady.Estimated100msToReady = retryInterval * 10; + ClasspSendNotification(fdoExtension, + &GUID_IO_DEVICE_BECOMING_READY, + sizeof(DEVICE_EVENT_BECOMING_READY), + ¬Ready); + break; + } + + case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Long write in progress\n")); + retry = FALSE; + break; + } + + case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Manual intervention required\n")); + *Status = STATUS_NO_MEDIA_IN_DEVICE; + retry = FALSE; + break; + } + + case SCSI_SENSEQ_FORMAT_IN_PROGRESS: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Format in progress\n")); + retry = FALSE; + break; + } + + case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: { + + if(!TEST_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) { + + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: " + "not ready, cause unknown\n")); + /* + Many non-WHQL certified drives (mostly CD-RW) return + this when they have no media instead of the obvious + choice of: + + SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE + + These drives should not pass WHQL certification due + to this discrepency. + + */ + retry = FALSE; + break; + + } else { + + // + // Treat this as init command required and fall through. + // + } + } + + case SCSI_SENSEQ_INIT_COMMAND_REQUIRED: + default: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Initializing command required\n")); + + // + // This sense code/additional sense code + // combination may indicate that the device + // needs to be started. Send an start unit if this + // is a disk device. + // + + if(TEST_FLAG(fdoExtension->DeviceFlags, + DEV_SAFE_START_UNIT) && + !TEST_FLAG(Srb->SrbFlags, + SRB_CLASS_FLAGS_LOW_PRIORITY)) { + ClassSendStartUnit(Fdo); + } + break; + } + + + } // end switch (senseBuffer->AdditionalSenseCodeQualifier) + break; + } + + case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "No Media in device.\n")); + *Status = STATUS_NO_MEDIA_IN_DEVICE; + retry = FALSE; + + // + // signal MCN that there isn't any media in the device + // + if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { + DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: " + "No Media in a non-removable device %p\n", + Fdo)); + } + ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE); + + break; + } + } // end switch (senseBuffer->AdditionalSenseCode) + + break; + } // end SCSI_SENSE_NOT_READY + + case SCSI_SENSE_DATA_PROTECT: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Media write protected\n")); + *Status = STATUS_MEDIA_WRITE_PROTECTED; + retry = FALSE; + break; + } // end SCSI_SENSE_DATA_PROTECT + + case SCSI_SENSE_MEDIUM_ERROR: { + DebugPrint((ClassDebugSenseInfo,"ClassInterpretSenseInfo: " + "Medium Error (bad block)\n")); + *Status = STATUS_DEVICE_DATA_ERROR; + + retry = FALSE; + logError = TRUE; + uniqueId = 256; + logStatus = IO_ERR_BAD_BLOCK; + + // + // Check if this error is due to unknown format + // + if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INVALID_MEDIA){ + + switch (senseBuffer->AdditionalSenseCodeQualifier) { + + case SCSI_SENSEQ_UNKNOWN_FORMAT: { + + *Status = STATUS_UNRECOGNIZED_MEDIA; + + // + // Log error only if this is a paging request + // + if(!TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) { + logError = FALSE; + } + break; + } + + case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: { + + *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED; + logError = FALSE; + break; + + } + default: { + break; + } + } // end switch AdditionalSenseCodeQualifier + + } // end SCSI_ADSENSE_INVALID_MEDIA + + break; + + } // end SCSI_SENSE_MEDIUM_ERROR + + case SCSI_SENSE_HARDWARE_ERROR: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Hardware error\n")); + *Status = STATUS_IO_DEVICE_ERROR; + logError = TRUE; + uniqueId = 257; + logStatus = IO_ERR_CONTROLLER_ERROR; + break; + } // end SCSI_SENSE_HARDWARE_ERROR + + case SCSI_SENSE_ILLEGAL_REQUEST: { + + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Illegal SCSI request\n")); + *Status = STATUS_INVALID_DEVICE_REQUEST; + retry = FALSE; + + switch (senseBuffer->AdditionalSenseCode) { + + case SCSI_ADSENSE_ILLEGAL_COMMAND: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Illegal command\n")); + break; + } + + case SCSI_ADSENSE_ILLEGAL_BLOCK: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Illegal block address\n")); + *Status = STATUS_NONEXISTENT_SECTOR; + break; + } + + case SCSI_ADSENSE_INVALID_LUN: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Invalid LUN\n")); + *Status = STATUS_NO_SUCH_DEVICE; + break; + } + + case SCSI_ADSENSE_MUSIC_AREA: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Music area\n")); + break; + } + + case SCSI_ADSENSE_DATA_AREA: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Data area\n")); + break; + } + + case SCSI_ADSENSE_VOLUME_OVERFLOW: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Volume overflow\n")); + break; + } + + case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Copy protection failure\n")); + + *Status = STATUS_COPY_PROTECTION_FAILURE; + + switch (senseBuffer->AdditionalSenseCodeQualifier) { + case SCSI_SENSEQ_AUTHENTICATION_FAILURE: + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: " + "Authentication failure\n")); + *Status = STATUS_CSS_AUTHENTICATION_FAILURE; + break; + case SCSI_SENSEQ_KEY_NOT_PRESENT: + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: " + "Key not present\n")); + *Status = STATUS_CSS_KEY_NOT_PRESENT; + break; + case SCSI_SENSEQ_KEY_NOT_ESTABLISHED: + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: " + "Key not established\n")); + *Status = STATUS_CSS_KEY_NOT_ESTABLISHED; + break; + case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION: + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: " + "Read of scrambled sector w/o " + "authentication\n")); + *Status = STATUS_CSS_SCRAMBLED_SECTOR; + break; + case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT: + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: " + "Media region does not logical unit " + "region\n")); + *Status = STATUS_CSS_REGION_MISMATCH; + break; + case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR: + DebugPrint((ClassDebugSenseInfo, + "ClassInterpretSenseInfo: " + "Region set error -- region may " + "be permanent\n")); + *Status = STATUS_CSS_RESETS_EXHAUSTED; + break; + } // end switch of ASCQ for COPY_PROTECTION_FAILURE + + break; + } + + + case SCSI_ADSENSE_INVALID_CDB: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Invalid CDB\n")); + + // + // Note: the retry interval is not typically used. + // it is set here only because a ClassErrorHandler + // cannot set the retryInterval, and the error may + // require a few commands to be sent to clear whatever + // caused this condition (i.e. disk clears the write + // cache, requiring at least two commands) + // + // hopefully, this shortcoming can be changed for + // blackcomb. + // + + retryInterval = 3; + break; + } + + } // end switch (senseBuffer->AdditionalSenseCode) + + break; + } // end SCSI_SENSE_ILLEGAL_REQUEST + + case SCSI_SENSE_UNIT_ATTENTION: { + + PVPB vpb; + ULONG count; + + // + // A media change may have occured so increment the change + // count for the physical device + // + + count = InterlockedIncrement(&fdoExtension->MediaChangeCount); + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Media change count for device %d incremented to %#lx\n", + fdoExtension->DeviceNumber, count)); + + + switch (senseBuffer->AdditionalSenseCode) { + case SCSI_ADSENSE_MEDIUM_CHANGED: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Media changed\n")); + + if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { + DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: " + "Media Changed on non-removable device %p\n", + Fdo)); + } + ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE); + break; + } + + case SCSI_ADSENSE_BUS_RESET: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Bus reset\n")); + break; + } + + case SCSI_ADSENSE_OPERATOR_REQUEST: { + switch (senseBuffer->AdditionalSenseCodeQualifier) { + + case SCSI_SENSEQ_MEDIUM_REMOVAL: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Ejection request received!\n")); + ClassSendEjectionNotification(fdoExtension); + break; + } + + case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Operator selected write permit?! " + "(unsupported!)\n")); + break; + } + + case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Operator selected write protect?! " + "(unsupported!)\n")); + break; + } + + } + } + + default: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Unit attention\n")); + break; + } + + } // end switch (senseBuffer->AdditionalSenseCode) + + if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) + { + // + // TODO : Is the media lockable? + // + + if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED)) + { + // + // Set bit to indicate that media may have changed + // and volume needs verification. + // + + SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME); + + *Status = STATUS_VERIFY_REQUIRED; + retry = FALSE; + } + } + else + { + *Status = STATUS_IO_DEVICE_ERROR; + } + + break; + + } // end SCSI_SENSE_UNIT_ATTENTION + + case SCSI_SENSE_ABORTED_COMMAND: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Command aborted\n")); + *Status = STATUS_IO_DEVICE_ERROR; + retryInterval = 1; + break; + } // end SCSI_SENSE_ABORTED_COMMAND + + case SCSI_SENSE_BLANK_CHECK: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Media blank check\n")); + retry = FALSE; + *Status = STATUS_NO_DATA_DETECTED; + break; + } // end SCSI_SENSE_BLANK_CHECK + + case SCSI_SENSE_RECOVERED_ERROR: { + + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Recovered error\n")); + *Status = STATUS_SUCCESS; + retry = FALSE; + logError = TRUE; + uniqueId = 258; + + switch(senseBuffer->AdditionalSenseCode) { + case SCSI_ADSENSE_SEEK_ERROR: + case SCSI_ADSENSE_TRACK_ERROR: { + logStatus = IO_ERR_SEEK_ERROR; + break; + } + + case SCSI_ADSENSE_REC_DATA_NOECC: + case SCSI_ADSENSE_REC_DATA_ECC: { + logStatus = IO_RECOVERED_VIA_ECC; + break; + } + + case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: { + UCHAR wmiEventData[5]; + + *((PULONG)wmiEventData) = sizeof(UCHAR); + wmiEventData[sizeof(ULONG)] = senseBuffer->AdditionalSenseCodeQualifier; + + // + // Don't log another eventlog if we have already logged once + // NOTE: this should have been interlocked, but the structure + // was publicly defined to use a BOOLEAN (char). Since + // media only reports these errors once per X minutes, + // the potential race condition is nearly non-existant. + // the worst case is duplicate log entries, so ignore. + // + + if (fdoExtension->FailurePredicted == 0) { + logError = TRUE; + } + fdoExtension->FailurePredicted = TRUE; + fdoExtension->FailureReason = senseBuffer->AdditionalSenseCodeQualifier; + logStatus = IO_WRN_FAILURE_PREDICTED; + + ClassNotifyFailurePredicted(fdoExtension, + (PUCHAR)&wmiEventData, + sizeof(wmiEventData), + 0, + 4, + Srb->PathId, + Srb->TargetId, + Srb->Lun); + break; + } + + default: { + logStatus = IO_ERR_CONTROLLER_ERROR; + break; + } + + } // end switch(senseBuffer->AdditionalSenseCode) + + if (senseBuffer->IncorrectLength) { + + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Incorrect length detected.\n")); + *Status = STATUS_INVALID_BLOCK_LENGTH ; + } + + break; + } // end SCSI_SENSE_RECOVERED_ERROR + + case SCSI_SENSE_NO_SENSE: { + + // + // Check other indicators. + // + + if (senseBuffer->IncorrectLength) { + + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Incorrect length detected.\n")); + *Status = STATUS_INVALID_BLOCK_LENGTH ; + retry = FALSE; + + } else { + + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "No specific sense key\n")); + *Status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + break; + } // end SCSI_SENSE_NO_SENSE + + default: { + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Unrecognized sense code\n")); + *Status = STATUS_IO_DEVICE_ERROR; + break; + } + + } // end switch (senseBuffer->SenseKey & 0xf) + + // + // Try to determine the bad sector from the inquiry data. + // + + if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_READ || + ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY || + ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_WRITE)) { + + for (index = 0; index < 4; index++) { + badSector = (badSector << 8) | senseBuffer->Information[index]; + } + + readSector = 0; + for (index = 0; index < 4; index++) { + readSector = (readSector << 8) | Srb->Cdb[index+2]; + } + + index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) | + ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb; + + // + // Make sure the bad sector is within the read sectors. + // + + if (!(badSector >= readSector && badSector < readSector + index)) { + badSector = readSector; + } + } + + } else { + + // + // Request sense buffer not valid. No sense information + // to pinpoint the error. Return general request fail. + // + + DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: " + "Request sense info not valid. SrbStatus %2x\n", + SRB_STATUS(Srb->SrbStatus))); + retry = TRUE; + + switch (SRB_STATUS(Srb->SrbStatus)) { + case SRB_STATUS_INVALID_LUN: + case SRB_STATUS_INVALID_TARGET_ID: + case SRB_STATUS_NO_DEVICE: + case SRB_STATUS_NO_HBA: + case SRB_STATUS_INVALID_PATH_ID: { + *Status = STATUS_NO_SUCH_DEVICE; + retry = FALSE; + break; + } + + case SRB_STATUS_COMMAND_TIMEOUT: + case SRB_STATUS_TIMEOUT: { + + // + // Update the error count for the device. + // + + incrementErrorCount = TRUE; + *Status = STATUS_IO_TIMEOUT; + break; + } + + case SRB_STATUS_ABORTED: { + + // + // Update the error count for the device. + // + + incrementErrorCount = TRUE; + *Status = STATUS_IO_TIMEOUT; + retryInterval = 1; + break; + } + + + case SRB_STATUS_SELECTION_TIMEOUT: { + logError = TRUE; + logStatus = IO_ERR_NOT_READY; + uniqueId = 260; + *Status = STATUS_DEVICE_NOT_CONNECTED; + retry = FALSE; + break; + } + + case SRB_STATUS_DATA_OVERRUN: { + *Status = STATUS_DATA_OVERRUN; + retry = FALSE; + break; + } + + case SRB_STATUS_PHASE_SEQUENCE_FAILURE: { + + // + // Update the error count for the device. + // + + incrementErrorCount = TRUE; + *Status = STATUS_IO_DEVICE_ERROR; + + // + // If there was phase sequence error then limit the number of + // retries. + // + + if (RetryCount > 1 ) { + retry = FALSE; + } + + break; + } + + case SRB_STATUS_REQUEST_FLUSHED: { + + // + // If the status needs verification bit is set. Then set + // the status to need verification and no retry; otherwise, + // just retry the request. + // + + if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) { + + *Status = STATUS_VERIFY_REQUIRED; + retry = FALSE; + + } else { + *Status = STATUS_IO_DEVICE_ERROR; + } + + break; + } + + case SRB_STATUS_INVALID_REQUEST: { + *Status = STATUS_INVALID_DEVICE_REQUEST; + retry = FALSE; + break; + } + + case SRB_STATUS_UNEXPECTED_BUS_FREE: + case SRB_STATUS_PARITY_ERROR: + + // + // Update the error count for the device + // and fall through to below + // + + incrementErrorCount = TRUE; + + case SRB_STATUS_BUS_RESET: { + *Status = STATUS_IO_DEVICE_ERROR; + break; + } + + case SRB_STATUS_ERROR: { + + *Status = STATUS_IO_DEVICE_ERROR; + if (Srb->ScsiStatus == 0) { + + // + // This is some strange return code. Update the error + // count for the device. + // + + incrementErrorCount = TRUE; + + } if (Srb->ScsiStatus == SCSISTAT_BUSY) { + + *Status = STATUS_DEVICE_NOT_READY; + + } if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT) { + + *Status = STATUS_DEVICE_BUSY; + retry = FALSE; + logError = FALSE; + + } + + break; + } + + default: { + logError = TRUE; + logStatus = IO_ERR_CONTROLLER_ERROR; + uniqueId = 259; + *Status = STATUS_IO_DEVICE_ERROR; + unhandledError = TRUE; + break; + } + + } + + // + // NTRAID #183546 - if we support GESN subtype NOT_READY events, and + // we know from a previous poll when the device will be ready (ETA) + // we should delay the retry more appropriately than just guessing. + // + /* + if (fdoExtension->MediaChangeDetectionInfo && + fdoExtension->MediaChangeDetectionInfo->Gesn.Supported && + TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask, + NOTIFICATION_DEVICE_BUSY_CLASS_MASK) + ) { + // check if Gesn.ReadyTime if greater than current tick count + // if so, delay that long (from 1 to 30 seconds max?) + // else, leave the guess of time alone. + } + */ + + } + + if (incrementErrorCount) { + + // + // if any error count occurred, delay the retry of this io by + // at least one second, if caller supports it. + // + + if (retryInterval == 0) { + retryInterval = 1; + } + ClasspPerfIncrementErrorCount(fdoExtension); + } + + // + // If there is a class specific error handler call it. + // + + if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) { + + fdoExtension->CommonExtension.DevInfo->ClassError(Fdo, + Srb, + Status, + &retry); + } + + // + // If the caller wants to know the suggested retry interval tell them. + // + + if(ARGUMENT_PRESENT(RetryInterval)) { + *RetryInterval = retryInterval; + } + + + /* + * LOG the error: + * Always log the error in our internal log. + * If logError is set, also log the error in the system log. + */ + { + ULONG totalSize; + ULONG senseBufferSize = 0; + IO_ERROR_LOG_PACKET staticErrLogEntry = {0}; + CLASS_ERROR_LOG_DATA staticErrLogData = {0}; + + // + // Calculate the total size of the error log entry. + // add to totalSize in the order that they are used. + // the advantage to calculating all the sizes here is + // that we don't have to do a bunch of extraneous checks + // later on in this code path. + // + totalSize = sizeof(IO_ERROR_LOG_PACKET) // required + - sizeof(ULONG) // struct includes one ULONG + + sizeof(CLASS_ERROR_LOG_DATA);// struct for ease + + // + // also save any available extra sense data, up to the maximum errlog + // packet size . WMI should be used for real-time analysis. + // the event log should only be used for post-mortem debugging. + // + if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) { + ULONG validSenseBytes; + BOOLEAN validSense; + + // + // make sure we can at least access the AdditionalSenseLength field + // + validSense = RTL_CONTAINS_FIELD(senseBuffer, + Srb->SenseInfoBufferLength, + AdditionalSenseLength); + if (validSense) { + + // + // if extra info exists, copy the maximum amount of available + // sense data that is safe into the the errlog. + // + validSenseBytes = senseBuffer->AdditionalSenseLength + + offsetof(SENSE_DATA, AdditionalSenseLength); + + // + // this is invalid because it causes overflow! + // whoever sent this type of request would cause + // a system crash. + // + ASSERT(validSenseBytes < MAX_ADDITIONAL_SENSE_BYTES); + + // + // set to save the most sense buffer possible + // + senseBufferSize = max(validSenseBytes, sizeof(SENSE_DATA)); + senseBufferSize = min(senseBufferSize, Srb->SenseInfoBufferLength); + } else { + // + // it's smaller than required to read the total number of + // valid bytes, so just use the SenseInfoBufferLength field. + // + senseBufferSize = Srb->SenseInfoBufferLength; + } + + /* + * Bump totalSize by the number of extra senseBuffer bytes + * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA). + * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE. + */ + if (senseBufferSize > sizeof(SENSE_DATA)){ + totalSize += senseBufferSize-sizeof(SENSE_DATA); + if (totalSize > ERROR_LOG_MAXIMUM_SIZE){ + senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE; + totalSize = ERROR_LOG_MAXIMUM_SIZE; + } + } + } + + // + // If we've used up all of our retry attempts, set the final status to + // reflect the appropriate result. + // + if (retry && RetryCount < MAXIMUM_RETRIES) { + staticErrLogEntry.FinalStatus = STATUS_SUCCESS; + staticErrLogData.ErrorRetried = TRUE; + } else { + staticErrLogEntry.FinalStatus = *Status; + } + if (TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) { + staticErrLogData.ErrorPaging = TRUE; + } + if (unhandledError) { + staticErrLogData.ErrorUnhandled = TRUE; + } + + // + // Calculate the device offset if there is a geometry. + // + staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)badSector; + staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)fdoExtension->DiskGeometry.BytesPerSector; + if (logStatus == -1){ + staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR; + } else { + staticErrLogEntry.ErrorCode = logStatus; + } + + /* + * The dump data follows the IO_ERROR_LOG_PACKET, + * with the first ULONG of dump data inside the packet. + */ + staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG); + + staticErrLogEntry.SequenceNumber = 0; + staticErrLogEntry.MajorFunctionCode = MajorFunctionCode; + staticErrLogEntry.IoControlCode = IoDeviceCode; + staticErrLogEntry.RetryCount = (UCHAR) RetryCount; + staticErrLogEntry.UniqueErrorValue = uniqueId; + + KeQueryTickCount(&staticErrLogData.TickCount); + staticErrLogData.PortNumber = (ULONG)-1; + + /* + * Save the entire contents of the SRB. + */ + staticErrLogData.Srb = *Srb; + + /* + * For our private log, save just the default length of the SENSE_DATA. + */ + if (senseBufferSize != 0){ + RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(SENSE_DATA))); + } + + /* + * Save the error log in our context. + * We only save the default sense buffer length. + */ + KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); + fdoData->ErrorLogs[fdoData->ErrorLogNextIndex] = staticErrLogData; + fdoData->ErrorLogNextIndex++; + fdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES; + KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); + + /* + * If logError is set, also save this log in the system's error log. + * But make sure we don't log TUR failures over and over + * (e.g. if an external drive was switched off and we're still sending TUR's to it every second). + */ + if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_TEST_UNIT_READY) && logError){ + if (fdoData->LoggedTURFailureSinceLastIO){ + logError = FALSE; + } + else { + fdoData->LoggedTURFailureSinceLastIO = TRUE; + } + } + if (logError){ + PIO_ERROR_LOG_PACKET errorLogEntry; + PCLASS_ERROR_LOG_DATA errlogData; + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize); + if (errorLogEntry){ + errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData; + + *errorLogEntry = staticErrLogEntry; + *errlogData = staticErrLogData; + + /* + * For the system log, copy as much of the sense buffer as possible. + */ + if (senseBufferSize != 0) { + RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize); + } + + /* + * Write the error log packet to the system error logging thread. + */ + IoWriteErrorLogEntry(errorLogEntry); + } + } + } + + return retry; + +} // end ClassInterpretSenseInfo() + + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassModeSense() + +Routine Description: + + This routine sends a mode sense command to a target ID and returns + when it is complete. + +Arguments: + + Fdo - Supplies the functional device object associated with this request. + + ModeSenseBuffer - Supplies a buffer to store the sense data. + + Length - Supplies the length in bytes of the mode sense buffer. + + PageMode - Supplies the page or pages of mode sense data to be retrived. + +Return Value: + + Length of the transferred data is returned. + +--*/ +ULONG ClassModeSense( IN PDEVICE_OBJECT Fdo, + IN PCHAR ModeSenseBuffer, + IN ULONG Length, + IN UCHAR PageMode) +{ + ULONG lengthTransferred = 0; + PMDL senseBufferMdl; + + PAGED_CODE(); + + senseBufferMdl = BuildDeviceInputMdl(ModeSenseBuffer, Length); + if (senseBufferMdl){ + + TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE); + if (pkt){ + KEVENT event; + NTSTATUS pktStatus; + IRP pseudoIrp = {0}; + + /* + * Store the number of packets servicing the irp (one) + * inside the original IRP. It will be used to counted down + * to zero when the packet completes. + * Initialize the original IRP's status to success. + * If the packet fails, we will set it to the error status. + */ + pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1); + pseudoIrp.IoStatus.Status = STATUS_SUCCESS; + pseudoIrp.IoStatus.Information = 0; + pseudoIrp.MdlAddress = senseBufferMdl; + + /* + * Set this up as a SYNCHRONOUS transfer, submit it, + * and wait for the packet to complete. The result + * status will be written to the original irp. + */ + ASSERT(Length <= 0x0ff); + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + SetupModeSenseTransferPacket(pkt, &event, ModeSenseBuffer, (UCHAR)Length, PageMode, &pseudoIrp); + SubmitTransferPacket(pkt); + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + + if (NT_SUCCESS(pseudoIrp.IoStatus.Status)){ + lengthTransferred = (ULONG)pseudoIrp.IoStatus.Information; + } + else { + /* + * This request can sometimes fail legitimately + * (e.g. when a SCSI device is attached but turned off) + * so this is not necessarily a device/driver bug. + */ + DBGTRACE(ClassDebugWarning, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status)); + } + } + + FreeDeviceInputMdl(senseBufferMdl); + } + + return lengthTransferred; +} + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassFindModePage() + +Routine Description: + + This routine scans through the mode sense data and finds the requested + mode sense page code. + +Arguments: + ModeSenseBuffer - Supplies a pointer to the mode sense data. + + Length - Indicates the length of valid data. + + PageMode - Supplies the page mode to be searched for. + + Use6Byte - Indicates whether 6 or 10 byte mode sense was used. + +Return Value: + + A pointer to the the requested mode page. If the mode page was not found + then NULL is return. + +--*/ +PVOID +ClassFindModePage( + IN PCHAR ModeSenseBuffer, + IN ULONG Length, + IN UCHAR PageMode, + IN BOOLEAN Use6Byte + ) +{ + PUCHAR limit; + ULONG parameterHeaderLength; + PVOID result = NULL; + + limit = ModeSenseBuffer + Length; + parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10); + + if (Length >= parameterHeaderLength) { + + PMODE_PARAMETER_HEADER10 modeParam10; + ULONG blockDescriptorLength; + + /* + * Skip the mode select header and block descriptors. + */ + if (Use6Byte){ + blockDescriptorLength = ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength; + } + else { + modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer; + blockDescriptorLength = modeParam10->BlockDescriptorLength[1]; + } + + ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength; + + // + // ModeSenseBuffer now points at pages. Walk the pages looking for the + // requested page until the limit is reached. + // + + while (ModeSenseBuffer + + RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit) { + + if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) { + + /* + * found the mode page. make sure it's safe to touch it all + * before returning the pointer to caller + */ + + if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit) { + /* + * Return NULL since the page is not safe to access in full + */ + result = NULL; + } + else { + result = ModeSenseBuffer; + } + break; + } + + // + // Advance to the next page which is 4-byte-aligned offset after this page. + // + ModeSenseBuffer += + ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength + + RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength); + + } + } + + return result; +} // end ClassFindModePage() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassSendSrbAsynchronous() + +Routine Description: + + This routine takes a partially built Srb and an Irp and sends it down to + the port driver. + + This routine must be called with the remove lock held for the specified + Irp. + +Arguments: + + Fdo - Supplies the functional device object for the orginal request. + + Srb - Supplies a paritally build ScsiRequestBlock. In particular, the + CDB and the SRB timeout value must be filled in. The SRB must not be + allocated from zone. + + Irp - Supplies the requesting Irp. + + BufferAddress - Supplies a pointer to the buffer to be transfered. + + BufferLength - Supplies the length of data transfer. + + WriteToDevice - Indicates the data transfer will be from system memory to + device. + +Return Value: + + Returns STATUS_PENDING if the request is dispatched (since the + completion routine may change the irp's status value we cannot simply + return the value of the dispatch) + + or returns a status value to indicate why it failed. + +--*/ +NTSTATUS +ClassSendSrbAsynchronous( + PDEVICE_OBJECT Fdo, + PSCSI_REQUEST_BLOCK Srb, + PIRP Irp, + PVOID BufferAddress, + ULONG BufferLength, + BOOLEAN WriteToDevice + ) +{ + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PIO_STACK_LOCATION irpStack; + + ULONG savedFlags; + + // + // Write length to SRB. + // + + Srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + // + // Set SCSI bus address. + // + + Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // This is a violation of the SCSI spec but it is required for + // some targets. + // + + // Srb->Cdb[1] |= deviceExtension->Lun << 5; + + // + // Indicate auto request sense by specifying buffer and size. + // + + Srb->SenseInfoBuffer = fdoExtension->SenseData; + Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + Srb->DataBuffer = BufferAddress; + + // + // Save the class driver specific flags away. + // + + savedFlags = Srb->SrbFlags & SRB_FLAGS_CLASS_DRIVER_RESERVED; + + // + // Allow the caller to specify that they do not wish + // IoStartNextPacket() to be called in the completion routine. + // + + SET_FLAG(savedFlags, (Srb->SrbFlags & SRB_FLAGS_DONT_START_NEXT_PACKET)); + + if (BufferAddress != NULL) { + + // + // Build Mdl if necessary. + // + + if (Irp->MdlAddress == NULL) { + + if (IoAllocateMdl(BufferAddress, + BufferLength, + FALSE, + FALSE, + Irp) == NULL) { + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + // + // ClassIoComplete() would have free'd the srb + // + + if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) { + FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb); + } + ClassFreeOrReuseSrb(fdoExtension, Srb); + ClassReleaseRemoveLock(Fdo, Irp); + ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + MmBuildMdlForNonPagedPool(Irp->MdlAddress); + + } else { + + // + // Make sure the buffer requested matches the MDL. + // + + ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress)); + } + + // + // Set read flag. + // + + Srb->SrbFlags = WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN; + + } else { + + // + // Clear flags. + // + + Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER; + } + + // + // Restore saved flags. + // + + SET_FLAG(Srb->SrbFlags, savedFlags); + + // + // Disable synchronous transfer for these requests. + // + + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + + // + // Set the transfer length. + // + + Srb->DataTransferLength = BufferLength; + + // + // Zero out status. + // + + Srb->ScsiStatus = Srb->SrbStatus = 0; + + Srb->NextSrb = 0; + + // + // Save a few parameters in the current stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + // + // Save retry count in current Irp stack. + // + + irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES; + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE); + + // + // Get next stack location and + // set major function code. + // + + irpStack = IoGetNextIrpStackLocation(Irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Save SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = Srb; + + // + // Set up Irp Address. + // + + Srb->OriginalRequest = Irp; + + // + // Call the port driver to process the request. + // + + IoMarkIrpPending(Irp); + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp); + + return STATUS_PENDING; + +} // end ClassSendSrbAsynchronous() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassDeviceControlDispatch() + +Routine Description: + + The routine is the common class driver device control dispatch entry point. + This routine is invokes the device-specific drivers DeviceControl routine, + (which may call the Class driver's common DeviceControl routine). + +Arguments: + + DeviceObject - Supplies a pointer to the device object for this request. + + Irp - Supplies the Irp making the request. + +Return Value: + + Returns the status returned from the device-specific driver. + +--*/ +NTSTATUS +ClassDeviceControlDispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +{ + + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + ULONG isRemoved; + + isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp); + + if(isRemoved) { + + ClassReleaseRemoveLock(DeviceObject, Irp); + + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + // + // Call the class specific driver DeviceControl routine. + // If it doesn't handle it, it will call back into ClassDeviceControl. + // + + ASSERT(commonExtension->DevInfo->ClassDeviceControl); + + return commonExtension->DevInfo->ClassDeviceControl(DeviceObject,Irp); +} // end ClassDeviceControlDispatch() + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassDeviceControl() + +Routine Description: + + The routine is the common class driver device control dispatch function. + This routine is called by a class driver when it get an unrecognized + device control request. This routine will perform the correct action for + common requests such as lock media. If the device request is unknown it + passed down to the next level. + + This routine must be called with the remove lock held for the specified + irp. + +Arguments: + + DeviceObject - Supplies a pointer to the device object for this request. + + Irp - Supplies the Irp making the request. + +Return Value: + + Returns back a STATUS_PENDING or a completion status. + +--*/ +NTSTATUS +ClassDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextStack = NULL; + + ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; + + PSCSI_REQUEST_BLOCK srb = NULL; + PCDB cdb = NULL; + + NTSTATUS status; + ULONG modifiedIoControlCode; + + // + // If this is a pass through I/O control, set the minor function code + // and device address and pass it to the port driver. + // + + if ((controlCode == IOCTL_SCSI_PASS_THROUGH) || + (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT)) { + + PSCSI_PASS_THROUGH scsiPass; + + // + // Validiate the user buffer. + // + #if defined (_WIN64) + + if (IoIs32bitProcess(Irp)) { + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32)){ + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + status = STATUS_INVALID_PARAMETER; + goto SetStatusAndReturn; + } + } + else + #endif + { + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(SCSI_PASS_THROUGH)) { + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + status = STATUS_INVALID_PARAMETER; + goto SetStatusAndReturn; + } + } + + IoCopyCurrentIrpStackLocationToNext(Irp); + + nextStack = IoGetNextIrpStackLocation(Irp); + nextStack->MinorFunction = 1; + + ClassReleaseRemoveLock(DeviceObject, Irp); + + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + goto SetStatusAndReturn; + } + + Irp->IoStatus.Information = 0; + + switch (controlCode) { + + case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: { + + PMOUNTDEV_UNIQUE_ID uniqueId; + + if (!commonExtension->MountedDeviceInterfaceName.Buffer) { + status = STATUS_INVALID_PARAMETER; + break; + } + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(MOUNTDEV_UNIQUE_ID)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); + break; + } + + uniqueId = Irp->AssociatedIrp.SystemBuffer; + uniqueId->UniqueIdLength = + commonExtension->MountedDeviceInterfaceName.Length; + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(USHORT) + uniqueId->UniqueIdLength) { + + status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); + break; + } + + RtlCopyMemory(uniqueId->UniqueId, + commonExtension->MountedDeviceInterfaceName.Buffer, + uniqueId->UniqueIdLength); + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(USHORT) + + uniqueId->UniqueIdLength; + break; + } + + case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: { + + PMOUNTDEV_NAME name; + + ASSERT(commonExtension->DeviceName.Buffer); + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(MOUNTDEV_NAME)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); + break; + } + + name = Irp->AssociatedIrp.SystemBuffer; + name->NameLength = commonExtension->DeviceName.Length; + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(USHORT) + name->NameLength) { + + status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); + break; + } + + RtlCopyMemory(name->Name, commonExtension->DeviceName.Buffer, + name->NameLength); + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength; + break; + } + + case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: { + + PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName; + WCHAR driveLetterNameBuffer[10]; + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + PWSTR valueName; + UNICODE_STRING driveLetterName; + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME); + break; + } + + valueName = ExAllocatePoolWithTag( + PagedPool, + commonExtension->DeviceName.Length + sizeof(WCHAR), + '8CcS'); + + if (!valueName) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlCopyMemory(valueName, commonExtension->DeviceName.Buffer, + commonExtension->DeviceName.Length); + valueName[commonExtension->DeviceName.Length/sizeof(WCHAR)] = 0; + + driveLetterName.Buffer = driveLetterNameBuffer; + driveLetterName.MaximumLength = 20; + driveLetterName.Length = 0; + + RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE)); + queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_DIRECT; + queryTable[0].Name = valueName; + queryTable[0].EntryContext = &driveLetterName; + + status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + L"\\Registry\\Machine\\System\\DISK", + queryTable, NULL, NULL); + + if (!NT_SUCCESS(status)) { + ExFreePool(valueName); + break; + } + + if (driveLetterName.Length == 4 && + driveLetterName.Buffer[0] == '%' && + driveLetterName.Buffer[1] == ':') { + + driveLetterName.Buffer[0] = 0xFF; + + } else if (driveLetterName.Length != 4 || + driveLetterName.Buffer[0] < FirstDriveLetter || + driveLetterName.Buffer[0] > LastDriveLetter || + driveLetterName.Buffer[1] != ':') { + + status = STATUS_NOT_FOUND; + ExFreePool(valueName); + break; + } + + suggestedName = Irp->AssociatedIrp.SystemBuffer; + suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE; + suggestedName->NameLength = 28; + + Irp->IoStatus.Information = + FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28; + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + Irp->IoStatus.Information) { + + Irp->IoStatus.Information = + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME); + status = STATUS_BUFFER_OVERFLOW; + ExFreePool(valueName); + break; + } + + RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, + L"\\Registry\\Machine\\System\\DISK", + valueName); + + ExFreePool(valueName); + + RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24); + suggestedName->Name[12] = driveLetterName.Buffer[0]; + suggestedName->Name[13] = ':'; + + // + // NT_SUCCESS(status) based on RtlQueryRegistryValues + // + status = STATUS_SUCCESS; + + break; + } + + default: + status = STATUS_PENDING; + break; + } + + if (status != STATUS_PENDING) { + ClassReleaseRemoveLock(DeviceObject, Irp); + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return status; + } + + if (commonExtension->IsFdo){ + + PULONG_PTR function; + + srb = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SCSI_REQUEST_BLOCK) + + (sizeof(ULONG_PTR) * 2), + '9CcS'); + + if (srb == NULL) { + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + status = STATUS_INSUFFICIENT_RESOURCES; + goto SetStatusAndReturn; + } + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + cdb = (PCDB)srb->Cdb; + + // + // Save the function code and the device object in the memory after + // the SRB. + // + + function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1)); + *function = (ULONG_PTR) DeviceObject; + function++; + *function = (ULONG_PTR) controlCode; + + } else { + srb = NULL; + } + + // + // Change the device type to storage for the switch statement, but only + // if from a legacy device type + // + + if (((controlCode & 0xffff0000) == (IOCTL_DISK_BASE << 16)) || + ((controlCode & 0xffff0000) == (IOCTL_TAPE_BASE << 16)) || + ((controlCode & 0xffff0000) == (IOCTL_CDROM_BASE << 16)) + ) { + + modifiedIoControlCode = (controlCode & ~0xffff0000); + modifiedIoControlCode |= (IOCTL_STORAGE_BASE << 16); + + } else { + + modifiedIoControlCode = controlCode; + + } + + DBGTRACE(ClassDebugTrace, ("> ioctl %xh (%s)", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode))); + + switch (modifiedIoControlCode) { + + case IOCTL_STORAGE_GET_HOTPLUG_INFO: { + + if (srb) { + ExFreePool(srb); + srb = NULL; + } + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(STORAGE_HOTPLUG_INFO)) { + + // + // Indicate unsuccessful status and no data transferred. + // + + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO); + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + status = STATUS_BUFFER_TOO_SMALL; + + } else if(!commonExtension->IsFdo) { + + // + // Just forward this down and return + // + + IoCopyCurrentIrpStackLocationToNext(Irp); + + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + } else { + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PSTORAGE_HOTPLUG_INFO info; + + fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension; + info = Irp->AssociatedIrp.SystemBuffer; + + *info = fdoExtension->PrivateFdoData->HotplugInfo; + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO); + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + status = STATUS_SUCCESS; + + } + break; + } + + case IOCTL_STORAGE_SET_HOTPLUG_INFO: { + + if (srb) + { + ExFreePool(srb); + srb = NULL; + } + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(STORAGE_HOTPLUG_INFO)) { + + // + // Indicate unsuccessful status and no data transferred. + // + + Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH; + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + status = STATUS_INFO_LENGTH_MISMATCH; + goto SetStatusAndReturn; + + } + + if(!commonExtension->IsFdo) { + + // + // Just forward this down and return + // + + IoCopyCurrentIrpStackLocationToNext(Irp); + + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + } else { + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension; + PSTORAGE_HOTPLUG_INFO info = Irp->AssociatedIrp.SystemBuffer; + + status = STATUS_SUCCESS; + + if (info->Size != fdoExtension->PrivateFdoData->HotplugInfo.Size) + { + status = STATUS_INVALID_PARAMETER_1; + } + + if (info->MediaRemovable != fdoExtension->PrivateFdoData->HotplugInfo.MediaRemovable) + { + status = STATUS_INVALID_PARAMETER_2; + } + + if (info->MediaHotplug != fdoExtension->PrivateFdoData->HotplugInfo.MediaHotplug) + { + status = STATUS_INVALID_PARAMETER_3; + } + + if (info->WriteCacheEnableOverride != fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride) + { + status = STATUS_INVALID_PARAMETER_5; + } + + if (NT_SUCCESS(status)) + { + fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug; + + // + // Store the user-defined override in the registry + // + + ClassSetDeviceParameter(fdoExtension, + CLASSP_REG_SUBKEY_NAME, + CLASSP_REG_REMOVAL_POLICY_VALUE_NAME, + (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval); + } + + Irp->IoStatus.Status = status; + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + } + + break; + } + + case IOCTL_STORAGE_CHECK_VERIFY: + case IOCTL_STORAGE_CHECK_VERIFY2: { + + PIRP irp2 = NULL; + PIO_STACK_LOCATION newStack; + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL; + + DebugPrint((1,"DeviceIoControl: Check verify\n")); + + // + // If a buffer for a media change count was provided, make sure it's + // big enough to hold the result + // + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) { + + // + // If the buffer is too small to hold the media change count + // then return an error to the caller + // + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(ULONG)) { + + DebugPrint((3,"DeviceIoControl: media count " + "buffer too small\n")); + + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(ULONG); + + if(srb != NULL) { + ExFreePool(srb); + } + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + status = STATUS_BUFFER_TOO_SMALL; + goto SetStatusAndReturn; + + } + } + + if(!commonExtension->IsFdo) { + + // + // If this is a PDO then we should just forward the request down + // + ASSERT(!srb); + + IoCopyCurrentIrpStackLocationToNext(Irp); + + ClassReleaseRemoveLock(DeviceObject, Irp); + + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + goto SetStatusAndReturn; + + } else { + + fdoExtension = DeviceObject->DeviceExtension; + + } + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) { + + // + // The caller has provided a valid buffer. Allocate an additional + // irp and stick the CheckVerify completion routine on it. We will + // then send this down to the port driver instead of the irp the + // caller sent in + // + + DebugPrint((2,"DeviceIoControl: Check verify wants " + "media count\n")); + + // + // Allocate a new irp to send the TestUnitReady to the port driver + // + + irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE); + + if(irp2 == NULL) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + ASSERT(srb); + ExFreePool(srb); + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + status = STATUS_INSUFFICIENT_RESOURCES; + goto SetStatusAndReturn; + + break; + } + + // + // Make sure to acquire the lock for the new irp. + // + + ClassAcquireRemoveLock(DeviceObject, irp2); + + irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread; + IoSetNextIrpStackLocation(irp2); + + // + // Set the top stack location and shove the master Irp into the + // top location + // + + newStack = IoGetCurrentIrpStackLocation(irp2); + newStack->Parameters.Others.Argument1 = Irp; + newStack->DeviceObject = DeviceObject; + + // + // Stick the check verify completion routine onto the stack + // and prepare the irp for the port driver + // + + IoSetCompletionRoutine(irp2, + ClassCheckVerifyComplete, + NULL, + TRUE, + TRUE, + TRUE); + + IoSetNextIrpStackLocation(irp2); + newStack = IoGetCurrentIrpStackLocation(irp2); + newStack->DeviceObject = DeviceObject; + newStack->MajorFunction = irpStack->MajorFunction; + newStack->MinorFunction = irpStack->MinorFunction; + + // + // Mark the master irp as pending - whether the lower level + // driver completes it immediately or not this should allow it + // to go all the way back up. + // + + IoMarkIrpPending(Irp); + + Irp = irp2; + + } + + // + // Test Unit Ready + // + + srb->CdbLength = 6; + cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; + + // + // Set timeout value. + // + + srb->TimeOutValue = fdoExtension->TimeOutValue; + + // + // If this was a CV2 then mark the request as low-priority so we don't + // spin up the drive just to satisfy it. + // + + if(controlCode == IOCTL_STORAGE_CHECK_VERIFY2) { + SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY); + } + + // + // Since this routine will always hand the request to the + // port driver if there isn't a data transfer to be done + // we don't have to worry about completing the request here + // on an error + // + + // + // This routine uses a completion routine so we don't want to release + // the remove lock until then. + // + + status = ClassSendSrbAsynchronous(DeviceObject, + srb, + Irp, + NULL, + 0, + FALSE); + + break; + } + + case IOCTL_STORAGE_MEDIA_REMOVAL: + case IOCTL_STORAGE_EJECTION_CONTROL: { + + PPREVENT_MEDIA_REMOVAL mediaRemoval = Irp->AssociatedIrp.SystemBuffer; + + DebugPrint((3, "DiskIoControl: ejection control\n")); + + if(srb) { + ExFreePool(srb); + } + + if(irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(PREVENT_MEDIA_REMOVAL)) { + + // + // Indicate unsuccessful status and no data transferred. + // + + Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH; + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + status = STATUS_INFO_LENGTH_MISMATCH; + goto SetStatusAndReturn; + } + + if(!commonExtension->IsFdo) { + + // + // Just forward this down and return + // + + IoCopyCurrentIrpStackLocationToNext(Irp); + + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + } + else { + + // i don't believe this assertion is valid. this is a request + // from user-mode, so they could request this for any device + // they want? also, we handle it properly. + // ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)); + status = ClasspEjectionControl( + DeviceObject, + Irp, + ((modifiedIoControlCode == + IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock : + SimpleMediaLock), + mediaRemoval->PreventMediaRemoval); + + Irp->IoStatus.Status = status; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + } + + break; + } + + case IOCTL_STORAGE_MCN_CONTROL: { + + DebugPrint((3, "DiskIoControl: MCN control\n")); + + if(irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(PREVENT_MEDIA_REMOVAL)) { + + // + // Indicate unsuccessful status and no data transferred. + // + + Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH; + Irp->IoStatus.Information = 0; + + if(srb) { + ExFreePool(srb); + } + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + status = STATUS_INFO_LENGTH_MISMATCH; + goto SetStatusAndReturn; + } + + if(!commonExtension->IsFdo) { + + // + // Just forward this down and return + // + + if(srb) { + ExFreePool(srb); + } + + IoCopyCurrentIrpStackLocationToNext(Irp); + + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + } else { + + // + // Call to the FDO - handle the ejection control. + // + + status = ClasspMcnControl(DeviceObject->DeviceExtension, + Irp, + srb); + } + goto SetStatusAndReturn; + } + + case IOCTL_STORAGE_RESERVE: + case IOCTL_STORAGE_RELEASE: { + + // + // Reserve logical unit. + // + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL; + + if(!commonExtension->IsFdo) { + + IoCopyCurrentIrpStackLocationToNext(Irp); + + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + goto SetStatusAndReturn; + } else { + fdoExtension = DeviceObject->DeviceExtension; + } + + srb->CdbLength = 6; + + if(modifiedIoControlCode == IOCTL_STORAGE_RESERVE) { + cdb->CDB6GENERIC.OperationCode = SCSIOP_RESERVE_UNIT; + } else { + cdb->CDB6GENERIC.OperationCode = SCSIOP_RELEASE_UNIT; + } + + // + // Set timeout value. + // + + srb->TimeOutValue = fdoExtension->TimeOutValue; + + status = ClassSendSrbAsynchronous(DeviceObject, + srb, + Irp, + NULL, + 0, + FALSE); + + break; + } + + case IOCTL_STORAGE_EJECT_MEDIA: + case IOCTL_STORAGE_LOAD_MEDIA: + case IOCTL_STORAGE_LOAD_MEDIA2:{ + + // + // Eject media. + // + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL; + + if(!commonExtension->IsFdo) { + + IoCopyCurrentIrpStackLocationToNext(Irp); + + ClassReleaseRemoveLock(DeviceObject, Irp); + + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + goto SetStatusAndReturn; + } else { + fdoExtension = DeviceObject->DeviceExtension; + } + + if(commonExtension->PagingPathCount != 0) { + + DebugPrint((1, "ClassDeviceControl: call to eject paging device - " + "failure\n")); + + status = STATUS_FILES_OPEN; + Irp->IoStatus.Status = status; + + Irp->IoStatus.Information = 0; + + if(srb) { + ExFreePool(srb); + } + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + goto SetStatusAndReturn; + } + + // + // Synchronize with ejection control and ejection cleanup code as + // well as other eject/load requests. + // + + KeEnterCriticalRegion(); + KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent), + UserRequest, + UserMode, + FALSE, + NULL); + + if(fdoExtension->ProtectedLockCount != 0) { + + DebugPrint((1, "ClassDeviceControl: call to eject protected locked " + "device - failure\n")); + + status = STATUS_DEVICE_BUSY; + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + if(srb) { + ExFreePool(srb); + } + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + KeSetEvent(&fdoExtension->EjectSynchronizationEvent, + IO_NO_INCREMENT, + FALSE); + KeLeaveCriticalRegion(); + + goto SetStatusAndReturn; + } + + srb->CdbLength = 6; + + cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; + cdb->START_STOP.LoadEject = 1; + + if(modifiedIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) { + cdb->START_STOP.Start = 0; + } else { + cdb->START_STOP.Start = 1; + } + + // + // Set timeout value. + // + + srb->TimeOutValue = fdoExtension->TimeOutValue; + status = ClassSendSrbAsynchronous(DeviceObject, + srb, + Irp, + NULL, + 0, + FALSE); + + KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE); + KeLeaveCriticalRegion(); + + break; + } + + case IOCTL_STORAGE_FIND_NEW_DEVICES: { + + if(srb) { + ExFreePool(srb); + } + + if(commonExtension->IsFdo) { + + IoInvalidateDeviceRelations( + ((PFUNCTIONAL_DEVICE_EXTENSION) commonExtension)->LowerPdo, + BusRelations); + + status = STATUS_SUCCESS; + Irp->IoStatus.Status = status; + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + } + else { + + IoCopyCurrentIrpStackLocationToNext(Irp); + + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + } + break; + } + + case IOCTL_STORAGE_GET_DEVICE_NUMBER: { + + if(srb) { + ExFreePool(srb); + } + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(STORAGE_DEVICE_NUMBER)) { + + PSTORAGE_DEVICE_NUMBER deviceNumber = + Irp->AssociatedIrp.SystemBuffer; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = + commonExtension->PartitionZeroExtension; + + deviceNumber->DeviceType = fdoExtension->CommonExtension.DeviceObject->DeviceType; + deviceNumber->DeviceNumber = fdoExtension->DeviceNumber; + deviceNumber->PartitionNumber = commonExtension->PartitionNumber; + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER); + + } else { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER); + } + + Irp->IoStatus.Status = status; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + break; + } + + default: { + + DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n", + controlCode, DeviceObject)); + + // + // Pass the device control to the next driver. + // + + if(srb) { + ExFreePool(srb); + } + + // + // Copy the Irp stack parameters to the next stack location. + // + + IoCopyCurrentIrpStackLocationToNext(Irp); + + ClassReleaseRemoveLock(DeviceObject, Irp); + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + break; + } + + } // end switch( ... + +SetStatusAndReturn: + + DBGTRACE(ClassDebugTrace, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode), status)); + + return status; +} // end ClassDeviceControl() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassShutdownFlush() + +Routine Description: + + This routine is called for a shutdown and flush IRPs. These are sent by the + system before it actually shuts down or when the file system does a flush. + If it exists, the device-specific driver's routine will be invoked. If there + wasn't one specified, the Irp will be completed with an Invalid device request. + +Arguments: + + DriverObject - Pointer to device object to being shutdown by system. + + Irp - IRP involved. + +Return Value: + + NT Status + +--*/ +NTSTATUS +ClassShutdownFlush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + ULONG isRemoved; + + NTSTATUS status; + + isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp); + + if(isRemoved) { + + ClassReleaseRemoveLock(DeviceObject, Irp); + + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + if (commonExtension->DevInfo->ClassShutdownFlush) { + + // + // Call the device-specific driver's routine. + // + + return commonExtension->DevInfo->ClassShutdownFlush(DeviceObject, Irp); + } + + // + // Device-specific driver doesn't support this. + // + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + return STATUS_INVALID_DEVICE_REQUEST; +} // end ClassShutdownFlush() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassCreateDeviceObject() + +Routine Description: + + This routine creates an object for the physical device specified and + sets up the deviceExtension's function pointers for each entry point + in the device-specific driver. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + ObjectNameBuffer - Dir. name of the object to create. + + LowerDeviceObject - Pointer to the lower device object + + IsFdo - should this be an fdo or a pdo + + DeviceObject - Pointer to the device object pointer we will return. + +Return Value: + + NTSTATUS + +--*/ +NTSTATUS +ClassCreateDeviceObject( + IN PDRIVER_OBJECT DriverObject, + IN PCCHAR ObjectNameBuffer, + IN PDEVICE_OBJECT LowerDevice, + IN BOOLEAN IsFdo, + IN OUT PDEVICE_OBJECT *DeviceObject + ) +{ + BOOLEAN isPartitionable; + STRING ntNameString; + UNICODE_STRING ntUnicodeString; + NTSTATUS status, status2; + PDEVICE_OBJECT deviceObject = NULL; + + ULONG characteristics; + + PCLASS_DRIVER_EXTENSION + driverExtension = IoGetDriverObjectExtension(DriverObject, + CLASS_DRIVER_EXTENSION_KEY); + + PCLASS_DEV_INFO devInfo; + + PAGED_CODE(); + + *DeviceObject = NULL; + RtlInitUnicodeString(&ntUnicodeString, NULL); + + DebugPrint((2, "ClassCreateFdo: Create device object\n")); + + ASSERT(LowerDevice); + + // + // Make sure that if we're making PDO's we have an enumeration routine + // + + isPartitionable = (driverExtension->InitData.ClassEnumerateDevice != NULL); + + ASSERT(IsFdo || isPartitionable); + + // + // Grab the correct dev-info structure out of the init data + // + + if(IsFdo) { + devInfo = &(driverExtension->InitData.FdoData); + } else { + devInfo = &(driverExtension->InitData.PdoData); + } + + characteristics = devInfo->DeviceCharacteristics; + + if(ARGUMENT_PRESENT(ObjectNameBuffer)) { + DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer)); + + RtlInitString(&ntNameString, ObjectNameBuffer); + + status = RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE); + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, + "ClassCreateFdo: Cannot convert string %s\n", + ObjectNameBuffer)); + + ntUnicodeString.Buffer = NULL; + return status; + } + } else { + DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n")); + + if(IsFdo == FALSE) { + + // + // PDO's have to have some sort of name. + // + + SET_FLAG(characteristics, FILE_AUTOGENERATED_DEVICE_NAME); + } + + RtlInitUnicodeString(&ntUnicodeString, NULL); + } + + status = IoCreateDevice(DriverObject, + devInfo->DeviceExtensionSize, + &ntUnicodeString, + devInfo->DeviceType, + devInfo->DeviceCharacteristics, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n", + status)); + ASSERT(deviceObject == NULL); + + // + // buffer is not used any longer here. + // + + if (ntUnicodeString.Buffer != NULL) { + DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n")); + ExFreePool(ntUnicodeString.Buffer); + RtlInitUnicodeString(&ntUnicodeString, NULL); + } + + } else { + + PCOMMON_DEVICE_EXTENSION commonExtension = deviceObject->DeviceExtension; + + RtlZeroMemory( + deviceObject->DeviceExtension, + devInfo->DeviceExtensionSize); + + // + // Setup version code + // + + commonExtension->Version = 0x03; + + // + // Setup the remove lock and event + // + + commonExtension->IsRemoved = NO_REMOVE; + commonExtension->RemoveLock = 0; + KeInitializeEvent(&commonExtension->RemoveEvent, + SynchronizationEvent, + FALSE); + + #if DBG + KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock); + commonExtension->RemoveTrackingList = NULL; + #else + commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1; + commonExtension->RemoveTrackingList = (PVOID) -1; + #endif + + // + // Acquire the lock once. This reference will be released when the + // remove IRP has been received. + // + + ClassAcquireRemoveLock(deviceObject, (PIRP) deviceObject); + + // + // Store a pointer to the driver extension so we don't have to do + // lookups to get it. + // + + commonExtension->DriverExtension = driverExtension; + + // + // Fill in entry points + // + + commonExtension->DevInfo = devInfo; + + // + // Initialize some of the common values in the structure + // + + commonExtension->DeviceObject = deviceObject; + + commonExtension->LowerDeviceObject = NULL; + + if(IsFdo) { + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PVOID) commonExtension; + + commonExtension->PartitionZeroExtension = deviceObject->DeviceExtension; + + // + // Set the initial device object flags. + // + + SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE); + + // + // Clear the PDO list + // + + commonExtension->ChildList = NULL; + + commonExtension->DriverData = + ((PFUNCTIONAL_DEVICE_EXTENSION) deviceObject->DeviceExtension + 1); + + if(isPartitionable) { + + commonExtension->PartitionNumber = 0; + } else { + commonExtension->PartitionNumber = (ULONG) (-1L); + } + + fdoExtension->DevicePowerState = PowerDeviceD0; + + KeInitializeEvent(&fdoExtension->EjectSynchronizationEvent, + SynchronizationEvent, + TRUE); + + KeInitializeEvent(&fdoExtension->ChildLock, + SynchronizationEvent, + TRUE); + + status = ClasspAllocateReleaseRequest(deviceObject); + + if(!NT_SUCCESS(status)) { + IoDeleteDevice(deviceObject); + *DeviceObject = NULL; + + if (ntUnicodeString.Buffer != NULL) { + DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n")); + ExFreePool(ntUnicodeString.Buffer); + RtlInitUnicodeString(&ntUnicodeString, NULL); + } + + return status; + } + + } else { + + PPHYSICAL_DEVICE_EXTENSION pdoExtension = + deviceObject->DeviceExtension; + + PFUNCTIONAL_DEVICE_EXTENSION p0Extension = + LowerDevice->DeviceExtension; + + SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE); + + commonExtension->PartitionZeroExtension = p0Extension; + + // + // Stick this onto the PDO list + // + + ClassAddChild(p0Extension, pdoExtension, TRUE); + + commonExtension->DriverData = (PVOID) (pdoExtension + 1); + + // + // Get the top of stack for the lower device - this allows + // filters to get stuck in between the partitions and the + // physical disk. + // + + commonExtension->LowerDeviceObject = + IoGetAttachedDeviceReference(LowerDevice); + + // + // Pnp will keep a reference to the lower device object long + // after this partition has been deleted. Dereference now so + // we don't have to deal with it later. + // + + ObDereferenceObject(commonExtension->LowerDeviceObject); + } + + KeInitializeEvent(&commonExtension->PathCountEvent, SynchronizationEvent, TRUE); + + commonExtension->IsFdo = IsFdo; + + commonExtension->DeviceName = ntUnicodeString; + + commonExtension->PreviousState = 0xff; + + InitializeDictionary(&(commonExtension->FileObjectDictionary)); + + commonExtension->CurrentState = IRP_MN_STOP_DEVICE; + } + + *DeviceObject = deviceObject; + + return status; +} // end ClassCreateDeviceObject() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassClaimDevice() + +Routine Description: + + This function claims a device in the port driver. The port driver object + is updated with the correct driver object if the device is successfully + claimed. + +Arguments: + + LowerDeviceObject - Supplies the base port device object. + + Release - Indicates the logical unit should be released rather than claimed. + +Return Value: + + Returns a status indicating success or failure of the operation. + +--*/ +NTSTATUS +ClassClaimDevice( + IN PDEVICE_OBJECT LowerDeviceObject, + IN BOOLEAN Release + ) +{ + IO_STATUS_BLOCK ioStatus; + PIRP irp; + PIO_STACK_LOCATION irpStack; + KEVENT event; + NTSTATUS status; + SCSI_REQUEST_BLOCK srb; + + PAGED_CODE(); + + // + // Clear the SRB fields. + // + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Write length to SRB. + // + + srb.Length = sizeof(SCSI_REQUEST_BLOCK); + + srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE : + SRB_FUNCTION_CLAIM_DEVICE; + + // + // Set the event object to the unsignaled state. + // It will be used to signal request completion + // + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + // + // Build synchronous request with no transfer. + // + + irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE, + LowerDeviceObject, + NULL, + 0, + NULL, + 0, + TRUE, + &event, + &ioStatus); + + if (irp == NULL) { + DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + irpStack = IoGetNextIrpStackLocation(irp); + + // + // Save SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = &srb; + + // + // Set up IRP Address. + // + + srb.OriginalRequest = irp; + + // + // Call the port driver with the request and wait for it to complete. + // + + status = IoCallDriver(LowerDeviceObject, irp); + if (status == STATUS_PENDING) { + + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + // + // If this is a release request, then just decrement the reference count + // and return. The status does not matter. + // + + if (Release) { + + // ObDereferenceObject(LowerDeviceObject); + return STATUS_SUCCESS; + } + + if (!NT_SUCCESS(status)) { + return status; + } + + ASSERT(srb.DataBuffer != NULL); + ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)); + + return status; +} // end ClassClaimDevice() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassInternalIoControl() + +Routine Description: + + This routine passes internal device controls to the port driver. + Internal device controls are used by higher level drivers both for ioctls + and to pass through scsi requests. + + If the IoControlCode does not match any of the handled ioctls and is + a valid system address then the request will be treated as an SRB and + passed down to the lower driver. If the IoControlCode is not a valid + system address the ioctl will be failed. + + Callers must therefore be extremely cautious to pass correct, initialized + values to this function. + +Arguments: + + DeviceObject - Supplies a pointer to the device object for this request. + + Irp - Supplies the Irp making the request. + +Return Value: + + Returns back a STATUS_PENDING or a completion status. + +--*/ +NTSTATUS +ClassInternalIoControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp); + + ULONG isRemoved; + + PSCSI_REQUEST_BLOCK srb; + + isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp); + + if(isRemoved) { + + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + + ClassReleaseRemoveLock(DeviceObject, Irp); + + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + // + // Get a pointer to the SRB. + // + + srb = irpStack->Parameters.Scsi.Srb; + + // + // Set the parameters in the next stack location. + // + + if(commonExtension->IsFdo) { + nextStack->Parameters.Scsi.Srb = srb; + nextStack->MajorFunction = IRP_MJ_SCSI; + nextStack->MinorFunction = IRP_MN_SCSI_CLASS; + + } else { + + IoCopyCurrentIrpStackLocationToNext(Irp); + } + + ClassReleaseRemoveLock(DeviceObject, Irp); + + return IoCallDriver(commonExtension->LowerDeviceObject, Irp); +} // end ClassInternalIoControl() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassQueryTimeOutRegistryValue() + +Routine Description: + + This routine determines whether a reg key for a user-specified timeout + value exists. This should be called at initialization time. + +Arguments: + + DeviceObject - Pointer to the device object we are retrieving the timeout + value for + +Return Value: + + None, but it sets a new default timeout for a class of devices. + +--*/ +ULONG +ClassQueryTimeOutRegistryValue( + IN PDEVICE_OBJECT DeviceObject + ) +{ + // + // Find the appropriate reg. key + // + + PCLASS_DRIVER_EXTENSION + driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, + CLASS_DRIVER_EXTENSION_KEY); + + PUNICODE_STRING registryPath = &(driverExtension->RegistryPath); + + PRTL_QUERY_REGISTRY_TABLE parameters = NULL; + PWSTR path; + NTSTATUS status; + LONG timeOut = 0; + ULONG zero = 0; + ULONG size; + + PAGED_CODE(); + + if (!registryPath) { + return 0; + } + + parameters = ExAllocatePoolWithTag(NonPagedPool, + sizeof(RTL_QUERY_REGISTRY_TABLE)*2, + '1BcS'); + + if (!parameters) { + return 0; + } + + size = registryPath->MaximumLength + sizeof(WCHAR); + path = ExAllocatePoolWithTag(NonPagedPool, size, '2BcS'); + + if (!path) { + ExFreePool(parameters); + return 0; + } + + RtlZeroMemory(path,size); + RtlCopyMemory(path, registryPath->Buffer, size - sizeof(WCHAR)); + + + // + // Check for the Timeout value. + // + + RtlZeroMemory(parameters, + (sizeof(RTL_QUERY_REGISTRY_TABLE)*2)); + + parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + parameters[0].Name = L"TimeOutValue"; + parameters[0].EntryContext = &timeOut; + parameters[0].DefaultType = REG_DWORD; + parameters[0].DefaultData = &zero; + parameters[0].DefaultLength = sizeof(ULONG); + + status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, + path, + parameters, + NULL, + NULL); + + if (!(NT_SUCCESS(status))) { + timeOut = 0; + } + + ExFreePool(parameters); + ExFreePool(path); + + DebugPrint((2, + "ClassQueryTimeOutRegistryValue: Timeout value %d\n", + timeOut)); + + + return timeOut; + +} // end ClassQueryTimeOutRegistryValue() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?! + +Routine Description: + + This routine executes when the port driver has completed a check verify + ioctl. It will set the status of the master Irp, copy the media change + count and complete the request. + +Arguments: + + Fdo - Supplies the functional device object which represents the logical unit. + + Irp - Supplies the Irp which has completed. + + Context - NULL + +Return Value: + + NT status + +--*/ +NTSTATUS +ClassCheckVerifyComplete( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp, + IN PVOID Context + ) +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PIRP originalIrp; + + ASSERT_FDO(Fdo); + + originalIrp = irpStack->Parameters.Others.Argument1; + + // + // Copy the media change count and status + // + + *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) = + fdoExtension->MediaChangeCount; + + DebugPrint((2, "ClassCheckVerifyComplete - Media change count for" + "device %d is %lx - saved as %lx\n", + fdoExtension->DeviceNumber, + fdoExtension->MediaChangeCount, + *((PULONG) originalIrp->AssociatedIrp.SystemBuffer))); + + originalIrp->IoStatus.Status = Irp->IoStatus.Status; + originalIrp->IoStatus.Information = sizeof(ULONG); + + ClassReleaseRemoveLock(Fdo, originalIrp); + ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT); + + IoFreeIrp(Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // end ClassCheckVerifyComplete() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassGetDescriptor() + +Routine Description: + + This routine will perform a query for the specified property id and will + allocate a non-paged buffer to store the data in. It is the responsibility + of the caller to ensure that this buffer is freed. + + This routine must be run at IRQL_PASSIVE_LEVEL + +Arguments: + + DeviceObject - the device to query + DeviceInfo - a location to store a pointer to the buffer we allocate + +Return Value: + + status + if status is unsuccessful *DeviceInfo will be set to NULL, else the + buffer allocated on behalf of the caller. + +--*/ +NTSTATUS +ClassGetDescriptor( + IN PDEVICE_OBJECT DeviceObject, + IN PSTORAGE_PROPERTY_ID PropertyId, + OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor + ) +{ + STORAGE_PROPERTY_QUERY query; + IO_STATUS_BLOCK ioStatus; + + PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL; + ULONG length; + + UCHAR pass = 0; + + PAGED_CODE(); + + // + // Set the passed-in descriptor pointer to NULL as default + // + + *Descriptor = NULL; + + + RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY)); + query.PropertyId = *PropertyId; + query.QueryType = PropertyStandardQuery; + + // + // On the first pass we just want to get the first few + // bytes of the descriptor so we can read it's size + // + + descriptor = (PVOID)&query; + + ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2)); + + ClassSendDeviceIoControlSynchronous( + IOCTL_STORAGE_QUERY_PROPERTY, + DeviceObject, + &query, + sizeof(STORAGE_PROPERTY_QUERY), + sizeof(ULONG) * 2, + FALSE, + &ioStatus + ); + + if(!NT_SUCCESS(ioStatus.Status)) { + + DebugPrint((1, "ClassGetDescriptor: error %lx trying to " + "query properties #1\n", ioStatus.Status)); + return ioStatus.Status; + } + + if (descriptor->Size == 0) { + + // + // This DebugPrint is to help third-party driver writers + // + + DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status " + "%x\n", ioStatus.Status)); + return STATUS_UNSUCCESSFUL; + + } + + // + // This time we know how much data there is so we can + // allocate a buffer of the correct size + // + + length = descriptor->Size; + + descriptor = ExAllocatePoolWithTag(NonPagedPool, length, '4BcS'); + + if(descriptor == NULL) { + + DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor " + "(%d bytes)\n", length)); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // setup the query again, as it was overwritten above + // + + RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY)); + query.PropertyId = *PropertyId; + query.QueryType = PropertyStandardQuery; + + // + // copy the input to the new outputbuffer + // + + RtlCopyMemory(descriptor, + &query, + sizeof(STORAGE_PROPERTY_QUERY) + ); + + ClassSendDeviceIoControlSynchronous( + IOCTL_STORAGE_QUERY_PROPERTY, + DeviceObject, + descriptor, + sizeof(STORAGE_PROPERTY_QUERY), + length, + FALSE, + &ioStatus + ); + + if(!NT_SUCCESS(ioStatus.Status)) { + + DebugPrint((1, "ClassGetDescriptor: error %lx trying to " + "query properties #1\n", ioStatus.Status)); + ExFreePool(descriptor); + return ioStatus.Status; + } + + // + // return the memory we've allocated to the caller + // + + *Descriptor = descriptor; + return ioStatus.Status; +} // end ClassGetDescriptor() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassSignalCompletion() + +Routine Description: + + This completion routine will signal the event given as context and then + return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is + the responsibility of the routine waiting on the event to complete the + request and free the event. + +Arguments: + + DeviceObject - a pointer to the device object + + Irp - a pointer to the irp + + Event - a pointer to the event to signal + +Return Value: + + STATUS_MORE_PROCESSING_REQUIRED + +--*/ +NTSTATUS +ClassSignalCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PKEVENT Event + ) +{ + KeSetEvent(Event, IO_NO_INCREMENT, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} // end ClassSignalCompletion() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassPnpQueryFdoRelations() + +Routine Description: + + This routine will call the driver's enumeration routine to update the + list of PDO's. It will then build a response to the + IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in + the irp. + +Arguments: + + Fdo - a pointer to the functional device object we are enumerating + + Irp - a pointer to the enumeration request + +Return Value: + + status + +--*/ +NTSTATUS +ClassPnpQueryFdoRelations( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCLASS_DRIVER_EXTENSION + driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject, + CLASS_DRIVER_EXTENSION_KEY); + NTSTATUS status; + + PAGED_CODE(); + + // + // If there's already an enumeration in progress then don't start another + // one. + // + + if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) { + status = driverExtension->InitData.ClassEnumerateDevice(Fdo); + } + + Irp->IoStatus.Information = (ULONG_PTR) NULL; + + Irp->IoStatus.Status = ClassRetrieveDeviceRelations( + Fdo, + BusRelations, + (PDEVICE_RELATIONS)&Irp->IoStatus.Information); + InterlockedDecrement(&(fdoExtension->EnumerationInterlock)); + + return Irp->IoStatus.Status; +} // end ClassPnpQueryFdoRelations() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassMarkChildrenMissing() + +Routine Description: + + This routine will call ClassMarkChildMissing() for all children. + It acquires the ChildLock before calling ClassMarkChildMissing(). + +Arguments: + + Fdo - the "bus's" device object, such as the disk FDO for non-removable + disks with multiple partitions. + +Return Value: + + None + +--*/ +VOID +ClassMarkChildrenMissing( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = &(Fdo->CommonExtension); + PPHYSICAL_DEVICE_EXTENSION nextChild = commonExtension->ChildList; + + PAGED_CODE(); + + ClassAcquireChildLock(Fdo); + + while (nextChild){ + PPHYSICAL_DEVICE_EXTENSION tmpChild; + + /* + * ClassMarkChildMissing will also dequeue the child extension. + * So get the next pointer before calling ClassMarkChildMissing. + */ + tmpChild = nextChild; + nextChild = tmpChild->CommonExtension.ChildList; + ClassMarkChildMissing(tmpChild, FALSE); + } + ClassReleaseChildLock(Fdo); + return; +} // end ClassMarkChildrenMissing() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassMarkChildMissing() + +Routine Description: + + This routine will make an active child "missing." If the device has never + been enumerated then it will be deleted on the spot. If the device has + not been enumerated then it will be marked as missing so that we can + not report it in the next device enumeration. + +Arguments: + + Child - the child device to be marked as missing. + + AcquireChildLock - TRUE if the child lock should be acquired before removing + the missing child. FALSE if the child lock is already + acquired by this thread. + +Return Value: + + returns whether or not the child device object has previously been reported + to PNP. + +--*/ +BOOLEAN +ClassMarkChildMissing( + IN PPHYSICAL_DEVICE_EXTENSION Child, + IN BOOLEAN AcquireChildLock + ) +{ + BOOLEAN returnValue = Child->IsEnumerated; + + PAGED_CODE(); + ASSERT_PDO(Child->DeviceObject); + + Child->IsMissing = TRUE; + + // + // Make sure this child is not in the active list. + // + + ClassRemoveChild(Child->CommonExtension.PartitionZeroExtension, + Child, + AcquireChildLock); + + if(Child->IsEnumerated == FALSE) { + ClassRemoveDevice(Child->DeviceObject, IRP_MN_REMOVE_DEVICE); + } + + return returnValue; +} // end ClassMarkChildMissing() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassRetrieveDeviceRelations() + +Routine Description: + + This routine will allocate a buffer to hold the specified list of + relations. It will then fill in the list with referenced device pointers + and will return the request. + +Arguments: + + Fdo - pointer to the FDO being queried + + RelationType - what type of relations are being queried + + DeviceRelations - a location to store a pointer to the response + +Return Value: + + status + +--*/ +NTSTATUS +ClassRetrieveDeviceRelations( + IN PDEVICE_OBJECT Fdo, + IN DEVICE_RELATION_TYPE RelationType, + OUT PDEVICE_RELATIONS *DeviceRelations + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + ULONG count = 0; + ULONG i; + + PPHYSICAL_DEVICE_EXTENSION nextChild; + + ULONG relationsSize; + PDEVICE_RELATIONS deviceRelations = NULL; + + NTSTATUS status; + + PAGED_CODE(); + + ClassAcquireChildLock(fdoExtension); + + nextChild = fdoExtension->CommonExtension.ChildList; + + // + // Count the number of PDO's attached to this disk + // + + while(nextChild != NULL) { + PCOMMON_DEVICE_EXTENSION commonExtension; + + commonExtension = &(nextChild->CommonExtension); + + ASSERTMSG("ClassPnp internal error: missing child on active list\n", + (nextChild->IsMissing == FALSE)); + + nextChild = commonExtension->ChildList; + + count++; + }; + + relationsSize = (sizeof(DEVICE_RELATIONS) + + (count * sizeof(PDEVICE_OBJECT))); + + deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS'); + + if(deviceRelations == NULL) { + + DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate " + "%d bytes for device relations\n", relationsSize)); + + ClassReleaseChildLock(fdoExtension); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(deviceRelations, relationsSize); + + nextChild = fdoExtension->CommonExtension.ChildList; + i = count - 1; + + while(nextChild != NULL) { + PCOMMON_DEVICE_EXTENSION commonExtension; + + commonExtension = &(nextChild->CommonExtension); + + ASSERTMSG("ClassPnp internal error: missing child on active list\n", + (nextChild->IsMissing == FALSE)); + + deviceRelations->Objects[i--] = nextChild->DeviceObject; + + status = ObReferenceObjectByPointer( + nextChild->DeviceObject, + 0, + NULL, + KernelMode); + ASSERT(NT_SUCCESS(status)); + + nextChild->IsEnumerated = TRUE; + nextChild = commonExtension->ChildList; + } + + ASSERTMSG("Child list has changed: ", i == -1); + + deviceRelations->Count = count; + *DeviceRelations = deviceRelations; + ClassReleaseChildLock(fdoExtension); + return STATUS_SUCCESS; +} // end ClassRetrieveDeviceRelations() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassGetPdoId() + +Routine Description: + + This routine will call into the driver to retrieve a copy of one of it's + id strings. + +Arguments: + + Pdo - a pointer to the pdo being queried + + IdType - which type of id string is being queried + + IdString - an allocated unicode string structure which the driver + can fill in. + +Return Value: + + status + +--*/ +NTSTATUS +ClassGetPdoId( + IN PDEVICE_OBJECT Pdo, + IN BUS_QUERY_ID_TYPE IdType, + IN PUNICODE_STRING IdString + ) +{ + PCLASS_DRIVER_EXTENSION + driverExtension = IoGetDriverObjectExtension(Pdo->DriverObject, + CLASS_DRIVER_EXTENSION_KEY); + + ASSERT_PDO(Pdo); + ASSERT(driverExtension->InitData.ClassQueryId); + + PAGED_CODE(); + + return driverExtension->InitData.ClassQueryId( Pdo, IdType, IdString); +} // end ClassGetPdoId() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassQueryPnpCapabilities() + +Routine Description: + + This routine will call into the class driver to retrieve it's pnp + capabilities. + +Arguments: + + PhysicalDeviceObject - The physical device object to retrieve properties + for. + +Return Value: + + status + +--*/ +NTSTATUS +ClassQueryPnpCapabilities( + IN PDEVICE_OBJECT DeviceObject, + IN PDEVICE_CAPABILITIES Capabilities + ) +{ + PCLASS_DRIVER_EXTENSION driverExtension = + ClassGetDriverExtension(DeviceObject->DriverObject); + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PCLASS_QUERY_PNP_CAPABILITIES queryRoutine = NULL; + + PAGED_CODE(); + + ASSERT(DeviceObject); + ASSERT(Capabilities); + + if(commonExtension->IsFdo) { + queryRoutine = driverExtension->InitData.FdoData.ClassQueryPnpCapabilities; + } else { + queryRoutine = driverExtension->InitData.PdoData.ClassQueryPnpCapabilities; + } + + if(queryRoutine) { + return queryRoutine(DeviceObject, + Capabilities); + } else { + return STATUS_NOT_IMPLEMENTED; + } +} // end ClassQueryPnpCapabilities() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassInvalidateBusRelations() + +Routine Description: + + This routine re-enumerates the devices on the "bus". It will call into + the driver's ClassEnumerate routine to update the device objects + immediately. It will then schedule a bus re-enumeration for pnp by calling + IoInvalidateDeviceRelations. + +Arguments: + + Fdo - a pointer to the functional device object for this bus + +Return Value: + + none + +--*/ +VOID +ClassInvalidateBusRelations( + IN PDEVICE_OBJECT Fdo + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCLASS_DRIVER_EXTENSION + driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject, + CLASS_DRIVER_EXTENSION_KEY); + + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + ASSERT_FDO(Fdo); + ASSERT(driverExtension->InitData.ClassEnumerateDevice != NULL); + + if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) { + status = driverExtension->InitData.ClassEnumerateDevice(Fdo); + } + InterlockedDecrement(&(fdoExtension->EnumerationInterlock)); + + if(!NT_SUCCESS(status)) { + + DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine " + "returned %lx\n", status)); + } + + IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations); + + return; +} // end ClassInvalidateBusRelations() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?! + +Routine Description: + + This routine is called to handle the "removal" of a device. It will + forward the request downwards if necesssary, call into the driver + to release any necessary resources (memory, events, etc) and then + will delete the device object. + +Arguments: + + DeviceObject - a pointer to the device object being removed + + RemoveType - indicates what type of remove this is (regular or surprise). + +Return Value: + + status + +--*/ +NTSTATUS +ClassRemoveDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR RemoveType + ) +{ + PCLASS_DRIVER_EXTENSION + driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, + CLASS_DRIVER_EXTENSION_KEY); + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject; + PCLASS_WMI_INFO classWmiInfo; + BOOLEAN proceedWithRemove = TRUE; + NTSTATUS status; + + PAGED_CODE(); + + commonExtension->IsRemoved = REMOVE_PENDING; + + /* + * Deregister from WMI. + */ + classWmiInfo = commonExtension->IsFdo ? + &driverExtension->InitData.FdoData.ClassWmiInfo : + &driverExtension->InitData.PdoData.ClassWmiInfo; + if (classWmiInfo->GuidRegInfo){ + status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER); + DBGTRACE(ClassDebugInfo, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject, status)); + } + + /* + * If we exposed a "shingle" (a named device interface openable by CreateFile) + * then delete it now. + */ + if (commonExtension->MountedDeviceInterfaceName.Buffer){ + IoSetDeviceInterfaceState(&commonExtension->MountedDeviceInterfaceName, FALSE); + RtlFreeUnicodeString(&commonExtension->MountedDeviceInterfaceName); + RtlInitUnicodeString(&commonExtension->MountedDeviceInterfaceName, NULL); + } + + // + // If this is a surprise removal we leave the device around - which means + // we don't have to (or want to) drop the remove lock and wait for pending + // requests to complete. + // + + if (RemoveType == IRP_MN_REMOVE_DEVICE){ + + // + // Release the lock we acquired when the device object was created. + // + + ClassReleaseRemoveLock(DeviceObject, (PIRP) DeviceObject); + + DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n", + commonExtension->RemoveLock)); + + KeWaitForSingleObject(&commonExtension->RemoveEvent, + Executive, + KernelMode, + FALSE, + NULL); + + DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject)); + + if(commonExtension->IsFdo) { + + DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a " + "remove request.\n", DeviceObject)); + + } + else { + PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension; + + if (pdoExtension->IsMissing){ + /* + * The child partition PDO is missing, so we are going to go ahead + * and delete it for the remove. + */ + DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject)); + } + else { + /* + * We got a remove for a child partition PDO which is not actually missing. + * So we will NOT actually delete it. + */ + DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject)); + + // + // Reacquire the remove lock for the next time this comes around. + // + + ClassAcquireRemoveLock(DeviceObject, (PIRP) DeviceObject); + + // + // the device wasn't missing so it's not really been removed. + // + + commonExtension->IsRemoved = NO_REMOVE; + + IoInvalidateDeviceRelations( + commonExtension->PartitionZeroExtension->LowerPdo, + BusRelations); + + proceedWithRemove = FALSE; + } + } + } + + + if (proceedWithRemove){ + + /* + * Call the class driver's remove handler. + * All this is supposed to do is clean up its data and device interfaces. + */ + ASSERT(commonExtension->DevInfo->ClassRemoveDevice); + status = commonExtension->DevInfo->ClassRemoveDevice(DeviceObject, RemoveType); + ASSERT(NT_SUCCESS(status)); + status = STATUS_SUCCESS; + + if (commonExtension->IsFdo){ + PDEVICE_OBJECT pdo; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + + ClasspDisableTimer(fdoExtension->DeviceObject); + + if (RemoveType == IRP_MN_REMOVE_DEVICE){ + + PPHYSICAL_DEVICE_EXTENSION child; + + // + // Cleanup the media detection resources now that the class driver + // has stopped it's timer (if any) and we can be sure they won't + // call us to do detection again. + // + + ClassCleanupMediaChangeDetection(fdoExtension); + + // + // Cleanup any Failure Prediction stuff + // + if (fdoExtension->FailurePredictionInfo) { + ExFreePool(fdoExtension->FailurePredictionInfo); + fdoExtension->FailurePredictionInfo = NULL; + } + + /* + * Ordinarily all child PDOs will be removed by the time + * that the parent gets the REMOVE_DEVICE. + * However, if a child PDO has been created but has not + * been announced in a QueryDeviceRelations, then it is + * just a private data structure unknown to pnp, and we have + * to delete it ourselves. + */ + ClassAcquireChildLock(fdoExtension); + while (child = ClassRemoveChild(fdoExtension, NULL, FALSE)){ + + // + // Yank the pdo. This routine will unlink the device from the + // pdo list so NextPdo will point to the next one when it's + // complete. + // + child->IsMissing = TRUE; + ClassRemoveDevice(child->DeviceObject, IRP_MN_REMOVE_DEVICE); + } + ClassReleaseChildLock(fdoExtension); + } + else if (RemoveType == IRP_MN_SURPRISE_REMOVAL){ + /* + * This is a surprise-remove on the parent FDO. + * We will mark the child PDOs as missing so that they + * will actually get deleted when they get a REMOVE_DEVICE. + */ + ClassMarkChildrenMissing(fdoExtension); + } + + ClasspFreeReleaseRequest(DeviceObject); + + if (RemoveType == IRP_MN_REMOVE_DEVICE){ + + // + // Free FDO-specific data structs + // + if (fdoExtension->PrivateFdoData){ + + DestroyAllTransferPackets(DeviceObject); + + ExFreePool(fdoExtension->PrivateFdoData); + fdoExtension->PrivateFdoData = NULL; + } + + if (commonExtension->DeviceName.Buffer) { + ExFreePool(commonExtension->DeviceName.Buffer); + RtlInitUnicodeString(&commonExtension->DeviceName, NULL); + } + + if (fdoExtension->AdapterDescriptor) { + ExFreePool(fdoExtension->AdapterDescriptor); + fdoExtension->AdapterDescriptor = NULL; + } + + if (fdoExtension->DeviceDescriptor) { + ExFreePool(fdoExtension->DeviceDescriptor); + fdoExtension->DeviceDescriptor = NULL; + } + + // + // Detach our device object from the stack - there's no reason + // to hold off our cleanup any longer. + // + + IoDetachDevice(lowerDeviceObject); + } + } + else { + /* + * This is a child partition PDO. + * We have already determined that it was previously marked + * as missing. So if this is a REMOVE_DEVICE, we will actually + * delete it. + */ + if (RemoveType == IRP_MN_REMOVE_DEVICE){ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = + commonExtension->PartitionZeroExtension; + PPHYSICAL_DEVICE_EXTENSION pdoExtension = + (PPHYSICAL_DEVICE_EXTENSION) commonExtension; + + // + // See if this device is in the child list (if this was a suprise + // removal it might be) and remove it. + // + + ClassRemoveChild(fdoExtension, pdoExtension, TRUE); + } + } + + commonExtension->PartitionLength.QuadPart = 0; + + if (RemoveType == IRP_MN_REMOVE_DEVICE){ + IoDeleteDevice(DeviceObject); + } + } + + return STATUS_SUCCESS; +} // end ClassRemoveDevice() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassGetDriverExtension() + +Routine Description: + + This routine will return the classpnp's driver extension. + +Arguments: + + DriverObject - the driver object for which to get classpnp's extension + +Return Value: + + Either NULL if none, or a pointer to the driver extension + +--*/ +PCLASS_DRIVER_EXTENSION +ClassGetDriverExtension( + IN PDRIVER_OBJECT DriverObject + ) +{ + return IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY); +} // end ClassGetDriverExtension() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspStartIo() + +Routine Description: + + This routine wraps the class driver's start io routine. If the device + is being removed it will complete any requests with + STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet. + +Arguments: + +Return Value: + + none + +--*/ +VOID +ClasspStartIo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + // + // We're already holding the remove lock so just check the variable and + // see what's going on. + // + + if(commonExtension->IsRemoved) { + + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + + ClassAcquireRemoveLock(DeviceObject, (PIRP) ClasspStartIo); + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT); + IoStartNextPacket(DeviceObject, FALSE); + ClassReleaseRemoveLock(DeviceObject, (PIRP) ClasspStartIo); + return; + } + + commonExtension->DriverExtension->InitData.ClassStartIo( + DeviceObject, + Irp); + + return; +} // ClasspStartIo() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassUpdateInformationInRegistry() + +Routine Description: + + This routine has knowledge about the layout of the device map information + in the registry. It will update this information to include a value + entry specifying the dos device name that is assumed to get assigned + to this NT device name. For more information on this assigning of the + dos device name look in the drive support routine in the hal that assigns + all dos names. + + Since some versions of some device's firmware did not work and some + vendors did not bother to follow the specification, the entire inquiry + information must also be stored in the registry so than someone can + figure out the firmware version. + +Arguments: + + DeviceObject - A pointer to the device object for the tape device. + +Return Value: + + None + +--*/ +VOID +ClassUpdateInformationInRegistry( + IN PDEVICE_OBJECT Fdo, + IN PCHAR DeviceName, + IN ULONG DeviceNumber, + IN PINQUIRYDATA InquiryData, + IN ULONG InquiryDataLength + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + NTSTATUS status; + SCSI_ADDRESS scsiAddress; + OBJECT_ATTRIBUTES objectAttributes; + PUCHAR buffer; + STRING string; + UNICODE_STRING unicodeName; + UNICODE_STRING unicodeRegistryPath; + UNICODE_STRING unicodeData; + HANDLE targetKey; + IO_STATUS_BLOCK ioStatus; + + + PAGED_CODE(); + + ASSERT(DeviceName); + fdoExtension = Fdo->DeviceExtension; + buffer = NULL; + targetKey = NULL; + RtlZeroMemory(&unicodeName, sizeof(UNICODE_STRING)); + RtlZeroMemory(&unicodeData, sizeof(UNICODE_STRING)); + RtlZeroMemory(&unicodeRegistryPath, sizeof(UNICODE_STRING)); + + TRY { + + // + // Issue GET_ADDRESS Ioctl to determine path, target, and lun information. + // + + ClassSendDeviceIoControlSynchronous( + IOCTL_SCSI_GET_ADDRESS, + Fdo, + &scsiAddress, + 0, + sizeof(SCSI_ADDRESS), + FALSE, + &ioStatus + ); + + if (!NT_SUCCESS(ioStatus.Status)) { + + status = ioStatus.Status; + DebugPrint((1, + "UpdateInformationInRegistry: Get Address failed %lx\n", + status)); + LEAVE; + + } else { + + DebugPrint((1, + "GetAddress: Port %x, Path %x, Target %x, Lun %x\n", + scsiAddress.PortNumber, + scsiAddress.PathId, + scsiAddress.TargetId, + scsiAddress.Lun)); + + } + + // + // Allocate a buffer for the reg. spooge. + // + + buffer = ExAllocatePoolWithTag(PagedPool, 1024, '6BcS'); + + if (buffer == NULL) { + + // + // There is not return value for this. Since this is done at + // claim device time (currently only system initialization) getting + // the registry information correct will be the least of the worries. + // + + LEAVE; + } + + sprintf(buffer, + "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d", + scsiAddress.PortNumber, + scsiAddress.PathId, + scsiAddress.TargetId, + scsiAddress.Lun); + + RtlInitString(&string, buffer); + + status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath, + &string, + TRUE); + + if (!NT_SUCCESS(status)) { + LEAVE; + } + + // + // Open the registry key for the scsi information for this + // scsibus, target, lun. + // + + InitializeObjectAttributes(&objectAttributes, + &unicodeRegistryPath, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + status = ZwOpenKey(&targetKey, + KEY_READ | KEY_WRITE, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + LEAVE; + } + + // + // Now construct and attempt to create the registry value + // specifying the device name in the appropriate place in the + // device map. + // + + RtlInitUnicodeString(&unicodeName, L"DeviceName"); + + sprintf(buffer, "%s%d", DeviceName, DeviceNumber); + RtlInitString(&string, buffer); + status = RtlAnsiStringToUnicodeString(&unicodeData, + &string, + TRUE); + if (NT_SUCCESS(status)) { + status = ZwSetValueKey(targetKey, + &unicodeName, + 0, + REG_SZ, + unicodeData.Buffer, + unicodeData.Length); + } + + // + // if they sent in data, update the registry + // + + if (InquiryDataLength) { + + ASSERT(InquiryData); + + RtlInitUnicodeString(&unicodeName, L"InquiryData"); + status = ZwSetValueKey(targetKey, + &unicodeName, + 0, + REG_BINARY, + InquiryData, + InquiryDataLength); + } + + // that's all, except to clean up. + + } FINALLY { + + if (unicodeData.Buffer) { + RtlFreeUnicodeString(&unicodeData); + } + if (unicodeRegistryPath.Buffer) { + RtlFreeUnicodeString(&unicodeRegistryPath); + } + if (targetKey) { + ZwClose(targetKey); + } + if (buffer) { + ExFreePool(buffer); + } + + } + +} // end ClassUpdateInformationInRegistry() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspSendSynchronousCompletion() + +Routine Description: + + This completion routine will set the user event in the irp after + freeing the irp and the associated MDL (if any). + +Arguments: + + DeviceObject - the device object which requested the completion routine + + Irp - the irp being completed + + Context - unused + +Return Value: + + STATUS_MORE_PROCESSING_REQUIRED + +--*/ +NTSTATUS +ClasspSendSynchronousCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n", + DeviceObject, Irp, Context)); + // + // First set the status and information fields in the io status block + // provided by the caller. + // + + *(Irp->UserIosb) = Irp->IoStatus; + + // + // Unlock the pages for the data buffer. + // + + if(Irp->MdlAddress) { + MmUnlockPages(Irp->MdlAddress); + IoFreeMdl(Irp->MdlAddress); + } + + // + // Signal the caller's event. + // + + KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE); + + // + // Free the MDL and the IRP. + // + + IoFreeIrp(Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; +} // end ClasspSendSynchronousCompletion() + +/*++ + + ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface + +--*/ +VOID +ClasspRegisterMountedDeviceInterface( + IN PDEVICE_OBJECT DeviceObject + ) +{ + + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + BOOLEAN isFdo = commonExtension->IsFdo; + + PDEVICE_OBJECT pdo; + UNICODE_STRING interfaceName; + + NTSTATUS status; + + if(isFdo) { + + PFUNCTIONAL_DEVICE_EXTENSION functionalExtension; + + functionalExtension = + (PFUNCTIONAL_DEVICE_EXTENSION) commonExtension; + pdo = functionalExtension->LowerPdo; + } else { + pdo = DeviceObject; + } + + status = IoRegisterDeviceInterface( + pdo, + &MOUNTDEV_MOUNTED_DEVICE_GUID, + NULL, + &interfaceName + ); + + if(NT_SUCCESS(status)) { + + // + // Copy the interface name before setting the interface state - the + // name is needed by the components we notify. + // + + commonExtension->MountedDeviceInterfaceName = interfaceName; + status = IoSetDeviceInterfaceState(&interfaceName, TRUE); + + if(!NT_SUCCESS(status)) { + RtlFreeUnicodeString(&interfaceName); + } + } + + if(!NT_SUCCESS(status)) { + RtlInitUnicodeString(&(commonExtension->MountedDeviceInterfaceName), + NULL); + } + return; +} // end ClasspRegisterMountedDeviceInterface() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassSendDeviceIoControlSynchronous() + +Routine Description: + + This routine is based upon IoBuildDeviceIoControlRequest(). It has been + modified to reduce code and memory by not double-buffering the io, using + the same buffer for both input and output, allocating and deallocating + the mdl on behalf of the caller, and waiting for the io to complete. + + This routine also works around the rare cases in which APC's are disabled. + Since IoBuildDeviceIoControl() used APC's to signal completion, this had + led to a number of difficult-to-detect hangs, where the irp was completed, + but the event passed to IoBuild..() was still being waited upon by the + caller. + +Arguments: + + IoControlCode - the IOCTL to send + + TargetDeviceObject - the device object that should handle the ioctl + + Buffer - the input and output buffer, or NULL if no input/output + + InputBufferLength - the number of bytes prepared for the IOCTL in Buffer + + OutputBufferLength - the number of bytes to be filled in upon success + + InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL + + IoStatus - the status block that contains the results of the operation + +Return Value: + +--*/ +VOID +ClassSendDeviceIoControlSynchronous( + IN ULONG IoControlCode, + IN PDEVICE_OBJECT TargetDeviceObject, + IN OUT PVOID Buffer OPTIONAL, + IN ULONG InputBufferLength, + IN ULONG OutputBufferLength, + IN BOOLEAN InternalDeviceIoControl, + OUT PIO_STATUS_BLOCK IoStatus + ) +{ + PIRP irp; + PIO_STACK_LOCATION irpSp; + ULONG method; + + PAGED_CODE(); + + irp = NULL; + method = IoControlCode & 3; + + + #if DBG // Begin Argument Checking (nop in fre version) + + ASSERT(ARGUMENT_PRESENT(IoStatus)); + + if ((InputBufferLength != 0) || (OutputBufferLength != 0)) { + ASSERT(ARGUMENT_PRESENT(Buffer)); + } + else { + ASSERT(!ARGUMENT_PRESENT(Buffer)); + } + #endif + + // + // Begin by allocating the IRP for this request. Do not charge quota to + // the current process for this IRP. + // + + irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE); + if (!irp) { + (*IoStatus).Information = 0; + (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES; + return; + } + + // + // Get a pointer to the stack location of the first driver which will be + // invoked. This is where the function codes and the parameters are set. + // + + irpSp = IoGetNextIrpStackLocation(irp); + + // + // Set the major function code based on the type of device I/O control + // function the caller has specified. + // + + if (InternalDeviceIoControl) { + irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + } else { + irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; + } + + // + // Copy the caller's parameters to the service-specific portion of the + // IRP for those parameters that are the same for all four methods. + // + + irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength; + irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength; + irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode; + + // + // Get the method bits from the I/O control code to determine how the + // buffers are to be passed to the driver. + // + + switch (method) { + // case 0 + case METHOD_BUFFERED: { + if ((InputBufferLength != 0) || (OutputBufferLength != 0)) { + + irp->AssociatedIrp.SystemBuffer = + ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + max(InputBufferLength, OutputBufferLength), + CLASS_TAG_DEVICE_CONTROL + ); + + if (irp->AssociatedIrp.SystemBuffer == NULL) { + IoFreeIrp(irp); + (*IoStatus).Information = 0; + (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES; + return; + } + + if (InputBufferLength != 0) { + RtlCopyMemory(irp->AssociatedIrp.SystemBuffer, + Buffer, + InputBufferLength); + } + } // end of buffering + + irp->UserBuffer = Buffer; + break; + } + + // case 1, case 2 + case METHOD_IN_DIRECT: + case METHOD_OUT_DIRECT: { + + + if (InputBufferLength != 0) { + irp->AssociatedIrp.SystemBuffer = Buffer; + } + + if (OutputBufferLength != 0) { + + irp->MdlAddress = IoAllocateMdl(Buffer, + OutputBufferLength, + FALSE, FALSE, + (PIRP) NULL); + + if (irp->MdlAddress == NULL) { + IoFreeIrp(irp); + (*IoStatus).Information = 0; + (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES; + return; + } + + if (method == METHOD_IN_DIRECT) { + MmProbeAndLockPages(irp->MdlAddress, + KernelMode, + IoReadAccess); + } else if (method == METHOD_OUT_DIRECT) { + MmProbeAndLockPages(irp->MdlAddress, + KernelMode, + IoWriteAccess); + } else { + ASSERT(!"If other methods reach here, code is out of date"); + } + } + break; + } + + // case 3 + case METHOD_NEITHER: { + + ASSERT(!"This routine does not support METHOD_NEITHER ioctls"); + IoStatus->Information = 0; + IoStatus->Status = STATUS_NOT_SUPPORTED; + return; + break; + } + } // end of switch(method) + + irp->Tail.Overlay.Thread = PsGetCurrentThread(); + + // + // send the irp synchronously + // + + ClassSendIrpSynchronous(TargetDeviceObject, irp); + + // + // copy the iostatus block for the caller + // + + *IoStatus = irp->IoStatus; + + // + // free any allocated resources + // + + switch (method) { + case METHOD_BUFFERED: { + + ASSERT(irp->UserBuffer == Buffer); + + // + // first copy the buffered result, if any + // Note that there are no security implications in + // not checking for success since only drivers can + // call into this routine anyways... + // + + if (OutputBufferLength != 0) { + RtlCopyMemory(Buffer, // irp->UserBuffer + irp->AssociatedIrp.SystemBuffer, + OutputBufferLength + ); + } + + // + // then free the memory allocated to buffer the io + // + + if ((InputBufferLength !=0) || (OutputBufferLength != 0)) { + ExFreePool(irp->AssociatedIrp.SystemBuffer); + irp->AssociatedIrp.SystemBuffer = NULL; + } + break; + } + + case METHOD_IN_DIRECT: + case METHOD_OUT_DIRECT: { + + // + // we alloc a mdl if there is an output buffer specified + // free it here after unlocking the pages + // + + if (OutputBufferLength != 0) { + ASSERT(irp->MdlAddress != NULL); + MmUnlockPages(irp->MdlAddress); + IoFreeMdl(irp->MdlAddress); + irp->MdlAddress = (PMDL) NULL; + } + break; + } + + case METHOD_NEITHER: { + ASSERT(!"Code is out of date"); + break; + } + } + + // + // we always have allocated an irp. free it here. + // + + IoFreeIrp(irp); + irp = (PIRP) NULL; + + // + // return the io status block's status to the caller + // + + return; +} // end ClassSendDeviceIoControlSynchronous() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassForwardIrpSynchronous() + +Routine Description: + + Forwards a given irp to the next lower device object. + +Arguments: + + CommonExtension - the common class extension + + Irp - the request to forward down the stack + +Return Value: + +--*/ +NTSTATUS +ClassForwardIrpSynchronous( + IN PCOMMON_DEVICE_EXTENSION CommonExtension, + IN PIRP Irp + ) +{ + IoCopyCurrentIrpStackLocationToNext(Irp); + return ClassSendIrpSynchronous(CommonExtension->LowerDeviceObject, Irp); +} // end ClassForwardIrpSynchronous() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassSendIrpSynchronous() + +Routine Description: + + This routine sends the given irp to the given device object, and waits for + it to complete. On debug versions, will print out a debug message and + optionally assert for "lost" irps based upon classpnp's globals + +Arguments: + + TargetDeviceObject - the device object to handle this irp + + Irp - the request to be sent + +Return Value: + +--*/ +NTSTATUS +ClassSendIrpSynchronous( + IN PDEVICE_OBJECT TargetDeviceObject, + IN PIRP Irp + ) +{ + KEVENT event; + NTSTATUS status; + + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + ASSERT(TargetDeviceObject != NULL); + ASSERT(Irp != NULL); + ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize); + + // + // ISSUE-2000/02/20-henrygab What if APCs are disabled? + // May need to enter critical section before IoCallDriver() + // until the event is hit? + // + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + IoSetCompletionRoutine(Irp, ClassSignalCompletion, &event, + TRUE, TRUE, TRUE); + + status = IoCallDriver(TargetDeviceObject, Irp); + + if (status == STATUS_PENDING) { + + #if DBG + LARGE_INTEGER timeout; + + timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000 * + ClasspnpGlobals.SecondsToWaitForIrps); + + do { + status = KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + &timeout); + + + if (status == STATUS_TIMEOUT) { + + // + // This DebugPrint should almost always be investigated by the + // party who sent the irp and/or the current owner of the irp. + // Synchronous Irps should not take this long (currently 30 + // seconds) without good reason. This points to a potentially + // serious problem in the underlying device stack. + // + + DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not " + "complete within %x seconds\n", + TargetDeviceObject, Irp, + ClasspnpGlobals.SecondsToWaitForIrps + )); + + if (ClasspnpGlobals.BreakOnLostIrps != 0) { + ASSERT(!" - Irp failed to complete within 30 seconds - "); + } + } + + + } while (status==STATUS_TIMEOUT); + #else + KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + #endif + + status = Irp->IoStatus.Status; + } + + return status; +} // end ClassSendIrpSynchronous() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassGetVpb() + +Routine Description: + + This routine returns the current VPB (Volume Parameter Block) for the + given device object. + The Vpb field is only visible in the ntddk.h (not the wdm.h) definition + of DEVICE_OBJECT; hence this exported function. + +Arguments: + + DeviceObject - the device to get the VPB for + +Return Value: + + the VPB, or NULL if none. + +--*/ +PVPB +ClassGetVpb( + IN PDEVICE_OBJECT DeviceObject + ) +{ + return DeviceObject->Vpb; +} // end ClassGetVpb() + +/*++ + + ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest + +--*/ +NTSTATUS +ClasspAllocateReleaseRequest( + IN PDEVICE_OBJECT Fdo + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PIO_STACK_LOCATION irpStack; + + KeInitializeSpinLock(&(fdoExtension->ReleaseQueueSpinLock)); + + fdoExtension->ReleaseQueueNeeded = FALSE; + fdoExtension->ReleaseQueueInProgress = FALSE; + fdoExtension->ReleaseQueueIrpFromPool = FALSE; + + // + // The class driver is responsible for allocating a properly sized irp, + // or ClassReleaseQueue will attempt to do it on the first error. + // + + fdoExtension->ReleaseQueueIrp = NULL; + + // + // Write length to SRB. + // + + fdoExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK); + + return STATUS_SUCCESS; +} // end ClasspAllocateReleaseRequest() + +/*++ + + ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest + +--*/ +VOID +ClasspFreeReleaseRequest( + IN PDEVICE_OBJECT Fdo + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + //KIRQL oldIrql; + + ASSERT(fdoExtension->CommonExtension.IsRemoved != NO_REMOVE); + + // + // free anything the driver allocated + // + + if (fdoExtension->ReleaseQueueIrp) { + if (fdoExtension->ReleaseQueueIrpFromPool) { + ExFreePool(fdoExtension->ReleaseQueueIrp); + } else { + IoFreeIrp(fdoExtension->ReleaseQueueIrp); + } + fdoExtension->ReleaseQueueIrp = NULL; + } + + // + // free anything that we allocated + // + + if ((fdoExtension->PrivateFdoData) && + (fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated)) { + + ExFreePool(fdoExtension->PrivateFdoData->ReleaseQueueIrp); + fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = FALSE; + fdoExtension->PrivateFdoData->ReleaseQueueIrp = NULL; + } + + return; +} // end ClasspFreeReleaseRequest() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassReleaseQueue() + +Routine Description: + + This routine issues an internal device control command + to the port driver to release a frozen queue. The call + is issued asynchronously as ClassReleaseQueue will be invoked + from the IO completion DPC (and will have no context to + wait for a synchronous call to complete). + + This routine must be called with the remove lock held. + +Arguments: + + Fdo - The functional device object for the device with the frozen queue. + +Return Value: + + None. + +--*/ +VOID +ClassReleaseQueue( + IN PDEVICE_OBJECT Fdo + ) +{ + ClasspReleaseQueue(Fdo, NULL); + return; +} // end ClassReleaseQueue() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspAllocateReleaseQueueIrp() + +Routine Description: + + This routine allocates the release queue irp held in classpnp's private + extension. This was added to allow no-memory conditions to be more + survivable. + +Return Value: + + NT_SUCCESS value. + +Notes: + + Does not grab the spinlock. Should only be called from StartDevice() + routine. May be called elsewhere for poorly-behaved drivers that cause + the queue to lockup before the device is started. This should *never* + occur, since it's illegal to send a request to a non-started PDO. This + condition is checked for in ClasspReleaseQueue(). + +--*/ +NTSTATUS +ClasspAllocateReleaseQueueIrp( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + KIRQL oldIrql; + UCHAR lowerStackSize; + + // + // do an initial check w/o the spinlock + // + + if (FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) { + return STATUS_SUCCESS; + } + + + lowerStackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize; + + // + // don't allocate one if one is in progress! this means whoever called + // this routine didn't check if one was in progress. + // + + ASSERT(!(FdoExtension->ReleaseQueueInProgress)); + + FdoExtension->PrivateFdoData->ReleaseQueueIrp = + ExAllocatePoolWithTag(NonPagedPool, + IoSizeOfIrp(lowerStackSize), + CLASS_TAG_RELEASE_QUEUE + ); + + if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) { + DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for " + "release queue irp\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + IoInitializeIrp(FdoExtension->PrivateFdoData->ReleaseQueueIrp, + IoSizeOfIrp(lowerStackSize), + lowerStackSize); + FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = TRUE; + + return STATUS_SUCCESS; +} + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspReleaseQueue() + +Routine Description: + + This routine issues an internal device control command + to the port driver to release a frozen queue. The call + is issued asynchronously as ClassReleaseQueue will be invoked + from the IO completion DPC (and will have no context to + wait for a synchronous call to complete). + + This routine must be called with the remove lock held. + +Arguments: + + Fdo - The functional device object for the device with the frozen queue. + + ReleaseQueueIrp - If this irp is supplied then the test to determine whether + a release queue request is in progress will be ignored. + The irp provided must be the IRP originally allocated + for release queue requests (so this parameter can only + really be provided by the release queue completion + routine.) + +Return Value: + + None. + +--*/ +VOID +ClasspReleaseQueue( + IN PDEVICE_OBJECT Fdo, + IN PIRP ReleaseQueueIrp OPTIONAL + ) +{ + PIO_STACK_LOCATION irpStack; + PIRP irp; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PDEVICE_OBJECT lowerDevice; + PSCSI_REQUEST_BLOCK srb; + KIRQL currentIrql; + + lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject; + + // + // we raise irql seperately so we're not swapped out or suspended + // while holding the release queue irp in this routine. this lets + // us release the spin lock before lowering irql. + // + + KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql); + + KeAcquireSpinLockAtDpcLevel(&(fdoExtension->ReleaseQueueSpinLock)); + + // + // make sure that if they passed us an irp, it matches our allocated irp. + // + + ASSERT((ReleaseQueueIrp == NULL) || + (ReleaseQueueIrp == fdoExtension->PrivateFdoData->ReleaseQueueIrp)); + + // + // ASSERT that we've already allocated this. (should not occur) + // try to allocate it anyways, then finally bugcheck if + // there's still no memory... + // + + ASSERT(fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated); + if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) { + ClasspAllocateReleaseQueueIrp(fdoExtension); + } + if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) { + KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL, 0x12, (ULONG_PTR)Fdo, 0x0, 0x0); + } + + if ((fdoExtension->ReleaseQueueInProgress) && (ReleaseQueueIrp == NULL)) { + + // + // Someone is already using the irp - just set the flag to indicate that + // we need to release the queue again. + // + + fdoExtension->ReleaseQueueNeeded = TRUE; + KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock)); + KeLowerIrql(currentIrql); + return; + + } + + // + // Mark that there is a release queue in progress and drop the spinlock. + // + + fdoExtension->ReleaseQueueInProgress = TRUE; + if (ReleaseQueueIrp) { + irp = ReleaseQueueIrp; + } else { + irp = fdoExtension->PrivateFdoData->ReleaseQueueIrp; + } + srb = &(fdoExtension->ReleaseQueueSrb); + + KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock)); + + ASSERT(irp != NULL); + + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + srb->OriginalRequest = irp; + + // + // Store the SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = srb; + + // + // If this device is removable then flush the queue. This will also + // release it. + // + + if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){ + srb->Function = SRB_FUNCTION_FLUSH_QUEUE; + } + else { + srb->Function = SRB_FUNCTION_RELEASE_QUEUE; + } + + ClassAcquireRemoveLock(Fdo, irp); + + IoSetCompletionRoutine(irp, + ClassReleaseQueueCompletion, + Fdo, + TRUE, + TRUE, + TRUE); + + IoCallDriver(lowerDevice, irp); + + KeLowerIrql(currentIrql); + + return; + +} // end ClassReleaseQueue() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassReleaseQueueCompletion() + +Routine Description: + + This routine is called when an asynchronous I/O request + which was issused by the class driver completes. Examples of such requests + are release queue or START UNIT. This routine releases the queue if + necessary. It then frees the context and the IRP. + +Arguments: + + DeviceObject - The device object for the logical unit; however since this + is the top stack location the value is NULL. + + Irp - Supplies a pointer to the Irp to be processed. + + Context - Supplies the context to be used to process this request. + +Return Value: + + None. + +--*/ +NTSTATUS +ClassReleaseQueueCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + KIRQL oldIrql; + + BOOLEAN releaseQueueNeeded; + + DeviceObject = Context; + + fdoExtension = DeviceObject->DeviceExtension; + + ClassReleaseRemoveLock(DeviceObject, Irp); + + // + // Grab the spinlock and clear the release queue in progress flag so others + // can run. Save (and clear) the state of the release queue needed flag + // so that we can issue a new release queue outside the spinlock. + // + + KeAcquireSpinLock(&(fdoExtension->ReleaseQueueSpinLock), &oldIrql); + + releaseQueueNeeded = fdoExtension->ReleaseQueueNeeded; + + fdoExtension->ReleaseQueueNeeded = FALSE; + fdoExtension->ReleaseQueueInProgress = FALSE; + + KeReleaseSpinLock(&(fdoExtension->ReleaseQueueSpinLock), oldIrql); + + // + // If we need a release queue then issue one now. Another processor may + // have already started one in which case we'll try to issue this one after + // it is done - but we should never recurse more than one deep. + // + + if(releaseQueueNeeded) { + ClasspReleaseQueue(DeviceObject, Irp); + } + + // + // Indicate the I/O system should stop processing the Irp completion. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // ClassAsynchronousCompletion() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassAcquireChildLock() + +Routine Description: + + This routine acquires the lock protecting children PDOs. It may be + acquired recursively by the same thread, but must be release by the + thread once for each acquisition. + +Arguments: + + FdoExtension - the device whose child list is protected. + +Return Value: + + None + +--*/ +VOID +ClassAcquireChildLock( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PAGED_CODE(); + + if(FdoExtension->ChildLockOwner != KeGetCurrentThread()) { + KeWaitForSingleObject(&FdoExtension->ChildLock, + Executive, KernelMode, + FALSE, NULL); + + ASSERT(FdoExtension->ChildLockOwner == NULL); + ASSERT(FdoExtension->ChildLockAcquisitionCount == 0); + + FdoExtension->ChildLockOwner = KeGetCurrentThread(); + } else { + ASSERT(FdoExtension->ChildLockAcquisitionCount != 0); + } + + FdoExtension->ChildLockAcquisitionCount++; + return; +} + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented + +Routine Description: + + This routine releases the lock protecting children PDOs. It must be + called once for each time ClassAcquireChildLock was called. + +Arguments: + + FdoExtension - the device whose child list is protected + +Return Value: + + None. + +--*/ +VOID +ClassReleaseChildLock( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + ASSERT(FdoExtension->ChildLockOwner == KeGetCurrentThread()); + ASSERT(FdoExtension->ChildLockAcquisitionCount != 0); + + FdoExtension->ChildLockAcquisitionCount -= 1; + + if(FdoExtension->ChildLockAcquisitionCount == 0) { + FdoExtension->ChildLockOwner = NULL; + KeSetEvent(&FdoExtension->ChildLock, IO_NO_INCREMENT, FALSE); + } + + return; +} // end ClassReleaseChildLock( + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassAddChild() + +Routine Description: + + This routine will insert a new child into the head of the child list. + +Arguments: + + Parent - the child's parent (contains the head of the list) + Child - the child to be inserted. + AcquireLock - whether the child lock should be acquired (TRUE) or whether + it's already been acquired by or on behalf of the caller + (FALSE). + +Return Value: + + None. + +--*/ +VOID +ClassAddChild( + IN PFUNCTIONAL_DEVICE_EXTENSION Parent, + IN PPHYSICAL_DEVICE_EXTENSION Child, + IN BOOLEAN AcquireLock + ) +{ + if(AcquireLock) { + ClassAcquireChildLock(Parent); + } + + #if DBG + // + // Make sure this child's not already in the list. + // + { + PPHYSICAL_DEVICE_EXTENSION testChild; + + for (testChild = Parent->CommonExtension.ChildList; + testChild != NULL; + testChild = testChild->CommonExtension.ChildList) { + + ASSERT(testChild != Child); + } + } + #endif + + Child->CommonExtension.ChildList = Parent->CommonExtension.ChildList; + Parent->CommonExtension.ChildList = Child; + + if(AcquireLock) { + ClassReleaseChildLock(Parent); + } + return; +} // end ClassAddChild() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassRemoveChild() + +Routine Description: + + This routine will remove a child from the child list. + +Arguments: + + Parent - the parent to be removed from. + + Child - the child to be removed or NULL if the first child should be + removed. + + AcquireLock - whether the child lock should be acquired (TRUE) or whether + it's already been acquired by or on behalf of the caller + (FALSE). + +Return Value: + + A pointer to the child which was removed or NULL if no such child could + be found in the list (or if Child was NULL but the list is empty). + +--*/ +PPHYSICAL_DEVICE_EXTENSION +ClassRemoveChild( + IN PFUNCTIONAL_DEVICE_EXTENSION Parent, + IN PPHYSICAL_DEVICE_EXTENSION Child, + IN BOOLEAN AcquireLock + ) +{ + if(AcquireLock) { + ClassAcquireChildLock(Parent); + } + + TRY { + PCOMMON_DEVICE_EXTENSION previousChild = &Parent->CommonExtension; + + // + // If the list is empty then bail out now. + // + + if(Parent->CommonExtension.ChildList == NULL) { + Child = NULL; + LEAVE; + } + + // + // If the caller specified a child then find the child object before + // it. If none was specified then the FDO is the child object before + // the one we want to remove. + // + + if(Child != NULL) { + + // + // Scan through the child list to find the entry which points to + // this one. + // + + do { + ASSERT(previousChild != &Child->CommonExtension); + + if(previousChild->ChildList == Child) { + break; + } + + previousChild = &previousChild->ChildList->CommonExtension; + } while(previousChild != NULL); + + if(previousChild == NULL) { + Child = NULL; + LEAVE; + } + } + + // + // Save the next child away then unlink it from the list. + // + + Child = previousChild->ChildList; + previousChild->ChildList = Child->CommonExtension.ChildList; + Child->CommonExtension.ChildList = NULL; + + } FINALLY { + if(AcquireLock) { + ClassReleaseChildLock(Parent); + } + } + return Child; +} // end ClassRemoveChild() + + +/*++ + + ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc + +--*/ +VOID +ClasspRetryRequestDpc( + IN PKDPC Dpc, + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Arg1, + IN PVOID Arg2 + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PCOMMON_DEVICE_EXTENSION commonExtension; + PCLASS_PRIVATE_FDO_DATA fdoData; + PCLASS_RETRY_INFO retryList; + KIRQL irql; + + + commonExtension = DeviceObject->DeviceExtension; + ASSERT(commonExtension->IsFdo); + fdoExtension = DeviceObject->DeviceExtension; + fdoData = fdoExtension->PrivateFdoData; + + + KeAcquireSpinLock(&fdoData->Retry.Lock, &irql); + { + LARGE_INTEGER now; + KeQueryTickCount(&now); + + // + // if CurrentTick is less than now + // fire another DPC + // else + // retry entire list + // endif + // + + if (now.QuadPart < fdoData->Retry.Tick.QuadPart) { + + ClasspRetryDpcTimer(fdoData); + retryList = NULL; + + } else { + + retryList = fdoData->Retry.ListHead; + fdoData->Retry.ListHead = NULL; + fdoData->Retry.Delta.QuadPart = (LONGLONG)0; + fdoData->Retry.Tick.QuadPart = (LONGLONG)0; + + } + } + KeReleaseSpinLock(&fdoData->Retry.Lock, irql); + + while (retryList != NULL) { + + PIRP irp; + + irp = CONTAINING_RECORD(retryList, IRP, Tail.Overlay.DriverContext[0]); + DebugPrint((ClassDebugDelayedRetry, "ClassRetry: -- %p\n", irp)); + retryList = retryList->Next; + #if DBG + irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0xdddddddd); // invalidate data + irp->Tail.Overlay.DriverContext[1] = ULongToPtr(0xdddddddd); // invalidate data + irp->Tail.Overlay.DriverContext[2] = ULongToPtr(0xdddddddd); // invalidate data + irp->Tail.Overlay.DriverContext[3] = ULongToPtr(0xdddddddd); // invalidate data + #endif + + IoCallDriver(commonExtension->LowerDeviceObject, irp); + + } + return; + +} // end ClasspRetryRequestDpc() + +/*++ + + ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest + +--*/ +VOID +ClassRetryRequest( + IN PDEVICE_OBJECT SelfDeviceObject, + IN PIRP Irp, + IN LARGE_INTEGER TimeDelta100ns // in 100ns units + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PCLASS_PRIVATE_FDO_DATA fdoData; + PCLASS_RETRY_INFO retryInfo; + PCLASS_RETRY_INFO *previousNext; + LARGE_INTEGER delta; + KIRQL irql; + + // + // this checks we aren't destroying irps + // + ASSERT(sizeof(CLASS_RETRY_INFO) <= (4*sizeof(PVOID))); + + fdoExtension = SelfDeviceObject->DeviceExtension; + fdoData = fdoExtension->PrivateFdoData; + + if (!fdoExtension->CommonExtension.IsFdo) { + + // + // this debug print/assertion should ALWAYS be investigated. + // ClassRetryRequest can currently only be used by FDO's + // + + DebugPrint((ClassDebugError, "ClassRetryRequestEx: LOST IRP %p\n", Irp)); + ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP"); + return; + + } + + if (TimeDelta100ns.QuadPart < 0) { + ASSERT(!"ClassRetryRequest - must use positive delay"); + TimeDelta100ns.QuadPart *= -1; + } + + // + // prepare what we can out of the loop + // + + retryInfo = (PCLASS_RETRY_INFO)(&Irp->Tail.Overlay.DriverContext[0]); + RtlZeroMemory(retryInfo, sizeof(CLASS_RETRY_INFO)); + + delta.QuadPart = (TimeDelta100ns.QuadPart / fdoData->Retry.Granularity); + if (TimeDelta100ns.QuadPart % fdoData->Retry.Granularity) { + delta.QuadPart ++; // round up to next tick + } + if (delta.QuadPart == (LONGLONG)0) { + delta.QuadPart = MINIMUM_RETRY_UNITS; + } + + // + // now determine if we should fire another DPC or not + // + + KeAcquireSpinLock(&fdoData->Retry.Lock, &irql); + + // + // always add request to the list + // + + retryInfo->Next = fdoData->Retry.ListHead; + fdoData->Retry.ListHead = retryInfo; + + if (fdoData->Retry.Delta.QuadPart == (LONGLONG)0) { + + DebugPrint((ClassDebugDelayedRetry, "ClassRetry: +++ %p\n", Irp)); + + // + // must be exactly one item on list + // + + ASSERT(fdoData->Retry.ListHead != NULL); + ASSERT(fdoData->Retry.ListHead->Next == NULL); + + // + // if currentDelta is zero, always fire a DPC + // + + KeQueryTickCount(&fdoData->Retry.Tick); + fdoData->Retry.Tick.QuadPart += delta.QuadPart; + fdoData->Retry.Delta.QuadPart = delta.QuadPart; + ClasspRetryDpcTimer(fdoData); + + } else if (delta.QuadPart > fdoData->Retry.Delta.QuadPart) { + + // + // if delta is greater than the list's current delta, + // increase the DPC handling time by difference + // and update the delta to new larger value + // allow the DPC to re-fire itself if needed + // + + DebugPrint((ClassDebugDelayedRetry, "ClassRetry: ++ %p\n", Irp)); + + // + // must be at least two items on list + // + + ASSERT(fdoData->Retry.ListHead != NULL); + ASSERT(fdoData->Retry.ListHead->Next != NULL); + + fdoData->Retry.Tick.QuadPart -= fdoData->Retry.Delta.QuadPart; + fdoData->Retry.Tick.QuadPart += delta.QuadPart; + + fdoData->Retry.Delta.QuadPart = delta.QuadPart; + + } else { + + // + // just inserting it on the list was enough + // + + DebugPrint((ClassDebugDelayedRetry, "ClassRetry: ++ %p\n", Irp)); + + } + + + KeReleaseSpinLock(&fdoData->Retry.Lock, irql); + + +} // end ClassRetryRequest() + +/*++ + + ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer + +--*/ +VOID +ClasspRetryDpcTimer( + IN PCLASS_PRIVATE_FDO_DATA FdoData + ) +{ + LARGE_INTEGER fire; + + ASSERT(FdoData->Retry.Tick.QuadPart != (LONGLONG)0); + ASSERT(FdoData->Retry.ListHead != NULL); // never fire an empty list + + // + // fire == (CurrentTick - now) * (100ns per tick) + // + // NOTE: Overflow is nearly impossible and is ignored here + // + + KeQueryTickCount(&fire); + fire.QuadPart = FdoData->Retry.Tick.QuadPart - fire.QuadPart; + fire.QuadPart *= FdoData->Retry.Granularity; + + // + // fire is now multiples of 100ns until should fire the timer. + // if timer should already have expired, or would fire too quickly, + // fire it in some arbitrary number of ticks to prevent infinitely + // recursing. + // + + if (fire.QuadPart < MINIMUM_RETRY_UNITS) { + fire.QuadPart = MINIMUM_RETRY_UNITS; + } + + DebugPrint((ClassDebugDelayedRetry, + "ClassRetry: ======= %I64x ticks\n", + fire.QuadPart)); + + // + // must use negative to specify relative time to fire + // + + fire.QuadPart = fire.QuadPart * ((LONGLONG)-1); + + // + // set the timer, since this is the first addition + // + + KeSetTimerEx(&FdoData->Retry.Timer, fire, 0, &FdoData->Retry.Dpc); + + return; +} // end ClasspRetryDpcTimer() + +NTSTATUS +ClasspInitializeHotplugInfo( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; + DEVICE_REMOVAL_POLICY deviceRemovalPolicy; + NTSTATUS status; + ULONG resultLength = 0; + ULONG writeCacheOverride; + + PAGED_CODE(); + + // + // start with some default settings + // + RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO)); + + // + // set the size (aka version) + // + + fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO); + + // + // set if the device has removable media + // + + if (FdoExtension->DeviceDescriptor->RemovableMedia) { + fdoData->HotplugInfo.MediaRemovable = TRUE; + } else { + fdoData->HotplugInfo.MediaRemovable = FALSE; + } + + // + // this refers to devices which, for reasons not yet understood, + // do not fail PREVENT_MEDIA_REMOVAL requests even though they + // have no way to lock the media into the drive. this allows + // the filesystems to turn off delayed-write caching for these + // devices as well. + // + + if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags, + FDO_HACK_CANNOT_LOCK_MEDIA)) { + fdoData->HotplugInfo.MediaHotplug = TRUE; + } else { + fdoData->HotplugInfo.MediaHotplug = FALSE; + } + + + // + // Look into the registry to see if the user has chosen + // to override the default setting for the removal policy + // + + RtlZeroMemory(&deviceRemovalPolicy, sizeof(DEVICE_REMOVAL_POLICY)); + + ClassGetDeviceParameter(FdoExtension, + CLASSP_REG_SUBKEY_NAME, + CLASSP_REG_REMOVAL_POLICY_VALUE_NAME, + (PULONG)&deviceRemovalPolicy); + + if (deviceRemovalPolicy == 0) + { + // + // Query the default removal policy from the kernel + // + + status = IoGetDeviceProperty(FdoExtension->LowerPdo, + DevicePropertyRemovalPolicy, + sizeof(DEVICE_REMOVAL_POLICY), + (PVOID)&deviceRemovalPolicy, + &resultLength); + if (!NT_SUCCESS(status)) + { + return status; + } + + if (resultLength != sizeof(DEVICE_REMOVAL_POLICY)) + { + return STATUS_UNSUCCESSFUL; + } + } + + // + // use this info to set the DeviceHotplug setting + // don't rely on DeviceCapabilities, since it can't properly + // determine device relations, etc. let the kernel figure this + // stuff out instead. + // + + if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) { + fdoData->HotplugInfo.DeviceHotplug = TRUE; + } else { + fdoData->HotplugInfo.DeviceHotplug = FALSE; + } + + // + // this refers to the *filesystem* caching, but has to be included + // here since it's a per-device setting. this may change to be + // stored by the system in the future. + // + + writeCacheOverride = FALSE; + ClassGetDeviceParameter(FdoExtension, + CLASSP_REG_SUBKEY_NAME, + CLASSP_REG_WRITE_CACHE_VALUE_NAME, + &writeCacheOverride); + + if (writeCacheOverride) { + fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE; + } else { + fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE; + } + + return STATUS_SUCCESS; +} + +VOID +ClasspScanForClassHacks( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN ULONG_PTR Data + ) +{ + PAGED_CODE(); + + // + // remove invalid flags and save + // + + CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS); + SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, Data); + return; +} + +VOID +ClasspScanForSpecialInRegistry( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + HANDLE deviceParameterHandle; // device instance key + HANDLE classParameterHandle; // classpnp subkey + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING subkeyName; + NTSTATUS status; + + // + // seeded in the ENUM tree by ClassInstaller + // + ULONG deviceHacks; + RTL_QUERY_REGISTRY_TABLE queryTable[2]; // null terminated array + + PAGED_CODE(); + + deviceParameterHandle = NULL; + classParameterHandle = NULL; + deviceHacks = 0; + + status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo, + PLUGPLAY_REGKEY_DEVICE, + KEY_WRITE, + &deviceParameterHandle + ); + + if (!NT_SUCCESS(status)) { + goto cleanupScanForSpecial; + } + + RtlInitUnicodeString(&subkeyName, CLASSP_REG_SUBKEY_NAME); + InitializeObjectAttributes(&objectAttributes, + &subkeyName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + deviceParameterHandle, + NULL + ); + + status = ZwOpenKey( &classParameterHandle, + KEY_READ, + &objectAttributes + ); + + if (!NT_SUCCESS(status)) { + goto cleanupScanForSpecial; + } + + // + // Zero out the memory + // + + RtlZeroMemory(&queryTable[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE)); + + // + // Setup the structure to read + // + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + queryTable[0].Name = CLASSP_REG_HACK_VALUE_NAME; + queryTable[0].EntryContext = &deviceHacks; + queryTable[0].DefaultType = REG_DWORD; + queryTable[0].DefaultData = &deviceHacks; + queryTable[0].DefaultLength = 0; + + // + // read values + // + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + (PWSTR)classParameterHandle, + &queryTable[0], + NULL, + NULL + ); + if (!NT_SUCCESS(status)) { + goto cleanupScanForSpecial; + } + + // + // remove unknown values and save... + // + + KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL, + "Classpnp => ScanForSpecial: HackFlags %#08x\n", + deviceHacks)); + + CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS); + SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, deviceHacks); + + +cleanupScanForSpecial: + + if (deviceParameterHandle) { + ZwClose(deviceParameterHandle); + } + + if (classParameterHandle) { + ZwClose(classParameterHandle); + } + + // + // we should modify the system hive to include another key for us to grab + // settings from. in this case: Classpnp\HackFlags + // + // the use of a DWORD value for the HackFlags allows 32 hacks w/o + // significant use of the registry, and also reduces OEM exposure. + // + // definition of bit flags: + // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but + // cannot actually prevent removal. + // 0x00000002 -- Device hard-hangs or times out for GESN requests. + // 0xfffffffc -- Currently reserved, may be used later. + // + + return; +} + + + + diff --git a/drivers/storage/classpnp/class.def b/drivers/storage/classpnp/class.def new file mode 100644 index 00000000000..f6a2311b642 --- /dev/null +++ b/drivers/storage/classpnp/class.def @@ -0,0 +1,61 @@ +NAME CLASSPNP.SYS + +EXPORTS + ClassInitialize@12 + ClassInitializeEx@12 + ClassGetDescriptor@12 + ClassReadDriveCapacity@4 + ClassReleaseQueue@4 + ClassAsynchronousCompletion@12 + ClassSplitRequest@12 + ClassDeviceControl@8 + ClassIoComplete@12 + ClassIoCompleteAssociated@12 + ClassInterpretSenseInfo@28 + ClassSendDeviceIoControlSynchronous@28 + ClassSendIrpSynchronous@8 + ClassForwardIrpSynchronous@8 + ClassSendSrbSynchronous@20 + ClassSendSrbAsynchronous@24 + ClassBuildRequest@8 + ClassModeSense@16 + ClassFindModePage@16 + ClassClaimDevice@8 + ClassInternalIoControl@8 + ClassCreateDeviceObject@20 + ClassRemoveDevice@8 + ClassInitializeSrbLookasideList@8 + ClassDeleteSrbLookasideList@4 + ClassQueryTimeOutRegistryValue@4 + ClassInvalidateBusRelations@4 + ClassMarkChildrenMissing@4 + ClassMarkChildMissing@8 + ClassDebugPrint + ClassGetDriverExtension@4 + ClassCompleteRequest@12 + ClassReleaseRemoveLock@8 + ClassAcquireRemoveLockEx@16 + ClassUpdateInformationInRegistry@20 + ClassWmiCompleteRequest@20 + ClassWmiFireEvent@20 + ClassGetVpb@4 + ClassSetFailurePredictionPoll@12 + ClassNotifyFailurePredicted@32 + ClassInitializeTestUnitPolling@8 + ClassSignalCompletion@12 + ClassSendStartUnit@4 + ClassSetMediaChangeState@12 + ClassResetMediaChangeTimer@4 + ClassCheckMediaState@4 + ClassInitializeMediaChangeDetection@8 + ClassCleanupMediaChangeDetection@4 + ClassEnableMediaChangeDetection@4 + ClassDisableMediaChangeDetection@4 + ClassSpinDownPowerHandler@8 + ClassStopUnitPowerHandler@8 + ClassAcquireChildLock@4 + ClassReleaseChildLock@4 + ClassScanForSpecial@12 + ClassSetDeviceParameter@16 + ClassGetDeviceParameter@16 + diff --git a/drivers/storage/classpnp/class.rc b/drivers/storage/classpnp/class.rc new file mode 100644 index 00000000000..4ee7e3d9eef --- /dev/null +++ b/drivers/storage/classpnp/class.rc @@ -0,0 +1,23 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 1996 - 1999 +// +// File: class.rc +// +//-------------------------------------------------------------------------- + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "SCSI Class System Dll" +#define VER_INTERNALNAME_STR "Classpnp.sys" +#define VER_ORIGINALFILENAME_STR "Classpnp.sys" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/drivers/storage/classpnp/classp.h b/drivers/storage/classpnp/classp.h new file mode 100644 index 00000000000..43dc218a309 --- /dev/null +++ b/drivers/storage/classpnp/classp.h @@ -0,0 +1,907 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + classp.h + +Abstract: + + Private header file for classpnp.sys modules. This contains private + structure and function declarations as well as constant values which do + not need to be exported. + +Author: + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + + +#include +#include +#include + +#include +#include +#include + +#if CLASS_INIT_GUID +#include +#endif + +#include +#include +#include + +extern CLASSPNP_SCAN_FOR_SPECIAL_INFO ClassBadItems[]; + +extern GUID ClassGuidQueryRegInfoEx; + + +#define CLASSP_REG_SUBKEY_NAME (L"Classpnp") + +#define CLASSP_REG_HACK_VALUE_NAME (L"HackMask") +#define CLASSP_REG_MMC_DETECTION_VALUE_NAME (L"MMCDetectionState") +#define CLASSP_REG_WRITE_CACHE_VALUE_NAME (L"WriteCacheEnableOverride") +#define CLASSP_REG_PERF_RESTORE_VALUE_NAME (L"RestorePerfAtCount") +#define CLASSP_REG_REMOVAL_POLICY_VALUE_NAME (L"UserRemovalPolicy") + +#define CLASS_PERF_RESTORE_MINIMUM (0x10) +#define CLASS_ERROR_LEVEL_1 (0x4) +#define CLASS_ERROR_LEVEL_2 (0x8) + +#define FDO_HACK_CANNOT_LOCK_MEDIA (0x00000001) +#define FDO_HACK_GESN_IS_BAD (0x00000002) +#define FDO_HACK_NO_SYNC_CACHE (0x00000004) + +#define FDO_HACK_VALID_FLAGS (0x00000007) +#define FDO_HACK_INVALID_FLAGS (~FDO_HACK_VALID_FLAGS) + +/* + * Lots of retries of synchronized SCSI commands that devices may not + * even support really slows down the system (especially while booting). + * (Even GetDriveCapacity may be failed on purpose if an external disk is powered off). + * If a disk cannot return a small initialization buffer at startup + * in two attempts (with delay interval) then we cannot expect it to return + * data consistently with four retries. + * So don't set the retry counts as high here as for data SRBs. + * + * If we find that these requests are failing consecutively, + * despite the retry interval, on otherwise reliable media, + * then we should either increase the retry interval for + * that failure or (by all means) increase these retry counts as appropriate. + */ +#define NUM_LOCKMEDIAREMOVAL_RETRIES 1 +#define NUM_MODESENSE_RETRIES 1 +#define NUM_DRIVECAPACITY_RETRIES 1 + + +#define CLASS_FILE_OBJECT_EXTENSION_KEY 'eteP' +#define CLASSP_VOLUME_VERIFY_CHECKED 0x34 + +#define CLASS_TAG_PRIVATE_DATA 'CPcS' +#define CLASS_TAG_PRIVATE_DATA_FDO 'FPcS' +#define CLASS_TAG_PRIVATE_DATA_PDO 'PPcS' + +struct _MEDIA_CHANGE_DETECTION_INFO { + + // + // Mutex to synchronize enable/disable requests and media state changes + // + + KMUTEX MediaChangeMutex; + + // + // The current state of the media (present, not present, unknown) + // protected by MediaChangeSynchronizationEvent + // + + MEDIA_CHANGE_DETECTION_STATE MediaChangeDetectionState; + + // + // This is a count of how many time MCD has been disabled. if it is + // set to zero, then we'll poll the device for MCN events with the + // then-current method (ie. TEST UNIT READY or GESN). this is + // protected by MediaChangeMutex + // + + LONG MediaChangeDetectionDisableCount; + + + // + // The timer value to support media change events. This is a countdown + // value used to determine when to poll the device for a media change. + // The max value for the timer is 255 seconds. This is not protected + // by an event -- simply InterlockedExchanged() as needed. + // + + LONG MediaChangeCountDown; + + // + // recent changes allowed instant retries of the MCN irp. Since this + // could cause an infinite loop, keep a count of how many times we've + // retried immediately so that we can catch if the count exceeds an + // arbitrary limit. + // + + LONG MediaChangeRetryCount; + + // + // use GESN if it's available + // + + struct { + BOOLEAN Supported; + BOOLEAN HackEventMask; + UCHAR EventMask; + UCHAR NoChangeEventMask; + PUCHAR Buffer; + PMDL Mdl; + ULONG BufferSize; + } Gesn; + + // + // If this value is one, then the irp is currently in use. + // If this value is zero, then the irp is available. + // Use InterlockedCompareExchange() to set from "available" to "in use". + // ASSERT that InterlockedCompareExchange() showed previous value of + // "in use" when changing back to "available" state. + // This also implicitly protects the MediaChangeSrb and SenseBuffer + // + + LONG MediaChangeIrpInUse; + + // + // Pointer to the irp to be used for media change detection. + // protected by Interlocked MediaChangeIrpInUse + // + + PIRP MediaChangeIrp; + + // + // The srb for the media change detection. + // protected by Interlocked MediaChangeIrpInUse + // + + SCSI_REQUEST_BLOCK MediaChangeSrb; + PUCHAR SenseBuffer; + ULONG SrbFlags; + + // + // Second timer to keep track of how long the media change IRP has been + // in use. If this value exceeds the timeout (#defined) then we should + // print out a message to the user and set the MediaChangeIrpLost flag + // protected by using Interlocked() operations in ClasspSendMediaStateIrp, + // the only routine which should modify this value. + // + + LONG MediaChangeIrpTimeInUse; + + // + // Set by CdRomTickHandler when we determine that the media change irp has + // been lost + // + + BOOLEAN MediaChangeIrpLost; + +}; + +typedef enum { + SimpleMediaLock, + SecureMediaLock, + InternalMediaLock +} MEDIA_LOCK_TYPE, *PMEDIA_LOCK_TYPE; + +typedef struct _FAILURE_PREDICTION_INFO { + FAILURE_PREDICTION_METHOD Method; + ULONG CountDown; // Countdown timer + ULONG Period; // Countdown period + + PIO_WORKITEM WorkQueueItem; + + KEVENT Event; +} FAILURE_PREDICTION_INFO, *PFAILURE_PREDICTION_INFO; + + + +// +// This struct must always fit within four PVOIDs of info, +// as it uses the irp's "PVOID DriverContext[4]" to store +// this info +// +typedef struct _CLASS_RETRY_INFO { + struct _CLASS_RETRY_INFO *Next; +} CLASS_RETRY_INFO, *PCLASS_RETRY_INFO; + + + +typedef struct _CSCAN_LIST { + + // + // The current block which has an outstanding request. + // + + ULONGLONG BlockNumber; + + // + // The list of blocks past the CurrentBlock to which we're going to do + // i/o. This list is maintained in sorted order. + // + + LIST_ENTRY CurrentSweep; + + // + // The list of blocks behind the current block for which we'll have to + // wait until the next scan across the disk. This is kept as a stack, + // the cost of sorting it is taken when it's moved over to be the + // running list. + // + + LIST_ENTRY NextSweep; + +} CSCAN_LIST, *PCSCAN_LIST; + +// +// add to the front of this structure to help prevent illegal +// snooping by other utilities. +// + + + +typedef enum _CLASS_DETECTION_STATE { + ClassDetectionUnknown = 0, + ClassDetectionUnsupported = 1, + ClassDetectionSupported = 2 +} CLASS_DETECTION_STATE, *PCLASS_DETECTION_STATE; + + +typedef struct _CLASS_ERROR_LOG_DATA { + LARGE_INTEGER TickCount; // Offset 0x00 + ULONG PortNumber; // Offset 0x08 + + UCHAR ErrorPaging : 1; // Offset 0x0c + UCHAR ErrorRetried : 1; + UCHAR ErrorUnhandled : 1; + UCHAR ErrorReserved : 5; + + UCHAR Reserved[3]; + + SCSI_REQUEST_BLOCK Srb; // Offset 0x10 + + /* + * We define the SenseData as the default length. + * Since the sense data returned by the port driver may be longer, + * SenseData must be at the end of this structure. + * For our internal error log, we only log the default length. + */ + SENSE_DATA SenseData; // Offset 0x50 for x86 (or 0x68 for ia64) (ULONG32 Alignment required!) +} CLASS_ERROR_LOG_DATA, *PCLASS_ERROR_LOG_DATA; + +#define NUM_ERROR_LOG_ENTRIES 16 + + + +typedef struct _TRANSFER_PACKET { + + LIST_ENTRY AllPktsListEntry; // entry in fdoData's static AllTransferPacketsList + SINGLE_LIST_ENTRY SlistEntry; // for when in free list (use fast slist) + + PIRP Irp; + PDEVICE_OBJECT Fdo; + + /* + * This is the client IRP that this TRANSFER_PACKET is currently + * servicing. + */ + PIRP OriginalIrp; + BOOLEAN CompleteOriginalIrpWhenLastPacketCompletes; + + /* + * Stuff for retrying the transfer. + */ + ULONG NumRetries; + KTIMER RetryTimer; + KDPC RetryTimerDPC; + ULONG RetryIntervalSec; + + /* + * Event for synchronizing the transfer (optional). + * (Note that we can't have the event in the packet itself because + * by the time a thread waits on an event the packet may have + * been completed and re-issued. + */ + PKEVENT SyncEventPtr; + + /* + * Stuff for retrying during extreme low-memory stress + * (when we retry 1 page at a time). + */ + BOOLEAN InLowMemRetry; + PUCHAR LowMemRetry_remainingBufPtr; + ULONG LowMemRetry_remainingBufLen; + LARGE_INTEGER LowMemRetry_nextChunkTargetLocation; + + /* + * Fields used for cancelling the packet. + */ + // BOOLEAN Cancelled; + // KEVENT CancelledEvent; + + /* + * We keep the buffer and length values here as well + * as in the SRB because some miniports return + * the transferred length in SRB.DataTransferLength, + * and if the SRB failed we need that value again for the retry. + * We don't trust the lower stack to preserve any of these values in the SRB. + */ + PUCHAR BufPtrCopy; + ULONG BufLenCopy; + LARGE_INTEGER TargetLocationCopy; + + /* + * This is a standard SCSI structure that receives a detailed + * report about a SCSI error on the hardware. + */ + SENSE_DATA SrbErrorSenseData; + + /* + * This is the SRB block for this TRANSFER_PACKET. + * For IOCTLs, the SRB block includes two DWORDs for + * device object and ioctl code; so these must + * immediately follow the SRB block. + */ + SCSI_REQUEST_BLOCK Srb; + // ULONG SrbIoctlDevObj; // not handling ioctls yet + // ULONG SrbIoctlCode; + +} TRANSFER_PACKET, *PTRANSFER_PACKET; + +/* + * MIN_INITIAL_TRANSFER_PACKETS is the minimum number of packets that + * we preallocate at startup for each device (we need at least one packet + * to guarantee forward progress during memory stress). + * MIN_WORKINGSET_TRANSFER_PACKETS is the number of TRANSFER_PACKETs + * we allow to build up and remain for each device; + * we _lazily_ work down to this number when they're not needed. + * MAX_WORKINGSET_TRANSFER_PACKETS is the number of TRANSFER_PACKETs + * that we _immediately_ reduce to when they are not needed. + * + * The absolute maximum number of packets that we will allocate is + * whatever is required by the current activity, up to the memory limit; + * as soon as stress ends, we snap down to MAX_WORKINGSET_TRANSFER_PACKETS; + * we then lazily work down to MIN_WORKINGSET_TRANSFER_PACKETS. + */ +#define MIN_INITIAL_TRANSFER_PACKETS 1 +#define MIN_WORKINGSET_TRANSFER_PACKETS_Consumer 4 +#define MAX_WORKINGSET_TRANSFER_PACKETS_Consumer 64 +#define MIN_WORKINGSET_TRANSFER_PACKETS_Server 64 +#define MAX_WORKINGSET_TRANSFER_PACKETS_Server 1024 +#define MIN_WORKINGSET_TRANSFER_PACKETS_Enterprise 256 +#define MAX_WORKINGSET_TRANSFER_PACKETS_Enterprise 2048 + + +// +// add to the front of this structure to help prevent illegal +// snooping by other utilities. +// +struct _CLASS_PRIVATE_FDO_DATA { + + // + // this private structure allows us to + // dynamically re-enable the perf benefits + // lost due to transient error conditions. + // in w2k, a reboot was required. :( + // + struct { + ULONG OriginalSrbFlags; + ULONG SuccessfulIO; + ULONG ReEnableThreshhold; // 0 means never + } Perf; + + ULONG_PTR HackFlags; + + STORAGE_HOTPLUG_INFO HotplugInfo; + + // Legacy. Still used by obsolete legacy code. + struct { + LARGE_INTEGER Delta; // in ticks + LARGE_INTEGER Tick; // when it should fire + PCLASS_RETRY_INFO ListHead; // singly-linked list + ULONG Granularity; // static + KSPIN_LOCK Lock; // protective spin lock + KDPC Dpc; // DPC routine object + KTIMER Timer; // timer to fire DPC + } Retry; + + BOOLEAN TimerStarted; + BOOLEAN LoggedTURFailureSinceLastIO; + + // + // privately allocated release queue irp + // protected by fdoExtension->ReleaseQueueSpinLock + // + BOOLEAN ReleaseQueueIrpAllocated; + PIRP ReleaseQueueIrp; + + /* + * Queues for TRANSFER_PACKETs that contextualize the IRPs and SRBs + * that we send down to the port driver. + * (The free list is an slist so that we can use fast + * interlocked operations on it; but the relatively-static + * AllTransferPacketsList list has to be + * a doubly-linked list since we have to dequeue from the middle). + */ + LIST_ENTRY AllTransferPacketsList; + SLIST_HEADER FreeTransferPacketsList; + ULONG NumFreeTransferPackets; + ULONG NumTotalTransferPackets; + ULONG DbgPeakNumTransferPackets; + + /* + * Queue for deferred client irps + */ + LIST_ENTRY DeferredClientIrpList; + + /* + * Precomputed maximum transfer length for the hardware. + */ + ULONG HwMaxXferLen; + + /* + * SCSI_REQUEST_BLOCK template preconfigured with the constant values. + * This is slapped into the SRB in the TRANSFER_PACKET for each transfer. + */ + SCSI_REQUEST_BLOCK SrbTemplate; + + KSPIN_LOCK SpinLock; + + /* + * Circular array of timestamped logs of errors that occurred on this device. + */ + ULONG ErrorLogNextIndex; + CLASS_ERROR_LOG_DATA ErrorLogs[NUM_ERROR_LOG_ENTRIES]; + +}; + + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + + +#define NOT_READY_RETRY_INTERVAL 10 +#define MINIMUM_RETRY_UNITS ((LONGLONG)32) + + +/* + * Simple singly-linked-list queuing macros, with no synchronization. + */ +static inline VOID SimpleInitSlistHdr(SINGLE_LIST_ENTRY *SListHdr) +{ + SListHdr->Next = NULL; +} +static inline VOID SimplePushSlist(SINGLE_LIST_ENTRY *SListHdr, SINGLE_LIST_ENTRY *SListEntry) +{ + SListEntry->Next = SListHdr->Next; + SListHdr->Next = SListEntry; +} +static inline SINGLE_LIST_ENTRY *SimplePopSlist(SINGLE_LIST_ENTRY *SListHdr) +{ + SINGLE_LIST_ENTRY *sListEntry = SListHdr->Next; + if (sListEntry){ + SListHdr->Next = sListEntry->Next; + sListEntry->Next = NULL; + } + return sListEntry; +} +static inline BOOLEAN SimpleIsSlistEmpty(SINGLE_LIST_ENTRY *SListHdr) +{ + return (SListHdr->Next == NULL); +} + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +ClassUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +ClassCreateClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +ClasspCreateClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +ClasspCleanupProtectedLocks( + IN PFILE_OBJECT_EXTENSION FsContext + ); + +NTSTATUS +ClasspEjectionControl( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp, + IN MEDIA_LOCK_TYPE LockType, + IN BOOLEAN Lock + ); + +NTSTATUS +ClassReadWrite( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +ClassDeviceControlDispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ); + +NTSTATUS +ClassDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ); + +NTSTATUS +ClassDispatchPnp( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ); + +NTSTATUS +ClassPnpStartDevice( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +ClassInternalIoControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +ClassShutdownFlush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +ClassSystemControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +// +// Class internal routines +// + +NTSTATUS +ClassAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN OUT PDEVICE_OBJECT PhysicalDeviceObject + ); + +NTSTATUS +ClasspSendSynchronousCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +RetryRequest( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PSCSI_REQUEST_BLOCK Srb, + BOOLEAN Associated, + ULONG RetryInterval + ); + +NTSTATUS +ClassIoCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +ClassPnpQueryFdoRelations( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp + ); + +NTSTATUS +ClassRetrieveDeviceRelations( + IN PDEVICE_OBJECT Fdo, + IN DEVICE_RELATION_TYPE RelationType, + OUT PDEVICE_RELATIONS *DeviceRelations + ); + +NTSTATUS +ClassGetPdoId( + IN PDEVICE_OBJECT Pdo, + IN BUS_QUERY_ID_TYPE IdType, + IN PUNICODE_STRING IdString + ); + +NTSTATUS +ClassQueryPnpCapabilities( + IN PDEVICE_OBJECT PhysicalDeviceObject, + IN PDEVICE_CAPABILITIES Capabilities + ); + +VOID +ClasspStartIo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +ClasspPagingNotificationCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PDEVICE_OBJECT RealDeviceObject + ); + +NTSTATUS +ClasspMediaChangeCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ); + +PFILE_OBJECT_EXTENSION +ClasspGetFsContext( + IN PCOMMON_DEVICE_EXTENSION CommonExtension, + IN PFILE_OBJECT FileObject + ); + +NTSTATUS +ClasspMcnControl( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PIRP Irp, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +ClasspRegisterMountedDeviceInterface( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +ClasspDisableTimer( + PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +ClasspEnableTimer( + PDEVICE_OBJECT DeviceObject + ); + +// +// routines for dictionary list support +// + +VOID +InitializeDictionary( + IN PDICTIONARY Dictionary + ); + +BOOLEAN +TestDictionarySignature( + IN PDICTIONARY Dictionary + ); + +NTSTATUS +AllocateDictionaryEntry( + IN PDICTIONARY Dictionary, + IN ULONGLONG Key, + IN ULONG Size, + IN ULONG Tag, + OUT PVOID *Entry + ); + +PVOID +GetDictionaryEntry( + IN PDICTIONARY Dictionary, + IN ULONGLONG Key + ); + +VOID +FreeDictionaryEntry( + IN PDICTIONARY Dictionary, + IN PVOID Entry + ); + + +NTSTATUS +ClasspAllocateReleaseRequest( + IN PDEVICE_OBJECT Fdo + ); + +VOID +ClasspFreeReleaseRequest( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +ClassReleaseQueueCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +ClasspReleaseQueue( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP ReleaseQueueIrp + ); + +VOID +ClasspDisablePowerNotification( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension +); + +// +// class power routines +// + +NTSTATUS +ClassDispatchPower( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +ClassMinimalPowerHandler( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +// +// Child list routines +// + +VOID +ClassAddChild( + IN PFUNCTIONAL_DEVICE_EXTENSION Parent, + IN PPHYSICAL_DEVICE_EXTENSION Child, + IN BOOLEAN AcquireLock + ); + +PPHYSICAL_DEVICE_EXTENSION +ClassRemoveChild( + IN PFUNCTIONAL_DEVICE_EXTENSION Parent, + IN PPHYSICAL_DEVICE_EXTENSION Child, + IN BOOLEAN AcquireLock + ); + +VOID +ClasspRetryDpcTimer( + IN PCLASS_PRIVATE_FDO_DATA FdoData + ); + +VOID +ClasspRetryRequestDpc( + IN PKDPC Dpc, + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Arg1, + IN PVOID Arg2 + ); + +VOID +ClassFreeOrReuseSrb( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +ClassRetryRequest( + IN PDEVICE_OBJECT SelfDeviceObject, + IN PIRP Irp, + IN LARGE_INTEGER TimeDelta100ns // in 100ns units + ); + +VOID +ClasspBuildRequestEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN PIRP Irp, + IN PSCSI_REQUEST_BLOCK Srb + ); + +NTSTATUS +ClasspAllocateReleaseQueueIrp( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +NTSTATUS +ClasspInitializeGesn( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PMEDIA_CHANGE_DETECTION_INFO Info + ); + +VOID +ClasspSendNotification( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN const GUID * Guid, + IN ULONG ExtraDataSize, + IN PVOID ExtraData + ); + +VOID +ClassSendEjectionNotification( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +VOID +ClasspScanForSpecialInRegistry( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +VOID +ClasspScanForClassHacks( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN ULONG_PTR Data + ); + +NTSTATUS +ClasspInitializeHotplugInfo( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +VOID +ClasspPerfIncrementErrorCount( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); +VOID +ClasspPerfIncrementSuccessfulIo( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + + +PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo); +VOID DestroyTransferPacket(PTRANSFER_PACKET Pkt); +VOID EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo, PTRANSFER_PACKET Pkt); +PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded); +VOID SetupReadWriteTransferPacket(PTRANSFER_PACKET pkt, PVOID Buf, ULONG Len, LARGE_INTEGER DiskLocation, PIRP OriginalIrp); +VOID SubmitTransferPacket(PTRANSFER_PACKET Pkt); +NTSTATUS TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context); +VOID ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp); +VOID TransferPacketRetryTimerDpc(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2); +BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt); +BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt); +VOID EnqueueDeferredClientIrp(PCLASS_PRIVATE_FDO_DATA FdoData, PIRP Irp); +PIRP DequeueDeferredClientIrp(PCLASS_PRIVATE_FDO_DATA FdoData); +VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation); +BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt); +VOID SetupEjectionTransferPacket(TRANSFER_PACKET *Pkt, BOOLEAN PreventMediaRemoval, PKEVENT SyncEventPtr, PIRP OriginalIrp); +VOID SetupModeSenseTransferPacket(TRANSFER_PACKET *Pkt, PKEVENT SyncEventPtr, PVOID ModeSenseBuffer, UCHAR ModeSenseBufferLen, UCHAR PageMode, PIRP OriginalIrp); +VOID SetupDriveCapacityTransferPacket(TRANSFER_PACKET *Pkt, PVOID ReadCapacityBuffer, ULONG ReadCapacityBufferLen, PKEVENT SyncEventPtr, PIRP OriginalIrp); +PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen); +VOID FreeDeviceInputMdl(PMDL Mdl); +NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo); +VOID DestroyAllTransferPackets(PDEVICE_OBJECT Fdo); + + + + + diff --git a/drivers/storage/classpnp/classpnp.rbuild b/drivers/storage/classpnp/classpnp.rbuild new file mode 100644 index 00000000000..668d79b79c9 --- /dev/null +++ b/drivers/storage/classpnp/classpnp.rbuild @@ -0,0 +1,37 @@ + + + + + + ntoskrnl + hal + pseh + libcntpr + ../inc + 0 + 100 + 1 + 0 + 512 + 512 + + -mrtd + -fno-builtin + -w + + autorun.c + class.c + classwmi.c + create.c + data.c + dictlib.c + lock.c + power.c + xferpkt.c + clntirp.c + retry.c + utils.c + obsolete.c + debug.c + class.rc + diff --git a/drivers/storage/classpnp/classwmi.c b/drivers/storage/classpnp/classwmi.c new file mode 100644 index 00000000000..bbd43a6999d --- /dev/null +++ b/drivers/storage/classpnp/classwmi.c @@ -0,0 +1,778 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + classwmi.c + +Abstract: + + SCSI class driver routines + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#include "stddef.h" +#include "ntddk.h" +#include "scsi.h" + +#include "classpnp.h" + +#include "mountdev.h" + +#include + +#include "wmistr.h" + +NTSTATUS +ClassSystemControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +BOOLEAN +ClassFindGuid( + PGUIDREGINFO GuidList, + ULONG GuidCount, + LPGUID Guid, + PULONG GuidIndex + ); + +// +// This is the name for the MOF resource that must be part of all drivers that +// register via this interface. +#define MOFRESOURCENAME L"MofResourceName" + +// +// What can be paged ??? +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, ClassSystemControl) +#pragma alloc_text(PAGE, ClassFindGuid) +#endif + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassFindGuid() + +Routine Description: + + This routine will search the list of guids registered and return + the index for the one that was registered. + +Arguments: + + GuidList is the list of guids to search + + GuidCount is the count of guids in the list + + Guid is the guid being searched for + + *GuidIndex returns the index to the guid + +Return Value: + + TRUE if guid is found else FALSE + +--*/ +BOOLEAN +ClassFindGuid( + PGUIDREGINFO GuidList, + ULONG GuidCount, + LPGUID Guid, + PULONG GuidIndex + ) +{ + ULONG i; + + PAGED_CODE(); + + for (i = 0; i < GuidCount; i++) + { + if (IsEqualGUID(Guid, &GuidList[i].Guid)) + { + *GuidIndex = i; + return(TRUE); + } + } + + return(FALSE); +} // end ClassFindGuid() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassSystemControl() + +Routine Description: + + Dispatch routine for IRP_MJ_SYSTEM_CONTROL. This routine will process + all wmi requests received, forwarding them if they are not for this + driver or determining if the guid is valid and if so passing it to + the driver specific function for handing wmi requests. + +Arguments: + + DeviceObject - Supplies a pointer to the device object for this request. + + Irp - Supplies the Irp making the request. + +Return Value: + + status + +--*/ +NTSTATUS +ClassSystemControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PCLASS_DRIVER_EXTENSION driverExtension; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG isRemoved; + ULONG bufferSize; + PUCHAR buffer; + NTSTATUS status; + UCHAR minorFunction; + ULONG guidIndex; + PCLASS_WMI_INFO classWmiInfo; + + PAGED_CODE(); + + // + // Make sure device has not been removed + isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp); + if(isRemoved) + { + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + // + // If the irp is not a WMI irp or it is not targetted at this device + // or this device has not regstered with WMI then just forward it on. + minorFunction = irpStack->MinorFunction; + if ((minorFunction > IRP_MN_EXECUTE_METHOD) || + (irpStack->Parameters.WMI.ProviderId != (ULONG_PTR)DeviceObject) || + ((minorFunction != IRP_MN_REGINFO) && + (commonExtension->GuidRegInfo == NULL))) + { + // + // CONSIDER: Do I need to hang onto lock until IoCallDriver returns ? + IoSkipCurrentIrpStackLocation(Irp); + ClassReleaseRemoveLock(DeviceObject, Irp); + return(IoCallDriver(commonExtension->LowerDeviceObject, Irp)); + } + + buffer = (PUCHAR)irpStack->Parameters.WMI.Buffer; + bufferSize = irpStack->Parameters.WMI.BufferSize; + + if (minorFunction != IRP_MN_REGINFO) + { + // + // For all requests other than query registration info we are passed + // a guid. Determine if the guid is one that is supported by the + // device. + if (ClassFindGuid(commonExtension->GuidRegInfo, + commonExtension->GuidCount, + (LPGUID)irpStack->Parameters.WMI.DataPath, + &guidIndex)) + { + status = STATUS_SUCCESS; + } else { + status = STATUS_WMI_GUID_NOT_FOUND; + } + + if (NT_SUCCESS(status) && + ((minorFunction == IRP_MN_QUERY_SINGLE_INSTANCE) || + (minorFunction == IRP_MN_CHANGE_SINGLE_INSTANCE) || + (minorFunction == IRP_MN_CHANGE_SINGLE_ITEM) || + (minorFunction == IRP_MN_EXECUTE_METHOD))) + { + if ( (((PWNODE_HEADER)buffer)->Flags) & + WNODE_FLAG_STATIC_INSTANCE_NAMES) + { + if ( ((PWNODE_SINGLE_INSTANCE)buffer)->InstanceIndex != 0 ) + { + status = STATUS_WMI_INSTANCE_NOT_FOUND; + } + } else { + status = STATUS_WMI_INSTANCE_NOT_FOUND; + } + } + + if (! NT_SUCCESS(status)) + { + Irp->IoStatus.Status = status; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + return(status); + } + } + + driverExtension = commonExtension->DriverExtension; + + classWmiInfo = commonExtension->IsFdo ? + &driverExtension->InitData.FdoData.ClassWmiInfo : + &driverExtension->InitData.PdoData.ClassWmiInfo; + switch(minorFunction) + { + case IRP_MN_REGINFO: + { + ULONG guidCount; + PGUIDREGINFO guidList; + PWMIREGINFOW wmiRegInfo; + PWMIREGGUIDW wmiRegGuid; + PDEVICE_OBJECT pdo; + PUNICODE_STRING regPath; + PWCHAR stringPtr; + ULONG retSize; + ULONG registryPathOffset; + ULONG mofResourceOffset; + ULONG bufferNeeded; + ULONG i; + ULONG_PTR nameInfo; + ULONG nameSize, nameOffset, nameFlags; + UNICODE_STRING name, mofName; + PCLASS_QUERY_WMI_REGINFO_EX ClassQueryWmiRegInfoEx; + + name.Buffer = NULL; + name.Length = 0; + name.MaximumLength = 0; + nameFlags = 0; + + ClassQueryWmiRegInfoEx = commonExtension->IsFdo ? + driverExtension->ClassFdoQueryWmiRegInfoEx : + driverExtension->ClassPdoQueryWmiRegInfoEx; + + if (ClassQueryWmiRegInfoEx == NULL) + { + status = classWmiInfo->ClassQueryWmiRegInfo( + DeviceObject, + &nameFlags, + &name); + + RtlInitUnicodeString(&mofName, MOFRESOURCENAME); + } else { + RtlInitUnicodeString(&mofName, L""); + status = (*ClassQueryWmiRegInfoEx)( + DeviceObject, + &nameFlags, + &name, + &mofName); + } + + if (NT_SUCCESS(status) && + (! (nameFlags & WMIREG_FLAG_INSTANCE_PDO) && + (name.Buffer == NULL))) + { + // + // if PDO flag not specified then an instance name must be + status = STATUS_INVALID_DEVICE_REQUEST; + } + + if (NT_SUCCESS(status)) + { + guidList = classWmiInfo->GuidRegInfo; + guidCount = classWmiInfo->GuidCount; + + nameOffset = sizeof(WMIREGINFO) + + guidCount * sizeof(WMIREGGUIDW); + + if (nameFlags & WMIREG_FLAG_INSTANCE_PDO) + { + nameSize = 0; + nameInfo = commonExtension->IsFdo ? + (ULONG_PTR)((PFUNCTIONAL_DEVICE_EXTENSION)commonExtension)->LowerPdo : + (ULONG_PTR)DeviceObject; + } else { + nameFlags |= WMIREG_FLAG_INSTANCE_LIST; + nameSize = name.Length + sizeof(USHORT); + nameInfo = nameOffset; + } + + mofResourceOffset = nameOffset + nameSize; + + registryPathOffset = mofResourceOffset + + mofName.Length + sizeof(USHORT); + + regPath = &driverExtension->RegistryPath; + bufferNeeded = registryPathOffset + + regPath->Length + sizeof(USHORT); + + if (bufferNeeded <= bufferSize) + { + retSize = bufferNeeded; + + commonExtension->GuidCount = guidCount; + commonExtension->GuidRegInfo = guidList; + + wmiRegInfo = (PWMIREGINFO)buffer; + wmiRegInfo->BufferSize = bufferNeeded; + wmiRegInfo->NextWmiRegInfo = 0; + wmiRegInfo->MofResourceName = mofResourceOffset; + wmiRegInfo->RegistryPath = registryPathOffset; + wmiRegInfo->GuidCount = guidCount; + + for (i = 0; i < guidCount; i++) + { + wmiRegGuid = &wmiRegInfo->WmiRegGuid[i]; + wmiRegGuid->Guid = guidList[i].Guid; + wmiRegGuid->Flags = guidList[i].Flags | nameFlags; + wmiRegGuid->InstanceInfo = nameInfo; + wmiRegGuid->InstanceCount = 1; + } + + if ( nameFlags & WMIREG_FLAG_INSTANCE_LIST) + { + stringPtr = (PWCHAR)((PUCHAR)buffer + nameOffset); + *stringPtr++ = name.Length; + RtlCopyMemory(stringPtr, + name.Buffer, + name.Length); + } + + stringPtr = (PWCHAR)((PUCHAR)buffer + mofResourceOffset); + *stringPtr++ = mofName.Length; + RtlCopyMemory(stringPtr, + mofName.Buffer, + mofName.Length); + + stringPtr = (PWCHAR)((PUCHAR)buffer + registryPathOffset); + *stringPtr++ = regPath->Length; + RtlCopyMemory(stringPtr, + regPath->Buffer, + regPath->Length); + } else { + *((PULONG)buffer) = bufferNeeded; + retSize = sizeof(ULONG); + } + } else { + retSize = 0; + } + + if (name.Buffer != NULL) + { + ExFreePool(name.Buffer); + } + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = retSize; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + return(status); + } + + case IRP_MN_QUERY_ALL_DATA: + { + PWNODE_ALL_DATA wnode; + ULONG bufferAvail; + + wnode = (PWNODE_ALL_DATA)buffer; + + if (bufferSize < sizeof(WNODE_ALL_DATA)) + { + bufferAvail = 0; + } else { + bufferAvail = bufferSize - sizeof(WNODE_ALL_DATA); + } + + wnode->DataBlockOffset = sizeof(WNODE_ALL_DATA); + + status = classWmiInfo->ClassQueryWmiDataBlock( + DeviceObject, + Irp, + guidIndex, + bufferAvail, + buffer + sizeof(WNODE_ALL_DATA)); + + break; + } + + case IRP_MN_QUERY_SINGLE_INSTANCE: + { + PWNODE_SINGLE_INSTANCE wnode; + ULONG dataBlockOffset; + + wnode = (PWNODE_SINGLE_INSTANCE)buffer; + + dataBlockOffset = wnode->DataBlockOffset; + + status = classWmiInfo->ClassQueryWmiDataBlock( + DeviceObject, + Irp, + guidIndex, + bufferSize - dataBlockOffset, + (PUCHAR)wnode + dataBlockOffset); + + break; + } + + case IRP_MN_CHANGE_SINGLE_INSTANCE: + { + PWNODE_SINGLE_INSTANCE wnode; + + wnode = (PWNODE_SINGLE_INSTANCE)buffer; + + status = classWmiInfo->ClassSetWmiDataBlock( + DeviceObject, + Irp, + guidIndex, + wnode->SizeDataBlock, + (PUCHAR)wnode + wnode->DataBlockOffset); + + break; + } + + case IRP_MN_CHANGE_SINGLE_ITEM: + { + PWNODE_SINGLE_ITEM wnode; + + wnode = (PWNODE_SINGLE_ITEM)buffer; + + status = classWmiInfo->ClassSetWmiDataItem( + DeviceObject, + Irp, + guidIndex, + wnode->ItemId, + wnode->SizeDataItem, + (PUCHAR)wnode + wnode->DataBlockOffset); + + break; + } + + case IRP_MN_EXECUTE_METHOD: + { + PWNODE_METHOD_ITEM wnode; + + wnode = (PWNODE_METHOD_ITEM)buffer; + + status = classWmiInfo->ClassExecuteWmiMethod( + DeviceObject, + Irp, + guidIndex, + wnode->MethodId, + wnode->SizeDataBlock, + bufferSize - wnode->DataBlockOffset, + buffer + wnode->DataBlockOffset); + + + break; + } + + case IRP_MN_ENABLE_EVENTS: + { + status = classWmiInfo->ClassWmiFunctionControl( + DeviceObject, + Irp, + guidIndex, + EventGeneration, + TRUE); + break; + } + + case IRP_MN_DISABLE_EVENTS: + { + status = classWmiInfo->ClassWmiFunctionControl( + DeviceObject, + Irp, + guidIndex, + EventGeneration, + FALSE); + break; + } + + case IRP_MN_ENABLE_COLLECTION: + { + status = classWmiInfo->ClassWmiFunctionControl( + DeviceObject, + Irp, + guidIndex, + DataBlockCollection, + TRUE); + break; + } + + case IRP_MN_DISABLE_COLLECTION: + { + status = classWmiInfo->ClassWmiFunctionControl( + DeviceObject, + Irp, + guidIndex, + DataBlockCollection, + FALSE); + break; + } + + default: + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + } + + return(status); +} // end ClassSystemControl() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassWmiCompleteRequest() + +Routine Description: + + + This routine will do the work of completing a WMI irp. Depending upon the + the WMI request this routine will fixup the returned WNODE appropriately. + + NOTE: This routine assumes that the ClassRemoveLock is held and it will + release it. + +Arguments: + + DeviceObject - Supplies a pointer to the device object for this request. + + Irp - Supplies the Irp making the request. + + Status - Status to complete the irp with. STATUS_BUFFER_TOO_SMALL is used + to indicate that more buffer is required for the data requested. + + BufferUsed - number of bytes of actual data to return (not including WMI + specific structures) + + PriorityBoost - priority boost to pass to ClassCompleteRequest + +Return Value: + + status + +--*/ +SCSIPORTAPI +NTSTATUS +ClassWmiCompleteRequest( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG BufferUsed, + IN CCHAR PriorityBoost + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + UCHAR MinorFunction; + PUCHAR buffer; + ULONG retSize; + UCHAR minorFunction; + ULONG bufferSize; + + minorFunction = irpStack->MinorFunction; + buffer = (PUCHAR)irpStack->Parameters.WMI.Buffer; + bufferSize = irpStack->Parameters.WMI.BufferSize; + + switch(minorFunction) + { + case IRP_MN_QUERY_ALL_DATA: + { + PWNODE_ALL_DATA wnode; + PWNODE_TOO_SMALL wnodeTooSmall; + ULONG bufferNeeded; + + wnode = (PWNODE_ALL_DATA)buffer; + + bufferNeeded = sizeof(WNODE_ALL_DATA) + BufferUsed; + + if (NT_SUCCESS(Status)) + { + retSize = bufferNeeded; + wnode->WnodeHeader.BufferSize = bufferNeeded; + KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp); + wnode->WnodeHeader.Flags |= WNODE_FLAG_FIXED_INSTANCE_SIZE; + wnode->FixedInstanceSize = BufferUsed; + wnode->InstanceCount = 1; + + } else if (Status == STATUS_BUFFER_TOO_SMALL) { + wnodeTooSmall = (PWNODE_TOO_SMALL)wnode; + + wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL); + wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL; + wnodeTooSmall->SizeNeeded = sizeof(WNODE_ALL_DATA) + BufferUsed; + retSize = sizeof(WNODE_TOO_SMALL); + Status = STATUS_SUCCESS; + } else { + retSize = 0; + } + break; + } + + case IRP_MN_QUERY_SINGLE_INSTANCE: + { + PWNODE_SINGLE_INSTANCE wnode; + PWNODE_TOO_SMALL wnodeTooSmall; + ULONG bufferNeeded; + + wnode = (PWNODE_SINGLE_INSTANCE)buffer; + + bufferNeeded = wnode->DataBlockOffset + BufferUsed; + + if (NT_SUCCESS(Status)) + { + retSize = bufferNeeded; + wnode->WnodeHeader.BufferSize = bufferNeeded; + KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp); + wnode->SizeDataBlock = BufferUsed; + + } else if (Status == STATUS_BUFFER_TOO_SMALL) { + wnodeTooSmall = (PWNODE_TOO_SMALL)wnode; + + wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL); + wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL; + wnodeTooSmall->SizeNeeded = bufferNeeded; + retSize = sizeof(WNODE_TOO_SMALL); + Status = STATUS_SUCCESS; + } else { + retSize = 0; + } + break; + } + + case IRP_MN_EXECUTE_METHOD: + { + PWNODE_METHOD_ITEM wnode; + PWNODE_TOO_SMALL wnodeTooSmall; + ULONG bufferNeeded; + + wnode = (PWNODE_METHOD_ITEM)buffer; + + bufferNeeded = wnode->DataBlockOffset + BufferUsed; + + if (NT_SUCCESS(Status)) + { + retSize = bufferNeeded; + wnode->WnodeHeader.BufferSize = bufferNeeded; + KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp); + wnode->SizeDataBlock = BufferUsed; + + } else if (Status == STATUS_BUFFER_TOO_SMALL) { + wnodeTooSmall = (PWNODE_TOO_SMALL)wnode; + + wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL); + wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL; + wnodeTooSmall->SizeNeeded = bufferNeeded; + retSize = sizeof(WNODE_TOO_SMALL); + Status = STATUS_SUCCESS; + } else { + retSize = 0; + } + break; + } + + default: + { + // + // All other requests don't return any data + retSize = 0; + break; + } + + } + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = retSize; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, PriorityBoost); + return(Status); +} // end ClassWmiCompleteRequest() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassWmiFireEvent() + +Routine Description: + + This routine will fire a WMI event using the data buffer passed. This + routine may be called at or below DPC level + +Arguments: + + DeviceObject - Supplies a pointer to the device object for this event + + Guid is pointer to the GUID that represents the event + + InstanceIndex is the index of the instance of the event + + EventDataSize is the number of bytes of data that is being fired with + with the event + + EventData is the data that is fired with the events. This may be NULL + if there is no data associated with the event + + +Return Value: + + status + +--*/ +NTSTATUS +ClassWmiFireEvent( + IN PDEVICE_OBJECT DeviceObject, + IN LPGUID Guid, + IN ULONG InstanceIndex, + IN ULONG EventDataSize, + IN PVOID EventData + ) +{ + + ULONG sizeNeeded; + PWNODE_SINGLE_INSTANCE event; + NTSTATUS status; + + if (EventData == NULL) + { + EventDataSize = 0; + } + + sizeNeeded = sizeof(WNODE_SINGLE_INSTANCE) + EventDataSize; + + event = ExAllocatePoolWithTag(NonPagedPool, sizeNeeded, CLASS_TAG_WMI); + if (event != NULL) + { + event->WnodeHeader.Guid = *Guid; + event->WnodeHeader.ProviderId = IoWMIDeviceObjectToProviderId(DeviceObject); + event->WnodeHeader.BufferSize = sizeNeeded; + event->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE | + WNODE_FLAG_EVENT_ITEM | + WNODE_FLAG_STATIC_INSTANCE_NAMES; + KeQuerySystemTime(&event->WnodeHeader.TimeStamp); + + event->InstanceIndex = InstanceIndex; + event->SizeDataBlock = EventDataSize; + event->DataBlockOffset = sizeof(WNODE_SINGLE_INSTANCE); + if (EventData != NULL) + { + RtlCopyMemory( &event->VariableData, EventData, EventDataSize); + } + + status = IoWMIWriteEvent(event); + if (! NT_SUCCESS(status)) + { + ExFreePool(event); + } + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + return(status); +} // end ClassWmiFireEvent() + diff --git a/drivers/storage/classpnp/clntirp.c b/drivers/storage/classpnp/clntirp.c new file mode 100644 index 00000000000..934e389bf19 --- /dev/null +++ b/drivers/storage/classpnp/clntirp.c @@ -0,0 +1,74 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + clntirp.c + +Abstract: + + Client IRP queuing routines for CLASSPNP + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#include "classp.h" +#include "debug.h" + + +/* + * EnqueueDeferredClientIrp + * + * Note: we currently do not support Cancel for storage irps. + */ +VOID EnqueueDeferredClientIrp(PCLASS_PRIVATE_FDO_DATA FdoData, PIRP Irp) +{ + KIRQL oldIrql; + + KeAcquireSpinLock(&FdoData->SpinLock, &oldIrql); + InsertTailList(&FdoData->DeferredClientIrpList, &Irp->Tail.Overlay.ListEntry); + KeReleaseSpinLock(&FdoData->SpinLock, oldIrql); +} + + +/* + * DequeueDeferredClientIrp + * + */ +PIRP DequeueDeferredClientIrp(PCLASS_PRIVATE_FDO_DATA FdoData) +{ + KIRQL oldIrql; + PLIST_ENTRY listEntry; + PIRP irp; + + KeAcquireSpinLock(&FdoData->SpinLock, &oldIrql); + if (IsListEmpty(&FdoData->DeferredClientIrpList)){ + listEntry = NULL; + } + else { + listEntry = RemoveHeadList(&FdoData->DeferredClientIrpList); + } + KeReleaseSpinLock(&FdoData->SpinLock, oldIrql); + + if (listEntry == NULL) { + irp = NULL; + } else { + irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry); + ASSERT(irp->Type == IO_TYPE_IRP); + InitializeListHead(&irp->Tail.Overlay.ListEntry); + } + + return irp; +} + + + diff --git a/drivers/storage/classpnp/create.c b/drivers/storage/classpnp/create.c new file mode 100644 index 00000000000..12fc002452f --- /dev/null +++ b/drivers/storage/classpnp/create.c @@ -0,0 +1,977 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + class.c + +Abstract: + + SCSI class driver routines + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#define CLASS_INIT_GUID 0 +#include "classp.h" +#include "debug.h" + +ULONG BreakOnClose = 0; + +PUCHAR LockTypeStrings[] = { + "Simple", + "Secure", + "Internal" +}; + + +PFILE_OBJECT_EXTENSION +ClasspGetFsContext( + IN PCOMMON_DEVICE_EXTENSION CommonExtension, + IN PFILE_OBJECT FileObject + ); + +VOID +ClasspCleanupDisableMcn( + IN PFILE_OBJECT_EXTENSION FsContext + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, ClassCreateClose) +#pragma alloc_text(PAGE, ClasspCreateClose) +#pragma alloc_text(PAGE, ClasspCleanupProtectedLocks) +#pragma alloc_text(PAGE, ClasspEjectionControl) +#pragma alloc_text(PAGE, ClasspCleanupDisableMcn) +#pragma alloc_text(PAGE, ClasspGetFsContext) +#endif + +NTSTATUS +ClassCreateClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + SCSI class driver create and close routine. This is called by the I/O system + when the device is opened or closed. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + Irp - IRP involved. + +Return Value: + + Device-specific drivers return value or STATUS_SUCCESS. + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + ULONG removeState; + NTSTATUS status; + + PAGED_CODE(); + + // + // If we're getting a close request then we know the device object hasn't + // been completely destroyed. Let the driver cleanup if necessary. + // + + removeState = ClassAcquireRemoveLock(DeviceObject, Irp); + + // + // Invoke the device-specific routine, if one exists. Otherwise complete + // with SUCCESS + // + + if((removeState == NO_REMOVE) || + IS_CLEANUP_REQUEST(IoGetCurrentIrpStackLocation(Irp)->MajorFunction)) { + + status = ClasspCreateClose(DeviceObject, Irp); + + if((NT_SUCCESS(status)) && + (commonExtension->DevInfo->ClassCreateClose)) { + + return commonExtension->DevInfo->ClassCreateClose(DeviceObject, Irp); + } + + } else { + status = STATUS_DEVICE_DOES_NOT_EXIST; + } + + Irp->IoStatus.Status = status; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + return status; +} + + +NTSTATUS +ClasspCreateClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine will handle create/close operations for a given classpnp + device if the class driver doesn't supply it's own handler. If there + is a file object supplied for our driver (if it's a FO_DIRECT_DEVICE_OPEN + file object) then it will initialize a file extension on create or destroy + the extension on a close. + +Arguments: + + DeviceObject - the device object being opened or closed. + + Irp - the create/close irp + +Return Value: + + status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + + PFILE_OBJECT fileObject = irpStack->FileObject; + + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + + // + // ISSUE-2000/3/28-henrygab - if lower stack fails create/close, we end up + // in an inconsistent state. re-write to verify all args and allocate all + // required resources, then pass the irp down, then complete the + // transaction. this is because we also cannot forward the irp, then fail + // it after it has succeeded a lower-level driver. + // + + if(irpStack->MajorFunction == IRP_MJ_CREATE) { + + PIO_SECURITY_CONTEXT securityContext = + irpStack->Parameters.Create.SecurityContext; + DebugPrint((2, + "ClasspCREATEClose: create received for device %p\n", + DeviceObject)); + DebugPrint((2, + "ClasspCREATEClose: desired access %lx\n", + securityContext->DesiredAccess)); + DebugPrint((2, + "ClasspCREATEClose: file object %lx\n", + irpStack->FileObject)); + + ASSERT(BreakOnClose == FALSE); + + if(irpStack->FileObject != NULL) { + + PFILE_OBJECT_EXTENSION fsContext; + + // + // Allocate our own file object extension for this device object. + // + + status = AllocateDictionaryEntry( + &commonExtension->FileObjectDictionary, + (ULONGLONG) irpStack->FileObject, + sizeof(FILE_OBJECT_EXTENSION), + CLASS_TAG_FILE_OBJECT_EXTENSION, + &fsContext); + + if(NT_SUCCESS(status)) { + + RtlZeroMemory(fsContext, + sizeof(FILE_OBJECT_EXTENSION)); + + fsContext->FileObject = irpStack->FileObject; + fsContext->DeviceObject = DeviceObject; + } else if (status == STATUS_OBJECT_NAME_COLLISION) { + status = STATUS_SUCCESS; + } + } + + } else { + + DebugPrint((2, + "ClasspCreateCLOSE: close received for device %p\n", + DeviceObject)); + DebugPrint((2, + "ClasspCreateCLOSE: file object %p\n", + fileObject)); + + if(irpStack->FileObject != NULL) { + + PFILE_OBJECT_EXTENSION fsContext = + ClasspGetFsContext(commonExtension, irpStack->FileObject); + + DebugPrint((2, + "ClasspCreateCLOSE: file extension %p\n", + fsContext)); + + if(fsContext != NULL) { + + DebugPrint((2, + "ClasspCreateCLOSE: extension is ours - " + "freeing\n")); + ASSERT(BreakOnClose == FALSE); + + ClasspCleanupProtectedLocks(fsContext); + + ClasspCleanupDisableMcn(fsContext); + + FreeDictionaryEntry(&(commonExtension->FileObjectDictionary), + fsContext); + } + } + } + + // + // Notify the lower levels about the create or close operation - give them + // a chance to cleanup too. + // + + DebugPrint((2, + "ClasspCreateClose: %s for devobj %p\n", + (NT_SUCCESS(status) ? "Success" : "FAILED"), + DeviceObject)); + + + if(NT_SUCCESS(status)) { + + KEVENT event; + + // + // Set up the event to wait on + // + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + IoCopyCurrentIrpStackLocationToNext(Irp); + IoSetCompletionRoutine( Irp, ClassSignalCompletion, &event, + TRUE, TRUE, TRUE); + + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + if(status == STATUS_PENDING) { + KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + status = Irp->IoStatus.Status; + } + + if (!NT_SUCCESS(status)) { + DebugPrint((ClassDebugError, + "ClasspCreateClose: Lower driver failed, but we " + "succeeded. This is a problem, lock counts will be " + "out of sync between levels.\n")); + } + + } + + + return status; +} + + +VOID +ClasspCleanupProtectedLocks( + IN PFILE_OBJECT_EXTENSION FsContext + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = + FsContext->DeviceObject->DeviceExtension; + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = + commonExtension->PartitionZeroExtension; + + ULONG newDeviceLockCount = 1; + + PAGED_CODE(); + + DebugPrint((2, + "ClasspCleanupProtectedLocks called for %p\n", + FsContext->DeviceObject)); + DebugPrint((2, + "ClasspCleanupProtectedLocks - FsContext %p is locked " + "%d times\n", FsContext, FsContext->LockCount)); + + ASSERT(BreakOnClose == FALSE); + + // + // Synchronize with ejection and ejection control requests. + // + + KeEnterCriticalRegion(); + KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent), + UserRequest, + UserMode, + FALSE, + NULL); + + // + // For each secure lock on this handle decrement the secured lock count + // for the FDO. Keep track of the new value. + // + + if(FsContext->LockCount != 0) { + + do { + + InterlockedDecrement(&FsContext->LockCount); + + newDeviceLockCount = + InterlockedDecrement(&fdoExtension->ProtectedLockCount); + + } while(FsContext->LockCount != 0); + + // + // If the new lock count has been dropped to zero then issue a lock + // command to the device. + // + + DebugPrint((2, + "ClasspCleanupProtectedLocks: FDO secured lock count = %d " + "lock count = %d\n", + fdoExtension->ProtectedLockCount, + fdoExtension->LockCount)); + + if((newDeviceLockCount == 0) && (fdoExtension->LockCount == 0)) { + + SCSI_REQUEST_BLOCK srb; + PCDB cdb; + NTSTATUS status; + + DebugPrint((2, + "ClasspCleanupProtectedLocks: FDO lock count dropped " + "to zero\n")); + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + cdb = (PCDB) &(srb.Cdb); + + srb.CdbLength = 6; + + cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; + + // + // TRUE - prevent media removal. + // FALSE - allow media removal. + // + + cdb->MEDIA_REMOVAL.Prevent = FALSE; + + // + // Set timeout value. + // + + srb.TimeOutValue = fdoExtension->TimeOutValue; + status = ClassSendSrbSynchronous(fdoExtension->DeviceObject, + &srb, + NULL, + 0, + FALSE); + + DebugPrint((2, + "ClasspCleanupProtectedLocks: unlock request to drive " + "returned status %lx\n", status)); + } + } + + KeSetEvent(&fdoExtension->EjectSynchronizationEvent, + IO_NO_INCREMENT, + FALSE); + KeLeaveCriticalRegion(); + return; +} + + +VOID +ClasspCleanupDisableMcn( + IN PFILE_OBJECT_EXTENSION FsContext + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = + FsContext->DeviceObject->DeviceExtension; + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = + commonExtension->PartitionZeroExtension; + + ULONG newCount = 1; + + PAGED_CODE(); + + DebugPrint((ClassDebugTrace, + "ClasspCleanupDisableMcn called for %p\n", + FsContext->DeviceObject)); + DebugPrint((ClassDebugTrace, + "ClasspCleanupDisableMcn - FsContext %p is disabled " + "%d times\n", FsContext, FsContext->McnDisableCount)); + + // + // For each secure lock on this handle decrement the secured lock count + // for the FDO. Keep track of the new value. + // + + while(FsContext->McnDisableCount != 0) { + FsContext->McnDisableCount--; + ClassEnableMediaChangeDetection(fdoExtension); + } + + return; +} + + +#if 1 +/* + * BUGBUG REMOVE this old function implementation as soon as the + * boottime pagefile problems with the new one (below) + * are resolved. + */ +NTSTATUS +ClasspEjectionControl( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp, + IN MEDIA_LOCK_TYPE LockType, + IN BOOLEAN Lock + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = + (PCOMMON_DEVICE_EXTENSION) FdoExtension; + + PFILE_OBJECT_EXTENSION fsContext = NULL; + NTSTATUS status; + PSCSI_REQUEST_BLOCK srb = NULL; + BOOLEAN countChanged = FALSE; + + PAGED_CODE(); + + // + // Interlock with ejection and secure lock cleanup code. This is a + // user request so we can allow the stack to get swapped out while we + // wait for synchronization. + // + + status = KeWaitForSingleObject( + &(FdoExtension->EjectSynchronizationEvent), + UserRequest, + UserMode, + FALSE, + NULL); + + ASSERT(status == STATUS_SUCCESS); + + DebugPrint((2, + "ClasspEjectionControl: " + "Received request for %s lock type\n", + LockTypeStrings[LockType] + )); + + _SEH2_TRY { + PCDB cdb; + + srb = ClasspAllocateSrb(FdoExtension); + + if(srb == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + _SEH2_LEAVE; + } + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + cdb = (PCDB) srb->Cdb; + + // + // Determine if this is a "secured" request. + // + + if(LockType == SecureMediaLock) { + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PFILE_OBJECT fileObject = irpStack->FileObject; + + // + // Make sure that the file object we are supplied has a + // proper FsContext before we try doing a secured lock. + // + + if(fileObject != NULL) { + fsContext = ClasspGetFsContext(commonExtension, fileObject); + } + + if (fsContext == NULL) { + + // + // This handle isn't setup correctly. We can't let the + // operation go. + // + + status = STATUS_INVALID_PARAMETER; + _SEH2_LEAVE; + } + } + + if(Lock) { + + // + // This is a lock command. Reissue the command in case bus or + // device was reset and the lock was cleared. + // note: may need to decrement count if actual lock operation + // failed.... + // + + switch(LockType) { + + case SimpleMediaLock: { + FdoExtension->LockCount++; + countChanged = TRUE; + break; + } + + case SecureMediaLock: { + fsContext->LockCount++; + FdoExtension->ProtectedLockCount++; + countChanged = TRUE; + break; + } + + case InternalMediaLock: { + FdoExtension->InternalLockCount++; + countChanged = TRUE; + break; + } + } + + } else { + + // + // This is an unlock command. If it's a secured one then make sure + // the caller has a lock outstanding or return an error. + // note: may need to re-increment the count if actual unlock + // operation fails.... + // + + switch(LockType) { + + case SimpleMediaLock: { + if(FdoExtension->LockCount != 0) { + FdoExtension->LockCount--; + countChanged = TRUE; + } + break; + } + + case SecureMediaLock: { + if(fsContext->LockCount == 0) { + status = STATUS_INVALID_DEVICE_STATE; + _SEH2_LEAVE; + } + fsContext->LockCount--; + FdoExtension->ProtectedLockCount--; + countChanged = TRUE; + break; + } + + case InternalMediaLock: { + ASSERT(FdoExtension->InternalLockCount != 0); + FdoExtension->InternalLockCount--; + countChanged = TRUE; + break; + } + } + + // + // We only send an unlock command to the drive if both the + // secured and unsecured lock counts have dropped to zero. + // + + if((FdoExtension->ProtectedLockCount != 0) || + (FdoExtension->InternalLockCount != 0) || + (FdoExtension->LockCount != 0)) { + + status = STATUS_SUCCESS; + _SEH2_LEAVE; + } + } + + status = STATUS_SUCCESS; + if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { + + srb->CdbLength = 6; + cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; + + // + // TRUE - prevent media removal. + // FALSE - allow media removal. + // + + cdb->MEDIA_REMOVAL.Prevent = Lock; + + // + // Set timeout value. + // + + srb->TimeOutValue = FdoExtension->TimeOutValue; + + // + // The actual lock operation on the device isn't so important + // as the internal lock counts. Ignore failures. + // + + status = ClassSendSrbSynchronous(FdoExtension->DeviceObject, + srb, + NULL, + 0, + FALSE); + } + + } _SEH2_FINALLY { + + if (!NT_SUCCESS(status)) { + DebugPrint((2, + "ClasspEjectionControl: FAILED status %x -- " + "reverting lock counts\n", status)); + + if (countChanged) { + + // + // have to revert to previous counts if the + // lock/unlock operation actually failed. + // + + if(Lock) { + + switch(LockType) { + + case SimpleMediaLock: { + FdoExtension->LockCount--; + break; + } + + case SecureMediaLock: { + fsContext->LockCount--; + FdoExtension->ProtectedLockCount--; + break; + } + + case InternalMediaLock: { + FdoExtension->InternalLockCount--; + break; + } + } + + } else { + + switch(LockType) { + + case SimpleMediaLock: { + FdoExtension->LockCount++; + break; + } + + case SecureMediaLock: { + fsContext->LockCount++; + FdoExtension->ProtectedLockCount++; + break; + } + + case InternalMediaLock: { + FdoExtension->InternalLockCount++; + break; + } + } + } + + } + + } else { + + DebugPrint((2, + "ClasspEjectionControl: Succeeded\n")); + + } + + DebugPrint((2, + "ClasspEjectionControl: " + "Current Counts: Internal: %x Secure: %x Simple: %x\n", + FdoExtension->InternalLockCount, + FdoExtension->ProtectedLockCount, + FdoExtension->LockCount + )); + + KeSetEvent(&(FdoExtension->EjectSynchronizationEvent), + IO_NO_INCREMENT, + FALSE); + if (srb) { + ClassFreeOrReuseSrb(FdoExtension, srb); + } + + } _SEH2_END; + return status; +} + +#else + +/* + * BUGBUG RESTORE + * This is a new implementation of the function that doesn't thrash memory + * or depend on the srbLookasideList. + * HOWEVER, it seems to cause pagefile initialization to fail during boot + * for some reason. Need to resolve this before switching to this function. + */ +NTSTATUS +ClasspEjectionControl( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp, + IN MEDIA_LOCK_TYPE LockType, + IN BOOLEAN Lock + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; + PFILE_OBJECT_EXTENSION fsContext; + BOOLEAN fileHandleOk = TRUE; + BOOLEAN countChanged = FALSE; + NTSTATUS status; + + PAGED_CODE(); + + status = KeWaitForSingleObject( + &fdoExt->EjectSynchronizationEvent, + UserRequest, + UserMode, + FALSE, + NULL); + ASSERT(status == STATUS_SUCCESS); + + /* + * If this is a "secured" request, we have to make sure + * that the file handle is valid. + */ + if (LockType == SecureMediaLock){ + PIO_STACK_LOCATION thisSp = IoGetCurrentIrpStackLocation(Irp); + + /* + * Make sure that the file object we are supplied has a + * proper FsContext before we try doing a secured lock. + */ + if (thisSp->FileObject){ + PCOMMON_DEVICE_EXTENSION commonExt = (PCOMMON_DEVICE_EXTENSION)fdoExt; + fsContext = ClasspGetFsContext(commonExt, thisSp->FileObject); + } + else { + fsContext = NULL; + } + + if (!fsContext){ + ASSERT(fsContext); + fileHandleOk = FALSE; + } + } + + if (fileHandleOk){ + + /* + * Adjust the lock counts and make sure they make sense. + */ + status = STATUS_SUCCESS; + if (Lock){ + switch(LockType) { + case SimpleMediaLock: + fdoExt->LockCount++; + countChanged = TRUE; + break; + case SecureMediaLock: + fsContext->LockCount++; + fdoExt->ProtectedLockCount++; + countChanged = TRUE; + break; + case InternalMediaLock: + fdoExt->InternalLockCount++; + countChanged = TRUE; + break; + } + } + else { + /* + * This is an unlock command. If it's a secured one then make sure + * the caller has a lock outstanding or return an error. + */ + switch (LockType){ + case SimpleMediaLock: + if (fdoExt->LockCount > 0){ + fdoExt->LockCount--; + countChanged = TRUE; + } + else { + ASSERT(fdoExt->LockCount > 0); + status = STATUS_INTERNAL_ERROR; + } + break; + case SecureMediaLock: + if (fsContext->LockCount > 0){ + ASSERT(fdoExt->ProtectedLockCount > 0); + fsContext->LockCount--; + fdoExt->ProtectedLockCount--; + countChanged = TRUE; + } + else { + ASSERT(fsContext->LockCount > 0); + status = STATUS_INVALID_DEVICE_STATE; + } + break; + case InternalMediaLock: + ASSERT(fdoExt->InternalLockCount > 0); + fdoExt->InternalLockCount--; + countChanged = TRUE; + break; + } + } + + if (NT_SUCCESS(status)){ + /* + * We only send an unlock command to the drive if + * all the lock counts have dropped to zero. + */ + if (!Lock && + (fdoExt->ProtectedLockCount || + fdoExt->InternalLockCount || + fdoExt->LockCount)){ + + /* + * The lock count is still positive, so don't unlock yet. + */ + status = STATUS_SUCCESS; + } + else if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { + /* + * The device isn't removable media. don't send a cmd. + */ + status = STATUS_SUCCESS; + } + else { + TRANSFER_PACKET *pkt; + + pkt = DequeueFreeTransferPacket(Fdo, TRUE); + if (pkt){ + KEVENT event; + + /* + * Store the number of packets servicing the irp (one) + * inside the original IRP. It will be used to counted down + * to zero when the packet completes. + * Initialize the original IRP's status to success. + * If the packet fails, we will set it to the error status. + */ + Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1); + Irp->IoStatus.Status = STATUS_SUCCESS; + + /* + * Set this up as a SYNCHRONOUS transfer, submit it, + * and wait for the packet to complete. The result + * status will be written to the original irp. + */ + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + SetupEjectionTransferPacket(pkt, Lock, &event, Irp); + SubmitTransferPacket(pkt); + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = Irp->IoStatus.Status; + } + else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + } + } + else { + status = STATUS_INVALID_PARAMETER; + } + + if (!NT_SUCCESS(status) && countChanged) { + + // + // have to revert to previous counts if the + // lock/unlock operation actually failed. + // + + if(Lock) { + + switch(LockType) { + + case SimpleMediaLock: { + FdoExtension->LockCount--; + break; + } + + case SecureMediaLock: { + fsContext->LockCount--; + FdoExtension->ProtectedLockCount--; + break; + } + + case InternalMediaLock: { + FdoExtension->InternalLockCount--; + break; + } + } + + } else { + + switch(LockType) { + + case SimpleMediaLock: { + FdoExtension->LockCount++; + break; + } + + case SecureMediaLock: { + fsContext->LockCount++; + FdoExtension->ProtectedLockCount++; + break; + } + + case InternalMediaLock: { + FdoExtension->InternalLockCount++; + break; + } + } + } + } + + + + KeSetEvent(&fdoExt->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE); + + return status; +} +#endif + +PFILE_OBJECT_EXTENSION +ClasspGetFsContext( + IN PCOMMON_DEVICE_EXTENSION CommonExtension, + IN PFILE_OBJECT FileObject + ) +{ + PAGED_CODE(); + return GetDictionaryEntry(&(CommonExtension->FileObjectDictionary), + (ULONGLONG) FileObject); +} + diff --git a/drivers/storage/classpnp/data.c b/drivers/storage/classpnp/data.c new file mode 100644 index 00000000000..9bd638ba13c --- /dev/null +++ b/drivers/storage/classpnp/data.c @@ -0,0 +1,48 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + disk.c + +Abstract: + + SCSI disk class driver + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "classp.h" + +#ifdef ALLOC_DATA_PRAGMA +#pragma data_seg("PAGE") +#endif + +/* +#define FDO_HACK_CANNOT_LOCK_MEDIA (0x00000001) +#define FDO_HACK_GESN_IS_BAD (0x00000002) +*/ + +CLASSPNP_SCAN_FOR_SPECIAL_INFO ClassBadItems[] = { + { "" , "MITSUMI CD-ROM FX240" , NULL, 0x02 }, + { "" , "MITSUMI CD-ROM FX320" , NULL, 0x02 }, + { "" , "MITSUMI CD-ROM FX322" , NULL, 0x02 }, + { "" , "COMPAQ CRD-8481B" , NULL, 0x04 }, + { NULL , NULL , NULL, 0x0 } +}; + + +GUID ClassGuidQueryRegInfoEx = GUID_CLASSPNP_QUERY_REGINFOEX; + +#ifdef ALLOC_DATA_PRAGMA +#pragma data_seg() +#endif + diff --git a/drivers/storage/classpnp/debug.c b/drivers/storage/classpnp/debug.c new file mode 100644 index 00000000000..bea6bfb872f --- /dev/null +++ b/drivers/storage/classpnp/debug.c @@ -0,0 +1,693 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + debug.c + +Abstract: + + CLASSPNP debug code and data + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + + +#include "classp.h" +#include "debug.h" + +#if DBG + + // + // default to not breaking in for lost irps, five minutes before we even + // bother checking for lost irps, using standard debug print macros, and + // using a 64k debug print buffer + // + + #ifndef CLASS_GLOBAL_BREAK_ON_LOST_IRPS + #error "CLASS_GLOBAL_BREAK_ON_LOST_IRPS undefined" + #define CLASS_GLOBAL_BREAK_ON_LOST_IRPS 0 + #endif // CLASS_GLOBAL_BREAK_ON_LOST_IRPS + + #ifndef CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB + #error "CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB undefined" + #define CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB 300 + #endif // CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB + + #ifndef CLASS_GLOBAL_USE_DELAYED_RETRY + #error "CLASS_GLOBAL_USE_DELAYED_RETRY undefined" + #define CLASS_GLOBAL_USE_DELAYED_RETRY 1 + #endif // CLASS_GLOBAL_USE_DELAYED_RETRY + + #ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT + #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT undefined" + #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT 0 + #endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT + + #ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE + #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE undefined" + #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE 512 + #endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE + + #ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS + #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS undefined" + #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS 512 + #endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS + + #pragma data_seg("NONPAGE") + + + + CLASSPNP_GLOBALS ClasspnpGlobals; + + // + // the low sixteen bits are used to see if the debug level is high enough + // the high sixteen bits are used to singly enable debug levels 1-16 + // + LONG ClassDebug = 0xFFFFFFFF; + + BOOLEAN DebugTrapOnWarn = FALSE; + + VOID ClasspInitializeDebugGlobals() + { + KIRQL irql; + + if (InterlockedCompareExchange(&ClasspnpGlobals.Initializing, 1, 0) == 0) { + + KeInitializeSpinLock(&ClasspnpGlobals.SpinLock); + + KeAcquireSpinLock(&ClasspnpGlobals.SpinLock, &irql); + + DebugPrint((1, "CLASSPNP.SYS => Initializing ClasspnpGlobals...\n")); + + ClasspnpGlobals.Buffer = NULL; + ClasspnpGlobals.Index = -1; + ClasspnpGlobals.BreakOnLostIrps = CLASS_GLOBAL_BREAK_ON_LOST_IRPS; + ClasspnpGlobals.EachBufferSize = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE; + ClasspnpGlobals.NumberOfBuffers = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS; + ClasspnpGlobals.SecondsToWaitForIrps = CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB; + + // + // this should be the last item set + // + + ClasspnpGlobals.UseBufferedDebugPrint = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT; + + KeReleaseSpinLock(&ClasspnpGlobals.SpinLock, irql); + + InterlockedExchange(&ClasspnpGlobals.Initialized, 1); + + } + } + + + + /*++//////////////////////////////////////////////////////////////////////////// + + ClassDebugPrint() + + Routine Description: + + Debug print for all class drivers, NOOP on FRE versions. + Allows printing to a debug buffer (with auto fallback to kdprint) by + properly setting the Globals in classpnp on CHK versions. + + Arguments: + + Debug print level, or from 0 to 3 for legacy drivers. + + Return Value: + + None + + --*/ + VOID ClassDebugPrint(CLASS_DEBUG_LEVEL DebugPrintLevel, PCCHAR DebugMessage, ...) + { + va_list ap; + va_start(ap, DebugMessage); + + if ((DebugPrintLevel <= (ClassDebug & 0x0000ffff)) || + ((1 << (DebugPrintLevel + 15)) & ClassDebug)) { + + if (ClasspnpGlobals.UseBufferedDebugPrint && + ClasspnpGlobals.Buffer == NULL) { + + // + // this double-check prevents always taking + // a spinlock just to ensure we have a buffer + // + + KIRQL irql; + + KeAcquireSpinLock(&ClasspnpGlobals.SpinLock, &irql); + if (ClasspnpGlobals.Buffer == NULL) { + + SIZE_T bufferSize; + bufferSize = ClasspnpGlobals.NumberOfBuffers * + ClasspnpGlobals.EachBufferSize; + DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL, + "ClassDebugPrint: Allocating %x bytes for " + "classdebugprint buffer\n", bufferSize); + ClasspnpGlobals.Index = -1; + ClasspnpGlobals.Buffer = + ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'bDcS'); + DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL, + "ClassDebugPrint: Allocated buffer at %p\n", + ClasspnpGlobals.Buffer); + + } + KeReleaseSpinLock(&ClasspnpGlobals.SpinLock, irql); + + } + + if (ClasspnpGlobals.UseBufferedDebugPrint && + ClasspnpGlobals.Buffer != NULL) { + + // + // we never free the buffer, so once it exists, + // we can just print to it with immunity + // + + ULONG index; + PUCHAR buffer; + index = InterlockedIncrement(&ClasspnpGlobals.Index); + index %= ClasspnpGlobals.NumberOfBuffers; + index *= (ULONG)ClasspnpGlobals.EachBufferSize; + + buffer = ClasspnpGlobals.Buffer; + buffer += index; + + _vsnprintf(buffer, ClasspnpGlobals.EachBufferSize, DebugMessage, ap); + + } else { + + // + // either we could not allocate a buffer for debug prints + // or buffered debug prints are disabled + // + vDbgPrintEx(-1, DPFLTR_ERROR_LEVEL, DebugMessage, ap); + + } + + } + + va_end(ap); + + } + + + char *DbgGetIoctlStr(ULONG ioctl) + { + char *ioctlStr = "?"; + + switch (ioctl){ + + #undef MAKE_CASE + #define MAKE_CASE(ioctlCode) case ioctlCode: ioctlStr = #ioctlCode; break; + + MAKE_CASE(IOCTL_STORAGE_CHECK_VERIFY) + MAKE_CASE(IOCTL_STORAGE_CHECK_VERIFY2) + MAKE_CASE(IOCTL_STORAGE_MEDIA_REMOVAL) + MAKE_CASE(IOCTL_STORAGE_EJECT_MEDIA) + MAKE_CASE(IOCTL_STORAGE_LOAD_MEDIA) + MAKE_CASE(IOCTL_STORAGE_LOAD_MEDIA2) + MAKE_CASE(IOCTL_STORAGE_RESERVE) + MAKE_CASE(IOCTL_STORAGE_RELEASE) + MAKE_CASE(IOCTL_STORAGE_FIND_NEW_DEVICES) + MAKE_CASE(IOCTL_STORAGE_EJECTION_CONTROL) + MAKE_CASE(IOCTL_STORAGE_MCN_CONTROL) + MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_TYPES) + MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_TYPES_EX) + MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER) + MAKE_CASE(IOCTL_STORAGE_GET_HOTPLUG_INFO) + MAKE_CASE(IOCTL_STORAGE_RESET_BUS) + MAKE_CASE(IOCTL_STORAGE_RESET_DEVICE) + MAKE_CASE(IOCTL_STORAGE_GET_DEVICE_NUMBER) + MAKE_CASE(IOCTL_STORAGE_PREDICT_FAILURE) + MAKE_CASE(IOCTL_STORAGE_QUERY_PROPERTY) + MAKE_CASE(OBSOLETE_IOCTL_STORAGE_RESET_BUS) + MAKE_CASE(OBSOLETE_IOCTL_STORAGE_RESET_DEVICE) + } + + return ioctlStr; + } + + char *DbgGetScsiOpStr(PSCSI_REQUEST_BLOCK Srb) + { + PCDB pCdb = (PCDB)Srb->Cdb; + UCHAR scsiOp = pCdb->CDB6GENERIC.OperationCode; + char *scsiOpStr = "?"; + + switch (scsiOp){ + + #undef MAKE_CASE + #define MAKE_CASE(scsiOpCode) case scsiOpCode: scsiOpStr = #scsiOpCode; break; + + MAKE_CASE(SCSIOP_TEST_UNIT_READY) + MAKE_CASE(SCSIOP_REWIND) // aka SCSIOP_REZERO_UNIT + MAKE_CASE(SCSIOP_REQUEST_BLOCK_ADDR) + MAKE_CASE(SCSIOP_REQUEST_SENSE) + MAKE_CASE(SCSIOP_FORMAT_UNIT) + MAKE_CASE(SCSIOP_READ_BLOCK_LIMITS) + MAKE_CASE(SCSIOP_INIT_ELEMENT_STATUS) // aka SCSIOP_REASSIGN_BLOCKS + MAKE_CASE(SCSIOP_RECEIVE) // aka SCSIOP_READ6 + MAKE_CASE(SCSIOP_SEND) // aka SCSIOP_WRITE6, SCSIOP_PRINT + MAKE_CASE(SCSIOP_SLEW_PRINT) // aka SCSIOP_SEEK6, SCSIOP_TRACK_SELECT + MAKE_CASE(SCSIOP_SEEK_BLOCK) + MAKE_CASE(SCSIOP_PARTITION) + MAKE_CASE(SCSIOP_READ_REVERSE) + MAKE_CASE(SCSIOP_FLUSH_BUFFER) // aka SCSIOP_WRITE_FILEMARKS + MAKE_CASE(SCSIOP_SPACE) + MAKE_CASE(SCSIOP_INQUIRY) + MAKE_CASE(SCSIOP_VERIFY6) + MAKE_CASE(SCSIOP_RECOVER_BUF_DATA) + MAKE_CASE(SCSIOP_MODE_SELECT) + MAKE_CASE(SCSIOP_RESERVE_UNIT) + MAKE_CASE(SCSIOP_RELEASE_UNIT) + MAKE_CASE(SCSIOP_COPY) + MAKE_CASE(SCSIOP_ERASE) + MAKE_CASE(SCSIOP_MODE_SENSE) + MAKE_CASE(SCSIOP_START_STOP_UNIT) // aka SCSIOP_STOP_PRINT, SCSIOP_LOAD_UNLOAD + MAKE_CASE(SCSIOP_RECEIVE_DIAGNOSTIC) + MAKE_CASE(SCSIOP_SEND_DIAGNOSTIC) + MAKE_CASE(SCSIOP_MEDIUM_REMOVAL) + MAKE_CASE(SCSIOP_READ_FORMATTED_CAPACITY) + MAKE_CASE(SCSIOP_READ_CAPACITY) + MAKE_CASE(SCSIOP_READ) + MAKE_CASE(SCSIOP_WRITE) + MAKE_CASE(SCSIOP_SEEK) // aka SCSIOP_LOCATE, SCSIOP_POSITION_TO_ELEMENT + MAKE_CASE(SCSIOP_WRITE_VERIFY) + MAKE_CASE(SCSIOP_VERIFY) + MAKE_CASE(SCSIOP_SEARCH_DATA_HIGH) + MAKE_CASE(SCSIOP_SEARCH_DATA_EQUAL) + MAKE_CASE(SCSIOP_SEARCH_DATA_LOW) + MAKE_CASE(SCSIOP_SET_LIMITS) + MAKE_CASE(SCSIOP_READ_POSITION) + MAKE_CASE(SCSIOP_SYNCHRONIZE_CACHE) + MAKE_CASE(SCSIOP_COMPARE) + MAKE_CASE(SCSIOP_COPY_COMPARE) + MAKE_CASE(SCSIOP_WRITE_DATA_BUFF) + MAKE_CASE(SCSIOP_READ_DATA_BUFF) + MAKE_CASE(SCSIOP_CHANGE_DEFINITION) + MAKE_CASE(SCSIOP_READ_SUB_CHANNEL) + MAKE_CASE(SCSIOP_READ_TOC) + MAKE_CASE(SCSIOP_READ_HEADER) + MAKE_CASE(SCSIOP_PLAY_AUDIO) + MAKE_CASE(SCSIOP_GET_CONFIGURATION) + MAKE_CASE(SCSIOP_PLAY_AUDIO_MSF) + MAKE_CASE(SCSIOP_PLAY_TRACK_INDEX) + MAKE_CASE(SCSIOP_PLAY_TRACK_RELATIVE) + MAKE_CASE(SCSIOP_GET_EVENT_STATUS) + MAKE_CASE(SCSIOP_PAUSE_RESUME) + MAKE_CASE(SCSIOP_LOG_SELECT) + MAKE_CASE(SCSIOP_LOG_SENSE) + MAKE_CASE(SCSIOP_STOP_PLAY_SCAN) + MAKE_CASE(SCSIOP_READ_DISK_INFORMATION) + MAKE_CASE(SCSIOP_READ_TRACK_INFORMATION) + MAKE_CASE(SCSIOP_RESERVE_TRACK_RZONE) + MAKE_CASE(SCSIOP_SEND_OPC_INFORMATION) + MAKE_CASE(SCSIOP_MODE_SELECT10) + MAKE_CASE(SCSIOP_MODE_SENSE10) + MAKE_CASE(SCSIOP_CLOSE_TRACK_SESSION) + MAKE_CASE(SCSIOP_READ_BUFFER_CAPACITY) + MAKE_CASE(SCSIOP_SEND_CUE_SHEET) + MAKE_CASE(SCSIOP_PERSISTENT_RESERVE_IN) + MAKE_CASE(SCSIOP_PERSISTENT_RESERVE_OUT) + MAKE_CASE(SCSIOP_REPORT_LUNS) + MAKE_CASE(SCSIOP_BLANK) + MAKE_CASE(SCSIOP_SEND_KEY) + MAKE_CASE(SCSIOP_REPORT_KEY) + MAKE_CASE(SCSIOP_MOVE_MEDIUM) + MAKE_CASE(SCSIOP_LOAD_UNLOAD_SLOT) // aka SCSIOP_EXCHANGE_MEDIUM + MAKE_CASE(SCSIOP_SET_READ_AHEAD) + MAKE_CASE(SCSIOP_READ_DVD_STRUCTURE) + MAKE_CASE(SCSIOP_REQUEST_VOL_ELEMENT) + MAKE_CASE(SCSIOP_SEND_VOLUME_TAG) + MAKE_CASE(SCSIOP_READ_ELEMENT_STATUS) + MAKE_CASE(SCSIOP_READ_CD_MSF) + MAKE_CASE(SCSIOP_SCAN_CD) + MAKE_CASE(SCSIOP_SET_CD_SPEED) + MAKE_CASE(SCSIOP_PLAY_CD) + MAKE_CASE(SCSIOP_MECHANISM_STATUS) + MAKE_CASE(SCSIOP_READ_CD) + MAKE_CASE(SCSIOP_SEND_DVD_STRUCTURE) + MAKE_CASE(SCSIOP_INIT_ELEMENT_RANGE) + } + + return scsiOpStr; + } + + + char *DbgGetSrbStatusStr(PSCSI_REQUEST_BLOCK Srb) + { + char *srbStatStr = "?"; + + switch (Srb->SrbStatus){ + + #undef MAKE_CASE + #define MAKE_CASE(srbStat) \ + case srbStat: \ + srbStatStr = #srbStat; \ + break; \ + case srbStat|SRB_STATUS_QUEUE_FROZEN: \ + srbStatStr = #srbStat "|SRB_STATUS_QUEUE_FROZEN"; \ + break; \ + case srbStat|SRB_STATUS_AUTOSENSE_VALID: \ + srbStatStr = #srbStat "|SRB_STATUS_AUTOSENSE_VALID"; \ + break; \ + case srbStat|SRB_STATUS_QUEUE_FROZEN|SRB_STATUS_AUTOSENSE_VALID: \ + srbStatStr = #srbStat "|SRB_STATUS_QUEUE_FROZEN|SRB_STATUS_AUTOSENSE_VALID"; \ + break; + + MAKE_CASE(SRB_STATUS_PENDING) + MAKE_CASE(SRB_STATUS_SUCCESS) + MAKE_CASE(SRB_STATUS_ABORTED) + MAKE_CASE(SRB_STATUS_ABORT_FAILED) + MAKE_CASE(SRB_STATUS_ERROR) + MAKE_CASE(SRB_STATUS_BUSY) + MAKE_CASE(SRB_STATUS_INVALID_REQUEST) + MAKE_CASE(SRB_STATUS_INVALID_PATH_ID) + MAKE_CASE(SRB_STATUS_NO_DEVICE) + MAKE_CASE(SRB_STATUS_TIMEOUT) + MAKE_CASE(SRB_STATUS_SELECTION_TIMEOUT) + MAKE_CASE(SRB_STATUS_COMMAND_TIMEOUT) + MAKE_CASE(SRB_STATUS_MESSAGE_REJECTED) + MAKE_CASE(SRB_STATUS_BUS_RESET) + MAKE_CASE(SRB_STATUS_PARITY_ERROR) + MAKE_CASE(SRB_STATUS_REQUEST_SENSE_FAILED) + MAKE_CASE(SRB_STATUS_NO_HBA) + MAKE_CASE(SRB_STATUS_DATA_OVERRUN) + MAKE_CASE(SRB_STATUS_UNEXPECTED_BUS_FREE) + MAKE_CASE(SRB_STATUS_PHASE_SEQUENCE_FAILURE) + MAKE_CASE(SRB_STATUS_BAD_SRB_BLOCK_LENGTH) + MAKE_CASE(SRB_STATUS_REQUEST_FLUSHED) + MAKE_CASE(SRB_STATUS_INVALID_LUN) + MAKE_CASE(SRB_STATUS_INVALID_TARGET_ID) + MAKE_CASE(SRB_STATUS_BAD_FUNCTION) + MAKE_CASE(SRB_STATUS_ERROR_RECOVERY) + MAKE_CASE(SRB_STATUS_NOT_POWERED) + MAKE_CASE(SRB_STATUS_INTERNAL_ERROR) + } + + return srbStatStr; + } + + + char *DbgGetSenseCodeStr(PSCSI_REQUEST_BLOCK Srb) + { + char *senseCodeStr = "?"; + + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){ + PSENSE_DATA senseData; + UCHAR senseCode; + + ASSERT(Srb->SenseInfoBuffer); + senseData = Srb->SenseInfoBuffer; + senseCode = senseData->SenseKey & 0xf; + + switch (senseCode){ + + #undef MAKE_CASE + #define MAKE_CASE(snsCod) case snsCod: senseCodeStr = #snsCod; break; + + MAKE_CASE(SCSI_SENSE_NO_SENSE) + MAKE_CASE(SCSI_SENSE_RECOVERED_ERROR) + MAKE_CASE(SCSI_SENSE_NOT_READY) + MAKE_CASE(SCSI_SENSE_MEDIUM_ERROR) + MAKE_CASE(SCSI_SENSE_HARDWARE_ERROR) + MAKE_CASE(SCSI_SENSE_ILLEGAL_REQUEST) + MAKE_CASE(SCSI_SENSE_UNIT_ATTENTION) + MAKE_CASE(SCSI_SENSE_DATA_PROTECT) + MAKE_CASE(SCSI_SENSE_BLANK_CHECK) + MAKE_CASE(SCSI_SENSE_UNIQUE) + MAKE_CASE(SCSI_SENSE_COPY_ABORTED) + MAKE_CASE(SCSI_SENSE_ABORTED_COMMAND) + MAKE_CASE(SCSI_SENSE_EQUAL) + MAKE_CASE(SCSI_SENSE_VOL_OVERFLOW) + MAKE_CASE(SCSI_SENSE_MISCOMPARE) + MAKE_CASE(SCSI_SENSE_RESERVED) + } + } + + return senseCodeStr; + } + + + char *DbgGetAdditionalSenseCodeStr(PSCSI_REQUEST_BLOCK Srb) + { + char *adSenseCodeStr = "?"; + + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){ + PSENSE_DATA senseData; + UCHAR adSenseCode; + + ASSERT(Srb->SenseInfoBuffer); + senseData = Srb->SenseInfoBuffer; + adSenseCode = senseData->AdditionalSenseCode; + + switch (adSenseCode){ + + #undef MAKE_CASE + #define MAKE_CASE(adSnsCod) case adSnsCod: adSenseCodeStr = #adSnsCod; break; + + MAKE_CASE(SCSI_ADSENSE_NO_SENSE) + MAKE_CASE(SCSI_ADSENSE_LUN_NOT_READY) + MAKE_CASE(SCSI_ADSENSE_TRACK_ERROR) + MAKE_CASE(SCSI_ADSENSE_SEEK_ERROR) + MAKE_CASE(SCSI_ADSENSE_REC_DATA_NOECC) + MAKE_CASE(SCSI_ADSENSE_REC_DATA_ECC) + MAKE_CASE(SCSI_ADSENSE_ILLEGAL_COMMAND) + MAKE_CASE(SCSI_ADSENSE_ILLEGAL_BLOCK) + MAKE_CASE(SCSI_ADSENSE_INVALID_CDB) + MAKE_CASE(SCSI_ADSENSE_INVALID_LUN) + MAKE_CASE(SCSI_ADSENSE_WRITE_PROTECT) // aka SCSI_ADWRITE_PROTECT + MAKE_CASE(SCSI_ADSENSE_MEDIUM_CHANGED) + MAKE_CASE(SCSI_ADSENSE_BUS_RESET) + MAKE_CASE(SCSI_ADSENSE_INVALID_MEDIA) + MAKE_CASE(SCSI_ADSENSE_NO_MEDIA_IN_DEVICE) + MAKE_CASE(SCSI_ADSENSE_POSITION_ERROR) + MAKE_CASE(SCSI_ADSENSE_OPERATOR_REQUEST) + MAKE_CASE(SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED) + MAKE_CASE(SCSI_ADSENSE_COPY_PROTECTION_FAILURE) + MAKE_CASE(SCSI_ADSENSE_VENDOR_UNIQUE) + MAKE_CASE(SCSI_ADSENSE_MUSIC_AREA) + MAKE_CASE(SCSI_ADSENSE_DATA_AREA) + MAKE_CASE(SCSI_ADSENSE_VOLUME_OVERFLOW) + } + } + + return adSenseCodeStr; + } + + + char *DbgGetAdditionalSenseCodeQualifierStr(PSCSI_REQUEST_BLOCK Srb) + { + char *adSenseCodeQualStr = "?"; + + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){ + PSENSE_DATA senseData; + UCHAR adSenseCode; + UCHAR adSenseCodeQual; + + ASSERT(Srb->SenseInfoBuffer); + senseData = Srb->SenseInfoBuffer; + adSenseCode = senseData->AdditionalSenseCode; + adSenseCodeQual = senseData->AdditionalSenseCodeQualifier; + + switch (adSenseCode){ + + #undef MAKE_CASE + #define MAKE_CASE(adSnsCodQual) case adSnsCodQual: adSenseCodeQualStr = #adSnsCodQual; break; + + case SCSI_ADSENSE_LUN_NOT_READY: + switch (adSenseCodeQual){ + MAKE_CASE(SCSI_SENSEQ_CAUSE_NOT_REPORTABLE) + MAKE_CASE(SCSI_SENSEQ_BECOMING_READY) + MAKE_CASE(SCSI_SENSEQ_INIT_COMMAND_REQUIRED) + MAKE_CASE(SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED) + MAKE_CASE(SCSI_SENSEQ_FORMAT_IN_PROGRESS) + MAKE_CASE(SCSI_SENSEQ_REBUILD_IN_PROGRESS) + MAKE_CASE(SCSI_SENSEQ_RECALCULATION_IN_PROGRESS) + MAKE_CASE(SCSI_SENSEQ_OPERATION_IN_PROGRESS) + MAKE_CASE(SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS) + } + break; + case SCSI_ADSENSE_NO_SENSE: + switch (adSenseCodeQual){ + MAKE_CASE(SCSI_SENSEQ_FILEMARK_DETECTED) + MAKE_CASE(SCSI_SENSEQ_END_OF_MEDIA_DETECTED) + MAKE_CASE(SCSI_SENSEQ_SETMARK_DETECTED) + MAKE_CASE(SCSI_SENSEQ_BEGINNING_OF_MEDIA_DETECTED) + } + break; + case SCSI_ADSENSE_ILLEGAL_BLOCK: + switch (adSenseCodeQual){ + MAKE_CASE(SCSI_SENSEQ_ILLEGAL_ELEMENT_ADDR) + } + break; + case SCSI_ADSENSE_POSITION_ERROR: + switch (adSenseCodeQual){ + MAKE_CASE(SCSI_SENSEQ_DESTINATION_FULL) + MAKE_CASE(SCSI_SENSEQ_SOURCE_EMPTY) + } + break; + case SCSI_ADSENSE_INVALID_MEDIA: + switch (adSenseCodeQual){ + MAKE_CASE(SCSI_SENSEQ_INCOMPATIBLE_MEDIA_INSTALLED) + MAKE_CASE(SCSI_SENSEQ_UNKNOWN_FORMAT) + MAKE_CASE(SCSI_SENSEQ_INCOMPATIBLE_FORMAT) + MAKE_CASE(SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED) + } + break; + case SCSI_ADSENSE_OPERATOR_REQUEST: + switch (adSenseCodeQual){ + MAKE_CASE(SCSI_SENSEQ_STATE_CHANGE_INPUT) + MAKE_CASE(SCSI_SENSEQ_MEDIUM_REMOVAL) + MAKE_CASE(SCSI_SENSEQ_WRITE_PROTECT_ENABLE) + MAKE_CASE(SCSI_SENSEQ_WRITE_PROTECT_DISABLE) + } + break; + case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: + switch (adSenseCodeQual){ + MAKE_CASE(SCSI_SENSEQ_AUTHENTICATION_FAILURE) + MAKE_CASE(SCSI_SENSEQ_KEY_NOT_PRESENT) + MAKE_CASE(SCSI_SENSEQ_KEY_NOT_ESTABLISHED) + MAKE_CASE(SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION) + MAKE_CASE(SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT) + MAKE_CASE(SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR) + } + break; + } + } + + return adSenseCodeQualStr; + } + + + /* + * DbgCheckReturnedPkt + * + * Check a completed TRANSFER_PACKET for all sorts of error conditions + * and warn/trap appropriately. + */ + VOID DbgCheckReturnedPkt(TRANSFER_PACKET *Pkt) + { + PCDB pCdb = (PCDB)Pkt->Srb.Cdb; + + ASSERT(Pkt->Srb.OriginalRequest == Pkt->Irp); + ASSERT(Pkt->Srb.DataBuffer == Pkt->BufPtrCopy); + ASSERT(Pkt->Srb.DataTransferLength <= Pkt->BufLenCopy); + ASSERT(!Pkt->Irp->CancelRoutine); + + if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_PENDING){ + DBGERR(("SRB completed with status PENDING in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)", + Pkt, + DBGGETSCSIOPSTR(&Pkt->Srb), + DBGGETSRBSTATUSSTR(&Pkt->Srb), + (ULONG)Pkt->Srb.SrbStatus, + Pkt->Irp->IoStatus.Status)); + } + else if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SUCCESS){ + /* + * Make sure SRB and IRP status match. + */ + if (!NT_SUCCESS(Pkt->Irp->IoStatus.Status)){ + DBGWARN(("SRB and IRP status don't match in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)", + Pkt, + DBGGETSCSIOPSTR(&Pkt->Srb), + DBGGETSRBSTATUSSTR(&Pkt->Srb), + (ULONG)Pkt->Srb.SrbStatus, + Pkt->Irp->IoStatus.Status)); + } + + if (Pkt->Irp->IoStatus.Information != Pkt->Srb.DataTransferLength){ + DBGERR(("SRB and IRP result transfer lengths don't match in succeeded packet %ph: (op=%s, SrbStatus=%s, Srb.DataTransferLength=%xh, Irp->IoStatus.Information=%xh).", + Pkt, + DBGGETSCSIOPSTR(&Pkt->Srb), + DBGGETSRBSTATUSSTR(&Pkt->Srb), + Pkt->Srb.DataTransferLength, + Pkt->Irp->IoStatus.Information)); + } + } + else { + if (NT_SUCCESS(Pkt->Irp->IoStatus.Status)){ + DBGWARN(("SRB and IRP status don't match in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)", + Pkt, + DBGGETSCSIOPSTR(&Pkt->Srb), + DBGGETSRBSTATUSSTR(&Pkt->Srb), + (ULONG)Pkt->Srb.SrbStatus, + Pkt->Irp->IoStatus.Status)); + } + DBGTRACE(ClassDebugWarning, ("Packet %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", + Pkt, + DBGGETSCSIOPSTR(&Pkt->Srb), + DBGGETSRBSTATUSSTR(&Pkt->Srb), + (ULONG)Pkt->Srb.SrbStatus, + Pkt->Irp->IoStatus.Status, + DBGGETSENSECODESTR(&Pkt->Srb), + DBGGETADSENSECODESTR(&Pkt->Srb), + DBGGETADSENSEQUALIFIERSTR(&Pkt->Srb))); + + /* + * If the SRB failed with underrun or overrun, then the actual + * transferred length should be returned in both SRB and IRP. + * (SRB's only have an error status for overrun, so it's overloaded). + */ + if ((SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) && + (Pkt->Irp->IoStatus.Information != Pkt->Srb.DataTransferLength)){ + DBGERR(("SRB and IRP result transfer lengths don't match in failed packet %ph: (op=%s, SrbStatus=%s, Srb.DataTransferLength=%xh, Irp->IoStatus.Information=%xh).", + Pkt, + DBGGETSCSIOPSTR(&Pkt->Srb), + DBGGETSRBSTATUSSTR(&Pkt->Srb), + Pkt->Srb.DataTransferLength, + Pkt->Irp->IoStatus.Information)); + } + } + + + /* + * Some miniport drivers have been caught changing the SCSI operation + * code in the SRB. This is absolutely disallowed as it breaks our error handling. + */ + switch (pCdb->CDB10.OperationCode){ + case SCSIOP_MEDIUM_REMOVAL: + case SCSIOP_MODE_SENSE: + case SCSIOP_READ_CAPACITY: + case SCSIOP_READ: + case SCSIOP_WRITE: + case SCSIOP_START_STOP_UNIT: + break; + default: + DBGERR(("Miniport illegally changed Srb.Cdb.OperationCode in packet %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", + Pkt, + DBGGETSCSIOPSTR(&Pkt->Srb), + DBGGETSRBSTATUSSTR(&Pkt->Srb), + (ULONG)Pkt->Srb.SrbStatus, + Pkt->Irp->IoStatus.Status, + DBGGETSENSECODESTR(&Pkt->Srb), + DBGGETADSENSECODESTR(&Pkt->Srb), + DBGGETADSENSEQUALIFIERSTR(&Pkt->Srb))); + break; + } + + } + +#else + + // We have to keep this in the retail build for legacy. + VOID ClassDebugPrint(CLASS_DEBUG_LEVEL DebugPrintLevel, PCCHAR DebugMessage, ...) + { + DbgPrint("retail build\n"); + } + +#endif + diff --git a/drivers/storage/classpnp/debug.h b/drivers/storage/classpnp/debug.h new file mode 100644 index 00000000000..2fa64c41fe1 --- /dev/null +++ b/drivers/storage/classpnp/debug.h @@ -0,0 +1,148 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + debug.h + +Abstract: + + +Author: + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + + +VOID ClassDebugPrint(CLASS_DEBUG_LEVEL DebugPrintLevel, PCCHAR DebugMessage, ...); + +#if DBG + + typedef struct _CLASSPNP_GLOBALS { + + // + // whether or not to ASSERT for lost irps + // + + ULONG BreakOnLostIrps; + ULONG SecondsToWaitForIrps; + + // + // use a buffered debug print to help + // catch timing issues that do not + // reproduce with std debugprints enabled + // + + ULONG UseBufferedDebugPrint; + ULONG UseDelayedRetry; + + // + // the next four are the buffered printing support + // (currently unimplemented) and require the spinlock + // to use + // + + ULONG Index; // index into buffer + KSPIN_LOCK SpinLock; + PUCHAR Buffer; // requires spinlock to access + ULONG NumberOfBuffers; // number of buffers available + SIZE_T EachBufferSize; // size of each buffer + + // + // interlocked variables to initialize + // this data only once + // + + LONG Initializing; + LONG Initialized; + + } CLASSPNP_GLOBALS, *PCLASSPNP_GLOBALS; + + #define DBGTRACE(dbgTraceLevel, args_in_parens) \ + if (ClassDebug & (1 << (dbgTraceLevel+15))){ \ + DbgPrint("CLASSPNP> *** TRACE *** (file %s, line %d)\n", __FILE__, __LINE__ ); \ + DbgPrint(" > "); \ + DbgPrint args_in_parens; \ + DbgPrint("\n"); \ + if (DebugTrapOnWarn && (dbgTraceLevel == ClassDebugWarning)){ \ + DbgBreakPoint(); \ + } \ + } + #define DBGWARN(args_in_parens) \ + { \ + DbgPrint("CLASSPNP> *** WARNING *** (file %s, line %d)\n", __FILE__, __LINE__ ); \ + DbgPrint(" > "); \ + DbgPrint args_in_parens; \ + DbgPrint("\n"); \ + if (DebugTrapOnWarn){ \ + DbgBreakPoint(); \ + } \ + } + #define DBGERR(args_in_parens) \ + { \ + DbgPrint("CLASSPNP> *** ERROR *** (file %s, line %d)\n", __FILE__, __LINE__ ); \ + DbgPrint(" > "); \ + DbgPrint args_in_parens; \ + DbgPrint("\n"); \ + DbgBreakPoint(); \ + } + #define DBGTRAP(args_in_parens) \ + { \ + DbgPrint("CLASSPNP> *** COVERAGE TRAP *** (file %s, line %d)\n", __FILE__, __LINE__ ); \ + DbgPrint(" > "); \ + DbgPrint args_in_parens; \ + DbgPrint("\n"); \ + DbgBreakPoint(); \ + } + + + #define DBGGETIOCTLSTR(_ioctl) DbgGetIoctlStr(_ioctl) + #define DBGGETSCSIOPSTR(_pSrb) DbgGetScsiOpStr(_pSrb) + #define DBGGETSENSECODESTR(_pSrb) DbgGetSenseCodeStr(_pSrb) + #define DBGGETADSENSECODESTR(_pSrb) DbgGetAdditionalSenseCodeStr(_pSrb) + #define DBGGETADSENSEQUALIFIERSTR(_pSrb) DbgGetAdditionalSenseCodeQualifierStr(_pSrb) + #define DBGCHECKRETURNEDPKT(_pkt) DbgCheckReturnedPkt(_pkt) + #define DBGGETSRBSTATUSSTR(_pSrb) DbgGetSrbStatusStr(_pSrb) + + VOID ClasspInitializeDebugGlobals(); + char *DbgGetIoctlStr(ULONG ioctl); + char *DbgGetScsiOpStr(PSCSI_REQUEST_BLOCK Srb); + char *DbgGetSenseCodeStr(PSCSI_REQUEST_BLOCK Srb); + char *DbgGetAdditionalSenseCodeStr(PSCSI_REQUEST_BLOCK Srb); + char *DbgGetAdditionalSenseCodeQualifierStr(PSCSI_REQUEST_BLOCK Srb); + VOID DbgCheckReturnedPkt(TRANSFER_PACKET *Pkt); + char *DbgGetSrbStatusStr(PSCSI_REQUEST_BLOCK Srb); + + + extern CLASSPNP_GLOBALS ClasspnpGlobals; + extern LONG ClassDebug; + extern BOOLEAN DebugTrapOnWarn; + +#else + + #define ClasspInitializeDebugGlobals() + #define DBGWARN(args_in_parens) + #define DBGERR(args_in_parens) + #define DBGTRACE(dbgTraceLevel, args_in_parens) + #define DBGTRAP(args_in_parens) + + #define DBGGETIOCTLSTR(_ioctl) + #define DBGGETSCSIOPSTR(_pSrb) + #define DBGGETSENSECODESTR(_pSrb) + #define DBGGETADSENSECODESTR(_pSrb) + #define DBGGETADSENSEQUALIFIERSTR(_pSrb) + #define DBGCHECKRETURNEDPKT(_pkt) + #define DBGGETSRBSTATUSSTR(_pSrb) + +#endif + + diff --git a/drivers/storage/classpnp/dictlib.c b/drivers/storage/classpnp/dictlib.c new file mode 100644 index 00000000000..ba479fe9b52 --- /dev/null +++ b/drivers/storage/classpnp/dictlib.c @@ -0,0 +1,216 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1990 - 1999 + +Module Name: + + dictlib.c + +Abstract: + + Support library for maintaining a dictionary list (list of objects + referenced by a key value). + +Environment: + + kernel mode only + +Notes: + + This module generates a static library + +Revision History: + +--*/ + +#include +#include + +#define DICTIONARY_SIGNATURE (((ULONG)'dict' << 32) + 'sig ') + +struct _DICTIONARY_HEADER { + struct _DICTIONARY_HEADER* Next; + ULONGLONG Key; + UCHAR Data[0]; +}; + +struct _DICTIONARY_HEADER; +typedef struct _DICTIONARY_HEADER DICTIONARY_HEADER, *PDICTIONARY_HEADER; + + +VOID +InitializeDictionary( + IN PDICTIONARY Dictionary + ) +{ + RtlZeroMemory(Dictionary, sizeof(Dictionary)); + Dictionary->Signature = DICTIONARY_SIGNATURE; + KeInitializeSpinLock(&Dictionary->SpinLock); + return; +} + + +BOOLEAN +TestDictionarySignature( + IN PDICTIONARY Dictionary + ) +{ + return Dictionary->Signature == DICTIONARY_SIGNATURE; +} + +NTSTATUS +AllocateDictionaryEntry( + IN PDICTIONARY Dictionary, + IN ULONGLONG Key, + IN ULONG Size, + IN ULONG Tag, + OUT PVOID *Entry + ) +{ + PDICTIONARY_HEADER header; + KIRQL oldIrql; + PDICTIONARY_HEADER *entry; + + NTSTATUS status = STATUS_SUCCESS; + + *Entry = NULL; + + header = ExAllocatePoolWithTag(NonPagedPool, + Size + sizeof(DICTIONARY_HEADER), + Tag); + + if(header == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(header, sizeof(DICTIONARY_HEADER) + Size); + header->Key = Key; + + // + // Find the correct location for this entry in the dictionary. + // + + KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql); + + TRY { + + entry = &(Dictionary->List); + + while(*entry != NULL) { + if((*entry)->Key == Key) { + + // + // Dictionary must have unique keys. + // + + status = STATUS_OBJECT_NAME_COLLISION; + LEAVE; + + } else if ((*entry)->Key < Key) { + + // + // We will go ahead and insert the key in here. + // + break; + } else { + entry = &((*entry)->Next); + } + } + + // + // If we make it here then we will go ahead and do the insertion. + // + + header->Next = *entry; + *entry = header; + + } FINALLY { + KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql); + + if(!NT_SUCCESS(status)) { + ExFreePool(header); + } else { + *Entry = (PVOID) header->Data; + } + } + return status; +} + + +PVOID +GetDictionaryEntry( + IN PDICTIONARY Dictionary, + IN ULONGLONG Key + ) +{ + PDICTIONARY_HEADER entry; + PVOID data; + KIRQL oldIrql; + + + data = NULL; + + KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql); + + entry = Dictionary->List; + while (entry != NULL) { + + if (entry->Key == Key) { + data = entry->Data; + break; + } else { + entry = entry->Next; + } + } + + KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql); + + return data; +} + + +VOID +FreeDictionaryEntry( + IN PDICTIONARY Dictionary, + IN PVOID Entry + ) +{ + PDICTIONARY_HEADER header; + PDICTIONARY_HEADER *entry; + KIRQL oldIrql; + BOOLEAN found; + + found = FALSE; + header = CONTAINING_RECORD(Entry, DICTIONARY_HEADER, Data); + + KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql); + + entry = &(Dictionary->List); + while(*entry != NULL) { + + if(*entry == header) { + *entry = header->Next; + found = TRUE; + break; + } else { + entry = &(*entry)->Next; + } + } + + KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql); + + // + // calling this w/an invalid pointer invalidates the dictionary system, + // so ASSERT() that we never try to Free something not in the list + // + + ASSERT(found); + if (found) { + ExFreePool(header); + } + + return; + +} + + diff --git a/drivers/storage/classpnp/lock.c b/drivers/storage/classpnp/lock.c new file mode 100644 index 00000000000..deea29eead6 --- /dev/null +++ b/drivers/storage/classpnp/lock.c @@ -0,0 +1,418 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1990 - 1998 + +Module Name: + + lock.c + +Abstract: + + This is the NT SCSI port driver. + +Environment: + + kernel mode only + +Notes: + + This module is a driver dll for scsi miniports. + +Revision History: + +--*/ + +#include "classp.h" +#include "debug.h" + + +LONG LockHighWatermark = 0; +LONG LockLowWatermark = 0; +LONG MaxLockedMinutes = 5; + +// +// Structure used for tracking remove lock allocations in checked builds +// +typedef struct _REMOVE_TRACKING_BLOCK { + struct _REMOVE_TRACKING_BLOCK *NextBlock; + PVOID Tag; + LARGE_INTEGER TimeLocked; + PCSTR File; + ULONG Line; +} REMOVE_TRACKING_BLOCK, *PREMOVE_TRACKING_BLOCK; + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassAcquireRemoveLockEx() + +Routine Description: + + This routine is called to acquire the remove lock on the device object. + While the lock is held, the caller can assume that no pending pnp REMOVE + requests will be completed. + + The lock should be acquired immediately upon entering a dispatch routine. + It should also be acquired before creating any new reference to the + device object if there's a chance of releasing the reference before the + new one is done. + + This routine will return TRUE if the lock was successfully acquired or + FALSE if it cannot be because the device object has already been removed. + +Arguments: + + DeviceObject - the device object to lock + + Tag - Used for tracking lock allocation and release. If an irp is + specified when acquiring the lock then the same Tag must be + used to release the lock before the Tag is completed. + +Return Value: + + The value of the IsRemoved flag in the device extension. If this is + non-zero then the device object has received a Remove irp and non-cleanup + IRP's should fail. + + If the value is REMOVE_COMPLETE, the caller should not even release the + lock. + +--*/ +ULONG +ClassAcquireRemoveLockEx( + IN PDEVICE_OBJECT DeviceObject, + IN OPTIONAL PVOID Tag, + IN PCSTR File, + IN ULONG Line + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + LONG lockValue; + + + + // + // Grab the remove lock + // + lockValue = InterlockedIncrement(&commonExtension->RemoveLock); + + #if DBG + + DebugPrint((ClassDebugRemoveLock, "ClassAcquireRemoveLock: " + "Acquired for Object %p & irp %p - count is %d\n", + DeviceObject, Tag, lockValue)); + + ASSERTMSG("ClassAcquireRemoveLock - lock value was negative : ", + (lockValue > 0)); + + ASSERTMSG("RemoveLock increased to meet LockHighWatermark", + ((LockHighWatermark == 0) || + (lockValue != LockHighWatermark))); + + if (commonExtension->IsRemoved != REMOVE_COMPLETE){ + PREMOVE_TRACKING_BLOCK trackingBlock; + + trackingBlock = ExAllocatePool(NonPagedPool, + sizeof(REMOVE_TRACKING_BLOCK)); + + if(trackingBlock == NULL) { + + KIRQL oldIrql; + + KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, + &oldIrql); + + commonExtension->RemoveTrackingUntrackedCount++; + + DebugPrint((ClassDebugWarning, ">>>>>ClassAcquireRemoveLock: " + "Cannot track Tag %p - currently %d untracked requsts\n", + Tag, commonExtension->RemoveTrackingUntrackedCount)); + + KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, + oldIrql); + } + else { + PREMOVE_TRACKING_BLOCK *removeTrackingList = + (PREMOVE_TRACKING_BLOCK)&commonExtension->RemoveTrackingList; + + KIRQL oldIrql; + + trackingBlock->Tag = Tag; + + trackingBlock->File = File; + trackingBlock->Line = Line; + + KeQueryTickCount((&trackingBlock->TimeLocked)); + + KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, + &oldIrql); + + while(*removeTrackingList != NULL) { + + if((*removeTrackingList)->Tag > Tag) { + break; + } + + if((*removeTrackingList)->Tag == Tag) { + + DebugPrint((ClassDebugError, ">>>>>ClassAcquireRemoveLock: " + "already tracking Tag %p\n", Tag)); + DebugPrint((ClassDebugError, ">>>>>ClassAcquireRemoveLock: " + "acquired in file %s on line %d\n", + (*removeTrackingList)->File, + (*removeTrackingList)->Line)); + ASSERT(FALSE); + } + + removeTrackingList = &((*removeTrackingList)->NextBlock); + } + + trackingBlock->NextBlock = *removeTrackingList; + *removeTrackingList = trackingBlock; + + KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, + oldIrql); + + } + } + + #endif + + return (commonExtension->IsRemoved); +} + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassReleaseRemoveLock() + +Routine Description: + + This routine is called to release the remove lock on the device object. It + must be called when finished using a previously locked reference to the + device object. If an Tag was specified when acquiring the lock then the + same Tag must be specified when releasing the lock. + + When the lock count reduces to zero, this routine will signal the waiting + remove Tag to delete the device object. As a result the DeviceObject + pointer should not be used again once the lock has been released. + +Arguments: + + DeviceObject - the device object to lock + + Tag - The irp (if any) specified when acquiring the lock. This is used + for lock tracking purposes + +Return Value: + + none + +--*/ +VOID +ClassReleaseRemoveLock( + IN PDEVICE_OBJECT DeviceObject, + IN OPTIONAL PIRP Tag + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + LONG lockValue; + + #if DBG + PREMOVE_TRACKING_BLOCK *listEntry = + (PREMOVE_TRACKING_BLOCK)&commonExtension->RemoveTrackingList; + + BOOLEAN found = FALSE; + + LONGLONG maxCount; + + BOOLEAN isRemoved = (commonExtension->IsRemoved == REMOVE_COMPLETE); + + KIRQL oldIrql; + + if(isRemoved) { + DBGTRAP(("ClassReleaseRemoveLock: REMOVE_COMPLETE set; this should never happen")); + InterlockedDecrement(&(commonExtension->RemoveLock)); + return; + } + + // + // Check the tick count and make sure this thing hasn't been locked + // for more than MaxLockedMinutes. + // + + maxCount = KeQueryTimeIncrement() * 10; // microseconds + maxCount *= 1000; // milliseconds + maxCount *= 1000; // seconds + maxCount *= 60; // minutes + maxCount *= MaxLockedMinutes; + + DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: " + "maxCount = %0I64x\n", maxCount)); + + KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, + &oldIrql); + + while(*listEntry != NULL) { + + PREMOVE_TRACKING_BLOCK block; + LARGE_INTEGER difference; + + block = *listEntry; + + KeQueryTickCount((&difference)); + + difference.QuadPart -= block->TimeLocked.QuadPart; + + DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: " + "Object %p (tag %p) locked for %I64d ticks\n", + DeviceObject, block->Tag, difference.QuadPart)); + + if(difference.QuadPart >= maxCount) { + + DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: " + "Object %p (tag %p) locked for %I64d ticks - TOO LONG\n", + DeviceObject, block->Tag, difference.QuadPart)); + DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: " + "Lock acquired in file %s on line %d\n", + block->File, block->Line)); + ASSERT(FALSE); + } + + if((found == FALSE) && ((*listEntry)->Tag == Tag)) { + + *listEntry = block->NextBlock; + ExFreePool(block); + found = TRUE; + + } else { + + listEntry = &((*listEntry)->NextBlock); + + } + } + + if(!found) { + if(commonExtension->RemoveTrackingUntrackedCount == 0) { + DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: " + "Couldn't find Tag %p in the lock tracking list\n", + Tag)); + ASSERT(FALSE); + } else { + DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: " + "Couldn't find Tag %p in the lock tracking list - " + "may be one of the %d untracked requests still " + "outstanding\n", + Tag, + commonExtension->RemoveTrackingUntrackedCount)); + + commonExtension->RemoveTrackingUntrackedCount--; + ASSERT(commonExtension->RemoveTrackingUntrackedCount >= 0); + } + } + + KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, + oldIrql); + + #endif + + lockValue = InterlockedDecrement(&commonExtension->RemoveLock); + + DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: " + "Released for Object %p & irp %p - count is %d\n", + DeviceObject, Tag, lockValue)); + + ASSERT(lockValue >= 0); + + ASSERTMSG("RemoveLock decreased to meet LockLowWatermark", + ((LockLowWatermark == 0) || !(lockValue == LockLowWatermark))); + + if(lockValue == 0) { + + ASSERT(commonExtension->IsRemoved); + + // + // The device needs to be removed. Signal the remove event + // that it's safe to go ahead. + // + + DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: " + "Release for object %p & irp %p caused lock to go to zero\n", + DeviceObject, Tag)); + + KeSetEvent(&commonExtension->RemoveEvent, + IO_NO_INCREMENT, + FALSE); + + } + return; +} + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassCompleteRequest() + +Routine Description: + + This routine is a wrapper around (and should be used instead of) + IoCompleteRequest. It is used primarily for debugging purposes. + The routine will assert if the Irp being completed is still holding + the release lock. + +Arguments: + + DeviceObject - the device object that was handling this request + + Irp - the irp to be completed by IoCompleteRequest + + PriorityBoost - the priority boost to pass to IoCompleteRequest + +Return Value: + + none + +--*/ +VOID +ClassCompleteRequest( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN CCHAR PriorityBoost + ) +{ + + #if DBG + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PREMOVE_TRACKING_BLOCK *listEntry = + (PREMOVE_TRACKING_BLOCK)&commonExtension->RemoveTrackingList; + + KIRQL oldIrql; + + KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, + &oldIrql); + + while(*listEntry != NULL) { + + if((*listEntry)->Tag == Irp) { + break; + } + + listEntry = &((*listEntry)->NextBlock); + } + + if(*listEntry != NULL) { + + DebugPrint((ClassDebugError, ">>>>>ClassCompleteRequest: " + "Irp %p completed while still holding the remove lock\n", + Irp)); + DebugPrint((ClassDebugError, ">>>>>ClassCompleteRequest: " + "Lock acquired in file %s on line %d\n", + (*listEntry)->File, (*listEntry)->Line)); + ASSERT(FALSE); + } + + KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql); + #endif + + IoCompleteRequest(Irp, PriorityBoost); + return; +} // end ClassCompleteRequest() + diff --git a/drivers/storage/classpnp/obsolete.c b/drivers/storage/classpnp/obsolete.c new file mode 100644 index 00000000000..1dec49c4306 --- /dev/null +++ b/drivers/storage/classpnp/obsolete.c @@ -0,0 +1,1060 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + obsolete.c + +Abstract: + + THESE ARE EXPORTED CLASSPNP FUNCTIONS (and their subroutines) + WHICH ARE NOW OBSOLETE. + BUT WE NEED TO KEEP THEM AROUND FOR LEGACY REASONS. + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#include "classp.h" +#include "debug.h" + +PIRP ClassRemoveCScanList(IN PCSCAN_LIST List); +VOID ClasspInitializeCScanList(IN PCSCAN_LIST List); + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, ClassDeleteSrbLookasideList) + #pragma alloc_text(PAGE, ClassInitializeSrbLookasideList) +#endif + +typedef struct _CSCAN_LIST_ENTRY { + LIST_ENTRY Entry; + ULONGLONG BlockNumber; +} CSCAN_LIST_ENTRY, *PCSCAN_LIST_ENTRY; + + + + + +/* + * ClassSplitRequest + * + * This is a legacy exported function. + * It is called by storage miniport driver that have their own + * StartIo routine when the transfer size is too large for the hardware. + * We map it to our new read/write handler. + */ +VOID ClassSplitRequest(IN PDEVICE_OBJECT Fdo, IN PIRP Irp, IN ULONG MaximumBytes) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + + if (MaximumBytes > fdoData->HwMaxXferLen) { + DBGERR(("ClassSplitRequest - driver requesting split to size that " + "hardware is unable to handle!\n")); + } + + if (MaximumBytes < fdoData->HwMaxXferLen){ + DBGWARN(("ClassSplitRequest - driver requesting smaller HwMaxXferLen " + "than required")); + fdoData->HwMaxXferLen = MAX(MaximumBytes, PAGE_SIZE); + } + + ServiceTransferRequest(Fdo, Irp); +} + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassIoCompleteAssociated() + +Routine Description: + + This routine executes when the port driver has completed a request. + It looks at the SRB status in the completing SRB and if not success + it checks for valid request sense buffer information. If valid, the + info is used to update status with more precise message of type of + error. This routine deallocates the SRB. This routine is used for + requests which were build by split request. After it has processed + the request it decrements the Irp count in the master Irp. If the + count goes to zero then the master Irp is completed. + +Arguments: + + Fdo - Supplies the functional device object which represents the target. + + Irp - Supplies the Irp which has completed. + + Context - Supplies a pointer to the SRB. + +Return Value: + + NT status + +--*/ +NTSTATUS +ClassIoCompleteAssociated( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp, + IN PVOID Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK srb = Context; + + PIRP originalIrp = Irp->AssociatedIrp.MasterIrp; + LONG irpCount; + + NTSTATUS status; + BOOLEAN retry; + + DBGWARN(("ClassIoCompleteAssociated is OBSOLETE !")); + + // + // Check SRB status for success of completing request. + // + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + ULONG retryInterval; + + DebugPrint((2,"ClassIoCompleteAssociated: IRP %p, SRB %p", Irp, srb)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ClassReleaseQueue(Fdo); + } + + retry = ClassInterpretSenseInfo( + Fdo, + srb, + irpStack->MajorFunction, + irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? + irpStack->Parameters.DeviceIoControl.IoControlCode : + 0, + MAXIMUM_RETRIES - + ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4), + &status, + &retryInterval); + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && (irpStack->Parameters.Others.Argument4--)) { + + // + // Retry request. If the class driver has supplied a StartIo, + // call it directly for retries. + // + + DebugPrint((1, "Retry request %p\n", Irp)); + + if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { + FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb); + } + + RetryRequest(Fdo, Irp, srb, TRUE, retryInterval); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; + + } // end if (SRB_STATUS(srb->SrbStatus) ... + + // + // Return SRB to list. + // + + if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { + FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb); + } + + ClassFreeOrReuseSrb(fdoExtension, srb); + + // + // Set status in completing IRP. + // + + Irp->IoStatus.Status = status; + + DebugPrint((2, "ClassIoCompleteAssociated: Partial xfer IRP %p\n", Irp)); + + // + // Get next stack location. This original request is unused + // except to keep track of the completing partial IRPs so the + // stack location is valid. + // + + irpStack = IoGetNextIrpStackLocation(originalIrp); + + // + // Update status only if error so that if any partial transfer + // completes with error, then the original IRP will return with + // error. If any of the asynchronous partial transfer IRPs fail, + // with an error then the original IRP will return 0 bytes transfered. + // This is an optimization for successful transfers. + // + + if (!NT_SUCCESS(status)) { + + originalIrp->IoStatus.Status = status; + originalIrp->IoStatus.Information = 0; + + // + // Set the hard error if necessary. + // + + if (IoIsErrorUserInduced(status)) { + + // + // Store DeviceObject for filesystem. + // + + IoSetHardErrorOrVerifyDevice(originalIrp, Fdo); + } + } + + // + // Decrement and get the count of remaining IRPs. + // + + irpCount = InterlockedDecrement( + (PLONG)&irpStack->Parameters.Others.Argument1); + + DebugPrint((2, "ClassIoCompleteAssociated: Partial IRPs left %d\n", + irpCount)); + + // + // Ensure that the irpCount doesn't go negative. This was happening once + // because classpnp would get confused if it ran out of resources when + // splitting the request. + // + + ASSERT(irpCount >= 0); + + if (irpCount == 0) { + + // + // All partial IRPs have completed. + // + + DebugPrint((2, + "ClassIoCompleteAssociated: All partial IRPs complete %p\n", + originalIrp)); + + if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) { + + // + // Acquire a separate copy of the remove lock so the debugging code + // works okay and we don't have to hold up the completion of this + // irp until after we start the next packet(s). + // + + KIRQL oldIrql; + UCHAR uniqueAddress; + ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddress); + ClassReleaseRemoveLock(Fdo, originalIrp); + ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT); + + KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); + IoStartNextPacket(Fdo, FALSE); + KeLowerIrql(oldIrql); + + ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddress); + + } else { + + // + // just complete this request + // + + ClassReleaseRemoveLock(Fdo, originalIrp); + ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT); + + } + + } + + // + // Deallocate IRP and indicate the I/O system should not attempt any more + // processing. + // + + IoFreeIrp(Irp); + return STATUS_MORE_PROCESSING_REQUIRED; + +} // end ClassIoCompleteAssociated() + + +/*++//////////////////////////////////////////////////////////////////////////// + +RetryRequest() + +Routine Description: + + This is a wrapper around the delayed retry DPC routine, RetryRequestDPC. + This reinitalizes the necessary fields, queues the request, and sets + a timer to call the DPC if someone hasn't already done so. + +Arguments: + + DeviceObject - Supplies the device object associated with this request. + + Irp - Supplies the request to be retried. + + Srb - Supplies a Pointer to the SCSI request block to be retied. + + Assocaiated - Indicates this is an assocatied Irp created by split request. + + RetryInterval - How long, in seconds, before retrying the request. + +Return Value: + + None + +--*/ +VOID +RetryRequest( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PSCSI_REQUEST_BLOCK Srb, + BOOLEAN Associated, + ULONG RetryInterval + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + ULONG transferByteCount; + + // This function is obsolete but is still used by some of our class drivers. + // DBGWARN(("RetryRequest is OBSOLETE !")); + + // + // Determine the transfer count of the request. If this is a read or a + // write then the transfer count is in the Irp stack. Otherwise assume + // the MDL contains the correct length. If there is no MDL then the + // transfer length must be zero. + // + + if (currentIrpStack->MajorFunction == IRP_MJ_READ || + currentIrpStack->MajorFunction == IRP_MJ_WRITE) { + + transferByteCount = currentIrpStack->Parameters.Read.Length; + + } else if (Irp->MdlAddress != NULL) { + + // + // Note this assumes that only read and write requests are spilt and + // other request do not need to be. If the data buffer address in + // the MDL and the SRB don't match then transfer length is most + // likely incorrect. + // + + ASSERT(Srb->DataBuffer == MmGetMdlVirtualAddress(Irp->MdlAddress)); + transferByteCount = Irp->MdlAddress->ByteCount; + + } else { + + transferByteCount = 0; + } + + // + // this is a safety net. this should not normally be hit, since we are + // not guaranteed to be an fdoExtension + // + + ASSERT(!TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)); + + // + // Reset byte count of transfer in SRB Extension. + // + + Srb->DataTransferLength = transferByteCount; + + // + // Zero SRB statuses. + // + + Srb->SrbStatus = Srb->ScsiStatus = 0; + + // + // Set the no disconnect flag, disable synchronous data transfers and + // disable tagged queuing. This fixes some errors. + // NOTE: Cannot clear these flags, just add to them + // + + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + CLEAR_FLAG(Srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); + + Srb->QueueTag = SP_UNTAGGED; + + // + // Set up major SCSI function. + // + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Save SRB address in next stack for port driver. + // + + nextIrpStack->Parameters.Scsi.Srb = Srb; + + + IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE); + + { + LARGE_INTEGER retry100ns; + retry100ns.QuadPart = RetryInterval; // seconds + retry100ns.QuadPart *= (LONGLONG)1000 * 1000 * 10; + + ClassRetryRequest(DeviceObject, Irp, retry100ns); + } + return; +} // end RetryRequest() + + +/*++ + +ClassBuildRequest() + +Routine Description: + + This routine allocates an SRB for the specified request then calls + ClasspBuildRequestEx to create a SCSI operation to read or write the device. + + If no SRB is available then the request will be queued to be issued later + when requests are available. Drivers which do not want the queueing + behavior should allocate the SRB themselves and call ClasspBuildRequestEx + to issue it. + +Arguments: + + Fdo - Supplies the functional device object associated with this request. + + Irp - Supplies the request to be retried. + +Note: + + If the IRP is for a disk transfer, the byteoffset field + will already have been adjusted to make it relative to + the beginning of the disk. + + +Return Value: + + NT Status + +--*/ +NTSTATUS +ClassBuildRequest( + PDEVICE_OBJECT Fdo, + PIRP Irp + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PSCSI_REQUEST_BLOCK srb; + + // This function is obsolete, but still called by CDROM.SYS . + // DBGWARN(("ClassBuildRequest is OBSOLETE !")); + + // + // Allocate an Srb. + // + + srb = ClasspAllocateSrb(fdoExtension); + + if(srb == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + ClasspBuildRequestEx(fdoExtension, Irp, srb); + return STATUS_SUCCESS; + +} // end ClassBuildRequest() + + +VOID +ClasspBuildRequestEx( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PIRP Irp, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +ClasspBuildRequestEx() + +Routine Description: + + This routine allocates and builds an Srb for a read or write request. + The block address and length are supplied by the Irp. The retry count + is stored in the current stack for use by ClassIoComplete which + processes these requests when they complete. The Irp is ready to be + passed to the port driver when this routine returns. + +Arguments: + + FdoExtension - Supplies the device extension associated with this request. + + Irp - Supplies the request to be issued. + + Srb - Supplies an SRB to be used for the request. + +Note: + + If the IRP is for a disk transfer, the byteoffset field + will already have been adjusted to make it relative to + the beginning of the disk. + + +Return Value: + + NT Status + +--*/ +{ + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + + LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset; + + PCDB cdb; + ULONG logicalBlockAddress; + USHORT transferBlocks; + + // This function is obsolete, but still called by CDROM.SYS . + // DBGWARN(("ClasspBuildRequestEx is OBSOLETE !")); + + // + // Prepare the SRB. + // + + RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Calculate relative sector address. + // + + logicalBlockAddress = + (ULONG)(Int64ShrlMod32(startingOffset.QuadPart, + FdoExtension->SectorShift)); + + // + // Write length to SRB. + // + + Srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + // + // Set up IRP Address. + // + + Srb->OriginalRequest = Irp; + + // + // Set up target ID and logical unit number. + // + + Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + Srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); + + // + // Save byte count of transfer in SRB Extension. + // + + Srb->DataTransferLength = currentIrpStack->Parameters.Read.Length; + + // + // Initialize the queue actions field. + // + + Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + + // + // Queue sort key is Relative Block Address. + // + + Srb->QueueSortKey = logicalBlockAddress; + + // + // Indicate auto request sense by specifying buffer and size. + // + + Srb->SenseInfoBuffer = FdoExtension->SenseData; + Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + // + // Set timeout value of one unit per 64k bytes of data. + // + + Srb->TimeOutValue = ((Srb->DataTransferLength + 0xFFFF) >> 16) * + FdoExtension->TimeOutValue; + + // + // Zero statuses. + // + + Srb->SrbStatus = Srb->ScsiStatus = 0; + Srb->NextSrb = 0; + + // + // Indicate that 10-byte CDB's will be used. + // + + Srb->CdbLength = 10; + + // + // Fill in CDB fields. + // + + cdb = (PCDB)Srb->Cdb; + + transferBlocks = (USHORT)(currentIrpStack->Parameters.Read.Length >> + FdoExtension->SectorShift); + + // + // Move little endian values into CDB in big endian format. + // + + cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3; + cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2; + cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1; + cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0; + + cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&transferBlocks)->Byte1; + cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&transferBlocks)->Byte0; + + // + // Set transfer direction flag and Cdb command. + // + + if (currentIrpStack->MajorFunction == IRP_MJ_READ) { + + DebugPrint((3, "ClassBuildRequest: Read Command\n")); + + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN); + cdb->CDB10.OperationCode = SCSIOP_READ; + + } else { + + DebugPrint((3, "ClassBuildRequest: Write Command\n")); + + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT); + cdb->CDB10.OperationCode = SCSIOP_WRITE; + } + + // + // If this is not a write-through request, then allow caching. + // + + if (!(currentIrpStack->Flags & SL_WRITE_THROUGH)) { + + SET_FLAG(Srb->SrbFlags, SRB_FLAGS_ADAPTER_CACHE_ENABLE); + + } else { + + // + // If write caching is enable then force media access in the + // cdb. + // + + if (FdoExtension->DeviceFlags & DEV_WRITE_CACHE) { + cdb->CDB10.ForceUnitAccess = TRUE; + } + } + + if(TEST_FLAG(Irp->Flags, (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO))) { + SET_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING); + } + + // + // OR in the default flags from the device object. + // + + SET_FLAG(Srb->SrbFlags, FdoExtension->SrbFlags); + + // + // Set up major SCSI function. + // + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Save SRB address in next stack for port driver. + // + + nextIrpStack->Parameters.Scsi.Srb = Srb; + + // + // Save retry count in current IRP stack. + // + + currentIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES; + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE); + +} + + +VOID ClasspInsertCScanList(IN PLIST_ENTRY ListHead, IN PCSCAN_LIST_ENTRY Entry) +{ + PCSCAN_LIST_ENTRY t; + + DBGWARN(("ClasspInsertCScanList is OBSOLETE !")); + + // + // Iterate through the list. Insert this entry in the sorted list in + // order (after other requests for the same block). At each stop if + // blockNumber(Entry) >= blockNumber(t) then move on. + // + + for(t = (PCSCAN_LIST_ENTRY) ListHead->Flink; + t != (PCSCAN_LIST_ENTRY) ListHead; + t = (PCSCAN_LIST_ENTRY) t->Entry.Flink) { + + if(Entry->BlockNumber < t->BlockNumber) { + + // + // Set the pointers in entry to the right location. + // + + Entry->Entry.Flink = &(t->Entry); + Entry->Entry.Blink = t->Entry.Blink; + + // + // Set the pointers in the surrounding elements to refer to us. + // + + t->Entry.Blink->Flink = &(Entry->Entry); + t->Entry.Blink = &(Entry->Entry); + return; + } + } + + // + // Insert this entry at the tail of the list. If the list was empty this + // will also be the head of the list. + // + + InsertTailList(ListHead, &(Entry->Entry)); + +} + + +VOID ClassInsertCScanList(IN PCSCAN_LIST List, IN PIRP Irp, IN ULONGLONG BlockNumber, IN BOOLEAN LowPriority) +/*++ + +Routine Description: + + This routine inserts an entry into the CScan list based on it's block number + and priority. It is assumed that the caller is providing synchronization + to the access of the list. + + Low priority requests are always scheduled to run on the next sweep across + the disk. Normal priority requests will be inserted into the current or + next sweep based on the standard C-SCAN algorithm. + +Arguments: + + List - the list to insert into + + Irp - the irp to be inserted. + + BlockNumber - the block number for this request. + + LowPriority - indicates that the request is lower priority and should be + done on the next sweep across the disk. + +Return Value: + + none + +--*/ +{ + PCSCAN_LIST_ENTRY entry = (PCSCAN_LIST_ENTRY)Irp->Tail.Overlay.DriverContext; + + DBGWARN(("ClassInsertCScanList is OBSOLETE !")); + + // + // Set the block number in the entry. We need this to keep the list sorted. + // + entry->BlockNumber = BlockNumber; + + // + // If it's a normal priority request and further down the disk than our + // current position then insert this entry into the current sweep. + // + + if((LowPriority != TRUE) && (BlockNumber > List->BlockNumber)) { + ClasspInsertCScanList(&(List->CurrentSweep), entry); + } else { + ClasspInsertCScanList(&(List->NextSweep), entry); + } + return; +} + + + + +VOID ClassFreeOrReuseSrb( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PSCSI_REQUEST_BLOCK Srb) +/*++ + +Routine Description: + + This routine will attempt to reuse the provided SRB to start a blocked + read/write request. + If there is no need to reuse the request it will be returned + to the SRB lookaside list. + +Arguments: + + Fdo - the device extension + + Srb - the SRB which is to be reused or freed. + +Return Value: + + none. + +--*/ + +{ + PCLASS_PRIVATE_FDO_DATA privateData = FdoExtension->PrivateFdoData; + PCOMMON_DEVICE_EXTENSION commonExt = &FdoExtension->CommonExtension; + KIRQL oldIrql; + PIRP blockedIrp; + + // This function is obsolete, but still called by DISK.SYS . + // DBGWARN(("ClassFreeOrReuseSrb is OBSOLETE !")); + + // + // safety net. this should never occur. if it does, it's a potential + // memory leak. + // + ASSERT(!TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)); + + if (commonExt->IsSrbLookasideListInitialized){ + /* + * Put the SRB back in our lookaside list. + * + * BUGBUG - Some class drivers use ClassIoComplete + * to complete SRBs that they themselves allocated. + * So we may be putting a "foreign" SRB + * (e.g. with a different pool tag) into our lookaside list. + */ + ClasspFreeSrb(FdoExtension, Srb); + } + else { + DBGERR(("ClassFreeOrReuseSrb: someone is trying to use an uninitialized SrbLookasideList !!!"));; + ExFreePool(Srb); + } +} + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassDeleteSrbLookasideList() + +Routine Description: + + This routine deletes a lookaside listhead for srbs, and should be called + only during the final removal. + + If called at other times, the caller is responsible for + synchronization and removal issues. + +Arguments: + + CommonExtension - Pointer to the CommonExtension containing the listhead. + +Return Value: + + None + +--*/ +VOID ClassDeleteSrbLookasideList(IN PCOMMON_DEVICE_EXTENSION CommonExtension) +{ + PAGED_CODE(); + + // This function is obsolete, but is still called by some of our code. + // DBGWARN(("ClassDeleteSrbLookasideList is OBSOLETE !")); + + if (CommonExtension->IsSrbLookasideListInitialized){ + CommonExtension->IsSrbLookasideListInitialized = FALSE; + ExDeleteNPagedLookasideList(&CommonExtension->SrbLookasideList); + } + else { + DBGWARN(("ClassDeleteSrbLookasideList: attempt to delete uninitialized or freed srblookasidelist")); + } +} + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassInitializeSrbLookasideList() + +Routine Description: + + This routine sets up a lookaside listhead for srbs, and should be called + only from the ClassInitDevice() routine to prevent race conditions. + + If called from other locations, the caller is responsible for + synchronization and removal issues. + +Arguments: + + CommonExtension - Pointer to the CommonExtension containing the listhead. + + NumberElements - Supplies the maximum depth of the lookaside list. + + +Note: + + The Windows 2000 version of classpnp did not return any status value from + this call. + +--*/ + +VOID ClassInitializeSrbLookasideList( IN PCOMMON_DEVICE_EXTENSION CommonExtension, + IN ULONG NumberElements) +{ + PAGED_CODE(); + + // This function is obsolete, but still called by DISK.SYS . + // DBGWARN(("ClassInitializeSrbLookasideList is OBSOLETE !")); + + ASSERT(!CommonExtension->IsSrbLookasideListInitialized); + if (!CommonExtension->IsSrbLookasideListInitialized){ + + ExInitializeNPagedLookasideList(&CommonExtension->SrbLookasideList, + NULL, + NULL, + NonPagedPool, + sizeof(SCSI_REQUEST_BLOCK), + '$scS', + (USHORT)NumberElements); + + CommonExtension->IsSrbLookasideListInitialized = TRUE; + } + +} + + + + +VOID ClasspInitializeCScanList(IN PCSCAN_LIST List) +{ + PAGED_CODE(); + RtlZeroMemory(List, sizeof(CSCAN_LIST)); + InitializeListHead(&(List->CurrentSweep)); + InitializeListHead(&(List->NextSweep)); +} + + + +VOID ClasspStartNextSweep(PCSCAN_LIST List) +{ + ASSERT(IsListEmpty(&(List->CurrentSweep)) == TRUE); + + // + // If the next sweep is empty then there's nothing to do. + // + + if(IsListEmpty(&(List->NextSweep))) { + return; + } + + // + // Copy the next sweep list head into the current sweep list head. + // + + List->CurrentSweep = List->NextSweep; + + // + // Unlink the next sweep list from the list head now that we have a copy + // of it. + // + + InitializeListHead(&(List->NextSweep)); + + // + // Update the next sweep list to point back to the current sweep list head. + // + + List->CurrentSweep.Flink->Blink = &(List->CurrentSweep); + List->CurrentSweep.Blink->Flink = &(List->CurrentSweep); + + return; +} + + + +PIRP ClassRemoveCScanList(IN PCSCAN_LIST List) +{ + PCSCAN_LIST_ENTRY entry; + + // + // If the current sweep is empty then promote the next sweep. + // + + if(IsListEmpty(&(List->CurrentSweep))) { + ClasspStartNextSweep(List); + } + + // + // If the current sweep is still empty then we're done. + // + + if(IsListEmpty(&(List->CurrentSweep))) { + return NULL; + } + + // + // Remove the head entry from the current sweep. Record it's block number + // so that nothing before it on the disk gets into the current sweep. + // + + entry = (PCSCAN_LIST_ENTRY) RemoveHeadList(&(List->CurrentSweep)); + + List->BlockNumber = entry->BlockNumber; + + return CONTAINING_RECORD(entry, IRP, Tail.Overlay.DriverContext); +} + diff --git a/drivers/storage/classpnp/power.c b/drivers/storage/classpnp/power.c new file mode 100644 index 00000000000..26d4d0172c2 --- /dev/null +++ b/drivers/storage/classpnp/power.c @@ -0,0 +1,1607 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + class.c + +Abstract: + + SCSI class driver routines + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#include "stddef.h" +#include "ntddk.h" +#include "scsi.h" +#include "classp.h" + +#include + +#define CLASS_TAG_POWER 'WLcS' + +NTSTATUS +ClasspPowerHandler( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN CLASS_POWER_OPTIONS Options + ); + +NTSTATUS +ClasspPowerDownCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PCLASS_POWER_CONTEXT Context + ); + +NTSTATUS +ClasspPowerUpCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PCLASS_POWER_CONTEXT Context + ); + +VOID +RetryPowerRequest( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PCLASS_POWER_CONTEXT Context + ); + +NTSTATUS +ClasspStartNextPowerIrpCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassDispatchPower() + +Routine Description: + + This routine acquires the removelock for the irp and then calls the + appropriate power callback. + +Arguments: + + DeviceObject - + Irp - + +Return Value: + +--*/ +NTSTATUS +ClassDispatchPower( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + ULONG isRemoved; + PCLASS_POWER_DEVICE powerRoutine = NULL; + + // + // NOTE: This code may be called at PASSIVE or DISPATCH, depending + // upon the device object it is being called for. + // don't do anything that would break under either circumstance. + // + + NTSTATUS status; + + isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp); + + if(isRemoved) { + ClassReleaseRemoveLock(DeviceObject, Irp); + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + PoStartNextPowerIrp(Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + return commonExtension->DevInfo->ClassPowerDevice(DeviceObject, Irp); +} // end ClassDispatchPower() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspPowerUpCompletion() + +Routine Description: + + This routine is used for intermediate completion of a power up request. + PowerUp requires four requests to be sent to the lower driver in sequence. + + * The queue is "power locked" to ensure that the class driver power-up + work can be done before request processing resumes. + + * The power irp is sent down the stack for any filter drivers and the + port driver to return power and resume command processing for the + device. Since the queue is locked, no queued irps will be sent + immediately. + + * A start unit command is issued to the device with appropriate flags + to override the "power locked" queue. + + * The queue is "power unlocked" to start processing requests again. + + This routine uses the function in the srb which just completed to determine + which state it is in. + +Arguments: + + DeviceObject - the device object being powered up + + Irp - the IO_REQUEST_PACKET containing the power request + + Srb - the SRB used to perform port/class operations. + +Return Value: + + STATUS_MORE_PROCESSING_REQUIRED or + STATUS_SUCCESS + +--*/ +NTSTATUS +ClasspPowerUpCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PCLASS_POWER_CONTEXT Context + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION currentStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp); + + + NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED; + + DebugPrint((1, "ClasspPowerUpCompletion: Device Object %p, Irp %p, " + "Context %p\n", + DeviceObject, Irp, Context)); + + ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)); + ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE)); + ASSERT(Context->Options.PowerDown == FALSE); + ASSERT(Context->Options.HandleSpinUp); + + if(Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + Context->PowerChangeState.PowerUp++; + + switch(Context->PowerChangeState.PowerUp) { + + case PowerUpDeviceLocked: { + + DebugPrint((1, "(%p)\tPreviously sent power lock\n", Irp)); + + // + // Issue the actual power request to the lower driver. + // + + IoCopyCurrentIrpStackLocationToNext(Irp); + + // + // If the lock wasn't successful then just bail out on the power + // request unless we can ignore failed locks + // + + if((Context->Options.LockQueue == TRUE) && + (!NT_SUCCESS(Irp->IoStatus.Status))) { + + DebugPrint((1, "(%p)\tIrp status was %lx\n", + Irp, Irp->IoStatus.Status)); + DebugPrint((1, "(%p)\tSrb status was %lx\n", + Irp, Context->Srb.SrbStatus)); + + // + // Lock was not successful - throw down the power IRP + // by itself and don't try to spin up the drive or unlock + // the queue. + // + + Context->InUse = FALSE; + Context = NULL; + + // + // Set the new power state + // + + fdoExtension->DevicePowerState = + currentStack->Parameters.Power.State.DeviceState; + + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + IoCopyCurrentIrpStackLocationToNext(Irp); + + IoSetCompletionRoutine(Irp, + ClasspStartNextPowerIrpCompletion, + NULL, + TRUE, + TRUE, + TRUE); + + // + // Indicate to Po that we've been successfully powered up so + // it can do it's notification stuff. + // + + PoSetPowerState(DeviceObject, + currentStack->Parameters.Power.Type, + currentStack->Parameters.Power.State); + + PoCallDriver(commonExtension->LowerDeviceObject, Irp); + + ClassReleaseRemoveLock(commonExtension->DeviceObject, + Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; + + } else { + Context->QueueLocked = (UCHAR) Context->Options.LockQueue; + } + + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + Context->PowerChangeState.PowerUp = PowerUpDeviceLocked; + + IoSetCompletionRoutine(Irp, + ClasspPowerUpCompletion, + Context, + TRUE, + TRUE, + TRUE); + + status = PoCallDriver(commonExtension->LowerDeviceObject, Irp); + + DebugPrint((2, "(%p)\tPoCallDriver returned %lx\n", Irp, status)); + break; + } + + case PowerUpDeviceOn: { + + PCDB cdb; + + if(NT_SUCCESS(Irp->IoStatus.Status)) { + + DebugPrint((1, "(%p)\tSending start unit to device\n", Irp)); + + // + // Issue the start unit command to the device. + // + + Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK); + Context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI; + + Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0; + Context->Srb.DataTransferLength = 0; + + Context->Srb.TimeOutValue = START_UNIT_TIMEOUT; + + Context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | + SRB_FLAGS_DISABLE_AUTOSENSE | + SRB_FLAGS_DISABLE_SYNCH_TRANSFER | + SRB_FLAGS_NO_QUEUE_FREEZE; + + if(Context->Options.LockQueue) { + SET_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_BYPASS_LOCKED_QUEUE); + } + + Context->Srb.CdbLength = 6; + + cdb = (PCDB) (Context->Srb.Cdb); + RtlZeroMemory(cdb, sizeof(CDB)); + + + cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; + cdb->START_STOP.Start = 1; + + Context->PowerChangeState.PowerUp = PowerUpDeviceOn; + + IoSetCompletionRoutine(Irp, + ClasspPowerUpCompletion, + Context, + TRUE, + TRUE, + TRUE); + + nextStack->Parameters.Scsi.Srb = &(Context->Srb); + nextStack->MajorFunction = IRP_MJ_SCSI; + + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + DebugPrint((2, "(%p)\tIoCallDriver returned %lx\n", Irp, status)); + + } else { + + // + // we're done. + // + + Context->FinalStatus = Irp->IoStatus.Status; + goto ClasspPowerUpCompletionFailure; + } + + break; + } + + case PowerUpDeviceStarted: { // 3 + + // + // First deal with an error if one occurred. + // + + if(SRB_STATUS(Context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) { + + BOOLEAN retry; + + DebugPrint((1, "%p\tError occured when issuing START_UNIT " + "command to device. Srb %p, Status %x\n", + Irp, + &Context->Srb, + Context->Srb.SrbStatus)); + + ASSERT(!(TEST_FLAG(Context->Srb.SrbStatus, + SRB_STATUS_QUEUE_FROZEN))); + ASSERT(Context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI); + + Context->RetryInterval = 0; + + retry = ClassInterpretSenseInfo( + commonExtension->DeviceObject, + &Context->Srb, + IRP_MJ_SCSI, + IRP_MJ_POWER, + MAXIMUM_RETRIES - Context->RetryCount, + &status, + &Context->RetryInterval); + + if((retry == TRUE) && (Context->RetryCount-- != 0)) { + + DebugPrint((1, "(%p)\tRetrying failed request\n", Irp)); + + // + // Decrement the state so we come back through here the + // next time. + // + + Context->PowerChangeState.PowerUp--; + + RetryPowerRequest(commonExtension->DeviceObject, + Irp, + Context); + + break; + + } + + // reset retries + Context->RetryCount = MAXIMUM_RETRIES; + + } + +ClasspPowerUpCompletionFailure: + + DebugPrint((1, "(%p)\tPreviously spun device up\n", Irp)); + + if (Context->QueueLocked) { + DebugPrint((1, "(%p)\tUnlocking queue\n", Irp)); + + Context->Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE; + Context->Srb.SrbFlags = SRB_FLAGS_BYPASS_LOCKED_QUEUE; + Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0; + Context->Srb.DataTransferLength = 0; + + nextStack->Parameters.Scsi.Srb = &(Context->Srb); + nextStack->MajorFunction = IRP_MJ_SCSI; + + Context->PowerChangeState.PowerUp = PowerUpDeviceStarted; + + IoSetCompletionRoutine(Irp, + ClasspPowerUpCompletion, + Context, + TRUE, + TRUE, + TRUE); + + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n", + Irp, status)); + break; + } + + // Fall-through to next case... + + } + + case PowerUpDeviceUnlocked: { + + // + // This is the end of the dance. Free the srb and complete the + // request finally. We're ignoring possible intermediate + // error conditions .... + // + + if (Context->QueueLocked) { + DebugPrint((1, "(%p)\tPreviously unlocked queue\n", Irp)); + ASSERT(NT_SUCCESS(Irp->IoStatus.Status)); + ASSERT(Context->Srb.SrbStatus == SRB_STATUS_SUCCESS); + } else { + DebugPrint((1, "(%p)\tFall-through (queue not locked)\n", Irp)); + } + + DebugPrint((1, "(%p)\tFreeing srb and completing\n", Irp)); + Context->InUse = FALSE; + + status = Context->FinalStatus; + Irp->IoStatus.Status = status; + + Context = NULL; + + // + // Set the new power state + // + + if(NT_SUCCESS(status)) { + fdoExtension->DevicePowerState = + currentStack->Parameters.Power.State.DeviceState; + } + + // + // Indicate to Po that we've been successfully powered up so + // it can do it's notification stuff. + // + + PoSetPowerState(DeviceObject, + currentStack->Parameters.Power.Type, + currentStack->Parameters.Power.State); + + DebugPrint((1, "(%p)\tStarting next power irp\n", Irp)); + ClassReleaseRemoveLock(DeviceObject, Irp); + PoStartNextPowerIrp(Irp); + + return status; + } + } + + return STATUS_MORE_PROCESSING_REQUIRED; +} // end ClasspPowerUpCompletion() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspPowerDownCompletion() + +Routine Description: + + This routine is used for intermediate completion of a power up request. + PowerUp requires four requests to be sent to the lower driver in sequence. + + * The queue is "power locked" to ensure that the class driver power-up + work can be done before request processing resumes. + + * The power irp is sent down the stack for any filter drivers and the + port driver to return power and resume command processing for the + device. Since the queue is locked, no queued irps will be sent + immediately. + + * A start unit command is issued to the device with appropriate flags + to override the "power locked" queue. + + * The queue is "power unlocked" to start processing requests again. + + This routine uses the function in the srb which just completed to determine + which state it is in. + +Arguments: + + DeviceObject - the device object being powered up + + Irp - the IO_REQUEST_PACKET containing the power request + + Srb - the SRB used to perform port/class operations. + +Return Value: + + STATUS_MORE_PROCESSING_REQUIRED or + STATUS_SUCCESS + +--*/ +NTSTATUS +ClasspPowerDownCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PCLASS_POWER_CONTEXT Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION currentStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp); + + NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED; + + DebugPrint((1, "ClasspPowerDownCompletion: Device Object %p, " + "Irp %p, Context %p\n", + DeviceObject, Irp, Context)); + + ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)); + ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE)); + ASSERT(Context->Options.PowerDown == TRUE); + ASSERT(Context->Options.HandleSpinDown); + + if(Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + Context->PowerChangeState.PowerDown2++; + + switch(Context->PowerChangeState.PowerDown2) { + + case PowerDownDeviceLocked2: { + + PCDB cdb; + + DebugPrint((1, "(%p)\tPreviously sent power lock\n", Irp)); + + if((Context->Options.LockQueue == TRUE) && + (!NT_SUCCESS(Irp->IoStatus.Status))) { + + DebugPrint((1, "(%p)\tIrp status was %lx\n", + Irp, + Irp->IoStatus.Status)); + DebugPrint((1, "(%p)\tSrb status was %lx\n", + Irp, + Context->Srb.SrbStatus)); + + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + // + // Lock was not successful - throw down the power IRP + // by itself and don't try to spin down the drive or unlock + // the queue. + // + + Context->InUse = FALSE; + Context = NULL; + + // + // Set the new power state + // + + fdoExtension->DevicePowerState = + currentStack->Parameters.Power.State.DeviceState; + + // + // Indicate to Po that we've been successfully powered down + // so it can do it's notification stuff. + // + + IoCopyCurrentIrpStackLocationToNext(Irp); + IoSetCompletionRoutine(Irp, + ClasspStartNextPowerIrpCompletion, + NULL, + TRUE, + TRUE, + TRUE); + + PoSetPowerState(DeviceObject, + currentStack->Parameters.Power.Type, + currentStack->Parameters.Power.State); + + fdoExtension->PowerDownInProgress = FALSE; + + PoCallDriver(commonExtension->LowerDeviceObject, Irp); + + ClassReleaseRemoveLock(commonExtension->DeviceObject, + Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; + + } else { + Context->QueueLocked = (UCHAR) Context->Options.LockQueue; + } + + if (!TEST_FLAG(fdoExtension->PrivateFdoData->HackFlags, + FDO_HACK_NO_SYNC_CACHE)) { + + // + // send SCSIOP_SYNCHRONIZE_CACHE + // + + Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK); + Context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI; + + Context->Srb.TimeOutValue = fdoExtension->TimeOutValue; + + Context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | + SRB_FLAGS_DISABLE_AUTOSENSE | + SRB_FLAGS_DISABLE_SYNCH_TRANSFER | + SRB_FLAGS_NO_QUEUE_FREEZE | + SRB_FLAGS_BYPASS_LOCKED_QUEUE; + + Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0; + Context->Srb.DataTransferLength = 0; + + Context->Srb.CdbLength = 10; + + cdb = (PCDB) Context->Srb.Cdb; + + RtlZeroMemory(cdb, sizeof(CDB)); + cdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE; + + IoSetCompletionRoutine(Irp, + ClasspPowerDownCompletion, + Context, + TRUE, + TRUE, + TRUE); + + nextStack->Parameters.Scsi.Srb = &(Context->Srb); + nextStack->MajorFunction = IRP_MJ_SCSI; + + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n", Irp, status)); + break; + + } else { + + DebugPrint((1, "(%p)\tPower Down: not sending SYNCH_CACHE\n", + DeviceObject)); + Context->PowerChangeState.PowerDown2++; + Context->Srb.SrbStatus = SRB_STATUS_SUCCESS; + // and fall through.... + } + // no break in case the device doesn't like synch_cache commands + + } + + case PowerDownDeviceFlushed2: { + + PCDB cdb; + + DebugPrint((1, "(%p)\tPreviously send SCSIOP_SYNCHRONIZE_CACHE\n", + Irp)); + + // + // SCSIOP_SYNCHRONIZE_CACHE was sent + // + + if(SRB_STATUS(Context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) { + + BOOLEAN retry; + + DebugPrint((1, "(%p)\tError occured when issuing " + "SYNCHRONIZE_CACHE command to device. " + "Srb %p, Status %lx\n", + Irp, + &Context->Srb, + Context->Srb.SrbStatus)); + + ASSERT(!(TEST_FLAG(Context->Srb.SrbStatus, + SRB_STATUS_QUEUE_FROZEN))); + ASSERT(Context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI); + + Context->RetryInterval = 0; + retry = ClassInterpretSenseInfo( + commonExtension->DeviceObject, + &Context->Srb, + IRP_MJ_SCSI, + IRP_MJ_POWER, + MAXIMUM_RETRIES - Context->RetryCount, + &status, + &Context->RetryInterval); + + if((retry == TRUE) && (Context->RetryCount-- != 0)) { + + DebugPrint((1, "(%p)\tRetrying failed request\n", Irp)); + + // + // decrement the state so we come back through here + // the next time. + // + + Context->PowerChangeState.PowerDown2--; + RetryPowerRequest(commonExtension->DeviceObject, + Irp, + Context); + break; + } + + DebugPrint((1, "(%p)\tSYNCHRONIZE_CACHE not retried\n", Irp)); + Context->RetryCount = MAXIMUM_RETRIES; + + } // end !SRB_STATUS_SUCCESS + + // + // note: we are purposefully ignoring any errors. if the drive + // doesn't support a synch_cache, then we're up a creek + // anyways. + // + + DebugPrint((1, "(%p)\tSending stop unit to device\n", Irp)); + + // + // Issue the start unit command to the device. + // + + Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK); + Context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI; + + Context->Srb.TimeOutValue = START_UNIT_TIMEOUT; + + Context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | + SRB_FLAGS_DISABLE_AUTOSENSE | + SRB_FLAGS_DISABLE_SYNCH_TRANSFER | + SRB_FLAGS_NO_QUEUE_FREEZE | + SRB_FLAGS_BYPASS_LOCKED_QUEUE; + + Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0; + Context->Srb.DataTransferLength = 0; + + Context->Srb.CdbLength = 6; + + cdb = (PCDB) Context->Srb.Cdb; + RtlZeroMemory(cdb, sizeof(CDB)); + + cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; + cdb->START_STOP.Start = 0; + cdb->START_STOP.Immediate = 1; + + IoSetCompletionRoutine(Irp, + ClasspPowerDownCompletion, + Context, + TRUE, + TRUE, + TRUE); + + nextStack->Parameters.Scsi.Srb = &(Context->Srb); + nextStack->MajorFunction = IRP_MJ_SCSI; + + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n", Irp, status)); + break; + + } + + case PowerDownDeviceStopped2: { + + BOOLEAN ignoreError = TRUE; + + // + // stop was sent + // + + if(SRB_STATUS(Context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) { + + BOOLEAN retry; + + DebugPrint((1, "(%p)\tError occured when issueing STOP_UNIT " + "command to device. Srb %p, Status %lx\n", + Irp, + &Context->Srb, + Context->Srb.SrbStatus)); + + ASSERT(!(TEST_FLAG(Context->Srb.SrbStatus, + SRB_STATUS_QUEUE_FROZEN))); + ASSERT(Context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI); + + Context->RetryInterval = 0; + retry = ClassInterpretSenseInfo( + commonExtension->DeviceObject, + &Context->Srb, + IRP_MJ_SCSI, + IRP_MJ_POWER, + MAXIMUM_RETRIES - Context->RetryCount, + &status, + &Context->RetryInterval); + + if((retry == TRUE) && (Context->RetryCount-- != 0)) { + + DebugPrint((1, "(%p)\tRetrying failed request\n", Irp)); + + // + // decrement the state so we come back through here + // the next time. + // + + Context->PowerChangeState.PowerDown2--; + RetryPowerRequest(commonExtension->DeviceObject, + Irp, + Context); + break; + } + + DebugPrint((1, "(%p)\tSTOP_UNIT not retried\n", Irp)); + Context->RetryCount = MAXIMUM_RETRIES; + + } // end !SRB_STATUS_SUCCESS + + + DebugPrint((1, "(%p)\tPreviously sent stop unit\n", Irp)); + + // + // some operations, such as a physical format in progress, + // should not be ignored and should fail the power operation. + // + + if (!NT_SUCCESS(status)) { + + PSENSE_DATA senseBuffer = Context->Srb.SenseInfoBuffer; + + if (TEST_FLAG(Context->Srb.SrbStatus, + SRB_STATUS_AUTOSENSE_VALID) && + ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_NOT_READY) && + (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) && + (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS) + ) { + ignoreError = FALSE; + Context->FinalStatus = STATUS_DEVICE_BUSY; + status = Context->FinalStatus; + } + + } + + if (NT_SUCCESS(status) || ignoreError) { + + // + // Issue the actual power request to the lower driver. + // + + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + IoCopyCurrentIrpStackLocationToNext(Irp); + + IoSetCompletionRoutine(Irp, + ClasspPowerDownCompletion, + Context, + TRUE, + TRUE, + TRUE); + + status = PoCallDriver(commonExtension->LowerDeviceObject, Irp); + + DebugPrint((1, "(%p)\tPoCallDriver returned %lx\n", Irp, status)); + break; + } + + // else fall through w/o sending the power irp, since the device + // is reporting an error that would be "really bad" to power down + // during. + + } + + case PowerDownDeviceOff2: { + + // + // SpinDown request completed ... whether it succeeded or not is + // another matter entirely. + // + + DebugPrint((1, "(%p)\tPreviously sent power irp\n", Irp)); + + if (Context->QueueLocked) { + + DebugPrint((1, "(%p)\tUnlocking queue\n", Irp)); + + Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK); + + Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0; + Context->Srb.DataTransferLength = 0; + + Context->Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE; + Context->Srb.SrbFlags = SRB_FLAGS_BYPASS_LOCKED_QUEUE; + nextStack->Parameters.Scsi.Srb = &(Context->Srb); + nextStack->MajorFunction = IRP_MJ_SCSI; + + IoSetCompletionRoutine(Irp, + ClasspPowerDownCompletion, + Context, + TRUE, + TRUE, + TRUE); + + status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); + DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n", + Irp, + status)); + break; + } + + } + + case PowerDownDeviceUnlocked2: { + + // + // This is the end of the dance. Free the srb and complete the + // request finally. We're ignoring possible intermediate + // error conditions .... + // + + if (Context->QueueLocked == FALSE) { + DebugPrint((1, "(%p)\tFall through (queue not locked)\n", Irp)); + } else { + DebugPrint((1, "(%p)\tPreviously unlocked queue\n", Irp)); + ASSERT(NT_SUCCESS(Irp->IoStatus.Status)); + ASSERT(Context->Srb.SrbStatus == SRB_STATUS_SUCCESS); + } + + DebugPrint((1, "(%p)\tFreeing srb and completing\n", Irp)); + Context->InUse = FALSE; + status = Context->FinalStatus; // allow failure to propogate + Context = NULL; + + if(Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + if (NT_SUCCESS(status)) { + + // + // Set the new power state + // + + fdoExtension->DevicePowerState = + currentStack->Parameters.Power.State.DeviceState; + + } + + + DebugPrint((1, "(%p)\tStarting next power irp\n", Irp)); + + ClassReleaseRemoveLock(DeviceObject, Irp); + PoStartNextPowerIrp(Irp); + fdoExtension->PowerDownInProgress = FALSE; + + return status; + } + } + + return STATUS_MORE_PROCESSING_REQUIRED; +} // end ClasspPowerDownCompletion() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspPowerHandler() + +Routine Description: + + This routine reduces the number of useless spinups and spindown requests + sent to a given device by ignoring transitions to power states we are + currently in. + + ISSUE-2000/02/20-henrygab - by ignoring spin-up requests, we may be + allowing the drive + +Arguments: + + DeviceObject - the device object which is transitioning power states + Irp - the power irp + Options - a set of flags indicating what the device handles + +Return Value: + +--*/ +NTSTATUS +ClasspPowerHandler( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN CLASS_POWER_OPTIONS Options // ISSUE-2000/02/20-henrygab - pass pointer, not whole struct + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDEVICE_OBJECT lowerDevice = commonExtension->LowerDeviceObject; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCLASS_POWER_CONTEXT context; + + if (!commonExtension->IsFdo) { + + // + // certain assumptions are made here, + // particularly: having the fdoExtension + // + + DebugPrint((0, "ClasspPowerHandler: Called for PDO %p???\n", + DeviceObject)); + ASSERT(!"PDO using ClasspPowerHandler"); + return STATUS_NOT_SUPPORTED; + } + + DebugPrint((1, "ClasspPowerHandler: Power irp %p to %s %p\n", + Irp, (commonExtension->IsFdo ? "fdo" : "pdo"), DeviceObject)); + + switch(irpStack->MinorFunction) { + + case IRP_MN_SET_POWER: { + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData; + + DebugPrint((1, "(%p)\tIRP_MN_SET_POWER\n", Irp)); + + DebugPrint((1, "(%p)\tSetting %s state to %d\n", + Irp, + (irpStack->Parameters.Power.Type == SystemPowerState ? + "System" : "Device"), + irpStack->Parameters.Power.State.SystemState)); + + switch (irpStack->Parameters.Power.ShutdownType){ + + case PowerActionSleep: + case PowerActionHibernate: + if (fdoData->HotplugInfo.MediaRemovable || fdoData->HotplugInfo.MediaHotplug){ + /* + * We are suspending and this drive is either hot-pluggable + * or contains removeable media. + * Set the media dirty bit, since the media may change while + * we are suspended. + */ + SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME); + } + break; + } + + break; + } + + default: { + + DebugPrint((1, "(%p)\tIrp minor code = %#x\n", + Irp, irpStack->MinorFunction)); + break; + } + } + + if (irpStack->Parameters.Power.Type != DevicePowerState || + irpStack->MinorFunction != IRP_MN_SET_POWER) { + + DebugPrint((1, "(%p)\tSending to lower device\n", Irp)); + + goto ClasspPowerHandlerCleanup; + + } + + nextIrpStack = IoGetNextIrpStackLocation(Irp); + + // + // already in exact same state, don't work to transition to it. + // + + if(irpStack->Parameters.Power.State.DeviceState == + fdoExtension->DevicePowerState) { + + DebugPrint((1, "(%p)\tAlready in device state %x\n", + Irp, fdoExtension->DevicePowerState)); + goto ClasspPowerHandlerCleanup; + + } + + // + // or powering down from non-d0 state (device already stopped) + // NOTE -- we're not sure whether this case can exist or not (the + // power system may never send this sort of request) but it's trivial + // to deal with. + // + + if ((irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0) && + (fdoExtension->DevicePowerState != PowerDeviceD0)) { + DebugPrint((1, "(%p)\tAlready powered down to %x???\n", + Irp, fdoExtension->DevicePowerState)); + fdoExtension->DevicePowerState = + irpStack->Parameters.Power.State.DeviceState; + goto ClasspPowerHandlerCleanup; + } + + // + // or going into a hibernation state when we're in the hibernation path. + // If the device is spinning then we should leave it spinning - if it's not + // then the dump driver will start it up for us. + // + + if((irpStack->Parameters.Power.State.DeviceState == PowerDeviceD3) && + (irpStack->Parameters.Power.ShutdownType == PowerActionHibernate) && + (commonExtension->HibernationPathCount != 0)) { + + DebugPrint((1, "(%p)\tdoing nothing for hibernation request for " + "state %x???\n", + Irp, fdoExtension->DevicePowerState)); + fdoExtension->DevicePowerState = + irpStack->Parameters.Power.State.DeviceState; + goto ClasspPowerHandlerCleanup; + } + // + // or when not handling powering up and are powering up + // + + if ((!Options.HandleSpinUp) && + (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0)) { + + DebugPrint((2, "(%p)\tNot handling spinup to state %x\n", + Irp, fdoExtension->DevicePowerState)); + fdoExtension->DevicePowerState = + irpStack->Parameters.Power.State.DeviceState; + goto ClasspPowerHandlerCleanup; + + } + + // + // or when not handling powering down and are powering down + // + + if ((!Options.HandleSpinDown) && + (irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0)) { + + DebugPrint((2, "(%p)\tNot handling spindown to state %x\n", + Irp, fdoExtension->DevicePowerState)); + fdoExtension->DevicePowerState = + irpStack->Parameters.Power.State.DeviceState; + goto ClasspPowerHandlerCleanup; + + } + + context = &(fdoExtension->PowerContext); + +#if DBG + // + // Mark the context as in use. We should be synchronizing this but + // since it's just for debugging purposes we don't worry too much. + // + + ASSERT(context->InUse == FALSE); +#endif + + RtlZeroMemory(context, sizeof(CLASS_POWER_CONTEXT)); + context->InUse = TRUE; + + nextIrpStack->Parameters.Scsi.Srb = &(context->Srb); + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + + context->FinalStatus = STATUS_SUCCESS; + + context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK); + context->Srb.OriginalRequest = Irp; + context->Srb.SrbFlags |= SRB_FLAGS_BYPASS_LOCKED_QUEUE + | SRB_FLAGS_NO_QUEUE_FREEZE; + context->Srb.Function = SRB_FUNCTION_LOCK_QUEUE; + + context->Srb.SenseInfoBuffer = + commonExtension->PartitionZeroExtension->SenseData; + context->Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE; + context->RetryCount = MAXIMUM_RETRIES; + + context->Options = Options; + context->DeviceObject = DeviceObject; + context->Irp = Irp; + + if(irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) { + + ASSERT(Options.HandleSpinUp); + + DebugPrint((2, "(%p)\tpower up - locking queue\n", Irp)); + + // + // We need to issue a queue lock request so that we + // can spin the drive back up after the power is restored + // but before any requests are processed. + // + + context->Options.PowerDown = FALSE; + context->PowerChangeState.PowerUp = PowerUpDeviceInitial; + context->CompletionRoutine = ClasspPowerUpCompletion; + + } else { + + ASSERT(Options.HandleSpinDown); + + fdoExtension->PowerDownInProgress = TRUE; + + DebugPrint((2, "(%p)\tPowering down - locking queue\n", Irp)); + + PoSetPowerState(DeviceObject, + irpStack->Parameters.Power.Type, + irpStack->Parameters.Power.State); + + context->Options.PowerDown = TRUE; + context->PowerChangeState.PowerDown2 = PowerDownDeviceInitial2; + context->CompletionRoutine = ClasspPowerDownCompletion; + + } + + // + // we are not dealing with port-allocated sense in these routines. + // + + ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)); + ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE)); + + // + // we are always returning STATUS_PENDING, so we need to always + // set the irp as pending. + // + + IoMarkIrpPending(Irp); + + if(Options.LockQueue) { + + // + // Send the lock irp down. + // + + IoSetCompletionRoutine(Irp, + context->CompletionRoutine, + context, + TRUE, + TRUE, + TRUE); + + IoCallDriver(lowerDevice, Irp); + + } else { + + // + // Call the completion routine directly. It won't care what the + // status of the "lock" was - it will just go and do the next + // step of the operation. + // + + context->CompletionRoutine(DeviceObject, Irp, context); + } + + return STATUS_PENDING; + +ClasspPowerHandlerCleanup: + + ClassReleaseRemoveLock(DeviceObject, Irp); + + DebugPrint((1, "(%p)\tStarting next power irp\n", Irp)); + IoCopyCurrentIrpStackLocationToNext(Irp); + IoSetCompletionRoutine(Irp, + ClasspStartNextPowerIrpCompletion, + NULL, + TRUE, + TRUE, + TRUE); + return PoCallDriver(lowerDevice, Irp); +} // end ClasspPowerHandler() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassMinimalPowerHandler() + +Routine Description: + + This routine is the minimum power handler for a storage driver. It does + the least amount of work possible. + +--*/ +NTSTATUS +ClassMinimalPowerHandler( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + NTSTATUS status; + + ClassReleaseRemoveLock(DeviceObject, Irp); + PoStartNextPowerIrp(Irp); + + if(commonExtension->IsFdo) { + + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = + DeviceObject->DeviceExtension; + + // + // Check if the system is going to hibernate or standby. + // + if (irpStack->MinorFunction == IRP_MN_SET_POWER){ + PVPB vpb; + + switch (irpStack->Parameters.Power.ShutdownType){ + + case PowerActionSleep: + case PowerActionHibernate: + // + // If the volume is mounted, set the verify bit so that + // the filesystem will be forced re-read the media + // after coming out of hibernation or standby. + // + vpb = ClassGetVpb(fdoExtension->DeviceObject); + if (vpb && (vpb->Flags & VPB_MOUNTED)){ + SET_FLAG(fdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME); + } + break; + } + } + } + + IoCopyCurrentIrpStackLocationToNext(Irp); + return PoCallDriver(commonExtension->LowerDeviceObject, Irp); + + } else { + + if (irpStack->MinorFunction != IRP_MN_SET_POWER && + irpStack->MinorFunction != IRP_MN_QUERY_POWER) { + + NOTHING; + + } else { + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + } + status = Irp->IoStatus.Status; + + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + return status; + } +} // end ClassMinimalPowerHandler() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassSpinDownPowerHandler() + +Routine Description: + + This routine is a callback for disks and other things which require both + a start and a stop to be sent to the device. (actually the starts are + almost always optional, since most device power themselves on to process + commands, but i digress). + + Determines proper use of spinup, spindown, and queue locking based upon + ScanForSpecialFlags in the FdoExtension. This is the most common power + handler passed into classpnp.sys + +Arguments: + + DeviceObject - Supplies the functional device object + + Irp - Supplies the request to be retried. + +Return Value: + + None + +--*/ +NTSTATUS +ClassSpinDownPowerHandler( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + CLASS_POWER_OPTIONS options; + + fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // this will set all options to FALSE + // + + RtlZeroMemory(&options, sizeof(CLASS_POWER_OPTIONS)); + + // + // check the flags to see what options we need to worry about + // + + if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_SPIN_DOWN)) { + options.HandleSpinDown = TRUE; + } + + if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_SPIN_UP)) { + options.HandleSpinUp = TRUE; + } + + if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_NO_QUEUE_LOCK)) { + options.LockQueue = TRUE; + } + + DebugPrint((3, "ClasspPowerHandler: Devobj %p\n" + "\t%shandling spin down\n" + "\t%shandling spin up\n" + "\t%slocking queue\n", + DeviceObject, + (options.HandleSpinDown ? "" : "not "), + (options.HandleSpinUp ? "" : "not "), + (options.LockQueue ? "" : "not ") + )); + + // + // do all the dirty work + // + + return ClasspPowerHandler(DeviceObject, Irp, options); +} // end ClassSpinDownPowerHandler() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClassStopUnitPowerHandler() + +Routine Description: + + This routine is an outdated call. To achieve equivalent functionality, + the driver should set the following flags in ScanForSpecialFlags in the + FdoExtension: + + CLASS_SPECIAL_DISABLE_SPIN_UP + CLASS_SPECIAL_NO_QUEUE_LOCK + +--*/ +NTSTATUS +ClassStopUnitPowerHandler( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + + DebugPrint((0, "ClassStopUnitPowerHandler - Devobj %p using outdated call\n" + "Drivers should set the following flags in ScanForSpecialFlags " + " in the FDO extension:\n" + "\tCLASS_SPECIAL_DISABLE_SPIN_UP\n" + "\tCLASS_SPECIAL_NO_QUEUE_LOCK\n" + "This will provide equivalent functionality if the power " + "routine is then set to ClassSpinDownPowerHandler\n\n", + DeviceObject)); + + fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + SET_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_SPIN_UP); + SET_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_NO_QUEUE_LOCK); + + return ClassSpinDownPowerHandler(DeviceObject, Irp); +} // end ClassStopUnitPowerHandler() + +/*++//////////////////////////////////////////////////////////////////////////// + +RetryPowerRequest() + +Routine Description: + + This routine reinitalizes the necessary fields, and sends the request + to the lower driver. + +Arguments: + + DeviceObject - Supplies the device object associated with this request. + + Irp - Supplies the request to be retried. + + Context - Supplies a pointer to the power up context for this request. + +Return Value: + + None + +--*/ +VOID +RetryPowerRequest( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PCLASS_POWER_CONTEXT Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK srb = &(Context->Srb); + LARGE_INTEGER dueTime; + + DebugPrint((1, "(%p)\tDelaying retry by queueing DPC\n", Irp)); + + ASSERT(Context->Irp == Irp); + ASSERT(Context->DeviceObject == DeviceObject); + ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)); + ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE)); + + // + // reset the retry interval + // + + Context->RetryInterval = 0; + + // + // Reset byte count of transfer in SRB Extension. + // + + srb->DataTransferLength = 0; + + // + // Zero SRB statuses. + // + + srb->SrbStatus = srb->ScsiStatus = 0; + + // + // Set up major SCSI function. + // + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Save SRB address in next stack for port driver. + // + + nextIrpStack->Parameters.Scsi.Srb = srb; + + // + // Set the completion routine up again. + // + + IoSetCompletionRoutine(Irp, Context->CompletionRoutine, Context, + TRUE, TRUE, TRUE); + + + if (Context->RetryInterval == 0) { + + DebugPrint((2, "(%p)\tDelaying minimum time (.2 sec)\n", Irp)); + dueTime.QuadPart = (LONGLONG)1000000 * 2; + + } else { + + DebugPrint((2, "(%p)\tDelaying %x seconds\n", + Irp, Context->RetryInterval)); + dueTime.QuadPart = (LONGLONG)1000000 * 10 * Context->RetryInterval; + + } + + ClassRetryRequest(DeviceObject, Irp, dueTime); + + return; + +} // end RetryRequest() + +/*++//////////////////////////////////////////////////////////////////////////// + +ClasspStartNextPowerIrpCompletion() + +Routine Description: + + This routine guarantees that the next power irp (power up or down) is not + sent until the previous one has fully completed. + +--*/ +NTSTATUS +ClasspStartNextPowerIrpCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + if(Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + PoStartNextPowerIrp(Irp); + return STATUS_SUCCESS; +} // end ClasspStartNextPowerIrpCompletion() + diff --git a/drivers/storage/classpnp/retry.c b/drivers/storage/classpnp/retry.c new file mode 100644 index 00000000000..3a88e647582 --- /dev/null +++ b/drivers/storage/classpnp/retry.c @@ -0,0 +1,349 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + retry.c + +Abstract: + + Packet retry routines for CLASSPNP + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#include "classp.h" +#include "debug.h" + + + +/* + * InterpretTransferPacketError + * + * Interpret the SRB error into a meaningful IRP status. + * ClassInterpretSenseInfo also may modify the SRB for the retry. + * + * Return TRUE iff packet should be retried. + */ +BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt) +{ + BOOLEAN shouldRetry = FALSE; + PCDB pCdb = (PCDB)Pkt->Srb.Cdb; + + /* + * Interpret the error using the returned sense info first. + */ + Pkt->RetryIntervalSec = 0; + if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL){ + /* + * This is an Ejection Control SRB. Interpret its sense info specially. + */ + shouldRetry = ClassInterpretSenseInfo( + Pkt->Fdo, + &Pkt->Srb, + IRP_MJ_SCSI, + 0, + MAXIMUM_RETRIES - Pkt->NumRetries, + &Pkt->Irp->IoStatus.Status, + &Pkt->RetryIntervalSec); + if (shouldRetry){ + /* + * If the device is not ready, wait at least 2 seconds before retrying. + */ + PSENSE_DATA senseInfoBuffer = Pkt->Srb.SenseInfoBuffer; + ASSERT(senseInfoBuffer); + if (((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) && + (senseInfoBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) || + (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)){ + + Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2); + } + } + } + else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) || + (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)){ + /* + * This is an Mode Sense SRB. Interpret its sense info specially. + */ + shouldRetry = ClassInterpretSenseInfo( + Pkt->Fdo, + &Pkt->Srb, + IRP_MJ_SCSI, + 0, + MAXIMUM_RETRIES - Pkt->NumRetries, + &Pkt->Irp->IoStatus.Status, + &Pkt->RetryIntervalSec); + if (shouldRetry){ + /* + * If the device is not ready, wait at least 2 seconds before retrying. + */ + PSENSE_DATA senseInfoBuffer = Pkt->Srb.SenseInfoBuffer; + ASSERT(senseInfoBuffer); + if (((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) && + (senseInfoBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) || + (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)){ + + Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2); + } + } + + /* + * Some special cases for mode sense. + */ + if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){ + shouldRetry = TRUE; + } + else if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN){ + /* + * This is a HACK. + * Atapi returns SRB_STATUS_DATA_OVERRUN when it really means + * underrun (i.e. success, and the buffer is longer than needed). + * So treat this as a success. + */ + Pkt->Irp->IoStatus.Status = STATUS_SUCCESS; + InterlockedExchangeAdd((PLONG)&Pkt->OriginalIrp->IoStatus.Information, (LONG)Pkt->Srb.DataTransferLength); + shouldRetry = FALSE; + } + } + else if (pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY){ + /* + * This is a Drive Capacity SRB. Interpret its sense info specially. + */ + shouldRetry = ClassInterpretSenseInfo( + Pkt->Fdo, + &Pkt->Srb, + IRP_MJ_SCSI, + 0, + MAXIMUM_RETRIES - Pkt->NumRetries, + &Pkt->Irp->IoStatus.Status, + &Pkt->RetryIntervalSec); + if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){ + shouldRetry = TRUE; + } + } + else if ((pCdb->CDB10.OperationCode == SCSIOP_READ) || + (pCdb->CDB10.OperationCode == SCSIOP_WRITE)){ + /* + * This is a Read/Write Data packet. + */ + PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp); + + shouldRetry = ClassInterpretSenseInfo( + Pkt->Fdo, + &Pkt->Srb, + origCurrentSp->MajorFunction, + 0, + MAXIMUM_RETRIES - Pkt->NumRetries, + &Pkt->Irp->IoStatus.Status, + &Pkt->RetryIntervalSec); + /* + * Deal with some special cases. + */ + if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){ + /* + * We are in extreme low-memory stress. + * We will retry in smaller chunks. + */ + shouldRetry = TRUE; + } + else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) && + (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)){ + /* + * We are still verifying a (possibly) reloaded disk/cdrom. + * So retry the request. + */ + Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR; + shouldRetry = TRUE; + } + } + else { + DBGERR(("Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt)); + } + + return shouldRetry; +} + + +/* + * RetryTransferPacket + * + * Retry sending a TRANSFER_PACKET. + * + * Return TRUE iff the packet is complete. + * (if so the status in pkt->irp is the final status). + */ +BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt) +{ + BOOLEAN packetDone; + + DBGTRACE(ClassDebugTrace, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(&Pkt->Srb))); + + ASSERT(Pkt->NumRetries > 0); + Pkt->NumRetries--; + + /* + * Tone down performance on the retry. + * This increases the chance for success on the retry. + * We've seen instances of drives that fail consistently but then start working + * once this scale-down is applied. + */ + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + CLEAR_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); + Pkt->Srb.QueueTag = SP_UNTAGGED; + + if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){ + PCDB pCdb = (PCDB)Pkt->Srb.Cdb; + BOOLEAN isReadWrite = ((pCdb->CDB10.OperationCode == SCSIOP_READ) || + (pCdb->CDB10.OperationCode == SCSIOP_WRITE)); + + if (Pkt->InLowMemRetry || !isReadWrite){ + /* + * This should never happen. + * The memory manager guarantees that at least four pages will + * be available to allow forward progress in the port driver. + * So a one-page transfer should never fail with insufficient resources. + */ + ASSERT(isReadWrite && !Pkt->InLowMemRetry); + packetDone = TRUE; + } + else { + /* + * We are in low-memory stress. + * Start the low-memory retry state machine, which tries to + * resend the packet in little one-page chunks. + */ + InitLowMemRetry( Pkt, + Pkt->BufPtrCopy, + Pkt->BufLenCopy, + Pkt->TargetLocationCopy); + StepLowMemRetry(Pkt); + packetDone = FALSE; + } + } + else { + /* + * Retry the packet by simply resending it after a delay. + * Put the packet back in the pending queue and + * schedule a timer to retry the transfer. + * + * Do not call SetupReadWriteTransferPacket again because: + * (1) The minidriver may have set some bits + * in the SRB that it needs again and + * (2) doing so would reset numRetries. + * + * BECAUSE we do not call SetupReadWriteTransferPacket again, + * we have to reset a couple fields in the SRB that + * some miniports overwrite when they fail an SRB. + */ + + Pkt->Srb.DataBuffer = Pkt->BufPtrCopy; + Pkt->Srb.DataTransferLength = Pkt->BufLenCopy; + + if (Pkt->RetryIntervalSec == 0){ + /* + * Always delay by at least a little when retrying. + * Some problems (e.g. CRC errors) are not recoverable without a slight delay. + */ + LARGE_INTEGER timerPeriod; + + timerPeriod.HighPart = -1; + timerPeriod.LowPart = -(LONG)((ULONG)MINIMUM_RETRY_UNITS*KeQueryTimeIncrement()); + KeInitializeTimer(&Pkt->RetryTimer); + KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt); + KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC); + } + else { + LARGE_INTEGER timerPeriod; + + ASSERT(Pkt->RetryIntervalSec < 100); // sanity check + timerPeriod.HighPart = -1; + timerPeriod.LowPart = Pkt->RetryIntervalSec*-10000000; + KeInitializeTimer(&Pkt->RetryTimer); + KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt); + KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC); + } + packetDone = FALSE; + } + + return packetDone; +} + + +VOID TransferPacketRetryTimerDpc( IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2) +{ + PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)DeferredContext; + SubmitTransferPacket(pkt); +} + + +VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation) +{ + ASSERT(Len > 0); + ASSERT(!Pkt->InLowMemRetry); + Pkt->InLowMemRetry = TRUE; + Pkt->LowMemRetry_remainingBufPtr = BufPtr; + Pkt->LowMemRetry_remainingBufLen = Len; + Pkt->LowMemRetry_nextChunkTargetLocation = TargetLocation; +} + + +/* + * StepLowMemRetry + * + * During extreme low-memory stress, this function retries + * a packet in small one-page chunks, sent serially. + * + * Returns TRUE iff the packet is done. + */ +BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt) +{ + BOOLEAN packetDone; + + if (Pkt->LowMemRetry_remainingBufLen == 0){ + packetDone = TRUE; + } + else { + ULONG thisChunkLen; + ULONG bytesToNextPageBoundary; + + /* + * Make sure the little chunk we send is <= a page length + * AND that it does not cross any page boundaries. + */ + bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE); + thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary); + + /* + * Set up the transfer packet for the new little chunk. + * This will reset numRetries so that we retry each chunk as required. + */ + SetupReadWriteTransferPacket(Pkt, + Pkt->LowMemRetry_remainingBufPtr, + thisChunkLen, + Pkt->LowMemRetry_nextChunkTargetLocation, + Pkt->OriginalIrp); + + Pkt->LowMemRetry_remainingBufPtr += thisChunkLen; + Pkt->LowMemRetry_remainingBufLen -= thisChunkLen; + Pkt->LowMemRetry_nextChunkTargetLocation.QuadPart += thisChunkLen; + + SubmitTransferPacket(Pkt); + packetDone = FALSE; + } + + return packetDone; +} + diff --git a/drivers/storage/classpnp/utils.c b/drivers/storage/classpnp/utils.c new file mode 100644 index 00000000000..43dd04916d1 --- /dev/null +++ b/drivers/storage/classpnp/utils.c @@ -0,0 +1,565 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + utils.c + +Abstract: + + SCSI class driver routines + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#include "classp.h" +#include "debug.h" + + + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, ClassGetDeviceParameter) + #pragma alloc_text(PAGE, ClassScanForSpecial) + #pragma alloc_text(PAGE, ClassSetDeviceParameter) +#endif + + + +// custom string match -- careful! +BOOLEAN ClasspMyStringMatches(IN PCHAR StringToMatch OPTIONAL, IN PCHAR TargetString) +{ + ULONG length; // strlen returns an int, not size_t (!) + PAGED_CODE(); + ASSERT(TargetString); + // if no match requested, return TRUE + if (StringToMatch == NULL) { + return TRUE; + } + // cache the string length for efficiency + length = strlen(StringToMatch); + // ZERO-length strings may only match zero-length strings + if (length == 0) { + return (strlen(TargetString) == 0); + } + // strncmp returns zero if the strings match + return (strncmp(StringToMatch, TargetString, length) == 0); +} + + +VOID ClassGetDeviceParameter( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PWSTR SubkeyName OPTIONAL, + IN PWSTR ParameterName, + IN OUT PULONG ParameterValue // also default value + ) +{ + NTSTATUS status; + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + HANDLE deviceParameterHandle; + HANDLE deviceSubkeyHandle; + ULONG defaultParameterValue; + + PAGED_CODE(); + + // + // open the given parameter + // + + status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo, + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + &deviceParameterHandle); + + if (NT_SUCCESS(status) && (SubkeyName != NULL)) { + + UNICODE_STRING subkeyName; + OBJECT_ATTRIBUTES objectAttributes; + + RtlInitUnicodeString(&subkeyName, SubkeyName); + InitializeObjectAttributes(&objectAttributes, + &subkeyName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + deviceParameterHandle, + NULL); + + status = ZwOpenKey(&deviceSubkeyHandle, + KEY_READ, + &objectAttributes); + if (!NT_SUCCESS(status)) { + ZwClose(deviceParameterHandle); + } + + } + + if (NT_SUCCESS(status)) { + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + defaultParameterValue = *ParameterValue; + + queryTable->Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; + queryTable->Name = ParameterName; + queryTable->EntryContext = ParameterValue; + queryTable->DefaultType = REG_DWORD; + queryTable->DefaultData = NULL; + queryTable->DefaultLength = 0; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + (PWSTR)(SubkeyName ? + deviceSubkeyHandle : + deviceParameterHandle), + queryTable, + NULL, + NULL); + if (!NT_SUCCESS(status)) { + *ParameterValue = defaultParameterValue; // use default value + } + + // + // close what we open + // + + if (SubkeyName) { + ZwClose(deviceSubkeyHandle); + } + + ZwClose(deviceParameterHandle); + } + + return; + +} // end ClassGetDeviceParameter() + + +NTSTATUS ClassSetDeviceParameter( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PWSTR SubkeyName OPTIONAL, + IN PWSTR ParameterName, + IN ULONG ParameterValue) +{ + NTSTATUS status; + HANDLE deviceParameterHandle; + HANDLE deviceSubkeyHandle; + + PAGED_CODE(); + + // + // open the given parameter + // + + status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo, + PLUGPLAY_REGKEY_DEVICE, + KEY_READ | KEY_WRITE, + &deviceParameterHandle); + + if (NT_SUCCESS(status) && (SubkeyName != NULL)) { + + UNICODE_STRING subkeyName; + OBJECT_ATTRIBUTES objectAttributes; + + RtlInitUnicodeString(&subkeyName, SubkeyName); + InitializeObjectAttributes(&objectAttributes, + &subkeyName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + deviceParameterHandle, + NULL); + + status = ZwCreateKey(&deviceSubkeyHandle, + KEY_READ | KEY_WRITE, + &objectAttributes, + 0, NULL, 0, NULL); + if (!NT_SUCCESS(status)) { + ZwClose(deviceParameterHandle); + } + + } + + if (NT_SUCCESS(status)) { + + status = RtlWriteRegistryValue( + RTL_REGISTRY_HANDLE, + (PWSTR) (SubkeyName ? + deviceSubkeyHandle : + deviceParameterHandle), + ParameterName, + REG_DWORD, + &ParameterValue, + sizeof(ULONG)); + + // + // close what we open + // + + if (SubkeyName) { + ZwClose(deviceSubkeyHandle); + } + + ZwClose(deviceParameterHandle); + } + + return status; + +} // end ClassSetDeviceParameter() + + +/* + * ClassScanForSpecial + * + * This routine was written to simplify scanning for special + * hardware based upon id strings. it does not check the registry. + */ + +VOID ClassScanForSpecial( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList[], + IN PCLASS_SCAN_FOR_SPECIAL_HANDLER Function) +{ + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; + PUCHAR vendorId; + PUCHAR productId; + PUCHAR productRevision; + UCHAR nullString[] = ""; + ULONG j; + + PAGED_CODE(); + ASSERT(DeviceList); + ASSERT(Function); + + deviceDescriptor = FdoExtension->DeviceDescriptor; + + if (DeviceList == NULL) { + return; + } + if (Function == NULL) { + return; + } + + // + // SCSI sets offsets to -1, ATAPI sets to 0. check for both. + // + + if (deviceDescriptor->VendorIdOffset != 0 && + deviceDescriptor->VendorIdOffset != -1) { + vendorId = ((PUCHAR)deviceDescriptor); + vendorId += deviceDescriptor->VendorIdOffset; + } else { + vendorId = nullString; + } + if (deviceDescriptor->ProductIdOffset != 0 && + deviceDescriptor->ProductIdOffset != -1) { + productId = ((PUCHAR)deviceDescriptor); + productId += deviceDescriptor->ProductIdOffset; + } else { + productId = nullString; + } + if (deviceDescriptor->VendorIdOffset != 0 && + deviceDescriptor->VendorIdOffset != -1) { + productRevision = ((PUCHAR)deviceDescriptor); + productRevision += deviceDescriptor->ProductRevisionOffset; + } else { + productRevision = nullString; + } + + // + // loop while the device list is valid (not null-filled) + // + + for (;(DeviceList->VendorId != NULL || + DeviceList->ProductId != NULL || + DeviceList->ProductRevision != NULL);DeviceList++) { + + if (ClasspMyStringMatches(DeviceList->VendorId, vendorId) && + ClasspMyStringMatches(DeviceList->ProductId, productId) && + ClasspMyStringMatches(DeviceList->ProductRevision, productRevision) + ) { + + DebugPrint((1, "ClasspScanForSpecialByInquiry: Found matching " + "controller Ven: %s Prod: %s Rev: %s\n", + vendorId, productId, productRevision)); + + // + // pass the context to the call back routine and exit + // + + (Function)(FdoExtension, DeviceList->Data); + + // + // for CHK builds, try to prevent wierd stacks by having a debug + // print here. it's a hack, but i know of no other way to prevent + // the stack from being wrong. + // + + DebugPrint((16, "ClasspScanForSpecialByInquiry: " + "completed callback\n")); + return; + + } // else the strings did not match + + } // none of the devices matched. + + DebugPrint((1, "ClasspScanForSpecialByInquiry: no match found for %p\n", + FdoExtension->DeviceObject)); + return; + +} // end ClasspScanForSpecialByInquiry() + + +// +// In order to provide better performance without the need to reboot, +// we need to implement a self-adjusting method to set and clear the +// srb flags based upon current performance. +// +// whenever there is an error, immediately grab the spin lock. the +// MP perf hit here is acceptable, since we're in an error path. this +// is also neccessary because we are guaranteed to be modifying the +// SRB flags here, setting SuccessfulIO to zero, and incrementing the +// actual error count (which is always done within this spinlock). +// +// whenever there is no error, increment a counter. if there have been +// errors on the device, and we've enabled dynamic perf, *and* we've +// just crossed the perf threshhold, then grab the spin lock and +// double check that the threshhold has, indeed been hit(*). then +// decrement the error count, and if it's dropped sufficiently, undo +// some of the safety changes made in the SRB flags due to the errors. +// +// * this works in all cases. even if lots of ios occur after the +// previous guy went in and cleared the successfulio counter, that +// just means that we've hit the threshhold again, and so it's proper +// to run the inner loop again. +// + +VOID +ClasspPerfIncrementErrorCount( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; + KIRQL oldIrql; + ULONG errors; + + KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); + + fdoData->Perf.SuccessfulIO = 0; // implicit interlock + errors = InterlockedIncrement(&FdoExtension->ErrorCount); + + if (errors >= CLASS_ERROR_LEVEL_1) { + + // + // If the error count has exceeded the error limit, then disable + // any tagged queuing, multiple requests per lu queueing + // and sychronous data transfers. + // + // Clearing the no queue freeze flag prevents the port driver + // from sending multiple requests per logical unit. + // + + CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); + + SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + + DebugPrint((ClassDebugError, "ClasspPerfIncrementErrorCount: " + "Too many errors; disabling tagged queuing and " + "synchronous data tranfers.\n")); + + } + + if (errors >= CLASS_ERROR_LEVEL_2) { + + // + // If a second threshold is reached, disable disconnects. + // + + SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); + DebugPrint((ClassDebugError, "ClasspPerfIncrementErrorCount: " + "Too many errors; disabling disconnects.\n")); + } + + KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); + return; +} + +VOID +ClasspPerfIncrementSuccessfulIo( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; + KIRQL oldIrql; + ULONG errors; + ULONG succeeded = 0; + + // + // don't take a hit from the interlocked op unless we're in + // a degraded state and we've got a threshold to hit. + // + + if (FdoExtension->ErrorCount == 0) { + return; + } + + if (fdoData->Perf.ReEnableThreshhold == 0) { + return; + } + + succeeded = InterlockedIncrement(&fdoData->Perf.SuccessfulIO); + if (succeeded < fdoData->Perf.ReEnableThreshhold) { + return; + } + + // + // if we hit the threshold, grab the spinlock and verify we've + // actually done so. this allows us to ignore the spinlock 99% + // of the time. + // + + KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); + + // + // re-read the value, so we don't run this multiple times + // for a single threshhold being hit. this keeps errorcount + // somewhat useful. + // + + succeeded = fdoData->Perf.SuccessfulIO; + + if ((FdoExtension->ErrorCount != 0) && + (fdoData->Perf.ReEnableThreshhold <= succeeded) + ) { + + fdoData->Perf.SuccessfulIO = 0; // implicit interlock + + ASSERT(FdoExtension->ErrorCount > 0); + errors = InterlockedDecrement(&FdoExtension->ErrorCount); + + // + // note: do in reverse order of the sets "just in case" + // + + if (errors < CLASS_ERROR_LEVEL_2) { + if (errors == CLASS_ERROR_LEVEL_2 - 1) { + DebugPrint((ClassDebugError, "ClasspPerfIncrementSuccessfulIo: " + "Error level 2 no longer required.\n")); + } + if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags, + SRB_FLAGS_DISABLE_DISCONNECT)) { + CLEAR_FLAG(FdoExtension->SrbFlags, + SRB_FLAGS_DISABLE_DISCONNECT); + } + } + + if (errors < CLASS_ERROR_LEVEL_1) { + if (errors == CLASS_ERROR_LEVEL_1 - 1) { + DebugPrint((ClassDebugError, "ClasspPerfIncrementSuccessfulIo: " + "Error level 1 no longer required.\n")); + } + if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags, + SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) { + CLEAR_FLAG(FdoExtension->SrbFlags, + SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + } + if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags, + SRB_FLAGS_QUEUE_ACTION_ENABLE)) { + SET_FLAG(FdoExtension->SrbFlags, + SRB_FLAGS_QUEUE_ACTION_ENABLE); + } + if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags, + SRB_FLAGS_NO_QUEUE_FREEZE)) { + SET_FLAG(FdoExtension->SrbFlags, + SRB_FLAGS_NO_QUEUE_FREEZE); + } + } + } // end of threshhold definitely being hit for first time + + KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); + return; +} + + +PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen) +{ + PMDL mdl; + + mdl = IoAllocateMdl(Buffer, BufferLen, FALSE, FALSE, NULL); + if (mdl){ + _SEH2_TRY { + /* + * We are reading from the device. + * Therefore, the device is WRITING to the locked memory. + * So we request IoWriteAccess. + */ + MmProbeAndLockPages(mdl, KernelMode, IoWriteAccess); + + } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + NTSTATUS status = _SEH2_GetExceptionCode(); + + DBGWARN(("BuildReadMdl: MmProbeAndLockPages failed with %xh.", status)); + IoFreeMdl(mdl); + mdl = NULL; + } _SEH2_END; + } + else { + DBGWARN(("BuildReadMdl: IoAllocateMdl failed")); + } + + return mdl; +} + + +VOID FreeDeviceInputMdl(PMDL Mdl) +{ + MmUnlockPages(Mdl); + IoFreeMdl(Mdl); +} + + +#if 0 + VOID + ClasspPerfResetCounters( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) + { + PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; + KIRQL oldIrql; + + KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); + DebugPrint((ClassDebugError, "ClasspPerfResetCounters: " + "Resetting all perf counters.\n")); + fdoData->Perf.SuccessfulIO = 0; + FdoExtension->ErrorCount = 0; + + if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags, + SRB_FLAGS_DISABLE_DISCONNECT)) { + CLEAR_FLAG(FdoExtension->SrbFlags, + SRB_FLAGS_DISABLE_DISCONNECT); + } + if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags, + SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) { + CLEAR_FLAG(FdoExtension->SrbFlags, + SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + } + if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags, + SRB_FLAGS_QUEUE_ACTION_ENABLE)) { + SET_FLAG(FdoExtension->SrbFlags, + SRB_FLAGS_QUEUE_ACTION_ENABLE); + } + if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags, + SRB_FLAGS_NO_QUEUE_FREEZE)) { + SET_FLAG(FdoExtension->SrbFlags, + SRB_FLAGS_NO_QUEUE_FREEZE); + } + KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); + return; + } +#endif + + diff --git a/drivers/storage/classpnp/xferpkt.c b/drivers/storage/classpnp/xferpkt.c new file mode 100644 index 00000000000..7cd1f92cd63 --- /dev/null +++ b/drivers/storage/classpnp/xferpkt.c @@ -0,0 +1,911 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + xferpkt.c + +Abstract: + + Packet routines for CLASSPNP + +Environment: + + kernel mode only + +Notes: + + +Revision History: + +--*/ + +#include "classp.h" +#include "debug.h" + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, InitializeTransferPackets) + #pragma alloc_text(PAGE, DestroyAllTransferPackets) + #pragma alloc_text(PAGE, SetupEjectionTransferPacket) + #pragma alloc_text(PAGE, SetupModeSenseTransferPacket) +#endif + + +ULONG MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer; +ULONG MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer; + + +/* + * InitializeTransferPackets + * + * Allocate/initialize TRANSFER_PACKETs and related resources. + */ +NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo) +{ + PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor; + ULONG hwMaxPages; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + /* + * Precompute the maximum transfer length + */ + ASSERT(adapterDesc->MaximumTransferLength); + ASSERT(adapterDesc->MaximumPhysicalPages); + hwMaxPages = adapterDesc->MaximumPhysicalPages ? adapterDesc->MaximumPhysicalPages-1 : 0; + +#if defined(_AMD64_SIMULATOR_) + + // + // The simulator appears to have a problem with large transfers. + // + + if (hwMaxPages > 4) { + hwMaxPages = 4; + } + +#endif + + fdoData->HwMaxXferLen = MIN(adapterDesc->MaximumTransferLength, hwMaxPages << PAGE_SHIFT); + fdoData->HwMaxXferLen = MAX(fdoData->HwMaxXferLen, PAGE_SIZE); + + fdoData->NumTotalTransferPackets = 0; + fdoData->NumFreeTransferPackets = 0; + InitializeSListHead(&fdoData->FreeTransferPacketsList); + InitializeListHead(&fdoData->AllTransferPacketsList); + InitializeListHead(&fdoData->DeferredClientIrpList); + + /* + * Set the packet threshold numbers based on the Windows SKU. + */ + if (ExVerifySuite(Personal)){ + // this is Windows Personal + MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer; + MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer; + } + else if (ExVerifySuite(Enterprise) || ExVerifySuite(DataCenter)){ + // this is Advanced Server or Datacenter + MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Enterprise; + MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Enterprise; + } + else if (ExVerifySuite(TerminalServer)){ + // this is standard Server or Pro with terminal server + MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Server; + MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Server; + } + else { + // this is Professional without terminal server + MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer; + MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer; + } + + while (fdoData->NumFreeTransferPackets < MIN_INITIAL_TRANSFER_PACKETS){ + PTRANSFER_PACKET pkt = NewTransferPacket(Fdo); + if (pkt){ + InterlockedIncrement(&fdoData->NumTotalTransferPackets); + EnqueueFreeTransferPacket(Fdo, pkt); + } + else { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + } + fdoData->DbgPeakNumTransferPackets = fdoData->NumTotalTransferPackets; + + /* + * Pre-initialize our SCSI_REQUEST_BLOCK template with all + * the constant fields. This will save a little time for each xfer. + * NOTE: a CdbLength field of 10 may not always be appropriate + */ + RtlZeroMemory(&fdoData->SrbTemplate, sizeof(SCSI_REQUEST_BLOCK)); + fdoData->SrbTemplate.Length = sizeof(SCSI_REQUEST_BLOCK); + fdoData->SrbTemplate.Function = SRB_FUNCTION_EXECUTE_SCSI; + fdoData->SrbTemplate.QueueAction = SRB_SIMPLE_TAG_REQUEST; + fdoData->SrbTemplate.SenseInfoBufferLength = sizeof(SENSE_DATA); + fdoData->SrbTemplate.CdbLength = 10; + + return status; +} + + +VOID DestroyAllTransferPackets(PDEVICE_OBJECT Fdo) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + TRANSFER_PACKET *pkt; + + PAGED_CODE(); + + ASSERT(IsListEmpty(&fdoData->DeferredClientIrpList)); + + while (pkt = DequeueFreeTransferPacket(Fdo, FALSE)){ + DestroyTransferPacket(pkt); + InterlockedDecrement(&fdoData->NumTotalTransferPackets); + } + + ASSERT(fdoData->NumTotalTransferPackets == 0); +} + + +PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + PTRANSFER_PACKET newPkt; + + newPkt = ExAllocatePoolWithTag(NonPagedPool, sizeof(TRANSFER_PACKET), 'pnPC'); + if (newPkt){ + RtlZeroMemory(newPkt, sizeof(TRANSFER_PACKET)); // just to be sure + + /* + * Allocate resources for the packet. + */ + newPkt->Irp = IoAllocateIrp(Fdo->StackSize, FALSE); + if (newPkt->Irp){ + KIRQL oldIrql; + + newPkt->Fdo = Fdo; + + /* + * Enqueue the packet in our static AllTransferPacketsList + * (just so we can find it during debugging if its stuck somewhere). + */ + KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); + InsertTailList(&fdoData->AllTransferPacketsList, &newPkt->AllPktsListEntry); + KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); + } + else { + ExFreePool(newPkt); + newPkt = NULL; + } + } + + return newPkt; +} + + +/* + * DestroyTransferPacket + * + */ +VOID DestroyTransferPacket(PTRANSFER_PACKET Pkt) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + KIRQL oldIrql; + + ASSERT(!Pkt->SlistEntry.Next); + ASSERT(!Pkt->OriginalIrp); + + KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); + + /* + * Delete the packet from our all-packets queue. + */ + ASSERT(!IsListEmpty(&Pkt->AllPktsListEntry)); + ASSERT(!IsListEmpty(&fdoData->AllTransferPacketsList)); + RemoveEntryList(&Pkt->AllPktsListEntry); + InitializeListHead(&Pkt->AllPktsListEntry); + + KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); + + IoFreeIrp(Pkt->Irp); + ExFreePool(Pkt); +} + + +VOID EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo, PTRANSFER_PACKET Pkt) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + KIRQL oldIrql; + ULONG newNumPkts; + + ASSERT(!Pkt->SlistEntry.Next); + + InterlockedPushEntrySList(&fdoData->FreeTransferPacketsList, &Pkt->SlistEntry); + newNumPkts = InterlockedIncrement(&fdoData->NumFreeTransferPackets); + ASSERT(newNumPkts <= fdoData->NumTotalTransferPackets); + + /* + * If the total number of packets is larger than MinWorkingSetTransferPackets, + * that means that we've been in stress. If all those packets are now + * free, then we are now out of stress and can free the extra packets. + * Free down to MaxWorkingSetTransferPackets immediately, and + * down to MinWorkingSetTransferPackets lazily (one at a time). + */ + if (fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets){ + + /* + * 1. Immediately snap down to our UPPER threshold. + */ + if (fdoData->NumTotalTransferPackets > MaxWorkingSetTransferPackets){ + SINGLE_LIST_ENTRY pktList; + PSINGLE_LIST_ENTRY slistEntry; + PTRANSFER_PACKET pktToDelete; + + DBGTRACE(ClassDebugTrace, ("Exiting stress, block freeing (%d-%d) packets.", fdoData->NumTotalTransferPackets, MaxWorkingSetTransferPackets)); + + /* + * Check the counter again with lock held. This eliminates a race condition + * while still allowing us to not grab the spinlock in the common codepath. + * + * Note that the spinlock does not synchronize with threads dequeuing free + * packets to send (DequeueFreeTransferPacket does that with a lightweight + * interlocked exchange); the spinlock prevents multiple threads in this function + * from deciding to free too many extra packets at once. + */ + SimpleInitSlistHdr(&pktList); + KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); + while ((fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets) && + (fdoData->NumTotalTransferPackets > MaxWorkingSetTransferPackets)){ + + pktToDelete = DequeueFreeTransferPacket(Fdo, FALSE); + if (pktToDelete){ + SimplePushSlist(&pktList, &pktToDelete->SlistEntry); + InterlockedDecrement(&fdoData->NumTotalTransferPackets); + } + else { + DBGTRACE(ClassDebugTrace, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (1).", MaxWorkingSetTransferPackets, Fdo, fdoData->NumTotalTransferPackets)); + break; + } + } + KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); + + while (slistEntry = SimplePopSlist(&pktList)){ + pktToDelete = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry); + DestroyTransferPacket(pktToDelete); + } + + } + + /* + * 2. Lazily work down to our LOWER threshold (by only freeing one packet at a time). + */ + if (fdoData->NumTotalTransferPackets > MinWorkingSetTransferPackets){ + /* + * Check the counter again with lock held. This eliminates a race condition + * while still allowing us to not grab the spinlock in the common codepath. + * + * Note that the spinlock does not synchronize with threads dequeuing free + * packets to send (DequeueFreeTransferPacket does that with a lightweight + * interlocked exchange); the spinlock prevents multiple threads in this function + * from deciding to free too many extra packets at once. + */ + PTRANSFER_PACKET pktToDelete = NULL; + + DBGTRACE(ClassDebugTrace, ("Exiting stress, lazily freeing one of %d/%d packets.", fdoData->NumTotalTransferPackets, MinWorkingSetTransferPackets)); + + KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); + if ((fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets) && + (fdoData->NumTotalTransferPackets > MinWorkingSetTransferPackets)){ + + pktToDelete = DequeueFreeTransferPacket(Fdo, FALSE); + if (pktToDelete){ + InterlockedDecrement(&fdoData->NumTotalTransferPackets); + } + else { + DBGTRACE(ClassDebugTrace, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (2).", MinWorkingSetTransferPackets, Fdo, fdoData->NumTotalTransferPackets)); + } + } + KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); + + if (pktToDelete){ + DestroyTransferPacket(pktToDelete); + } + } + + } + +} + + +PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + PTRANSFER_PACKET pkt; + PSINGLE_LIST_ENTRY slistEntry; + KIRQL oldIrql; + + slistEntry = InterlockedPopEntrySList(&fdoData->FreeTransferPacketsList); + if (slistEntry){ + slistEntry->Next = NULL; + pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry); + ASSERT(fdoData->NumFreeTransferPackets > 0); + InterlockedDecrement(&fdoData->NumFreeTransferPackets); + } + else { + if (AllocIfNeeded){ + /* + * We are in stress and have run out of lookaside packets. + * In order to service the current transfer, + * allocate an extra packet. + * We will free it lazily when we are out of stress. + */ + pkt = NewTransferPacket(Fdo); + if (pkt){ + InterlockedIncrement(&fdoData->NumTotalTransferPackets); + fdoData->DbgPeakNumTransferPackets = max(fdoData->DbgPeakNumTransferPackets, fdoData->NumTotalTransferPackets); + } + else { + DBGWARN(("DequeueFreeTransferPacket: packet allocation failed")); + } + } + else { + pkt = NULL; + } + } + + return pkt; +} + + + +/* + * SetupReadWriteTransferPacket + * + * This function is called once to set up the first attempt to send a packet. + * It is not called before a retry, as SRB fields may be modified for the retry. + * + * Set up the Srb of the TRANSFER_PACKET for the transfer. + * The Irp is set up in SubmitTransferPacket because it must be reset + * for each packet submission. + */ +VOID SetupReadWriteTransferPacket( PTRANSFER_PACKET Pkt, + PVOID Buf, + ULONG Len, + LARGE_INTEGER DiskLocation, + PIRP OriginalIrp) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + PIO_STACK_LOCATION origCurSp = IoGetCurrentIrpStackLocation(OriginalIrp); + UCHAR majorFunc = origCurSp->MajorFunction; + ULONG logicalBlockAddr; + ULONG numTransferBlocks; + PCDB pCdb; + + logicalBlockAddr = (ULONG)Int64ShrlMod32(DiskLocation.QuadPart, fdoExt->SectorShift); + numTransferBlocks = Len >> fdoExt->SectorShift; + + /* + * Slap the constant SRB fields in from our pre-initialized template. + * We'll then only have to fill in the unique fields for this transfer. + * Tell lower drivers to sort the SRBs by the logical block address + * so that disk seeks are minimized. + */ + Pkt->Srb = fdoData->SrbTemplate; // copies _contents_ of SRB blocks + Pkt->Srb.DataBuffer = Buf; + Pkt->Srb.DataTransferLength = Len; + Pkt->Srb.QueueSortKey = logicalBlockAddr; + Pkt->Srb.OriginalRequest = Pkt->Irp; + Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData; + Pkt->Srb.TimeOutValue = (Len/0x10000) + ((Len%0x10000) ? 1 : 0); + Pkt->Srb.TimeOutValue *= fdoExt->TimeOutValue; + + /* + * Arrange values in CDB in big-endian format. + */ + pCdb = (PCDB)Pkt->Srb.Cdb; + pCdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte3; + pCdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte2; + pCdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte1; + pCdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte0; + pCdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte1; + pCdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte0; + pCdb->CDB10.OperationCode = (majorFunc==IRP_MJ_READ) ? SCSIOP_READ : SCSIOP_WRITE; + + /* + * Set SRB and IRP flags + */ + Pkt->Srb.SrbFlags = fdoExt->SrbFlags; + if (TEST_FLAG(OriginalIrp->Flags, IRP_PAGING_IO) || + TEST_FLAG(OriginalIrp->Flags, IRP_SYNCHRONOUS_PAGING_IO)){ + SET_FLAG(Pkt->Srb.SrbFlags, SRB_CLASS_FLAGS_PAGING); + } + SET_FLAG(Pkt->Srb.SrbFlags, (majorFunc==IRP_MJ_READ) ? SRB_FLAGS_DATA_IN : SRB_FLAGS_DATA_OUT); + + /* + * Allow caching only if this is not a write-through request. + * If write-through and caching is enabled on the device, force + * media access. + */ + if (TEST_FLAG(origCurSp->Flags, SL_WRITE_THROUGH)){ + if (TEST_FLAG(fdoExt->DeviceFlags, DEV_WRITE_CACHE)){ + pCdb->CDB10.ForceUnitAccess = TRUE; + } + } + else { + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_ADAPTER_CACHE_ENABLE); + } + + /* + * Remember the buf and len in the SRB because miniports + * can overwrite SRB.DataTransferLength and we may need it again + * for the retry. + */ + Pkt->BufPtrCopy = Buf; + Pkt->BufLenCopy = Len; + Pkt->TargetLocationCopy = DiskLocation; + + Pkt->OriginalIrp = OriginalIrp; + Pkt->NumRetries = MAXIMUM_RETRIES; + Pkt->SyncEventPtr = NULL; + Pkt->CompleteOriginalIrpWhenLastPacketCompletes = TRUE; +} + + +/* + * SubmitTransferPacket + * + * Set up the IRP for the TRANSFER_PACKET submission and send it down. + */ +VOID SubmitTransferPacket(PTRANSFER_PACKET Pkt) +{ + PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension; + PDEVICE_OBJECT nextDevObj = commonExtension->LowerDeviceObject; + PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Pkt->Irp); + + ASSERT(Pkt->Irp->CurrentLocation == Pkt->Irp->StackCount+1); + + /* + * Attach the SRB to the IRP. + * The reused IRP's stack location has to be rewritten for each retry + * call because IoCompleteRequest clears the stack locations. + */ + IoReuseIrp(Pkt->Irp, STATUS_NOT_SUPPORTED); + nextSp->MajorFunction = IRP_MJ_SCSI; + nextSp->Parameters.Scsi.Srb = &Pkt->Srb; + Pkt->Srb.ScsiStatus = Pkt->Srb.SrbStatus = 0; + if (Pkt->CompleteOriginalIrpWhenLastPacketCompletes){ + /* + * Only dereference the "original IRP"'s stack location + * if its a real client irp (as opposed to a static irp + * we're using just for result status for one of the non-IO scsi commands). + * + * For read/write, propagate the storage-specific IRP stack location flags + * (e.g. SL_OVERRIDE_VERIFY_VOLUME, SL_WRITE_THROUGH). + */ + PIO_STACK_LOCATION origCurSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp); + nextSp->Flags = origCurSp->Flags; + } + + /* + * Write MDL address to new IRP. In the port driver the SRB DataBuffer + * field is used as the actual buffer pointer within the MDL, + * so the same MDL can be used for each partial transfer. + * This saves having to build a new MDL for each partial transfer. + */ + Pkt->Irp->MdlAddress = Pkt->OriginalIrp->MdlAddress; + + IoSetCompletionRoutine(Pkt->Irp, TransferPktComplete, Pkt, TRUE, TRUE, TRUE); + IoCallDriver(nextDevObj, Pkt->Irp); +} + + +NTSTATUS TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context) +{ + PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)Context; + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = pkt->Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(pkt->OriginalIrp); + BOOLEAN packetDone = FALSE; + + /* + * Put all the assertions and spew in here so we don't have to look at them. + */ + DBGCHECKRETURNEDPKT(pkt); + + if (SRB_STATUS(pkt->Srb.SrbStatus) == SRB_STATUS_SUCCESS){ + + fdoData->LoggedTURFailureSinceLastIO = FALSE; + + /* + * The port driver should not have allocated a sense buffer + * if the SRB succeeded. + */ + ASSERT(!PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)); + + /* + * Add this packet's transferred length to the original IRP's. + */ + InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information, + (LONG)pkt->Srb.DataTransferLength); + + if (pkt->InLowMemRetry){ + packetDone = StepLowMemRetry(pkt); + } + else { + packetDone = TRUE; + } + + } + else { + /* + * The packet failed. We may retry it if possible. + */ + BOOLEAN shouldRetry; + + /* + * Make sure IRP status matches SRB error status (since we propagate it). + */ + if (NT_SUCCESS(Irp->IoStatus.Status)){ + Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + } + + /* + * Interpret the SRB error (to a meaningful IRP status) + * and determine if we should retry this packet. + * This call looks at the returned SENSE info to figure out what to do. + */ + shouldRetry = InterpretTransferPacketError(pkt); + + /* + * Sometimes the port driver can allocates a new 'sense' buffer + * to report transfer errors, e.g. when the default sense buffer + * is too small. If so, it is up to us to free it. + * Now that we're done interpreting the sense info, free it if appropriate. + */ + if (PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)) { + DBGTRACE(ClassDebugSenseInfo, ("Freeing port-allocated sense buffer for pkt %ph.", pkt)); + FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExt, &pkt->Srb); + pkt->Srb.SenseInfoBuffer = &pkt->SrbErrorSenseData; + pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA); + } + + /* + * If the SRB queue is locked-up, release it. + * Do this after calling the error handler. + */ + if (pkt->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN){ + ClassReleaseQueue(pkt->Fdo); + } + + if (shouldRetry && (pkt->NumRetries > 0)){ + packetDone = RetryTransferPacket(pkt); + } + else { + packetDone = TRUE; + } + + } + + /* + * If the packet is completed, put it back in the free list. + * If it is the last packet servicing the original request, complete the original irp. + */ + if (packetDone){ + LONG numPacketsRemaining; + PIRP deferredIrp; + PDEVICE_OBJECT Fdo = pkt->Fdo; + UCHAR uniqueAddr; + + /* + * In case a remove is pending, bump the lock count so we don't get freed + * right after we complete the original irp. + */ + ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddr); + + /* + * The original IRP should get an error code + * if any one of the packets failed. + */ + if (!NT_SUCCESS(Irp->IoStatus.Status)){ + pkt->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status; + + /* + * If the original I/O originated in user space (i.e. it is thread-queued), + * and the error is user-correctable (e.g. media is missing, for removable media), + * alert the user. + * Since this is only one of possibly several packets completing for the original IRP, + * we may do this more than once for a single request. That's ok; this allows + * us to test each returned status with IoIsErrorUserInduced(). + */ + if (IoIsErrorUserInduced(Irp->IoStatus.Status) && + pkt->CompleteOriginalIrpWhenLastPacketCompletes && + pkt->OriginalIrp->Tail.Overlay.Thread){ + + IoSetHardErrorOrVerifyDevice(pkt->OriginalIrp, pkt->Fdo); + } + } + + /* + * We use a field in the original IRP to count + * down the transfer pieces as they complete. + */ + numPacketsRemaining = InterlockedDecrement( + (PLONG)&pkt->OriginalIrp->Tail.Overlay.DriverContext[0]); + + if (numPacketsRemaining > 0){ + /* + * More transfer pieces remain for the original request. + * Wait for them to complete before completing the original irp. + */ + } + else { + + /* + * All the transfer pieces are done. + * Complete the original irp if appropriate. + */ + ASSERT(numPacketsRemaining == 0); + if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){ + if (NT_SUCCESS(pkt->OriginalIrp->IoStatus.Status)){ + ASSERT((ULONG)pkt->OriginalIrp->IoStatus.Information == origCurrentSp->Parameters.Read.Length); + ClasspPerfIncrementSuccessfulIo(fdoExt); + } + ClassReleaseRemoveLock(pkt->Fdo, pkt->OriginalIrp); + + ClassCompleteRequest(pkt->Fdo, pkt->OriginalIrp, IO_DISK_INCREMENT); + + /* + * We may have been called by one of the class drivers (e.g. cdrom) + * via the legacy API ClassSplitRequest. + * This is the only case for which the packet engine is called for an FDO + * with a StartIo routine; in that case, we have to call IoStartNextPacket + * now that the original irp has been completed. + */ + if (fdoExt->CommonExtension.DriverExtension->InitData.ClassStartIo) { + if (TEST_FLAG(pkt->Srb.SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET)){ + DBGTRAP(("SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (?)")); + } + else { + KIRQL oldIrql; + KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); + IoStartNextPacket(pkt->Fdo, FALSE); + KeLowerIrql(oldIrql); + } + } + } + } + + /* + * If the packet was synchronous, write the final + * result back to the issuer's status buffer and + * signal his event. + */ + if (pkt->SyncEventPtr){ + KeSetEvent(pkt->SyncEventPtr, 0, FALSE); + pkt->SyncEventPtr = NULL; + } + + /* + * Free the completed packet. + */ + pkt->OriginalIrp = NULL; + pkt->InLowMemRetry = FALSE; + EnqueueFreeTransferPacket(pkt->Fdo, pkt); + + /* + * Now that we have freed some resources, + * try again to send one of the previously deferred irps. + */ + deferredIrp = DequeueDeferredClientIrp(fdoData); + if (deferredIrp){ + DBGWARN(("... retrying deferred irp %xh.", deferredIrp)); + ServiceTransferRequest(pkt->Fdo, deferredIrp); + } + + ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddr); + } + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +/* + * SetupEjectionTransferPacket + * + * Set up a transferPacket for a synchronous Ejection Control transfer. + */ +VOID SetupEjectionTransferPacket( TRANSFER_PACKET *Pkt, + BOOLEAN PreventMediaRemoval, + PKEVENT SyncEventPtr, + PIRP OriginalIrp) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + PCDB pCdb; + + PAGED_CODE(); + + RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK)); + + Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK); + Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI; + Pkt->Srb.QueueAction = SRB_SIMPLE_TAG_REQUEST; + Pkt->Srb.CdbLength = 6; + Pkt->Srb.OriginalRequest = Pkt->Irp; + Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData; + Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA); + Pkt->Srb.TimeOutValue = fdoExt->TimeOutValue; + + Pkt->Srb.SrbFlags = fdoExt->SrbFlags; + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + + pCdb = (PCDB)Pkt->Srb.Cdb; + pCdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; + pCdb->MEDIA_REMOVAL.Prevent = PreventMediaRemoval; + + Pkt->BufPtrCopy = NULL; + Pkt->BufLenCopy = 0; + + Pkt->OriginalIrp = OriginalIrp; + Pkt->NumRetries = NUM_LOCKMEDIAREMOVAL_RETRIES; + Pkt->SyncEventPtr = SyncEventPtr; + Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE; +} + + +/* + * SetupModeSenseTransferPacket + * + * Set up a transferPacket for a synchronous Mode Sense transfer. + */ +VOID SetupModeSenseTransferPacket( TRANSFER_PACKET *Pkt, + PKEVENT SyncEventPtr, + PVOID ModeSenseBuffer, + UCHAR ModeSenseBufferLen, + UCHAR PageMode, + PIRP OriginalIrp) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + PCDB pCdb; + + PAGED_CODE(); + + RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK)); + + Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK); + Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI; + Pkt->Srb.QueueAction = SRB_SIMPLE_TAG_REQUEST; + Pkt->Srb.CdbLength = 6; + Pkt->Srb.OriginalRequest = Pkt->Irp; + Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData; + Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA); + Pkt->Srb.TimeOutValue = fdoExt->TimeOutValue; + Pkt->Srb.DataBuffer = ModeSenseBuffer; + Pkt->Srb.DataTransferLength = ModeSenseBufferLen; + + Pkt->Srb.SrbFlags = fdoExt->SrbFlags; + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DATA_IN); + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + + pCdb = (PCDB)Pkt->Srb.Cdb; + pCdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + pCdb->MODE_SENSE.PageCode = PageMode; + pCdb->MODE_SENSE.AllocationLength = (UCHAR)ModeSenseBufferLen; + + Pkt->BufPtrCopy = ModeSenseBuffer; + Pkt->BufLenCopy = ModeSenseBufferLen; + + Pkt->OriginalIrp = OriginalIrp; + Pkt->NumRetries = NUM_MODESENSE_RETRIES; + Pkt->SyncEventPtr = SyncEventPtr; + Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE; +} + + +/* + * SetupDriveCapacityTransferPacket + * + * Set up a transferPacket for a synchronous Drive Capacity transfer. + */ +VOID SetupDriveCapacityTransferPacket( TRANSFER_PACKET *Pkt, + PVOID ReadCapacityBuffer, + ULONG ReadCapacityBufferLen, + PKEVENT SyncEventPtr, + PIRP OriginalIrp) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + PCDB pCdb; + + RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK)); + + Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK); + Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI; + Pkt->Srb.QueueAction = SRB_SIMPLE_TAG_REQUEST; + Pkt->Srb.CdbLength = 10; + Pkt->Srb.OriginalRequest = Pkt->Irp; + Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData; + Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA); + Pkt->Srb.TimeOutValue = fdoExt->TimeOutValue; + Pkt->Srb.DataBuffer = ReadCapacityBuffer; + Pkt->Srb.DataTransferLength = ReadCapacityBufferLen; + + Pkt->Srb.SrbFlags = fdoExt->SrbFlags; + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DATA_IN); + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + + pCdb = (PCDB)Pkt->Srb.Cdb; + pCdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; + + Pkt->BufPtrCopy = ReadCapacityBuffer; + Pkt->BufLenCopy = ReadCapacityBufferLen; + + Pkt->OriginalIrp = OriginalIrp; + Pkt->NumRetries = NUM_DRIVECAPACITY_RETRIES; + Pkt->SyncEventPtr = SyncEventPtr; + Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE; +} + + +#if 0 + /* + * SetupSendStartUnitTransferPacket + * + * Set up a transferPacket for a synchronous Send Start Unit transfer. + */ + VOID SetupSendStartUnitTransferPacket( TRANSFER_PACKET *Pkt, + PIRP OriginalIrp) + { + PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; + PCDB pCdb; + + PAGED_CODE(); + + RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK)); + + /* + * Initialize the SRB. + * Use a very long timeout value to give the drive time to spin up. + */ + Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK); + Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI; + Pkt->Srb.TimeOutValue = START_UNIT_TIMEOUT; + Pkt->Srb.CdbLength = 6; + Pkt->Srb.OriginalRequest = Pkt->Irp; + Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData; + Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA); + Pkt->Srb.Lun = 0; + + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE); + SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + + pCdb = (PCDB)Pkt->Srb.Cdb; + pCdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; + pCdb->START_STOP.Start = 1; + pCdb->START_STOP.Immediate = 0; + pCdb->START_STOP.LogicalUnitNumber = 0; + + Pkt->OriginalIrp = OriginalIrp; + Pkt->NumRetries = 0; + Pkt->SyncEventPtr = NULL; + Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE; + } +#endif + + diff --git a/drivers/storage/directory.rbuild b/drivers/storage/directory.rbuild index c0c43724a9e..e25b40aae9e 100644 --- a/drivers/storage/directory.rbuild +++ b/drivers/storage/directory.rbuild @@ -16,4 +16,7 @@ + + + diff --git a/drivers/storage/inc/class.h b/drivers/storage/inc/class.h new file mode 100644 index 00000000000..f983d4ce3c0 --- /dev/null +++ b/drivers/storage/inc/class.h @@ -0,0 +1,374 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + class.h + +Abstract: + + These are the structures and defines that are used in the + SCSI class drivers. + +Author: + + Mike Glass (mglass) + Jeff Havens (jhavens) + +Revision History: + +--*/ + +#ifndef _CLASS_ + +#include +#include +#include +#include +#include +#include "ntddscsi.h" +#include + +// begin_ntminitape + +#if DBG + +#define DebugPrint(x) ScsiDebugPrint x + +#else + +#define DebugPrint(x) + +#endif // DBG + +// end_ntminitape + +#ifdef POOL_TAGGING +#undef ExAllocatePool +#undef ExAllocatePoolWithQuota +#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'HscS') +#define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a,b,'HscS') +#endif + +#define MAXIMUM_RETRIES 4 + +typedef +VOID +(*PCLASS_ERROR) ( + IN PDEVICE_OBJECT DeviceObject, + IN PSCSI_REQUEST_BLOCK Srb, + IN OUT NTSTATUS *Status, + IN OUT BOOLEAN *Retry + ); + +typedef struct _DEVICE_EXTENSION { + + // + // Back pointer to device object + // + + PDEVICE_OBJECT DeviceObject; + + // + // Pointer to port device object + // + + PDEVICE_OBJECT PortDeviceObject; + + // + // Length of partition in bytes + // + + LARGE_INTEGER PartitionLength; + + // + // Number of bytes before start of partition + // + + LARGE_INTEGER StartingOffset; + + // + // Bytes to skew all requests, since DM Driver has been placed on an IDE drive. + // + + ULONG DMByteSkew; + + // + // Sectors to skew all requests. + // + + ULONG DMSkew; + + // + // Flag to indicate whether DM driver has been located on an IDE drive. + // + + BOOLEAN DMActive; + + // + // Pointer to the specific class error routine. + // + + PCLASS_ERROR ClassError; + + // + // SCSI port driver capabilities + // + + PIO_SCSI_CAPABILITIES PortCapabilities; + + // + // Buffer for drive parameters returned in IO device control. + // + + PDISK_GEOMETRY DiskGeometry; + + // + // Back pointer to device object of physical device + // + + PDEVICE_OBJECT PhysicalDevice; + + // + // Request Sense Buffer + // + + PSENSE_DATA SenseData; + + // + // Request timeout in seconds; + // + + ULONG TimeOutValue; + + // + // System device number + // + + ULONG DeviceNumber; + + // + // Add default Srb Flags. + // + + ULONG SrbFlags; + + // + // Total number of SCSI protocol errors on the device. + // + + ULONG ErrorCount; + + // + // Spinlock for split requests + // + + KSPIN_LOCK SplitRequestSpinLock; + + // + // Zone header and spin lock for zoned SRB requests. + // + + PZONE_HEADER SrbZone; + + PKSPIN_LOCK SrbZoneSpinLock; + + // + // Lock count for removable media. + // + + LONG LockCount; + + // + // Scsi port number + // + + UCHAR PortNumber; + + // + // SCSI path id + // + + UCHAR PathId; + + // + // SCSI bus target id + // + + UCHAR TargetId; + + // + // SCSI bus logical unit number + // + + UCHAR Lun; + + // + // Log2 of sector size + // + + UCHAR SectorShift; + + // + // Flag to indicate that the device has write caching enabled. + // + + BOOLEAN WriteCache; + + // + // Build SCSI 1 or SCSI 2 CDBs + // + + BOOLEAN UseScsi1; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +// +// Define context structure for asynchronous completions. +// + +typedef struct _COMPLETION_CONTEXT { + PDEVICE_OBJECT DeviceObject; + SCSI_REQUEST_BLOCK Srb; +}COMPLETION_CONTEXT, *PCOMPLETION_CONTEXT; + + +NTSTATUS +ScsiClassGetCapabilities( + IN PDEVICE_OBJECT PortDeviceObject, + OUT PIO_SCSI_CAPABILITIES *PortCapabilities + ); + +NTSTATUS +ScsiClassGetInquiryData( + IN PDEVICE_OBJECT PortDeviceObject, + IN PSCSI_ADAPTER_BUS_INFO *ConfigInfo + ); + +NTSTATUS +ScsiClassReadDriveCapacity( + IN PDEVICE_OBJECT DeviceObject + ); + +VOID +ScsiClassReleaseQueue( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +ScsiClassRemoveDevice( + IN PDEVICE_OBJECT PortDeviceObject, + IN UCHAR PathId, + IN UCHAR TargetId, + IN UCHAR Lun + ); + +NTSTATUS +ScsiClassAsynchronousCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ); + +VOID +ScsiClassSplitRequest( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG MaximumBytes + ); + +NTSTATUS +ScsiClassDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ); + +NTSTATUS +ScsiClassIoComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +ScsiClassIoCompleteAssociated( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +BOOLEAN +ScsiClassInterpretSenseInfo( + IN PDEVICE_OBJECT DeviceObject, + IN PSCSI_REQUEST_BLOCK Srb, + IN UCHAR MajorFunctionCode, + IN ULONG IoDeviceCode, + IN ULONG RetryCount, + OUT NTSTATUS *Status + ); + +NTSTATUS +ScsiClassSendSrbSynchronous( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + PVOID BufferAddress, + ULONG BufferLength, + BOOLEAN WriteToDevice + ); + +NTSTATUS +ScsiClassSendSrbAsynchronous( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + PIRP Irp, + PVOID BufferAddress, + ULONG BufferLength, + BOOLEAN WriteToDevice + ); + +VOID +ScsiClassBuildRequest( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ); + +ULONG +ScsiClassModeSense( + IN PDEVICE_OBJECT DeviceObject, + IN PCHAR ModeSenseBuffer, + IN ULONG Length, + IN UCHAR PageMode + ); + +BOOLEAN +ScsiClassModeSelect( + IN PDEVICE_OBJECT DeviceObject, + IN PCHAR ModeSelectBuffer, + IN ULONG Length, + IN BOOLEAN SavePage + ); + +PVOID +ScsiClassFindModePage( + IN PCHAR ModeSenseBuffer, + IN ULONG Length, + IN UCHAR PageMode + ); + +NTSTATUS +ScsiClassClaimDevice( + IN PDEVICE_OBJECT PortDeviceObject, + IN PSCSI_INQUIRY_DATA LunInfo, + IN BOOLEAN Release, + OUT PDEVICE_OBJECT *NewPortDeviceObject OPTIONAL + ); + +NTSTATUS +ScsiClassInternalIoControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +#endif /* _CLASS_ */ + diff --git a/drivers/storage/inc/ide.h b/drivers/storage/inc/ide.h new file mode 100644 index 00000000000..9c868ca587e --- /dev/null +++ b/drivers/storage/inc/ide.h @@ -0,0 +1,495 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1999 - 1999 + +Module Name: + + ide.h + +Abstract: + + These are the structures and defines that are used in the + PCI IDE mini drivers. + +Revision History: + +--*/ + + +#if !defined (___ide_h___) +#define ___ide_h___ + +#include "ideuser.h" + +#define MAX_IDE_DEVICE 2 +#define MAX_IDE_LINE 2 +#define MAX_IDE_CHANNEL 2 + +// +// Some miniports need this structure. +// IdentifyData is passed to the miniport in +// the XfermodeSelect structure +// + +// +// IDENTIFY data +// + +#pragma pack (1) +typedef struct _IDENTIFY_DATA { + USHORT GeneralConfiguration; // 00 00 + USHORT NumCylinders; // 02 1 + USHORT Reserved1; // 04 2 + USHORT NumHeads; // 06 3 + USHORT UnformattedBytesPerTrack; // 08 4 + USHORT UnformattedBytesPerSector; // 0A 5 + USHORT NumSectorsPerTrack; // 0C 6 + USHORT VendorUnique1[3]; // 0E 7-9 + UCHAR SerialNumber[20]; // 14 10-19 + USHORT BufferType; // 28 20 + USHORT BufferSectorSize; // 2A 21 + USHORT NumberOfEccBytes; // 2C 22 + UCHAR FirmwareRevision[8]; // 2E 23-26 + UCHAR ModelNumber[40]; // 36 27-46 + UCHAR MaximumBlockTransfer; // 5E 47 + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 48 + USHORT Capabilities; // 62 49 + USHORT Reserved2; // 64 50 + UCHAR VendorUnique3; // 66 51 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 52 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:3; // 6A 53 + USHORT Reserved3:13; + USHORT NumberOfCurrentCylinders; // 6C 54 + USHORT NumberOfCurrentHeads; // 6E 55 + USHORT CurrentSectorsPerTrack; // 70 56 + ULONG CurrentSectorCapacity; // 72 57-58 + USHORT CurrentMultiSectorSetting; // 59 + ULONG UserAddressableSectors; // 60-61 + USHORT SingleWordDMASupport : 8; // 62 + USHORT SingleWordDMAActive : 8; + USHORT MultiWordDMASupport : 8; // 63 + USHORT MultiWordDMAActive : 8; + USHORT AdvancedPIOModes : 8; // 64 + USHORT Reserved4 : 8; + USHORT MinimumMWXferCycleTime; // 65 + USHORT RecommendedMWXferCycleTime; // 66 + USHORT MinimumPIOCycleTime; // 67 + USHORT MinimumPIOCycleTimeIORDY; // 68 + USHORT Reserved5[11]; // 69-79 + USHORT MajorRevision; // 80 + USHORT MinorRevision; // 81 + USHORT Reserved6; // 82 + USHORT CommandSetSupport; // 83 + USHORT Reserved6a[2]; // 84-85 + USHORT CommandSetActive; // 86 + USHORT Reserved6b; // 87 + USHORT UltraDMASupport : 8; // 88 + USHORT UltraDMAActive : 8; // + USHORT Reserved7[11]; // 89-99 + ULONG Max48BitLBA[2]; // 100-103 + USHORT Reserved7a[22]; // 104-125 + USHORT LastLun:3; // 126 + USHORT Reserved8:13; + USHORT MediaStatusNotification:2; // 127 + USHORT Reserved9:6; + USHORT DeviceWriteProtect:1; + USHORT Reserved10:7; + USHORT Reserved11[128]; // 128-255 +} IDENTIFY_DATA, *PIDENTIFY_DATA; + +// +// Identify data without the Reserved4. +// + +//typedef struct _IDENTIFY_DATA2 { +// USHORT GeneralConfiguration; // 00 00 +// USHORT NumCylinders; // 02 1 +// USHORT Reserved1; // 04 2 +// USHORT NumHeads; // 06 3 +// USHORT UnformattedBytesPerTrack; // 08 4 +// USHORT UnformattedBytesPerSector; // 0A 5 +// USHORT NumSectorsPerTrack; // 0C 6 +// USHORT VendorUnique1[3]; // 0E 7-9 +// UCHAR SerialNumber[20]; // 14 10-19 +// USHORT BufferType; // 28 20 +// USHORT BufferSectorSize; // 2A 21 +// USHORT NumberOfEccBytes; // 2C 22 +// UCHAR FirmwareRevision[8]; // 2E 23-26 +// UCHAR ModelNumber[40]; // 36 27-46 +// UCHAR MaximumBlockTransfer; // 5E 47 +// UCHAR VendorUnique2; // 5F +// USHORT DoubleWordIo; // 60 48 +// USHORT Capabilities; // 62 49 +// USHORT Reserved2; // 64 50 +// UCHAR VendorUnique3; // 66 51 +// UCHAR PioCycleTimingMode; // 67 +// UCHAR VendorUnique4; // 68 52 +// UCHAR DmaCycleTimingMode; // 69 +// USHORT TranslationFieldsValid:3; // 6A 53 +// USHORT Reserved3:13; +// USHORT NumberOfCurrentCylinders; // 6C 54 +// USHORT NumberOfCurrentHeads; // 6E 55 +// USHORT CurrentSectorsPerTrack; // 70 56 +// ULONG CurrentSectorCapacity; // 72 57-58 +// USHORT CurrentMultiSectorSetting; // 59 +// ULONG UserAddressableSectors; // 60-61 +// USHORT SingleWordDMASupport : 8; // 62 +// USHORT SingleWordDMAActive : 8; +// USHORT MultiWordDMASupport : 8; // 63 +// USHORT MultiWordDMAActive : 8; +// USHORT AdvancedPIOModes : 8; // 64 +// USHORT Reserved4 : 8; +// USHORT MinimumMWXferCycleTime; // 65 +// USHORT RecommendedMWXferCycleTime; // 66 +// USHORT MinimumPIOCycleTime; // 67 +// USHORT MinimumPIOCycleTimeIORDY; // 68 +// USHORT Reserved5[11]; // 69-79 +// USHORT MajorRevision; // 80 +// USHORT MinorRevision; // 81 +// USHORT Reserved6[6]; // 82-87 +// USHORT UltraDMASupport : 8; // 88 +// USHORT UltraDMAActive : 8; // +// USHORT Reserved7[37]; // 89-125 +// USHORT LastLun:3; // 126 +// USHORT Reserved8:13; +// USHORT MediaStatusNotification:2; // 127 +// USHORT Reserved9:6; +// USHORT DeviceWriteProtect:1; +// USHORT Reserved10:7; +//} IDENTIFY_DATA2, *PIDENTIFY_DATA2; +#pragma pack () + +#define IDENTIFY_DATA_SIZE sizeof(IDENTIFY_DATA) + + +// +// The structure is passed to pci ide mini driver +// TransferModeSelect callback for selecting +// proper transfer mode the the devices connected +// to the given IDE channel +// +typedef struct _PCIIDE_TRANSFER_MODE_SELECT { + + // + // Input Parameters + // + + // + // IDE Channel Number. 0 or 1 + // + ULONG Channel; + + // + // Indicate whether devices are present + // + BOOLEAN DevicePresent[MAX_IDE_DEVICE * MAX_IDE_LINE]; + + // + // Indicate whether devices are ATA harddisk + // + BOOLEAN FixedDisk[MAX_IDE_DEVICE * MAX_IDE_LINE]; + + // + // Indicate whether devices support IO Ready Line + // + BOOLEAN IoReadySupported[MAX_IDE_DEVICE * MAX_IDE_LINE]; + + // + // Indicate the data transfer modes devices support + // + ULONG DeviceTransferModeSupported[MAX_IDE_DEVICE * MAX_IDE_LINE]; + + // + // Indicate devices' best timings for PIO, single word DMA, + // multiword DMA, and Ultra DMA modes + // + ULONG BestPioCycleTime[MAX_IDE_DEVICE * MAX_IDE_LINE]; + ULONG BestSwDmaCycleTime[MAX_IDE_DEVICE * MAX_IDE_LINE]; + ULONG BestMwDmaCycleTime[MAX_IDE_DEVICE * MAX_IDE_LINE]; + ULONG BestUDmaCycleTime[MAX_IDE_DEVICE * MAX_IDE_LINE]; + + // + // Indicate devices' current data transfer modes + // + ULONG DeviceTransferModeCurrent[MAX_IDE_DEVICE * MAX_IDE_LINE]; + + // + // The user's choice. This will allow pciidex to + // default to a transfer mode indicated by the mini driver + // + ULONG UserChoiceTransferMode[MAX_IDE_DEVICE * MAX_IDE_LINE]; + + // + // This enables UDMA66 on the intel chipsets + // + ULONG EnableUDMA66; + + // + //Some miniports need this + // The miniport will save this data in their deviceExtension + // + IDENTIFY_DATA IdentifyData[MAX_IDE_DEVICE]; + + + // + // Output Parameters + // + + // + // Indicate devices' data transfer modes chosen by + // the pcii ide mini drive + // + ULONG DeviceTransferModeSelected[MAX_IDE_DEVICE * MAX_IDE_LINE]; + + // + // Transfermode timings + // + PULONG TransferModeTimingTable; + ULONG TransferModeTableLength; + +} PCIIDE_TRANSFER_MODE_SELECT, *PPCIIDE_TRANSFER_MODE_SELECT; + +// +// possible ide channel state +// + +typedef enum { + ChannelDisabled = 0, + ChannelEnabled, + ChannelStateUnknown +} IDE_CHANNEL_STATE; + + +// +// Prototype for different PCI IDE mini driver +// callbacks +// +typedef IDE_CHANNEL_STATE + (*PCIIDE_CHANNEL_ENABLED) ( + IN PVOID DeviceExtension, + IN ULONG Channel + ); + +typedef BOOLEAN + (*PCIIDE_SYNC_ACCESS_REQUIRED) ( + IN PVOID DeviceExtension + ); + +typedef NTSTATUS + (*PCIIDE_TRANSFER_MODE_SELECT_FUNC) ( + IN PVOID DeviceExtension, + IN OUT PPCIIDE_TRANSFER_MODE_SELECT TransferModeSelect + ); + +typedef ULONG + (*PCIIDE_USEDMA_FUNC)( + IN PVOID deviceExtension, + IN PVOID cdbCmd, + IN UCHAR targetID + ); + +typedef NTSTATUS + (*PCIIDE_UDMA_MODES_SUPPORTED) ( + IDENTIFY_DATA IdentifyData, + PULONG BestXferMode, + PULONG CurrentMode + ); +// +// This structure is for the PCI IDE mini driver to +// return its properties +// +typedef struct _IDE_CONTROLLER_PROPERTIES { + + // + // sizeof (IDE_CONTROLLER_PROPERTIES) + // + ULONG Size; + + // + // Indicate the amount of memory PCI IDE mini driver + // needs for its private data + // + ULONG ExtensionSize; + + // + // Indicate all the data transfer modes the PCI IDE + // controller supports + // + ULONG SupportedTransferMode[MAX_IDE_CHANNEL][MAX_IDE_DEVICE]; + + // + // callback to query whether a IDE channel is enabled + // + PCIIDE_CHANNEL_ENABLED PciIdeChannelEnabled; + + // + // callback to query whether both IDE channels requires + // synchronized access. (one channel at a time) + // + PCIIDE_SYNC_ACCESS_REQUIRED PciIdeSyncAccessRequired; + + // + // callback to select proper transfer modes for the + // given devices + // + PCIIDE_TRANSFER_MODE_SELECT_FUNC PciIdeTransferModeSelect; + + // + // at the end of a ATA data transfer, ignores busmaster + // status active bit. Normally, it should be FALSE + // + BOOLEAN IgnoreActiveBitForAtaDevice; + + // + // always clear the busmaster interrupt on every interrupt + // generated by the device. Normnally, it should be FALSE + // + BOOLEAN AlwaysClearBusMasterInterrupt; + + // + // callback to determine whether DMA should be used or not + // called for every IO + // + PCIIDE_USEDMA_FUNC PciIdeUseDma; + + + // + // if the miniport needs a different alignment + // + ULONG AlignmentRequirement; + + ULONG DefaultPIO; + + // + // retrieves the supported udma modes from the Identify data + // + PCIIDE_UDMA_MODES_SUPPORTED PciIdeUdmaModesSupported; + +} IDE_CONTROLLER_PROPERTIES, *PIDE_CONTROLLER_PROPERTIES; + +// +// callback to query PCI IDE controller properties +// +typedef +NTSTATUS (*PCONTROLLER_PROPERTIES) ( + IN PVOID DeviceExtension, + IN PIDE_CONTROLLER_PROPERTIES ControllerProperties + ); + + +// +// To initailize PCI IDE mini driver +// +NTSTATUS +PciIdeXInitialize( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + IN PCONTROLLER_PROPERTIES PciIdeGetControllerProperties, + IN ULONG ExtensionSize + ); + +// +// To query PCI IDE config space data +// +NTSTATUS +PciIdeXGetBusData( + IN PVOID DeviceExtension, + IN PVOID Buffer, + IN ULONG ConfigDataOffset, + IN ULONG BufferLength + ); + +// +// To save PCI IDE config space data +// +NTSTATUS +PciIdeXSetBusData( + IN PVOID DeviceExtension, + IN PVOID Buffer, + IN PVOID DataMask, + IN ULONG ConfigDataOffset, + IN ULONG BufferLength + ); + + +#pragma pack(1) +typedef struct _PCIIDE_CONFIG_HEADER { + + USHORT VendorID; // (ro) + USHORT DeviceID; // (ro) + + // + // Command + // + union { + + struct { + + USHORT IoAccessEnable:1; // Device control + USHORT MemAccessEnable:1; + USHORT MasterEnable:1; + USHORT SpecialCycle:1; + USHORT MemWriteInvalidateEnable:1; + USHORT VgaPaletteSnoopEnable:1; + USHORT ParityErrorResponse:1; + USHORT WaitCycleEnable:1; + USHORT SystemErrorEnable:1; + USHORT FastBackToBackEnable:1; + USHORT CommandReserved:6; + } b; + + USHORT w; + + } Command; + + + USHORT Status; + UCHAR RevisionID; // (ro) + + // + // Program Interface + // + UCHAR Chan0OpMode:1; + UCHAR Chan0Programmable:1; + UCHAR Chan1OpMode:1; + UCHAR Chan1Programmable:1; + UCHAR ProgIfReserved:3; + UCHAR MasterIde:1; + + UCHAR SubClass; // (ro) + UCHAR BaseClass; // (ro) + UCHAR CacheLineSize; // (ro+) + UCHAR LatencyTimer; // (ro+) + UCHAR HeaderType; // (ro) + UCHAR BIST; // Built in self test + + struct _PCI_HEADER_TYPE_0 type0; + +} PCIIDE_CONFIG_HEADER, *PPCIIDE_CONFIG_HEADER; +#pragma pack() + +// +// Debug Print +// +#if DBG + +VOID +PciIdeXDebugPrint( + ULONG DebugPrintLevel, + PCCHAR DebugMessage, + ... + ); + +#define PciIdeXDebugPrint(x) PciIdeXDebugPrint x + +#else + +#define PciIdeXDebugPrint(x) + +#endif // DBG + +#endif // ___ide_h___ + diff --git a/drivers/storage/inc/ideuser.h b/drivers/storage/inc/ideuser.h new file mode 100644 index 00000000000..babde7162f2 --- /dev/null +++ b/drivers/storage/inc/ideuser.h @@ -0,0 +1,132 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1999 - 1999 + +Module Name: + + ideuser.h + +Abstract: + + These are the structures and defines that are used in the + PCI IDE mini drivers. + +Revision History: + +--*/ + +#if !defined (___ideuser_h___) +#define ___ideuser_h___ + + +#define PIO_MODE0 (1 << 0) +#define PIO_MODE1 (1 << 1) +#define PIO_MODE2 (1 << 2) +#define PIO_MODE3 (1 << 3) +#define PIO_MODE4 (1 << 4) + +#define SWDMA_MODE0 (1 << 5) +#define SWDMA_MODE1 (1 << 6) +#define SWDMA_MODE2 (1 << 7) + +#define MWDMA_MODE0 (1 << 8) +#define MWDMA_MODE1 (1 << 9) +#define MWDMA_MODE2 (1 << 10) + +#define UDMA_MODE0 (1 << 11) +#define UDMA_MODE1 (1 << 12) +#define UDMA_MODE2 (1 << 13) +#define UDMA_MODE3 (1 << 14) +#define UDMA_MODE4 (1 << 15) +#define UDMA_MODE5 (1 << 16) + +#define PIO_SUPPORT (PIO_MODE0 | PIO_MODE1 | PIO_MODE2 | PIO_MODE3 | PIO_MODE4) +#define SWDMA_SUPPORT (SWDMA_MODE0 | SWDMA_MODE1 | SWDMA_MODE2) +#define MWDMA_SUPPORT (MWDMA_MODE0 | MWDMA_MODE1 | MWDMA_MODE2) +#define UDMA33_SUPPORT (UDMA_MODE0 | UDMA_MODE1 | UDMA_MODE2) +#define UDMA66_SUPPORT (UDMA_MODE3 | UDMA_MODE4) +#define UDMA100_SUPPORT (UDMA_MODE5 ) +#define UDMA_SUPPORT (UNINITIALIZED_TRANSFER_MODE & (~(PIO_SUPPORT | SWDMA_SUPPORT | MWDMA_SUPPORT))) + +#define DMA_SUPPORT (SWDMA_SUPPORT | MWDMA_SUPPORT | UDMA_SUPPORT) +#define ALL_MODE_SUPPORT (PIO_SUPPORT | DMA_SUPPORT) + +#define PIO0 0 +#define PIO1 1 +#define PIO2 2 +#define PIO3 3 +#define PIO4 4 +#define SWDMA0 5 +#define SWDMA1 6 +#define SWDMA2 7 +#define MWDMA0 8 +#define MWDMA1 9 +#define MWDMA2 10 +#define UDMA0 11 + +#define MAX_XFER_MODE 17 +#define UNINITIALIZED_CYCLE_TIME 0xffffffff +#define UNINITIALIZED_TRANSFER_MODE 0x7fffffff +#define IS_DEFAULT(mode) (!(mode & 0x80000000)) + +#define GenTransferModeMask(i, mode) {\ + ULONG temp=0xffffffff; \ + mode |= (temp >> (31-(i)));\ +} + +// +// mode should not be 0 +// +#define GetHighestTransferMode(mode, i) {\ + ULONG temp=(mode); \ + ASSERT(temp); \ + i=0; \ + while ( temp) { \ + temp = (temp >> 1);\ + i++;\ + } \ + i--; \ +} + +#define GetHighestDMATransferMode(mode, i) {\ + ULONG temp=mode >> 5;\ + i=5; \ + while ( temp) { \ + temp = (temp >> 1); \ + i++; \ + } \ + i--; \ +} +#define GetHighestPIOTransferMode(mode, i) { \ + ULONG temp = (mode & PIO_SUPPORT); \ + i=0; \ + temp = temp >> 1; \ + while (temp) { \ + temp = temp >> 1; \ + i++; \ + } \ +} + +#define SetDefaultTiming(timingTable, length) {\ + timingTable[0]=PIO_MODE0_CYCLE_TIME; \ + timingTable[1]=PIO_MODE1_CYCLE_TIME; \ + timingTable[2]=PIO_MODE2_CYCLE_TIME; \ + timingTable[3]=PIO_MODE3_CYCLE_TIME; \ + timingTable[4]=PIO_MODE4_CYCLE_TIME; \ + timingTable[5]=SWDMA_MODE0_CYCLE_TIME; \ + timingTable[6]=SWDMA_MODE1_CYCLE_TIME; \ + timingTable[7]=SWDMA_MODE2_CYCLE_TIME; \ + timingTable[8]=MWDMA_MODE0_CYCLE_TIME; \ + timingTable[9]=MWDMA_MODE1_CYCLE_TIME; \ + timingTable[10]=MWDMA_MODE2_CYCLE_TIME; \ + timingTable[11]=UDMA_MODE0_CYCLE_TIME; \ + timingTable[12]=UDMA_MODE1_CYCLE_TIME; \ + timingTable[13]=UDMA_MODE2_CYCLE_TIME; \ + timingTable[14]=UDMA_MODE3_CYCLE_TIME; \ + timingTable[15]=UDMA_MODE4_CYCLE_TIME; \ + timingTable[16]=UDMA_MODE5_CYCLE_TIME; \ + length = MAX_XFER_MODE; \ +} + +#endif // ___ideuser_h___ + diff --git a/drivers/storage/inc/physlogi.h b/drivers/storage/inc/physlogi.h new file mode 100644 index 00000000000..57e71e0c7c8 --- /dev/null +++ b/drivers/storage/inc/physlogi.h @@ -0,0 +1,115 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + physlogi.h + +Abstract: + + This file contains structures and defines that are used + specifically for the tape drivers. Contains #define's, + function protypes, etc. for use in calling functions in + physlogi.c that do physical to pseudo-logical and pseudo- + logical to physical tape block address/position translation. + +Author: + + Mike Colandreo (Maynard) + +Revision History: + +--*/ + +// begin_ntminitape + +// +// defines for QIC tape density codes +// + +#define QIC_XX 0 // ???? +#define QIC_24 5 // 0x05 +#define QIC_120 15 // 0x0F +#define QIC_150 16 // 0x10 +#define QIC_525 17 // 0x11 +#define QIC_1350 18 // 0x12 +#define QIC_1000 21 // 0x15 +#define QIC_1000C 30 // 0x1E +#define QIC_2100 31 // 0x1F +#define QIC_2GB 34 // 0x22 +#define QIC_5GB 40 // 0x28 + +// +// defines for QIC tape media codes +// + +#define DCXXXX 0 +#define DC300 1 +#define DC300XLP 2 +#define DC615 3 +#define DC600 4 +#define DC6037 5 +#define DC6150 6 +#define DC6250 7 +#define DC6320 8 +#define DC6525 9 +#define DC9135SL 33 //0x21 +#define DC9210 34 //0x22 +#define DC9135 35 //0x23 +#define DC9100 36 //0x24 +#define DC9120 37 //0x25 +#define DC9120SL 38 //0x26 +#define DC9164 39 //0x27 +#define DCXXXXFW 48 //0x30 +#define DC9200SL 49 //0x31 +#define DC9210XL 50 //0x32 +#define DC10GB 51 //0x33 +#define DC9200 52 //0x34 +#define DC9120XL 53 //0x35 +#define DC9210SL 54 //0x36 +#define DC9164XL 55 //0x37 +#define DC9200XL 64 //0x40 +#define DC9400 65 //0x41 +#define DC9500 66 //0x42 +#define DC9500SL 70 //0x46 + +// +// defines for translation reference point +// + +#define NOT_FROM_BOT 0 +#define FROM_BOT 1 + +// +// info/structure returned by/from +// TapeLogicalBlockToPhysicalBlock( ) +// + +typedef struct _TAPE_PHYS_POSITION { + ULONG SeekBlockAddress; + ULONG SpaceBlockCount; +} TAPE_PHYS_POSITION, PTAPE_PHYS_POSITION; + +// +// function prototypes +// + +TAPE_PHYS_POSITION +TapeClassLogicalBlockToPhysicalBlock( + IN UCHAR DensityCode, + IN ULONG LogicalBlockAddress, + IN ULONG BlockLength, + IN BOOLEAN FromBOT + ); + +ULONG +TapeClassPhysicalBlockToLogicalBlock( + IN UCHAR DensityCode, + IN ULONG PhysicalBlockAddress, + IN ULONG BlockLength, + IN BOOLEAN FromBOT + ); + +// end_ntminitape + diff --git a/drivers/storage/inc/rbc.h b/drivers/storage/inc/rbc.h new file mode 100644 index 00000000000..5afa8bb1d7a --- /dev/null +++ b/drivers/storage/inc/rbc.h @@ -0,0 +1,180 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1998 - 1999 + +Module Name: + + rbc.h + +Abstract: + + These are the structures and defines used in the Reduced Block Command set + +Authors: + + George Chrysanthakopoulos(georgioc) - April 1998 + +Revision History: + + Dan Knudson (DanKn), 23 Sep 1999 - updated per rev 10 of RBC spec + +--*/ +#ifndef _NTRBC_ +#define _NTRBC_ + +#include "scsi.h" + +// +// Command Descriptor Block. encapsulated under the bus/protocol specific request block +// + +typedef union _CDB_RBC { + + // + // format unit + // + + struct _FORMAT_RBC { + UCHAR OperationCode; + UCHAR VendorSpecific; + UCHAR Increment : 1; + UCHAR Percent_Time : 1; + UCHAR Reserved1 : 1; + UCHAR VendorSpecific1 : 5; + UCHAR Reserved2[2]; + UCHAR Control; + } FORMAT_RBC, *PFORMAT_RBC; + + // + // prevent/allow medium removal + // + + struct _MEDIA_REMOVAL_RBC { + UCHAR OperationCode; + UCHAR Reserved[3]; + + UCHAR Prevent : 1; + UCHAR Persistant : 1; + UCHAR Reserved3 : 6; + + UCHAR Control; + } MEDIA_REMOVAL_RBC, *PMEDIA_REMOVAL_RBC; + + // + // START_STOP_UNIT + // + + struct _START_STOP_RBC { + UCHAR OperationCode; + UCHAR Immediate: 1; + UCHAR Reserved1 : 7; + UCHAR Reserved2[2]; + UCHAR Start : 1; + UCHAR LoadEject : 1; + UCHAR Reserved3 : 2; + UCHAR PowerConditions : 4; + UCHAR Control; + } START_STOP_RBC, *PSTART_STOP_RBC; + + struct _SYNCHRONIZE_CACHE_RBC { + + UCHAR OperationCode; // 0x35 + UCHAR Reserved[8]; + UCHAR Control; + + } SYNCHRONIZE_CACHE_RBC, *PSYNCHRONIZE_CACHE_RBC; + + +} CDB_RBC, *PCDB_RBC; + + +// +// START_STOP_UNIT Power Condition descriptions +// + +#define START_STOP_RBC_POWER_CND_NO_CHANGE 0 +#define START_STOP_RBC_POWER_CND_ACTIVE 1 +#define START_STOP_RBC_POWER_CND_IDLE 2 +#define START_STOP_RBC_POWER_CND_STANDBY 3 +#define START_STOP_RBC_POWER_CND_SLEEP 5 +#define START_STOP_RBC_POWER_CND_DEVICE_CTRL 7 + + +// +// Mode Sense/Select page constants. +// + +#define MODE_PAGE_RBC_DEVICE_PARAMETERS 0x06 + + +// +// DeviceType field in inquiry Data +// + +#define RBC_DEVICE 0x0E + +// +// Define Device Capabilities page. +// + +typedef struct _MODE_RBC_DEVICE_PARAMETERS_PAGE { + UCHAR PageCode : 6; + UCHAR Reserved : 1; + UCHAR PageSavable : 1; + UCHAR PageLength; + UCHAR WriteCacheDisable : 1; + UCHAR Reserved1 : 7; + UCHAR LogicalBlockSize[2]; + UCHAR NumberOfLogicalBlocks[5]; + UCHAR PowerPerformance; + UCHAR LockDisabled : 1; + UCHAR FormatDisabled : 1; + UCHAR WriteDisabled : 1; + UCHAR ReadDisabled : 1; + UCHAR Reserved2 : 4; + UCHAR Reserved3; + +}MODE_RBC_DEVICE_PARAMETERS_PAGE, *PMODE_RBC_DEVICE_PARAMETERS_PAGE; + +typedef struct _MODE_RBC_DEVICE_PARAMETERS_HEADER_AND_PAGE { + + MODE_PARAMETER_HEADER Header; + MODE_RBC_DEVICE_PARAMETERS_PAGE Page; + +}MODE_RBC_DEVICE_PARAMETERS_HEADER_AND_PAGE, + *PMODE_RBC_DEVICE_PARAMETERS_HEADER_AND_PAGE; + + +// +// unsolicited status sense code qualifier values +// + +#define RBC_UNSOLICITED_STATUS 0x02 +#define RBC_UNSOLICITED_SENSE_KEY 0x06 + +#define RBC_UNSOLICITED_SC_PWR_STATE_CHNG 0xFF +#define RBC_UNSOLICITED_SC_EVENT_STATUS 0xFE + +#define RBC_UNSOLICITED_CLASS_ASQ_DEVICE 0x06 +#define RBC_UNSOLICITED_CLASS_ASQ_MEDIA 0x04 +#define RBC_UNSOLICITED_CLASS_ASQ_POWER 0x02 + + + + +// +// Translation routine used to convert SCSI requests that differ from RBC +// + +NTSTATUS +Rbc_Scsi_Conversion( + IN PSCSI_REQUEST_BLOCK Srb, + IN PSCSI_REQUEST_BLOCK *OriginalSrb, + IN PMODE_RBC_DEVICE_PARAMETERS_HEADER_AND_PAGE RbcHeaderAndPage, + IN BOOLEAN OutgoingRequest, + IN BOOLEAN RemovableMedia + ); + + +#endif + diff --git a/drivers/storage/inc/tape.h b/drivers/storage/inc/tape.h new file mode 100644 index 00000000000..2f11917a24c --- /dev/null +++ b/drivers/storage/inc/tape.h @@ -0,0 +1,312 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + tape.h + +Abstract: + + These are the structures and defines that are used in the + SCSI tape class drivers. The tape class driver is separated + into two modules. Tape.c contains code common to all tape + class drivers including the driver's major entry points. + The major entry point names each begin with the prefix + 'ScsiTape.' The second module is the device specific code. + It provides support for a set of functions. Each device + specific function name is prefixed by 'Tape.' + +Author: + + Mike Glass + +Revision History: + +--*/ + +#include "scsi.h" +#include "class.h" + +// +// Define the maximum inquiry data length. +// + +#define MAXIMUM_TAPE_INQUIRY_DATA 252 + +// +// Tape device data +// + +typedef struct _TAPE_DATA { + ULONG Flags; + ULONG CurrentPartition; + PVOID DeviceSpecificExtension; + PSCSI_INQUIRY_DATA InquiryData; +} TAPE_DATA, *PTAPE_DATA; + +#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION) + sizeof(TAPE_DATA) + + +// +// Define Device Configuration Page +// + +typedef struct _MODE_DEVICE_CONFIGURATION_PAGE { + + UCHAR PageCode : 6; + UCHAR Reserved1 : 1; + UCHAR PS : 1; + UCHAR PageLength; + UCHAR ActiveFormat : 5; + UCHAR CAFBit : 1; + UCHAR CAPBit : 1; + UCHAR Reserved2 : 1; + UCHAR ActivePartition; + UCHAR WriteBufferFullRatio; + UCHAR ReadBufferEmptyRatio; + UCHAR WriteDelayTime[2]; + UCHAR REW : 1; + UCHAR RBO : 1; + UCHAR SOCF : 2; + UCHAR AVC : 1; + UCHAR RSmk : 1; + UCHAR BIS : 1; + UCHAR DBR : 1; + UCHAR GapSize; + UCHAR Reserved3 : 3; + UCHAR SEW : 1; + UCHAR EEG : 1; + UCHAR EODdefined : 3; + UCHAR BufferSize[3]; + UCHAR DCAlgorithm; + UCHAR Reserved4; + +} MODE_DEVICE_CONFIGURATION_PAGE, *PMODE_DEVICE_CONFIGURATION_PAGE; + +// +// Define Medium Partition Page +// + +typedef struct _MODE_MEDIUM_PARTITION_PAGE { + + UCHAR PageCode : 6; + UCHAR Reserved1 : 1; + UCHAR PSBit : 1; + UCHAR PageLength; + UCHAR MaximumAdditionalPartitions; + UCHAR AdditionalPartitionDefined; + UCHAR Reserved2 : 3; + UCHAR PSUMBit : 2; + UCHAR IDPBit : 1; + UCHAR SDPBit : 1; + UCHAR FDPBit : 1; + UCHAR MediumFormatRecognition; + UCHAR Reserved3[2]; + UCHAR Partition0Size[2]; + UCHAR Partition1Size[2]; + +} MODE_MEDIUM_PARTITION_PAGE, *PMODE_MEDIUM_PARTITION_PAGE; + +// +// Define Data Compression Page +// + +typedef struct _MODE_DATA_COMPRESSION_PAGE { + + UCHAR PageCode : 6; + UCHAR Reserved1 : 2; + UCHAR PageLength; + UCHAR Reserved2 : 6; + UCHAR DCC : 1; + UCHAR DCE : 1; + UCHAR Reserved3 : 5; + UCHAR RED : 2; + UCHAR DDE : 1; + UCHAR CompressionAlgorithm[4]; + UCHAR DecompressionAlgorithm[4]; + UCHAR Reserved4[4]; + +} MODE_DATA_COMPRESSION_PAGE, *PMODE_DATA_COMPRESSION_PAGE; + +// +// Mode parameter list header and medium partition page - +// used in creating partitions +// + +typedef struct _MODE_MEDIUM_PART_PAGE { + + MODE_PARAMETER_HEADER ParameterListHeader; + MODE_MEDIUM_PARTITION_PAGE MediumPartPage; + +} MODE_MEDIUM_PART_PAGE, *PMODE_MEDIUM_PART_PAGE; + + +// +// Mode parameters for retrieving tape or media information +// + +typedef struct _MODE_TAPE_MEDIA_INFORMATION { + + MODE_PARAMETER_HEADER ParameterListHeader; + MODE_PARAMETER_BLOCK ParameterListBlock; + MODE_MEDIUM_PARTITION_PAGE MediumPartPage; + +} MODE_TAPE_MEDIA_INFORMATION, *PMODE_TAPE_MEDIA_INFORMATION; + +// +// Mode parameter list header and device configuration page - +// used in retrieving device configuration information +// + +typedef struct _MODE_DEVICE_CONFIG_PAGE { + + MODE_PARAMETER_HEADER ParameterListHeader; + MODE_DEVICE_CONFIGURATION_PAGE DeviceConfigPage; + +} MODE_DEVICE_CONFIG_PAGE, *PMODE_DEVICE_CONFIG_PAGE; + + +// +// Mode parameter list header and data compression page - +// used in retrieving data compression information +// + +typedef struct _MODE_DATA_COMPRESS_PAGE { + + MODE_PARAMETER_HEADER ParameterListHeader; + MODE_DATA_COMPRESSION_PAGE DataCompressPage; + +} MODE_DATA_COMPRESS_PAGE, *PMODE_DATA_COMPRESS_PAGE; + + + +// +// The following routines are the exported entry points for +// all tape class drivers. Note all these routines name start +// with 'ScsiTape.' +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +ScsiTapeInitialize( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +ScsiTapeCreate ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +ScsiTapeReadWrite ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +ScsiTapeDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + + +// +// The following routines are provided by the tape +// device-specific module. Each routine name is +// prefixed with 'Tape.' + +NTSTATUS +TapeCreatePartition( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +TapeErase( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +TapeError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +NTSTATUS +TapeGetDriveParameters( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +TapeGetMediaParameters( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +TapeGetPosition( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +TapeGetStatus( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +TapePrepare( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +TapeReadWrite( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +TapeSetDriveParameters( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +TapeSetMediaParameters( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +TapeSetPosition( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +BOOLEAN +TapeVerifyInquiry( + IN PSCSI_INQUIRY_DATA LunInfo + ); + +NTSTATUS +TapeWriteMarks( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + + diff --git a/drivers/storage/scsiport/scsiport.c b/drivers/storage/scsiport/scsiport.c index 74b787bfb12..817d9de3866 100644 --- a/drivers/storage/scsiport/scsiport.c +++ b/drivers/storage/scsiport/scsiport.c @@ -1041,10 +1041,8 @@ ScsiPortInitialize(IN PVOID Argument1, WCHAR DosNameBuffer[80]; UNICODE_STRING DosDeviceName; PIO_SCSI_CAPABILITIES PortCapabilities; - ULONG MappedIrq; - KIRQL Dirql; - KAFFINITY Affinity; + KIRQL OldIrql; PCM_RESOURCE_LIST ResourceList; BOOLEAN Conflict; @@ -1137,6 +1135,7 @@ ScsiPortInitialize(IN PVOID Argument1, /* Fill Device Extension */ DeviceExtension = PortDeviceObject->DeviceExtension; + RtlZeroMemory(DeviceExtension, DeviceExtensionSize); DeviceExtension->Length = DeviceExtensionSize; DeviceExtension->DeviceObject = PortDeviceObject; DeviceExtension->PortNumber = SystemConfig->ScsiPortCount; @@ -1509,45 +1508,55 @@ CreatePortConfig: (PortConfig->BusInterruptLevel == 0 && PortConfig->BusInterruptVector == 0)) { /* No interrupts */ - KeInitializeSpinLock(&DeviceExtension->IrqLock); + DeviceExtension->InterruptCount = 0; - /* FIXME: Use synchronization routine */ - ASSERT("No interrupts branch requires changes in synchronization\n"); + DPRINT1("Interrupt Count: 0\n"); - DeviceExtension->Interrupt = (PVOID)DeviceExtension; - DPRINT("No interrupts\n"); + UNIMPLEMENTED; + /* This code path will ALWAYS crash so stop it now */ + while(TRUE); } else { - /* Are 2 interrupts needed? */ - if (DeviceExtension->HwInterrupt != NULL && - (PortConfig->BusInterruptLevel != 0 || PortConfig->BusInterruptVector != 0) && - (PortConfig->BusInterruptLevel2 != 0 || PortConfig->BusInterruptVector2 != 0)) + BOOLEAN InterruptShareable; + KINTERRUPT_MODE InterruptMode[2]; + ULONG InterruptVector[2], i, MappedIrq[2]; + KIRQL Dirql[2], MaxDirql; + KAFFINITY Affinity[2]; + + DeviceExtension->InterruptLevel[0] = PortConfig->BusInterruptLevel; + DeviceExtension->InterruptLevel[1] = PortConfig->BusInterruptLevel2; + + InterruptVector[0] = PortConfig->BusInterruptVector; + InterruptVector[1] = PortConfig->BusInterruptVector2; + + InterruptMode[0] = PortConfig->InterruptMode; + InterruptMode[1] = PortConfig->InterruptMode2; + + DeviceExtension->InterruptCount = (PortConfig->BusInterruptLevel2 != 0 || PortConfig->BusInterruptVector2 != 0) ? 2 : 1; + + for (i = 0; i < DeviceExtension->InterruptCount; i++) { - DPRINT1("2 interrupts requested! Not yet supported\n"); - ASSERT(FALSE); - } - else - { - BOOLEAN InterruptShareable; - - /* No, only 1 interrupt */ - DPRINT("1 interrupt, IRQ is %d\n", PortConfig->BusInterruptLevel); - - DeviceExtension->InterruptLevel = PortConfig->BusInterruptLevel; - /* Register an interrupt handler for this device */ - MappedIrq = HalGetInterruptVector(PortConfig->AdapterInterfaceType, - PortConfig->SystemIoBusNumber, - PortConfig->BusInterruptLevel, - PortConfig->BusInterruptVector, - &Dirql, - &Affinity); - + MappedIrq[i] = HalGetInterruptVector(PortConfig->AdapterInterfaceType, + PortConfig->SystemIoBusNumber, + DeviceExtension->InterruptLevel[i], + InterruptVector[i], + &Dirql[i], + &Affinity[i]); + } + + if (DeviceExtension->InterruptCount == 1 || Dirql[0] > Dirql[1]) + MaxDirql = Dirql[0]; + else + MaxDirql = Dirql[1]; + + for (i = 0; i < DeviceExtension->InterruptCount; i++) + { /* Determing IRQ sharability as usual */ if (PortConfig->AdapterInterfaceType == MicroChannel || - PortConfig->InterruptMode == LevelSensitive) + InterruptMode[i] == LevelSensitive) { InterruptShareable = TRUE; } @@ -1556,27 +1565,29 @@ CreatePortConfig: InterruptShareable = FALSE; } - Status = IoConnectInterrupt(&DeviceExtension->Interrupt, + Status = IoConnectInterrupt(&DeviceExtension->Interrupt[i], (PKSERVICE_ROUTINE)ScsiPortIsr, DeviceExtension, - NULL, - MappedIrq, - Dirql, - Dirql, - PortConfig->InterruptMode, + &DeviceExtension->IrqLock, + MappedIrq[i], + Dirql[i], + MaxDirql, + InterruptMode[i], InterruptShareable, - Affinity, + Affinity[i], FALSE); if (!(NT_SUCCESS(Status))) { DPRINT1("Could not connect interrupt %d\n", - PortConfig->BusInterruptVector); - DeviceExtension->Interrupt = NULL; + InterruptVector[i]); + DeviceExtension->Interrupt[i] = NULL; break; } - } + + if (!NT_SUCCESS(Status)) + break; } /* Save IoAddress (from access ranges) */ @@ -1607,14 +1618,14 @@ CreatePortConfig: } /* Call HwInitialize at DISPATCH_LEVEL */ - KeRaiseIrql(DISPATCH_LEVEL, &Dirql); + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); - if (!KeSynchronizeExecution(DeviceExtension->Interrupt, + if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0], DeviceExtension->HwInitialize, DeviceExtension->MiniPortDeviceExtension)) { DPRINT1("HwInitialize() failed!\n"); - KeLowerIrql(Dirql); + KeLowerIrql(OldIrql); Status = STATUS_ADAPTER_HARDWARE_ERROR; break; } @@ -1630,7 +1641,7 @@ CreatePortConfig: } /* Lower irql back to what it was */ - KeLowerIrql(Dirql); + KeLowerIrql(OldIrql); /* Start our timer */ IoStartTimer(PortDeviceObject); @@ -1720,11 +1731,14 @@ SpiCleanupAfterInit(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension) if (DeviceExtension == NULL) return; - /* Stop the timer and disconnect the interrupt */ - if (DeviceExtension->Interrupt) + /* Stop the timer */ + IoStopTimer(DeviceExtension->DeviceObject); + + /* Disconnect the interrupts */ + while (DeviceExtension->InterruptCount) { - IoStopTimer(DeviceExtension->DeviceObject); - IoDisconnectInterrupt(DeviceExtension->Interrupt); + if (DeviceExtension->Interrupt[--DeviceExtension->InterruptCount]) + IoDisconnectInterrupt(DeviceExtension->Interrupt[DeviceExtension->InterruptCount]); } /* Delete ConfigInfo */ @@ -2042,6 +2056,8 @@ SpiResourceToConfig(IN PHW_INITIALIZATION_DATA HwInitializationData, PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialData; ULONG RangeNumber; ULONG Index; + ULONG Interrupt = 0; + ULONG Dma = 0; RangeNumber = 0; @@ -2081,24 +2097,71 @@ SpiResourceToConfig(IN PHW_INITIALIZATION_DATA HwInitializationData, break; case CmResourceTypeInterrupt: - /* Copy interrupt data */ - PortConfig->BusInterruptLevel = PartialData->u.Interrupt.Level; - PortConfig->BusInterruptVector = PartialData->u.Interrupt.Vector; - /* Set interrupt mode accordingly to the resource */ - if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LATCHED) + if (Interrupt == 0) { - PortConfig->InterruptMode = Latched; + /* Copy interrupt data */ + PortConfig->BusInterruptLevel = PartialData->u.Interrupt.Level; + PortConfig->BusInterruptVector = PartialData->u.Interrupt.Vector; + + /* Set interrupt mode accordingly to the resource */ + if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LATCHED) + { + PortConfig->InterruptMode = Latched; + } + else if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) + { + PortConfig->InterruptMode = LevelSensitive; + } } - else if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) + else if (Interrupt == 1) { - PortConfig->InterruptMode = LevelSensitive; + /* Copy interrupt data */ + PortConfig->BusInterruptLevel2 = PartialData->u.Interrupt.Level; + PortConfig->BusInterruptVector2 = PartialData->u.Interrupt.Vector; + + /* Set interrupt mode accordingly to the resource */ + if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LATCHED) + { + PortConfig->InterruptMode2 = Latched; + } + else if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) + { + PortConfig->InterruptMode2 = LevelSensitive; + } } + + Interrupt++; break; case CmResourceTypeDma: - PortConfig->DmaChannel = PartialData->u.Dma.Channel; - PortConfig->DmaPort = PartialData->u.Dma.Port; + + if (Dma == 0) + { + PortConfig->DmaChannel = PartialData->u.Dma.Channel; + PortConfig->DmaPort = PartialData->u.Dma.Port; + + if (PartialData->Flags & CM_RESOURCE_DMA_8) + PortConfig->DmaWidth = Width8Bits; + else if ((PartialData->Flags & CM_RESOURCE_DMA_16) || + (PartialData->Flags & CM_RESOURCE_DMA_8_AND_16)) //??? + PortConfig->DmaWidth = Width16Bits; + else if (PartialData->Flags & CM_RESOURCE_DMA_32) + PortConfig->DmaWidth = Width32Bits; + } + else if (Dma == 1) + { + PortConfig->DmaChannel2 = PartialData->u.Dma.Channel; + PortConfig->DmaPort2 = PartialData->u.Dma.Port; + + if (PartialData->Flags & CM_RESOURCE_DMA_8) + PortConfig->DmaWidth2 = Width8Bits; + else if ((PartialData->Flags & CM_RESOURCE_DMA_16) || + (PartialData->Flags & CM_RESOURCE_DMA_8_AND_16)) //??? + PortConfig->DmaWidth2 = Width16Bits; + else if (PartialData->Flags & CM_RESOURCE_DMA_32) + PortConfig->DmaWidth2 = Width32Bits; + } break; } } @@ -2112,9 +2175,8 @@ SpiConfigToResource(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, PCM_RESOURCE_LIST ResourceList; PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor; PACCESS_RANGE AccessRange; - BOOLEAN Dma; ULONG ListLength = 0, i, FullSize; - ULONG Interrupt; + ULONG Interrupt, Dma; /* Get current Atdisk usage from the system */ ConfigInfo = IoGetConfigurationInformation(); @@ -2129,34 +2191,21 @@ SpiConfigToResource(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, if (PortConfig->DmaChannel != SP_UNINITIALIZED_VALUE || PortConfig->DmaPort != SP_UNINITIALIZED_VALUE) { - Dma = TRUE; - ListLength++; + Dma = 1; + + if (PortConfig->DmaChannel2 != SP_UNINITIALIZED_VALUE || + PortConfig->DmaPort2 != SP_UNINITIALIZED_VALUE) + Dma++; } else { - Dma = FALSE; + Dma = 0; } + ListLength += Dma; /* How many interrupts to we have? */ - if (DeviceExtension->HwInterrupt == NULL || - (PortConfig->BusInterruptLevel == 0 && - PortConfig->BusInterruptVector == 0)) - { - Interrupt = 0; - } - else - { - Interrupt = 1; - ListLength++; - } - - if (DeviceExtension->HwInterrupt != NULL && - (PortConfig->BusInterruptLevel2 != 0 || - PortConfig->BusInterruptVector2 != 0)) - { - Interrupt++; - ListLength++; - } + Interrupt = DeviceExtension->InterruptCount; + ListLength += Interrupt; /* How many access ranges do we use? */ AccessRange = &((*(PortConfig->AccessRanges))[0]); @@ -2216,12 +2265,12 @@ SpiConfigToResource(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, } /* If we use interrupt(s), copy them */ - if (Interrupt) + while (Interrupt) { ResourceDescriptor->Type = CmResourceTypeInterrupt; if (PortConfig->AdapterInterfaceType == MicroChannel || - PortConfig->InterruptMode == LevelSensitive) + ((Interrupt == 2) ? PortConfig->InterruptMode2 : PortConfig->InterruptMode) == LevelSensitive) { ResourceDescriptor->ShareDisposition = CmResourceShareShared; ResourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; @@ -2232,53 +2281,38 @@ SpiConfigToResource(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, ResourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED; } - ResourceDescriptor->u.Interrupt.Level = PortConfig->BusInterruptLevel; - ResourceDescriptor->u.Interrupt.Vector = PortConfig->BusInterruptVector; + ResourceDescriptor->u.Interrupt.Level = (Interrupt == 2) ? PortConfig->BusInterruptLevel2 : PortConfig->BusInterruptLevel; + ResourceDescriptor->u.Interrupt.Vector = (Interrupt == 2) ? PortConfig->BusInterruptVector2 : PortConfig->BusInterruptVector; ResourceDescriptor->u.Interrupt.Affinity = 0; ResourceDescriptor++; Interrupt--; } - /* Copy 2nd interrupt - FIXME: Stupid code duplication, remove */ - if (Interrupt) - { - ResourceDescriptor->Type = CmResourceTypeInterrupt; - - if (PortConfig->AdapterInterfaceType == MicroChannel || - PortConfig->InterruptMode == LevelSensitive) - { - ResourceDescriptor->ShareDisposition = CmResourceShareShared; - ResourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; - } - else - { - ResourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; - ResourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED; - } - - ResourceDescriptor->u.Interrupt.Level = PortConfig->BusInterruptLevel; - ResourceDescriptor->u.Interrupt.Vector = PortConfig->BusInterruptVector; - ResourceDescriptor->u.Interrupt.Affinity = 0; - - ResourceDescriptor++; - } - /* Copy DMA data */ - if (Dma) + while (Dma) { ResourceDescriptor->Type = CmResourceTypeDma; ResourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; - ResourceDescriptor->u.Dma.Channel = PortConfig->DmaChannel; - ResourceDescriptor->u.Dma.Port = PortConfig->DmaPort; + ResourceDescriptor->u.Dma.Channel = (Dma == 2) ? PortConfig->DmaChannel2 : PortConfig->DmaChannel; + ResourceDescriptor->u.Dma.Port = (Dma == 2) ? PortConfig->DmaPort2 : PortConfig->DmaPort; ResourceDescriptor->Flags = 0; + + if (((Dma == 2) ? PortConfig->DmaWidth2 : PortConfig->DmaWidth) == Width8Bits) + ResourceDescriptor->Flags |= CM_RESOURCE_DMA_8; + else if (((Dma == 2) ? PortConfig->DmaWidth2 : PortConfig->DmaWidth) == Width16Bits) + ResourceDescriptor->Flags |= CM_RESOURCE_DMA_16; + else + ResourceDescriptor->Flags |= CM_RESOURCE_DMA_32; - if (PortConfig->DmaChannel == SP_UNINITIALIZED_VALUE) + if (((Dma == 2) ? PortConfig->DmaChannel2 : PortConfig->DmaChannel) == SP_UNINITIALIZED_VALUE) ResourceDescriptor->u.Dma.Channel = 0; - if (PortConfig->DmaPort == SP_UNINITIALIZED_VALUE) + if (((Dma == 2) ? PortConfig->DmaPort2 : PortConfig->DmaPort) == SP_UNINITIALIZED_VALUE) ResourceDescriptor->u.Dma.Port = 0; + + ResourceDescriptor++; + Dma--; } return ResourceList; @@ -3006,7 +3040,7 @@ ScsiPortStartIo(IN PDEVICE_OBJECT DeviceObject, KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); - if (!KeSynchronizeExecution(DeviceExtension->Interrupt, + if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0], ScsiPortStartPacket, DeviceObject)) { @@ -3259,7 +3293,7 @@ SpiAdapterControl(PDEVICE_OBJECT DeviceObject, /* Schedule an active request */ InterlockedIncrement(&DeviceExtension->ActiveRequestCounter ); KeAcquireSpinLock(&DeviceExtension->SpinLock, &CurrentIrql); - KeSynchronizeExecution(DeviceExtension->Interrupt, + KeSynchronizeExecution(DeviceExtension->Interrupt[0], ScsiPortStartPacket, DeviceObject); KeReleaseSpinLock(&DeviceExtension->SpinLock, CurrentIrql); @@ -4821,7 +4855,7 @@ TryAgain: Context.InterruptData = &InterruptData; Context.DeviceExtension = DeviceExtension; - if (!KeSynchronizeExecution(DeviceExtension->Interrupt, + if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0], SpiSaveInterruptData, &Context)) { @@ -5114,7 +5148,7 @@ ScsiPortIoTimer(PDEVICE_OBJECT DeviceObject, if (DeviceExtension->TimerCount == 0) { /* Timeout, process it */ - if (KeSynchronizeExecution(DeviceExtension->Interrupt, + if (KeSynchronizeExecution(DeviceExtension->Interrupt[0], SpiProcessTimeout, DeviceExtension->DeviceObject)) { @@ -5168,7 +5202,7 @@ ScsiPortIoTimer(PDEVICE_OBJECT DeviceObject, ResetParams.PathId = LunExtension->PathId; ResetParams.DeviceExtension = DeviceExtension; - if (!KeSynchronizeExecution(DeviceExtension->Interrupt, + if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0], SpiResetBus, &ResetParams)) { @@ -5679,6 +5713,8 @@ SpiCreatePortConfig(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, ConfigInfo->InterruptMode = Latched; ConfigInfo->DmaChannel = SP_UNINITIALIZED_VALUE; ConfigInfo->DmaPort = SP_UNINITIALIZED_VALUE; + ConfigInfo->DmaChannel2 = SP_UNINITIALIZED_VALUE; + ConfigInfo->DmaPort2 = SP_UNINITIALIZED_VALUE; ConfigInfo->MaximumTransferLength = SP_UNINITIALIZED_VALUE; ConfigInfo->NumberOfAccessRanges = HwInitData->NumberOfAccessRanges; ConfigInfo->MaximumNumberOfTargets = 8; @@ -5901,11 +5937,12 @@ SpiParseDeviceInfo(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, PCM_FULL_RESOURCE_DESCRIPTOR FullResource; PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor; PCM_SCSI_DEVICE_DATA ScsiDeviceData; - ULONG Length, Count; + ULONG Length, Count, Dma = 0, Interrupt = 0; ULONG Index = 0, RangeCount = 0; UNICODE_STRING UnicodeString; ANSI_STRING AnsiString; NTSTATUS Status = STATUS_SUCCESS; + KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer; @@ -6194,16 +6231,61 @@ SpiParseDeviceInfo(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, break; case CmResourceTypeInterrupt: - ConfigInfo->BusInterruptLevel = + + if (Interrupt == 0) + { + ConfigInfo->BusInterruptLevel = + PartialDescriptor->u.Interrupt.Level; + + ConfigInfo->BusInterruptVector = + PartialDescriptor->u.Interrupt.Vector; + + ConfigInfo->InterruptMode = (PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive; + } + else if (Interrupt == 1) + { + ConfigInfo->BusInterruptLevel2 = PartialDescriptor->u.Interrupt.Level; - ConfigInfo->BusInterruptVector = + ConfigInfo->BusInterruptVector2 = PartialDescriptor->u.Interrupt.Vector; + + ConfigInfo->InterruptMode2 = (PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive; + } + + Interrupt++; break; case CmResourceTypeDma: - ConfigInfo->DmaChannel = PartialDescriptor->u.Dma.Channel; - ConfigInfo->DmaPort = PartialDescriptor->u.Dma.Port; + + if (Dma == 0) + { + ConfigInfo->DmaChannel = PartialDescriptor->u.Dma.Channel; + ConfigInfo->DmaPort = PartialDescriptor->u.Dma.Port; + + if (PartialDescriptor->Flags & CM_RESOURCE_DMA_8) + ConfigInfo->DmaWidth = Width8Bits; + else if ((PartialDescriptor->Flags & CM_RESOURCE_DMA_16) || + (PartialDescriptor->Flags & CM_RESOURCE_DMA_8_AND_16)) //??? + ConfigInfo->DmaWidth = Width16Bits; + else if (PartialDescriptor->Flags & CM_RESOURCE_DMA_32) + ConfigInfo->DmaWidth = Width32Bits; + } + else if (Dma == 1) + { + ConfigInfo->DmaChannel2 = PartialDescriptor->u.Dma.Channel; + ConfigInfo->DmaPort2 = PartialDescriptor->u.Dma.Port; + + if (PartialDescriptor->Flags & CM_RESOURCE_DMA_8) + ConfigInfo->DmaWidth2 = Width8Bits; + else if ((PartialDescriptor->Flags & CM_RESOURCE_DMA_16) || + (PartialDescriptor->Flags & CM_RESOURCE_DMA_8_AND_16)) //??? + ConfigInfo->DmaWidth2 = Width16Bits; + else if (PartialDescriptor->Flags & CM_RESOURCE_DMA_32) + ConfigInfo->DmaWidth2 = Width32Bits; + } + + Dma++; break; case CmResourceTypeDeviceSpecific: @@ -6265,7 +6347,7 @@ ScsiPortAllocateAdapterChannel(IN PDEVICE_OBJECT DeviceObject, DeviceExtension->MapRegisterBase = MapRegisterBase; /* Start pending request */ - KeSynchronizeExecution(DeviceExtension->Interrupt, + KeSynchronizeExecution(DeviceExtension->Interrupt[0], ScsiPortStartPacket, DeviceObject); /* Release spinlock we took */ diff --git a/drivers/storage/scsiport/scsiport_int.h b/drivers/storage/scsiport/scsiport_int.h index 9b6899f9b6c..97ef8d4fcad 100644 --- a/drivers/storage/scsiport/scsiport_int.h +++ b/drivers/storage/scsiport/scsiport_int.h @@ -222,7 +222,7 @@ typedef struct _SCSI_PORT_DEVICE_EXTENSION KSPIN_LOCK IrqLock; /* Used when there are 2 irqs */ ULONG SequenceNumber; /* Global sequence number for packets */ KSPIN_LOCK SpinLock; - PKINTERRUPT Interrupt; + PKINTERRUPT Interrupt[2]; PIRP CurrentIrp; ULONG IrpFlags; @@ -281,7 +281,7 @@ typedef struct _SCSI_PORT_DEVICE_EXTENSION PHYSICAL_ADDRESS PhysicalAddress; ULONG CommonBufferLength; - ULONG InterruptLevel; + ULONG InterruptLevel[2]; ULONG IoAddress; BOOLEAN NeedSrbExtensionAlloc; @@ -289,6 +289,8 @@ typedef struct _SCSI_PORT_DEVICE_EXTENSION ULONG RequestsNumber; + ULONG InterruptCount; + UCHAR MiniPortDeviceExtension[1]; /* must be the last entry */ } SCSI_PORT_DEVICE_EXTENSION, *PSCSI_PORT_DEVICE_EXTENSION; diff --git a/drivers/wdm/audio/backpln/portcls/filter_wavecyclic.cpp b/drivers/wdm/audio/backpln/portcls/filter_wavecyclic.cpp index fe0232d7674..e7882bb8da9 100644 --- a/drivers/wdm/audio/backpln/portcls/filter_wavecyclic.cpp +++ b/drivers/wdm/audio/backpln/portcls/filter_wavecyclic.cpp @@ -327,7 +327,7 @@ CPortFilterWaveCyclic::Init( NTSTATUS NTAPI CPortFilterWaveCyclic::FreePin( - IN struct IPortPinWaveCyclic* Pin) + IN PPORTPINWAVECYCLIC Pin) { ULONG Index; diff --git a/drivers/wdm/audio/backpln/portcls/filter_wavepci.cpp b/drivers/wdm/audio/backpln/portcls/filter_wavepci.cpp index c62cfbf3d61..639fb86720f 100644 --- a/drivers/wdm/audio/backpln/portcls/filter_wavepci.cpp +++ b/drivers/wdm/audio/backpln/portcls/filter_wavepci.cpp @@ -96,13 +96,14 @@ CPortFilterWavePci::NewIrpTarget( return STATUS_UNSUCCESSFUL; } - if (m_Pins[ConnectDetails->PinId] && m_Descriptor->Factory.Instances[ConnectDetails->PinId].CurrentPinInstanceCount) + if (m_Pins[ConnectDetails->PinId] && + (m_Descriptor->Factory.Instances[ConnectDetails->PinId].CurrentPinInstanceCount == m_Descriptor->Factory.Instances[ConnectDetails->PinId].MaxFilterInstanceCount)) { - // release existing instance - PC_ASSERT(0); - m_Pins[ConnectDetails->PinId]->Close(DeviceObject, NULL); + // no available instance + return STATUS_UNSUCCESSFUL; } + // now create the pin Status = NewPortPinWavePci(&Pin); if (!NT_SUCCESS(Status)) @@ -305,6 +306,26 @@ CPortFilterWavePci::Init( return STATUS_SUCCESS; } +NTSTATUS +NTAPI +CPortFilterWavePci::FreePin( + IN struct IPortPinWavePci* Pin) +{ + ULONG Index; + + for(Index = 0; Index < m_Descriptor->Factory.PinDescriptorCount; Index++) + { + if (m_Pins[Index] == Pin) + { + m_Descriptor->Factory.Instances[Index].CurrentPinInstanceCount--; + m_Pins[Index] = NULL; + return STATUS_SUCCESS; + } + } + return STATUS_UNSUCCESSFUL; +} + + NTSTATUS NewPortFilterWavePci( OUT IPortFilterWavePci ** OutFilter) diff --git a/drivers/wdm/audio/backpln/portcls/interfaces.hpp b/drivers/wdm/audio/backpln/portcls/interfaces.hpp index dc030ed1168..6caebb951d5 100644 --- a/drivers/wdm/audio/backpln/portcls/interfaces.hpp +++ b/drivers/wdm/audio/backpln/portcls/interfaces.hpp @@ -599,6 +599,8 @@ DECLARE_INTERFACE_(IIrpStreamVirtual, IIrpStream) #undef INTERFACE #define INTERFACE IPortFilterWavePci +struct IPortPinWavePci; + DECLARE_INTERFACE_(IPortFilterWavePci, IIrpTarget) { DEFINE_ABSTRACT_UNKNOWN() @@ -607,6 +609,9 @@ DECLARE_INTERFACE_(IPortFilterWavePci, IIrpTarget) STDMETHOD_(NTSTATUS, Init)(THIS_ IN PPORTWAVEPCI Port)PURE; + + STDMETHOD_(NTSTATUS, FreePin)(THIS_ + IN struct IPortPinWavePci* Pin)PURE; }; typedef IPortFilterWavePci *PPORTFILTERWAVEPCI; @@ -614,7 +619,10 @@ typedef IPortFilterWavePci *PPORTFILTERWAVEPCI; #define IMP_IPortFilterPci \ IMP_IIrpTarget; \ STDMETHODIMP_(NTSTATUS) Init(THIS_ \ - IN PPORTWAVEPCI Port) + IN PPORTWAVEPCI Port); \ + STDMETHODIMP_(NTSTATUS) FreePin(THIS_ \ + IN struct IPortPinWavePci* Pin) + /***************************************************************************** * IPortPinWavePci diff --git a/drivers/wdm/audio/backpln/portcls/pin_wavecyclic.cpp b/drivers/wdm/audio/backpln/portcls/pin_wavecyclic.cpp index 003b9b339cd..e615413b7e2 100644 --- a/drivers/wdm/audio/backpln/portcls/pin_wavecyclic.cpp +++ b/drivers/wdm/audio/backpln/portcls/pin_wavecyclic.cpp @@ -960,27 +960,29 @@ CPortPinWaveCyclic::Close( { // free format FreeItem(m_Format, TAG_PORTCLASS); + + // format is freed m_Format = NULL; } if (m_IrpQueue) { - // fixme cancel irps + // cancel remaining irps + m_IrpQueue->CancelBuffers(); + + // release irp queue m_IrpQueue->Release(); - } - - if (m_Port) - { - // release reference to port driver - m_Port->Release(); - m_Port = NULL; + // queue is freed + m_IrpQueue = NULL; } if (m_ServiceGroup) { // remove member from service group m_ServiceGroup->RemoveMember(PSERVICESINK(this)); + + // do not release service group, it is released by the miniport object m_ServiceGroup = NULL; } @@ -999,22 +1001,37 @@ CPortPinWaveCyclic::Close( // set state to stop m_State = KSSTATE_STOP; - DPRINT("Closing stream at Irql %u\n", KeGetCurrentIrql()); + // release stream m_Stream->Release(); + // stream is now freed + m_Stream = NULL; } if (m_Filter) { - // release reference to filter instance + // disconnect pin from filter m_Filter->FreePin((PPORTPINWAVECYCLIC)this); + + // release filter reference m_Filter->Release(); + + // pin is done with filter m_Filter = NULL; } + if (m_Port) + { + // release reference to port driver + m_Port->Release(); + + // work is done for port + m_Port = NULL; + } + Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); diff --git a/drivers/wdm/audio/backpln/portcls/pin_wavepci.cpp b/drivers/wdm/audio/backpln/portcls/pin_wavepci.cpp index 430ae5e8190..a2b89a4408f 100644 --- a/drivers/wdm/audio/backpln/portcls/pin_wavepci.cpp +++ b/drivers/wdm/audio/backpln/portcls/pin_wavepci.cpp @@ -62,8 +62,6 @@ protected: KSAUDIO_POSITION m_Position; ULONG m_StopCount; - ULONG m_Delay; - BOOL m_bUsePrefetch; ULONG m_PrefetchOffset; SUBDEVICE_DESCRIPTOR m_Descriptor; @@ -637,41 +635,85 @@ CPortPinWavePci::Close( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { - ISubdevice *SubDevice; NTSTATUS Status; - PSUBDEVICE_DESCRIPTOR Descriptor; + + if (m_Format) + { + // free format + FreeItem(m_Format, TAG_PORTCLASS); + + // format is freed + m_Format = NULL; + } + + if (m_IrpQueue) + { + // cancel remaining irps + m_IrpQueue->CancelBuffers(); + + // release irp queue + m_IrpQueue->Release(); + + // queue is freed + m_IrpQueue = NULL; + } + if (m_ServiceGroup) { + // remove member from service group m_ServiceGroup->RemoveMember(PSERVICESINK(this)); + + // do not release service group, it is released by the miniport object + m_ServiceGroup = NULL; } if (m_Stream) { if (m_State != KSSTATE_STOP) { - m_Stream->SetState(KSSTATE_STOP); + // stop stream + Status = m_Stream->SetState(KSSTATE_STOP); + if (!NT_SUCCESS(Status)) + { + DPRINT("Warning: failed to stop stream with %x\n", Status); + PC_ASSERT(0); + } } + // set state to stop + m_State = KSSTATE_STOP; + + DPRINT("Closing stream at Irql %u\n", KeGetCurrentIrql()); + + // release stream m_Stream->Release(); + + // stream is now freed + m_Stream = NULL; } - Status = m_Port->QueryInterface(IID_ISubdevice, (PVOID*)&SubDevice); - if (NT_SUCCESS(Status)) + if (m_Filter) { - Status = SubDevice->GetDescriptor(&Descriptor); - if (NT_SUCCESS(Status)) - { - Descriptor->Factory.Instances[m_ConnectDetails->PinId].CurrentPinInstanceCount--; - } - SubDevice->Release(); + // disconnect pin from filter + m_Filter->FreePin((PPORTPINWAVEPCI)this); + + // release filter reference + m_Filter->Release(); + + // pin is done with filter + m_Filter = NULL; } - if (m_Format) + if (m_Port) { - FreeItem(m_Format, TAG_PORTCLASS); - m_Format = NULL; + // release reference to port driver + m_Port->Release(); + + // work is done for port + m_Port = NULL; } + // successfully complete irp Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); @@ -756,33 +798,18 @@ CPortPinWavePci::Init( NTSTATUS Status; PKSDATAFORMAT DataFormat; BOOLEAN Capture; + ISubdevice * Subdevice = NULL; + PSUBDEVICE_DESCRIPTOR SubDeviceDescriptor = NULL; - Port->AddRef(); - Filter->AddRef(); - - m_Port = Port; - m_Filter = Filter; - m_KsPinDescriptor = KsPinDescriptor; - m_ConnectDetails = ConnectDetails; - m_Miniport = GetWavePciMiniport(Port); - m_DeviceObject = DeviceObject; - - DataFormat = (PKSDATAFORMAT)(ConnectDetails + 1); - - DPRINT("IPortPinWavePci_fnInit entered\n"); - - m_Format = (PKSDATAFORMAT)AllocateItem(NonPagedPool, DataFormat->FormatSize, TAG_PORTCLASS); - if (!m_Format) - return STATUS_INSUFFICIENT_RESOURCES; - - RtlMoveMemory(m_Format, DataFormat, DataFormat->FormatSize); - + // check if it is a source / sink pin if (KsPinDescriptor->Communication == KSPIN_COMMUNICATION_SINK && KsPinDescriptor->DataFlow == KSPIN_DATAFLOW_IN) { + // sink pin Capture = FALSE; } else if (KsPinDescriptor->Communication == KSPIN_COMMUNICATION_SINK && KsPinDescriptor->DataFlow == KSPIN_DATAFLOW_OUT) { + // source pin Capture = TRUE; } else @@ -792,6 +819,45 @@ CPortPinWavePci::Init( while(TRUE); } + // add port / filter reference + Port->AddRef(); + Filter->AddRef(); + + // initialize pin + m_Port = Port; + m_Filter = Filter; + m_KsPinDescriptor = KsPinDescriptor; + m_ConnectDetails = ConnectDetails; + m_Miniport = GetWavePciMiniport(Port); + m_DeviceObject = DeviceObject; + m_State = KSSTATE_STOP; + m_Capture = Capture; + + DPRINT("IPortPinWavePci_fnInit entered\n"); + + // get dataformat + DataFormat = (PKSDATAFORMAT)(ConnectDetails + 1); + + // allocate data format + m_Format = (PKSDATAFORMAT)AllocateItem(NonPagedPool, DataFormat->FormatSize, TAG_PORTCLASS); + if (!m_Format) + { + // release references + m_Port->Release(); + m_Filter->Release(); + + // no dangling pointers + Port = NULL; + Filter = NULL; + + // failed to allocate data format + return STATUS_INSUFFICIENT_RESOURCES; + } + + // copy data format + RtlMoveMemory(m_Format, DataFormat, DataFormat->FormatSize); + + // allocate new stream Status = m_Miniport->NewStream(&m_Stream, NULL, NonPagedPool, @@ -805,47 +871,81 @@ CPortPinWavePci::Init( DPRINT("IPortPinWavePci_fnInit Status %x\n", Status); if (!NT_SUCCESS(Status)) - return Status; - - if (m_ServiceGroup) { - Status = m_ServiceGroup->AddMember(PSERVICESINK(this)); - if (!NT_SUCCESS(Status)) - { - DPRINT("Failed to add pin to service group\n"); - return Status; - } + // free references + Port->Release(); + Filter->Release(); + + // free data format + FreeItem(m_Format, TAG_PORTCLASS); + + // no dangling pointers + m_Port = NULL; + m_Filter = NULL; + m_Format = NULL; + + // failed to allocate stream + return Status; } - // delay of 10 milisec - m_Delay = Int32x32To64(10, -10000); - + // get allocator requirements for pin Status = m_Stream->GetAllocatorFraming(&m_AllocatorFraming); + if (NT_SUCCESS(Status)) + { + DPRINT("OptionFlags %x RequirementsFlag %x PoolType %x Frames %lu FrameSize %lu FileAlignment %lu\n", + m_AllocatorFraming.OptionsFlags, m_AllocatorFraming.RequirementsFlags, m_AllocatorFraming.PoolType, m_AllocatorFraming.Frames, m_AllocatorFraming.FrameSize, m_AllocatorFraming.FileAlignment); + } + + // allocate new irp queue + Status = NewIrpQueue(&m_IrpQueue); if (!NT_SUCCESS(Status)) { - DPRINT("GetAllocatorFraming failed with %x\n", Status); + // free references + Port->Release(); + Filter->Release(); + m_Stream->Release(); + + // free data format + FreeItem(m_Format, TAG_PORTCLASS); + + // no dangling pointers + m_Port = NULL; + m_Filter = NULL; + m_Format = NULL; + m_Stream = NULL; + + // failed to allocate irp queue + return Status; } - DPRINT("OptionFlags %x RequirementsFlag %x PoolType %x Frames %lu FrameSize %lu FileAlignment %lu\n", - m_AllocatorFraming.OptionsFlags, m_AllocatorFraming.RequirementsFlags, m_AllocatorFraming.PoolType, m_AllocatorFraming.Frames, m_AllocatorFraming.FrameSize, m_AllocatorFraming.FileAlignment); + // initialize irp queue + Status = m_IrpQueue->Init(ConnectDetails, m_AllocatorFraming.FrameSize, m_AllocatorFraming.FileAlignment, NULL); + if (!NT_SUCCESS(Status)) + { + // this should never happen + ASSERT(0); + } - ISubdevice * Subdevice = NULL; // get subdevice interface Status = Port->QueryInterface(IID_ISubdevice, (PVOID*)&Subdevice); if (!NT_SUCCESS(Status)) - return Status; - - PSUBDEVICE_DESCRIPTOR SubDeviceDescriptor = NULL; + { + // this function should never fail + ASSERT(0); + } + // get subdevice descriptor Status = Subdevice->GetDescriptor(&SubDeviceDescriptor); if (!NT_SUCCESS(Status)) { - // failed to get descriptor - Subdevice->Release(); - return Status; + // this function should never fail + ASSERT(0); } + // release subdevice + Subdevice->Release(); + /* set up subdevice descriptor */ RtlZeroMemory(&m_Descriptor, sizeof(SUBDEVICE_DESCRIPTOR)); m_Descriptor.FilterPropertySet = PinWavePciPropertySet; @@ -855,21 +955,30 @@ CPortPinWavePci::Init( m_Descriptor.UnknownMiniport = SubDeviceDescriptor->UnknownMiniport; m_Descriptor.PortPin = (PVOID)this; - - - Status = NewIrpQueue(&m_IrpQueue); - if (!NT_SUCCESS(Status)) - return Status; - - Status = m_IrpQueue->Init(ConnectDetails, m_AllocatorFraming.FrameSize, m_AllocatorFraming.FileAlignment, NULL); - if (!NT_SUCCESS(Status)) + if (m_ServiceGroup) { - DPRINT("IrpQueue_Init failed with %x\n", Status); - return Status; + Status = m_ServiceGroup->AddMember(PSERVICESINK(this)); + if (!NT_SUCCESS(Status)) + { + // free references + m_Stream->Release(); + Port->Release(); + Filter->Release(); + + // free data format + FreeItem(m_Format, TAG_PORTCLASS); + + // no dangling pointers + m_Stream = NULL; + m_Port = NULL; + m_Filter = NULL; + m_Format = NULL; + + // failed to add to service group + return Status; + } } - m_State = KSSTATE_STOP; - m_Capture = Capture; return STATUS_SUCCESS; } diff --git a/drivers/wdm/audio/backpln/portcls/port_wavecyclic.cpp b/drivers/wdm/audio/backpln/portcls/port_wavecyclic.cpp index 6566fe67299..41879cd8950 100644 --- a/drivers/wdm/audio/backpln/portcls/port_wavecyclic.cpp +++ b/drivers/wdm/audio/backpln/portcls/port_wavecyclic.cpp @@ -275,12 +275,12 @@ CPortWaveCyclic::Init( } // create the subdevice descriptor - Status = PcCreateSubdeviceDescriptor(&m_SubDeviceDescriptor, + Status = PcCreateSubdeviceDescriptor(&m_SubDeviceDescriptor, 4, InterfaceGuids, - 0, + 0, NULL, - 2, + 2, WaveCyclicPropertySet, 0, 0, diff --git a/drivers/wdm/audio/backpln/portcls/port_wavepci.cpp b/drivers/wdm/audio/backpln/portcls/port_wavepci.cpp index 7e55116e52c..ec451fe6067 100644 --- a/drivers/wdm/audio/backpln/portcls/port_wavepci.cpp +++ b/drivers/wdm/audio/backpln/portcls/port_wavepci.cpp @@ -324,6 +324,9 @@ CPortWavePci::Init( m_ServiceGroup->AddRef(); } + // store for node property requests + m_SubDeviceDescriptor->UnknownMiniport = UnknownMiniport; + // check if it supports IPinCount interface Status = UnknownMiniport->QueryInterface(IID_IPinCount, (PVOID*)&PinCount); if (NT_SUCCESS(Status)) diff --git a/drivers/wdm/audio/legacy/wdmaud/mmixer.c b/drivers/wdm/audio/legacy/wdmaud/mmixer.c index 397bf994ef2..72e7776ce9d 100644 --- a/drivers/wdm/audio/legacy/wdmaud/mmixer.c +++ b/drivers/wdm/audio/legacy/wdmaud/mmixer.c @@ -318,6 +318,53 @@ FreeEventData(IN PVOID EventData) FreeItem(Data); } +VOID +EventCallback( + IN PVOID MixerEventContext, + IN HANDLE hMixer, + IN ULONG NotificationType, + IN ULONG Value) +{ + PWDMAUD_CLIENT ClientInfo; + PEVENT_ENTRY Entry; + ULONG Index; + + /* get client context */ + ClientInfo = (PWDMAUD_CLIENT)MixerEventContext; + + /* now search for the mixer which originated the request */ + for(Index = 0; Index < ClientInfo->NumPins; Index++) + { + if (ClientInfo->hPins[Index].Handle == hMixer && ClientInfo->hPins[Index].Type == MIXER_DEVICE_TYPE) + { + if (ClientInfo->hPins[Index].NotifyEvent) + { + /* allocate event entry */ + Entry = AllocateItem(NonPagedPool, sizeof(EVENT_ENTRY)); + if (!Entry) + { + /* no memory */ + break; + } + + /* setup event entry */ + Entry->NotificationType = NotificationType; + Entry->Value = Value; + Entry->hMixer = hMixer; + + /* insert entry */ + InsertTailList(&ClientInfo->MixerEventList, &Entry->Entry); + + /* now notify the client */ + KeSetEvent(ClientInfo->hPins[Index].NotifyEvent, 0, FALSE); + } + /* done */ + break; + } + } +} + + NTSTATUS WdmAudMixerInitialize( IN PDEVICE_OBJECT DeviceObject) @@ -378,7 +425,7 @@ WdmAudControlOpenMixer( } } - if (MMixerOpen(&MixerContext, DeviceInfo->DeviceIndex, EventObject, NULL /* FIXME */, &hMixer) != MM_STATUS_SUCCESS) + if (MMixerOpen(&MixerContext, DeviceInfo->DeviceIndex, ClientInfo, EventCallback, &hMixer) != MM_STATUS_SUCCESS) { ObDereferenceObject(EventObject); DPRINT1("Failed to open mixer\n"); @@ -511,7 +558,39 @@ WdmAudGetMixerEvent( IN PWDMAUD_DEVICE_INFO DeviceInfo, IN PWDMAUD_CLIENT ClientInfo) { - UNIMPLEMENTED + PLIST_ENTRY Entry; + PEVENT_ENTRY EventEntry; + + /* enumerate event list and check if there is a new event */ + Entry = ClientInfo->MixerEventList.Flink; + + while(Entry != &ClientInfo->MixerEventList) + { + /* grab event entry */ + EventEntry = (PEVENT_ENTRY)CONTAINING_RECORD(Entry, EVENT_ENTRY, Entry); + + if (EventEntry->hMixer == DeviceInfo->hDevice) + { + /* found an entry */ + DeviceInfo->u.MixerEvent.hMixer = EventEntry->hMixer; + DeviceInfo->u.MixerEvent.NotificationType = EventEntry->NotificationType; + DeviceInfo->u.MixerEvent.Value = EventEntry->Value; + + /* remove entry from list */ + RemoveEntryList(&EventEntry->Entry); + + /* free event entry */ + FreeItem(EventEntry); + + /* done */ + return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO)); + } + + /* move to next */ + Entry = Entry->Flink; + } + + /* no event entry available */ return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, sizeof(WDMAUD_DEVICE_INFO)); } diff --git a/drivers/wdm/audio/legacy/wdmaud/wdmaud.h b/drivers/wdm/audio/legacy/wdmaud/wdmaud.h index 0258c8e6691..dff0d8f75fb 100644 --- a/drivers/wdm/audio/legacy/wdmaud/wdmaud.h +++ b/drivers/wdm/audio/legacy/wdmaud/wdmaud.h @@ -35,6 +35,14 @@ typedef struct LIST_ENTRY MixerEventList; }WDMAUD_CLIENT, *PWDMAUD_CLIENT; +typedef struct +{ + LIST_ENTRY Entry; + ULONG NotificationType; + ULONG Value; + HANDLE hMixer; +}EVENT_ENTRY, *PEVENT_ENTRY; + typedef struct { LIST_ENTRY Entry; diff --git a/include/ndk/mmtypes.h b/include/ndk/mmtypes.h index da4a63edf33..f7c553446db 100644 --- a/include/ndk/mmtypes.h +++ b/include/ndk/mmtypes.h @@ -740,6 +740,27 @@ typedef struct _MMVAD_LONG } u4; } MMVAD_LONG, *PMMVAD_LONG; +// +// Short VAD used in virtual memory allocations +// +typedef struct _MMVAD_SHORT +{ + union + { + LONG_PTR Balance:2; + PMMVAD Parent; + } u1; + PMMVAD LeftChild; + PMMVAD RightChild; + ULONG StartingVpn; + ULONG EndingVpn; + union + { + ULONG LongFlags; + MMVAD_FLAGS VadFlags; + } u; +} MMVAD_SHORT, *PMMVAD_SHORT; + // // Actual Section Object // diff --git a/include/psdk/msi.h b/include/psdk/msi.h index b331057d53c..550ce954b62 100644 --- a/include/psdk/msi.h +++ b/include/psdk/msi.h @@ -19,14 +19,14 @@ #ifndef __WINE_MSI_H #define __WINE_MSI_H -#ifdef __cplusplus -extern "C" { -#endif - #ifndef _MSI_NO_CRYPTO #include #endif +#ifdef __cplusplus +extern "C" { +#endif + typedef ULONG MSIHANDLE; typedef enum tagINSTALLSTATE @@ -663,6 +663,12 @@ UINT WINAPI MsiApplyMultiplePatchesA(LPCSTR, LPCSTR, LPCSTR); UINT WINAPI MsiApplyMultiplePatchesW(LPCWSTR, LPCWSTR, LPCWSTR); #define MsiApplyMultiplePatches WINELIB_NAME_AW(MsiApplyMultiplePatches) +UINT WINAPI MsiBeginTransactionA(LPCSTR, DWORD, MSIHANDLE *, HANDLE *); +UINT WINAPI MsiBeginTransactionW(LPCWSTR, DWORD, MSIHANDLE *, HANDLE *); +#define MsiBeginTransaction WINELIB_NAME_AW(MsiBeginTransaction) + +UINT WINAPI MsiEndTransaction(DWORD); + /* Non Unicode */ UINT WINAPI MsiCloseHandle(MSIHANDLE); UINT WINAPI MsiCloseAllHandles(void); diff --git a/include/psdk/ntsecapi.h b/include/psdk/ntsecapi.h index 885315385b9..7aaf903ec56 100644 --- a/include/psdk/ntsecapi.h +++ b/include/psdk/ntsecapi.h @@ -671,14 +671,16 @@ typedef struct _TRUSTED_DOMAIN_FULL_INFORMATION { BOOLEAN WINAPI RtlGenRandom(PVOID,ULONG); NTSTATUS NTAPI LsaAddAccountRights(LSA_HANDLE,PSID,PLSA_UNICODE_STRING,ULONG); +NTSTATUS NTAPI LsaAddPrivilegesToAccount(LSA_HANDLE, PPRIVILEGE_SET); NTSTATUS NTAPI LsaCallAuthenticationPackage(HANDLE,ULONG,PVOID,ULONG,PVOID*, PULONG,PNTSTATUS); NTSTATUS NTAPI LsaClose(LSA_HANDLE); NTSTATUS NTAPI LsaConnectUntrusted(PHANDLE); -NTSTATUS NTAPI LsaCreateTrustedDomainEx(LSA_HANDLE, - PTRUSTED_DOMAIN_INFORMATION_EX, - PTRUSTED_DOMAIN_AUTH_INFORMATION,ACCESS_MASK, - PLSA_HANDLE); +NTSTATUS NTAPI LsaCreateAccount(LSA_HANDLE, PSID, ULONG, PLSA_HANDLE); +NTSTATUS NTAPI LsaCreateTrustedDomain(LSA_HANDLE, PLSA_TRUST_INFORMATION, + ACCESS_MASK, PLSA_HANDLE); +NTSTATUS NTAPI LsaCreateTrustedDomainEx(LSA_HANDLE, PTRUSTED_DOMAIN_INFORMATION_EX, + PTRUSTED_DOMAIN_AUTH_INFORMATION, ACCESS_MASK, PLSA_HANDLE); NTSTATUS NTAPI LsaDeleteTrustedDomain(LSA_HANDLE,PSID); NTSTATUS NTAPI LsaDeregisterLogonProcess(HANDLE); NTSTATUS NTAPI LsaEnumerateAccountRights(LSA_HANDLE,PSID,PLSA_UNICODE_STRING*,PULONG); @@ -703,8 +705,11 @@ NTSTATUS NTAPI LsaLookupPrivilegeValue(LSA_HANDLE, PLSA_UNICODE_STRING, PLUID); NTSTATUS NTAPI LsaLookupSids(LSA_HANDLE,ULONG,PSID*, PLSA_REFERENCED_DOMAIN_LIST*,PLSA_TRANSLATED_NAME*); ULONG NTAPI LsaNtStatusToWinError(NTSTATUS); +NTSTATUS NTAPI LsaOpenAccount(LSA_HANDLE, PSID, ULONG, PLSA_HANDLE); NTSTATUS NTAPI LsaOpenPolicy(PLSA_UNICODE_STRING,PLSA_OBJECT_ATTRIBUTES, ACCESS_MASK,PLSA_HANDLE); +NTSTATUS NTAPI LsaOpenTrustedDomainByName(LSA_HANDLE, PLSA_UNICODE_STRING, + ACCESS_MASK, PLSA_HANDLE); NTSTATUS NTAPI LsaQueryDomainInformationPolicy(LSA_HANDLE, POLICY_DOMAIN_INFORMATION_CLASS,PVOID*); NTSTATUS NTAPI LsaQueryInformationPolicy(LSA_HANDLE,POLICY_INFORMATION_CLASS,PVOID*); diff --git a/include/psdk/winbase.h b/include/psdk/winbase.h index 79c52ac5579..ee06339189e 100644 --- a/include/psdk/winbase.h +++ b/include/psdk/winbase.h @@ -1399,6 +1399,8 @@ BOOL WINAPI DebugSetProcessKillOnExit(BOOL); #endif PVOID WINAPI DecodePointer(PVOID); PVOID WINAPI DecodeSystemPointer(PVOID); +BOOL WINAPI DecryptFileA(LPCSTR,DWORD); +BOOL WINAPI DecryptFileW(LPCWSTR,DWORD); BOOL WINAPI DefineDosDeviceA(DWORD,LPCSTR,LPCSTR); BOOL WINAPI DefineDosDeviceW(DWORD,LPCWSTR,LPCWSTR); #define DefineHandleTable(w) ((w),TRUE) @@ -2374,6 +2376,7 @@ typedef PCACTCTXW PCACTCTX; #define CreateProcessAsUser CreateProcessAsUserW #define CreateSemaphore CreateSemaphoreW #define CreateWaitableTimer CreateWaitableTimerW +#define DecryptFile DecryptFileW #define DefineDosDevice DefineDosDeviceW #define DeleteFile DeleteFileW #if (_WIN32_WINNT >= 0x0500) @@ -2577,6 +2580,7 @@ typedef ENUMRESTYPEPROCA ENUMRESTYPEPROC; #define CreateProcessAsUser CreateProcessAsUserA #define CreateSemaphore CreateSemaphoreA #define CreateWaitableTimer CreateWaitableTimerA +#define DecryptFile DecryptFileA #define DefineDosDevice DefineDosDeviceA #define DeleteFile DeleteFileA #if (_WIN32_WINNT >= 0x0500) diff --git a/include/psdk/wingdi.h b/include/psdk/wingdi.h index 550ef68c626..052b04be334 100644 --- a/include/psdk/wingdi.h +++ b/include/psdk/wingdi.h @@ -1319,6 +1319,7 @@ extern "C" { #define LAYOUT_VBH 4 // Vertical before horizontal #define LAYOUT_ORIENTATIONMASK (LAYOUT_RTL | LAYOUT_BTT | LAYOUT_VBH) #define LAYOUT_BITMAPORIENTATIONPRESERVED 8 +#define NOMIRRORBITMAP 0x80000000 #if (WINVER > 0x400) #define CS_ENABLE 0x00000001 #define CS_DISABLE 0x00000002 diff --git a/include/psdk/winuser.h b/include/psdk/winuser.h index 340c7cde8eb..769c0e12129 100644 --- a/include/psdk/winuser.h +++ b/include/psdk/winuser.h @@ -3060,6 +3060,32 @@ typedef struct _ICONINFO { HBITMAP hbmColor; } ICONINFO,*PICONINFO; +typedef struct _ICONINFOEXA +{ + DWORD cbSize; + BOOL fIcon; + DWORD xHotspot; + DWORD yHotspot; + HBITMAP hbmMask; + HBITMAP hbmColor; + WORD wResID; + CHAR szModName[MAX_PATH]; + CHAR szResName[MAX_PATH]; +} ICONINFOEXA, *PICONINFOEXA; + +typedef struct _ICONINFOEXW +{ + DWORD cbSize; + BOOL fIcon; + DWORD xHotspot; + DWORD yHotspot; + HBITMAP hbmMask; + HBITMAP hbmColor; + WORD wResID; + WCHAR szModName[MAX_PATH]; + WCHAR szResName[MAX_PATH]; +} ICONINFOEXW, *PICONINFOEXW; + typedef struct tagNMHDR { HWND hwndFrom; UINT_PTR idFrom; @@ -4112,6 +4138,8 @@ HWND WINAPI GetForegroundWindow(void); DWORD WINAPI GetGuiResources(HANDLE,DWORD); #endif BOOL WINAPI GetIconInfo(HICON,PICONINFO); +BOOL WINAPI GetIconInfoExA(HICON,ICONINFOEXA*); +BOOL WINAPI GetIconInfoExW(HICON,ICONINFOEXW*); BOOL WINAPI GetInputState(void); UINT WINAPI GetKBCodePage(void); HKL WINAPI GetKeyboardLayout(DWORD); diff --git a/include/reactos/idl/lsa.idl b/include/reactos/idl/lsa.idl index 706554a0717..00949cf4779 100644 --- a/include/reactos/idl/lsa.idl +++ b/include/reactos/idl/lsa.idl @@ -592,34 +592,34 @@ cpp_quote("#endif") interface lsarpc { /* Function 0 */ - NTSTATUS LsarClose( + NTSTATUS __stdcall LsarClose( [in, out] LSAPR_HANDLE *ObjectHandle); /* Function 1 */ - NTSTATUS LsarDelete( + NTSTATUS __stdcall LsarDelete( [in] LSAPR_HANDLE ObjectHandle); /* Function 2 */ - NTSTATUS LsarEnumeratePrivileges( + NTSTATUS __stdcall LsarEnumeratePrivileges( [in] LSAPR_HANDLE PolicyHandle, [in, out] DWORD *EnumerationContext, [out] PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer, [in] DWORD PreferedMaximumLength); /* Function 3 */ - NTSTATUS LsarQuerySecurityObject( + NTSTATUS __stdcall LsarQuerySecurityObject( [in] LSAPR_HANDLE ObjectHandle, [in] SECURITY_INFORMATION SecurityInformation, [out] PLSAPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor); /* Function 4 */ - NTSTATUS LsarSetSecurityObject( + NTSTATUS __stdcall LsarSetSecurityObject( [in] LSAPR_HANDLE ObjectHandle, [in] SECURITY_INFORMATION SecurityInformation, [in] PLSAPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor); /* Function 5 */ - NTSTATUS LsarChangePassword( + NTSTATUS __stdcall LsarChangePassword( [in] PRPC_UNICODE_STRING String1, [in] PRPC_UNICODE_STRING String2, [in] PRPC_UNICODE_STRING String3, @@ -627,58 +627,58 @@ interface lsarpc [in] PRPC_UNICODE_STRING String5); /* Function 6 */ - NTSTATUS LsarOpenPolicy( + NTSTATUS __stdcall LsarOpenPolicy( [in, unique] PLSAPR_SERVER_NAME SystemName, [in] PLSAPR_OBJECT_ATTRIBUTES ObjectAttributes, [in] ACCESS_MASK DesiredAccess, [out] LSAPR_HANDLE *PolicyHandle); /* Function 7 */ - NTSTATUS LsarQueryInformationPolicy( + NTSTATUS __stdcall LsarQueryInformationPolicy( [in] LSAPR_HANDLE PolicyHandle, [in] POLICY_INFORMATION_CLASS InformationClass, [out, switch_is(InformationClass)] PLSAPR_POLICY_INFORMATION *PolicyInformation); /* Function 8 */ - NTSTATUS LsarSetInformationPolicy( + NTSTATUS __stdcall LsarSetInformationPolicy( [in] LSAPR_HANDLE PolicyHandle, [in] POLICY_INFORMATION_CLASS InformationClass, [in, switch_is(InformationClass)] PLSAPR_POLICY_INFORMATION PolicyInformation); /* Function 9 */ - NTSTATUS LsarClearAuditLog( + NTSTATUS __stdcall LsarClearAuditLog( [in] LSAPR_HANDLE ObjectHandle); /* Function 10 */ - NTSTATUS LsarCreateAccount( + NTSTATUS __stdcall LsarCreateAccount( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_SID AccountSid, [in] ACCESS_MASK DesiredAccess, [out] LSAPR_HANDLE *AccountHandle); /* Function 11 */ - NTSTATUS LsarEnumerateAccounts( + NTSTATUS __stdcall LsarEnumerateAccounts( [in] LSAPR_HANDLE PolicyHandle, [in, out] DWORD *EnumerationContext, [out] PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer, [in] DWORD PreferedMaximumLength); /* Function 12 */ - NTSTATUS LsarCreateTrustedDomain( + NTSTATUS __stdcall LsarCreateTrustedDomain( [in] LSAPR_HANDLE PolicyHandle, [in] PLSAPR_TRUST_INFORMATION TrustedDomainInformation, [in] ACCESS_MASK DesiredAccess, [out] LSAPR_HANDLE *TrustedDomainHandle); /* Function 13 */ - NTSTATUS LsarEnumerateTrustedDomains( + NTSTATUS __stdcall LsarEnumerateTrustedDomains( [in] LSAPR_HANDLE PolicyHandle, [in, out] DWORD *EnumerationContext, [out] PLSAPR_TRUSTED_ENUM_BUFFER EnumerationBuffer, [in] DWORD PreferedMaximumLength); /* Function 14 */ - NTSTATUS LsarLookupNames( + NTSTATUS __stdcall LsarLookupNames( [in] LSAPR_HANDLE PolicyHandle, [in] DWORD Count, [in, size_is(Count)] PRPC_UNICODE_STRING Names, @@ -688,7 +688,7 @@ interface lsarpc [in, out] DWORD *MappedCount); /* Function 15 */ - NTSTATUS LsarLookupSids( + NTSTATUS __stdcall LsarLookupSids( [in] LSAPR_HANDLE PolicyHandle, [in] PLSAPR_SID_ENUM_BUFFER SidEnumBuffer, [out] PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains, @@ -697,89 +697,89 @@ interface lsarpc [in, out] DWORD *MappedCount); /* Function 16 */ - NTSTATUS LsarCreateSecret( + NTSTATUS __stdcall LsarCreateSecret( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_UNICODE_STRING SecretName, [in] ACCESS_MASK DesiredAccess, [out] LSAPR_HANDLE *SecretHandle); /* Function 17 */ - NTSTATUS LsarOpenAccount( + NTSTATUS __stdcall LsarOpenAccount( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_SID AccountSid, [in] ACCESS_MASK DesiredAccess, [out] LSAPR_HANDLE *AccountHandle); /* Function 18 */ - NTSTATUS LsarEnumeratePrivilegesAccount( + NTSTATUS __stdcall LsarEnumeratePrivilegesAccount( [in] LSAPR_HANDLE AccountHandle, [out] PLSAPR_PRIVILEGE_SET *Privileges); /* Function 19 */ - NTSTATUS LsarAddPrivilegesToAccount( + NTSTATUS __stdcall LsarAddPrivilegesToAccount( [in] LSAPR_HANDLE AccountHandle, [in] PLSAPR_PRIVILEGE_SET Privileges); /* Function 20 */ - NTSTATUS LsarRemovePrivilegesFromAccount( + NTSTATUS __stdcall LsarRemovePrivilegesFromAccount( [in] LSAPR_HANDLE AccountHandle, [in] BOOL AllPrivileges, [in, unique] PLSAPR_PRIVILEGE_SET Privileges); /* Function 21 */ - NTSTATUS LsarGetQuotasForAccount( + NTSTATUS __stdcall LsarGetQuotasForAccount( [in] LSAPR_HANDLE AccountHandle, [out] PQUOTA_LIMITS QuotaLimits); /* Function 22 */ - NTSTATUS LsarSetQuotasForAccount( + NTSTATUS __stdcall LsarSetQuotasForAccount( [in] LSAPR_HANDLE AccountHandle, [in] PQUOTA_LIMITS QuotaLimits); /* Function 23 */ - NTSTATUS LsarGetSystemAccessAccount( + NTSTATUS __stdcall LsarGetSystemAccessAccount( [in] LSAPR_HANDLE AccountHandle, [out] ACCESS_MASK *SystemAccess); /* Function 24 */ - NTSTATUS LsarSetSystemAccessAccount( + NTSTATUS __stdcall LsarSetSystemAccessAccount( [in] LSAPR_HANDLE AccountHandle, [in] ACCESS_MASK SystemAccess); /* Function 25 */ - NTSTATUS LsarOpenTrustedDomain( + NTSTATUS __stdcall LsarOpenTrustedDomain( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_SID TrustedDomainSid, [in] ACCESS_MASK DesiredAccess, [out] LSAPR_HANDLE *TrustedDomainHandle); /* Function 26 */ - NTSTATUS LsarQueryInfoTrustedDomain( + NTSTATUS __stdcall LsarQueryInfoTrustedDomain( [in] LSAPR_HANDLE TrustedDomainHandle, [in] TRUSTED_INFORMATION_CLASS InformationClass, [out, switch_is(InformationClass)] PLSAPR_TRUSTED_DOMAIN_INFO *TrustedDomainInformation); /* Function 27 */ - NTSTATUS LsarSetInformationTrustedDomain( + NTSTATUS __stdcall LsarSetInformationTrustedDomain( [in] LSAPR_HANDLE TrustedDomainHandle, [in] TRUSTED_INFORMATION_CLASS InformationClass, [in, switch_is(InformationClass)] PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation); /* Function 28 */ - NTSTATUS LsarOpenSecret( + NTSTATUS __stdcall LsarOpenSecret( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_UNICODE_STRING SecretName, [in] ACCESS_MASK DesiredAccess, [out] LSAPR_HANDLE *SecretHandle); /* Function 29 */ - NTSTATUS LsarSetSecret( + NTSTATUS __stdcall LsarSetSecret( [in] LSAPR_HANDLE *SecretHandle, [in, unique] PLSAPR_CR_CIPHER_VALUE EncryptedCurrentValue, [in, unique] PLSAPR_CR_CIPHER_VALUE EncryptedOldValue); /* Function 30 */ - NTSTATUS LsarQuerySecret( + NTSTATUS __stdcall LsarQuerySecret( [in] LSAPR_HANDLE SecretHandle, [in, out, unique] PLSAPR_CR_CIPHER_VALUE *EncryptedCurrentValue, [in, out, unique] PLARGE_INTEGER CurrentValueSetTime, @@ -787,19 +787,19 @@ interface lsarpc [in, out, unique] PLARGE_INTEGER OldValueSetTime); /* Function 31 */ - NTSTATUS LsarLookupPrivilegeValue( + NTSTATUS __stdcall LsarLookupPrivilegeValue( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_UNICODE_STRING Name, [out] PLUID Value); /* Function 32 */ - NTSTATUS LsarLookupPrivilegeName( + NTSTATUS __stdcall LsarLookupPrivilegeName( [in] LSAPR_HANDLE PolicyHandle, [in] PLUID Value, [out] PRPC_UNICODE_STRING *Name); /* Function 33 */ - NTSTATUS LsarLookupPrivilegeDisplayName( + NTSTATUS __stdcall LsarLookupPrivilegeDisplayName( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_UNICODE_STRING Name, [in] USHORT ClientLanguage, @@ -808,76 +808,76 @@ interface lsarpc [out] USHORT *LanguageReturned); /* Function 34 */ - NTSTATUS LsarDeleteObject( + NTSTATUS __stdcall LsarDeleteObject( [in, out] LSAPR_HANDLE *ObjectHandle); cpp_quote("#if _WIN32_WINNT >= 0x0351") /* Function 35 */ - NTSTATUS LsarEnumerateAccountsWithUserRight( + NTSTATUS __stdcall LsarEnumerateAccountsWithUserRight( [in] LSAPR_HANDLE PolicyHandle, [in, unique] PRPC_UNICODE_STRING UserRight, [out] PLSAPR_ACCOUNT_ENUM_BUFFER EnumerationBuffer); /* Function 36 */ - NTSTATUS LsarEnmuerateAccountRights( + NTSTATUS __stdcall LsarEnmuerateAccountRights( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_SID AccountSid, [out] PLSAPR_USER_RIGHT_SET UserRights); /* Function 37 */ - NTSTATUS LsarAddAccountRights( + NTSTATUS __stdcall LsarAddAccountRights( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_SID AccountSid, [in] PLSAPR_USER_RIGHT_SET UserRights); /* Function 38 */ - NTSTATUS LsarRemoveAccountRights( + NTSTATUS __stdcall LsarRemoveAccountRights( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_SID AccountSid, [in] BOOL AllRights, [in] PLSAPR_USER_RIGHT_SET UserRights); /* Function 39 */ - NTSTATUS LsarQueryTrustedDomainInfo( + NTSTATUS __stdcall LsarQueryTrustedDomainInfo( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_SID TrustedDomainSid, [in] TRUSTED_INFORMATION_CLASS InformationClass, [out, switch_is(InformationClass)] PLSAPR_TRUSTED_DOMAIN_INFO *TrustedDomainInformation); /* Function 40 */ - NTSTATUS LsarSetTrustedDomainInfo( + NTSTATUS __stdcall LsarSetTrustedDomainInfo( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_SID TrustedDomainSid, [in] TRUSTED_INFORMATION_CLASS InformationClass, [in, switch_is(InformationClass)] PLSAPR_TRUSTED_DOMAIN_INFO TrustedDomainInformation); /* Function 41 */ - NTSTATUS LsarDeleteTrustedDomain( + NTSTATUS __stdcall LsarDeleteTrustedDomain( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_SID TrustedDomainSid); /* Function 42 */ - NTSTATUS LsarStorePrivateData( + NTSTATUS __stdcall LsarStorePrivateData( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_UNICODE_STRING KeyName, [in, unique] PLSAPR_CR_CIPHER_VALUE EncryptedData); /* Function 43 */ - NTSTATUS LsarRetrievePrivateData( + NTSTATUS __stdcall LsarRetrievePrivateData( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_UNICODE_STRING KeyName, [in, out] PLSAPR_CR_CIPHER_VALUE *EncryptedData); /* Function 44 */ - NTSTATUS LsarOpenPolicy2( + NTSTATUS __stdcall LsarOpenPolicy2( [in, unique, string] PLSAPR_SERVER_NAME SystemName, [in] PLSAPR_OBJECT_ATTRIBUTES ObjectAttributes, [in] ACCESS_MASK DesiredAccess, [out] LSAPR_HANDLE *PolicyHandle); /* Function 45 */ - NTSTATUS LsarGetUserName( + NTSTATUS __stdcall LsarGetUserName( [in, unique, string] PLSAPR_SERVER_NAME SystemName, [in, out] PRPC_UNICODE_STRING *UserName, [in, out, unique] PRPC_UNICODE_STRING *DomainName); @@ -885,21 +885,21 @@ cpp_quote("#if _WIN32_WINNT >= 0x0351") cpp_quote("#if _WIN32_WINNT >= 0x0500") /* Function 46 */ - NTSTATUS LsarQueryInformationPolicy2( + NTSTATUS __stdcall LsarQueryInformationPolicy2( [in] LSAPR_HANDLE PolicyHandle, [in] POLICY_INFORMATION_CLASS InformationClass, [out] unsigned long *PolicyInformation); /* FIXME: should be [out, switch_is(InformationClass)] PLSAPR_POLICY_INFORMATION *PolicyInformation); */ /* Function 47 */ - NTSTATUS LsarSetInformationPolicy2( + NTSTATUS __stdcall LsarSetInformationPolicy2( [in] LSAPR_HANDLE PolicyHandle, [in] POLICY_INFORMATION_CLASS InformationClass, [in] unsigned long PolicyInformation); /* FIXME: should be [in, switch_is(InformationClass)] PLSAPR_POLICY_INFORMATION PolicyInformation); */ /* Function 48 */ - NTSTATUS LsarQueryTrustedDomainInfoByName( + NTSTATUS __stdcall LsarQueryTrustedDomainInfoByName( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_UNICODE_STRING TrustedDomainName, [in] POLICY_INFORMATION_CLASS InformationClass, @@ -907,7 +907,7 @@ cpp_quote("#if _WIN32_WINNT >= 0x0500") /* FIXME: should be [out, switch_is(InformationClass)] PLSAPR_POLICY_INFORMATION *PolicyInformation); */ /* Function 49 */ - NTSTATUS LsarSetTrustedDomainInfoByName( + NTSTATUS __stdcall LsarSetTrustedDomainInfoByName( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_UNICODE_STRING TrustedDomainName, [in] POLICY_INFORMATION_CLASS InformationClass, @@ -915,14 +915,14 @@ cpp_quote("#if _WIN32_WINNT >= 0x0500") /* FIXME: should be [in, switch_is(InformationClass)] PLSAPR_POLICY_INFORMATION PolicyInformation); */ /* Function 50 */ - NTSTATUS LsarEnumerateTrustedDomainsEx( + NTSTATUS __stdcall LsarEnumerateTrustedDomainsEx( [in] LSAPR_HANDLE PolicyHandle, [in, out] DWORD *EnumerationContext, [out] PLSAPR_TRUSTED_ENUM_BUFFER_EX EnumerationBuffer, [in] DWORD PreferedMaximumLength); /* Function 51 */ - NTSTATUS LsarCreateTrustedDomainEx( + NTSTATUS __stdcall LsarCreateTrustedDomainEx( [in] LSAPR_HANDLE PolicyHandle, [in] PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX TrustedDomainInformation, [in] PLSAPR_TRUSTED_DOMAIN_AUTH_INFORMATION AuthentificationInformation, @@ -930,36 +930,36 @@ cpp_quote("#if _WIN32_WINNT >= 0x0500") [out] LSAPR_HANDLE *TrustedDomainHandle); /* Function 52 */ - NTSTATUS LsarSetPolicyReplicationHandle( + NTSTATUS __stdcall LsarSetPolicyReplicationHandle( [in, out] PLSAPR_HANDLE PolicyHandle); /* Function 53 */ - NTSTATUS LsarQueryDomainInformationPolicy( + NTSTATUS __stdcall LsarQueryDomainInformationPolicy( [in] LSAPR_HANDLE PolicyHandle, [in] POLICY_INFORMATION_CLASS InformationClass, [out] unsigned long *PolicyInformation); /* FIXME: should be [out, switch_is(InformationClass)] PLSAPR_POLICY_INFORMATION *PolicyInformation); */ /* Function 54 */ - NTSTATUS LsarSetDomainInformationPolicy( + NTSTATUS __stdcall LsarSetDomainInformationPolicy( [in] LSAPR_HANDLE PolicyHandle, [in] POLICY_INFORMATION_CLASS InformationClass, [in] unsigned long PolicyInformation); /* FIXME: should be [in, switch_is(InformationClass)] PLSAPR_POLICY_INFORMATION PolicyInformation); */ /* Function 55 */ - NTSTATUS LsarOpenTrustedDomainByName( + NTSTATUS __stdcall LsarOpenTrustedDomainByName( [in] LSAPR_HANDLE PolicyHandle, [in] PRPC_UNICODE_STRING TrustedDomainName, [in] ACCESS_MASK DesiredAccess, [out] LSAPR_HANDLE *TrustedDomainHandle); /* Function 56 */ - NTSTATUS LsarTestCall( + NTSTATUS __stdcall LsarTestCall( [in] handle_t hBinding); /* FIXME */ /* Function 57 */ - NTSTATUS LsarLookupSids2( + NTSTATUS __stdcall LsarLookupSids2( [in] LSAPR_HANDLE PolicyHandle, [in] PLSAPR_SID_ENUM_BUFFER SidEnumBuffer, [out] PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains, @@ -970,7 +970,7 @@ cpp_quote("#if _WIN32_WINNT >= 0x0500") [in] DWORD ClientRevision); /* Function 58 */ - NTSTATUS LsarLookupNames2( + NTSTATUS __stdcall LsarLookupNames2( [in] LSAPR_HANDLE PolicyHandle, [in] DWORD Count, [in, size_is(Count)] PRPC_UNICODE_STRING Names, @@ -982,7 +982,7 @@ cpp_quote("#if _WIN32_WINNT >= 0x0500") [in] DWORD ClientRevision); /* Function 59 */ - NTSTATUS LsarCreateTrustedDomainEx2( + NTSTATUS __stdcall LsarCreateTrustedDomainEx2( [in] LSAPR_HANDLE PolicyHandle, [in] PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX TrustedDomainInformation, [in] PLSAPR_TRUSTED_DOMAIN_AUTH_INFORMATION_INTERNAL AuthentificationInformation, @@ -992,39 +992,39 @@ cpp_quote("#if _WIN32_WINNT >= 0x0500") cpp_quote("#if _WIN32_WINNT >= 0x0501") /* Function 60 */ - NTSTATUS CredrWrite( + NTSTATUS __stdcall CredrWrite( [in] handle_t hBinding); /* FIXME */ /* Function 61 */ - NTSTATUS CredrRead( + NTSTATUS __stdcall CredrRead( [in] handle_t hBinding); /* FIXME */ /* Function 62 */ - NTSTATUS CredrEnumerate( + NTSTATUS __stdcall CredrEnumerate( [in] handle_t hBinding); /* FIXME */ /* Function 63 */ - NTSTATUS CredrWriteDomainCredentials( + NTSTATUS __stdcall CredrWriteDomainCredentials( [in] handle_t hBinding); /* FIXME */ /* Function 64 */ - NTSTATUS CredrReadDomainCredentials( + NTSTATUS __stdcall CredrReadDomainCredentials( [in] handle_t hBinding); /* FIXME */ /* Function 65 */ - NTSTATUS CredrDelete( + NTSTATUS __stdcall CredrDelete( [in] handle_t hBinding); /* FIXME */ /* Function 66 */ - NTSTATUS CredrGetTargetInfo( + NTSTATUS __stdcall CredrGetTargetInfo( [in] handle_t hBinding); /* FIXME */ /* Function 67 */ - NTSTATUS CredrProfileLoaded( + NTSTATUS __stdcall CredrProfileLoaded( [in] handle_t hBinding); /* FIXME */ /* Function 68 */ - NTSTATUS LsarLookupNames3( + NTSTATUS __stdcall LsarLookupNames3( [in] LSAPR_HANDLE PolicyHandle, [in] DWORD Count, [in, size_is(Count)] PRPC_UNICODE_STRING Names, @@ -1036,30 +1036,30 @@ cpp_quote("#if _WIN32_WINNT >= 0x0501") [in] DWORD ClientRevision); /* Function 69 */ - NTSTATUS CredrGetSessionTypes( + NTSTATUS __stdcall CredrGetSessionTypes( [in] handle_t hBinding); /* FIXME */ /* Function 70 */ - NTSTATUS LsarRegisterAuditEvent( + NTSTATUS __stdcall LsarRegisterAuditEvent( [in] handle_t hBinding); /* FIXME */ /* Function 71 */ - NTSTATUS LsarGenAuditEvent( + NTSTATUS __stdcall LsarGenAuditEvent( [in] handle_t hBinding); /* FIXME */ /* Function 72 */ - NTSTATUS LsarUnregisterAuditEvent( + NTSTATUS __stdcall LsarUnregisterAuditEvent( [in] handle_t hBinding); /* FIXME */ /* Function 73 */ - NTSTATUS LsarQueryForestTrustInformation( + NTSTATUS __stdcall LsarQueryForestTrustInformation( [in] LSAPR_HANDLE PolicyHandle, [in] PLSA_UNICODE_STRING TrustedDomainName, [in] LSA_FOREST_TRUST_RECORD_TYPE HighestRecordType, [out] PLSA_FOREST_TRUST_INFORMATION *ForestTrustInfo); /* Function 74 */ - NTSTATUS LsarSetForestTrustInformation( + NTSTATUS __stdcall LsarSetForestTrustInformation( [in] LSAPR_HANDLE PolicyHandle, [in] PLSA_UNICODE_STRING TrustedDomainName, [in] LSA_FOREST_TRUST_RECORD_TYPE HighestRecordType, @@ -1068,11 +1068,11 @@ cpp_quote("#if _WIN32_WINNT >= 0x0501") [out] PLSA_FOREST_TRUST_COLLISION_INFORMATION *CollisionInfo); /* Function 75 */ - NTSTATUS CredrRename( + NTSTATUS __stdcall CredrRename( [in] handle_t hBinding); /* FIXME */ /* Function 76 */ - NTSTATUS LsarLookupSids3( + NTSTATUS __stdcall LsarLookupSids3( [in] LSAPR_HANDLE PolicyHandle, [in] PLSAPR_SID_ENUM_BUFFER SidEnumBuffer, [out] PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains, @@ -1083,7 +1083,7 @@ cpp_quote("#if _WIN32_WINNT >= 0x0501") [in] DWORD ClientRevision); /* Function 77 */ - NTSTATUS LsarLookupNames4( + NTSTATUS __stdcall LsarLookupNames4( [in] LSAPR_HANDLE PolicyHandle, [in] DWORD Count, [in, size_is(Count)] PRPC_UNICODE_STRING Names, @@ -1095,19 +1095,19 @@ cpp_quote("#if _WIN32_WINNT >= 0x0501") [in] DWORD ClientRevision); /* Function 78 */ - NTSTATUS LsarOpenPolicySce( + NTSTATUS __stdcall LsarOpenPolicySce( [in] handle_t hBinding); /* FIXME */ /* Function 79 */ - NTSTATUS LsarAdtRegisterSecurityEventSource( + NTSTATUS __stdcall LsarAdtRegisterSecurityEventSource( [in] handle_t hBinding); /* FIXME */ /* Function 80 */ - NTSTATUS LsarAdtUnregisterSecurityEventSource( + NTSTATUS __stdcall LsarAdtUnregisterSecurityEventSource( [in] handle_t hBinding); /* FIXME */ /* Function 81 */ - NTSTATUS LsarAdtReportSecurityEvent( + NTSTATUS __stdcall LsarAdtReportSecurityEvent( [in] handle_t hBinding); /* FIXME */ cpp_quote("#endif /* _WIN32_WINNT >= 0x0501 */") diff --git a/include/reactos/subsys/lsass/lsasrv.h b/include/reactos/subsys/lsass/lsasrv.h index 48120bf0bd3..445506f4527 100644 --- a/include/reactos/subsys/lsass/lsasrv.h +++ b/include/reactos/subsys/lsass/lsasrv.h @@ -5,4 +5,7 @@ NTSTATUS WINAPI LsapInitLsa(VOID); +NTSTATUS WINAPI +ServiceInit(VOID); + #endif /* __LSASRV_H */ diff --git a/include/reactos/win32k/ntuser.h b/include/reactos/win32k/ntuser.h index 920e4e1abff..64071cb9066 100644 --- a/include/reactos/win32k/ntuser.h +++ b/include/reactos/win32k/ntuser.h @@ -51,13 +51,15 @@ VOID NTAPI RtlInitLargeAnsiString(IN OUT PLARGE_ANSI_STRING,IN PCSZ,IN INT); VOID NTAPI RtlInitLargeUnicodeString(IN OUT PLARGE_UNICODE_STRING,IN PCWSTR,IN INT); BOOL NTAPI RtlLargeStringToUnicodeString( PUNICODE_STRING, PLARGE_STRING); +#define NB_HOOKS (WH_MAXHOOK-WH_MINHOOK+1) + typedef struct _DESKTOPINFO { PVOID pvDesktopBase; PVOID pvDesktopLimit; struct _WND *spwnd; DWORD fsHooks; - struct tagHOOK * aphkStart[16]; + LIST_ENTRY aphkStart[NB_HOOKS]; HWND hTaskManWindow; HWND hProgmanWindow; @@ -127,15 +129,23 @@ typedef struct _PROCMARKHEAD /* Window Client Information structure */ struct _ETHREAD; +#define WEF_SETBYWNDPTI 0x0001 + typedef struct tagHOOK { THRDESKHEAD head; + struct tagHOOK *phkNext; /* This is for user space. */ + int HookId; /* Hook table index */ + ULONG_PTR offPfn; + ULONG flags; /* Some internal flags */ + INT ihmod; + PTHREADINFO ptiHooked; + struct _DESKTOP *rpdesk; + /* ReactOS */ LIST_ENTRY Chain; /* Hook chain entry */ struct _ETHREAD* Thread; /* Thread owning the hook */ - int HookId; /* Hook table index */ HOOKPROC Proc; /* Hook function */ BOOLEAN Ansi; /* Is it an Ansi hook? */ - ULONG Flags; /* Some internal flags */ UNICODE_STRING ModuleName; /* Module name for global hooks */ } HOOK, *PHOOK; @@ -3149,7 +3159,6 @@ typedef struct tagKMDDELPARAM #define NOPARAM_ROUTINE_ANYPOPUP 0xffff0006 #define ONEPARAM_ROUTINE_CSRSS_GUICHECK 0xffff0008 #define ONEPARAM_ROUTINE_SWITCHCARETSHOWING 0xfffe0008 -#define ONEPARAM_ROUTINE_ISWINDOWINDESTROY 0xfffe000c #define ONEPARAM_ROUTINE_ENABLEPROCWNDGHSTING 0xfffe000d #define ONEPARAM_ROUTINE_GETDESKTOPMAPPING 0xfffe000e #define ONEPARAM_ROUTINE_MSQSETWAKEMASK 0xfffe0027 @@ -3167,6 +3176,7 @@ typedef struct tagKMDDELPARAM #define TWOPARAM_ROUTINE_SETCARETPOS 0xfffd0060 #define TWOPARAM_ROUTINE_REGISTERLOGONPROC 0xfffd0062 #define TWOPARAM_ROUTINE_ROS_UPDATEUISTATE 0x1004 +#define HWNDPARAM_ROUTINE_ROS_NOTIFYWINEVENT 0x1005 DWORD NTAPI diff --git a/lib/drivers/sound/mmixer/controls.c b/lib/drivers/sound/mmixer/controls.c index 5b0ac71ed34..6963f49e467 100644 --- a/lib/drivers/sound/mmixer/controls.c +++ b/lib/drivers/sound/mmixer/controls.c @@ -8,153 +8,11 @@ #include "priv.h" -MIXER_STATUS -MMixerGetTargetPinsByNodeConnectionIndex( - IN PMIXER_CONTEXT MixerContext, - IN PKSMULTIPLE_ITEM NodeConnections, - IN PKSMULTIPLE_ITEM NodeTypes, - IN ULONG bUpDirection, - IN ULONG NodeConnectionIndex, - IN ULONG PinCount, - OUT PULONG Pins) -{ - PKSTOPOLOGY_CONNECTION Connection; - ULONG PinId, NodeConnectionCount, Index; - PULONG NodeConnection; - MIXER_STATUS Status; - - - /* sanity check */ - ASSERT(NodeConnectionIndex < NodeConnections->Count); - - Connection = (PKSTOPOLOGY_CONNECTION)(NodeConnections + 1); - - //DPRINT("FromNode %u FromNodePin %u -> ToNode %u ToNodePin %u\n", Connection[NodeConnectionIndex].FromNode, Connection[NodeConnectionIndex].FromNodePin, Connection[NodeConnectionIndex].ToNode, Connection[NodeConnectionIndex].ToNodePin ); - - if ((Connection[NodeConnectionIndex].ToNode == KSFILTER_NODE && bUpDirection == FALSE) || - (Connection[NodeConnectionIndex].FromNode == KSFILTER_NODE && bUpDirection == TRUE)) - { - /* iteration stops here */ - if (bUpDirection) - PinId = Connection[NodeConnectionIndex].FromNodePin; - else - PinId = Connection[NodeConnectionIndex].ToNodePin; - - //DPRINT("GetTargetPinsByNodeIndex FOUND Target Pin %u Parsed %u\n", PinId, Pins[PinId]); - - // sanity check - ASSERT(PinId < PinCount); - - /* mark pin index as a target pin */ - Pins[PinId] = TRUE; - return MM_STATUS_SUCCESS; - } - - // get all node indexes referenced by that node - if (bUpDirection) - { - Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, Connection[NodeConnectionIndex].FromNode, TRUE, FALSE, &NodeConnectionCount, &NodeConnection); - } - else - { - Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, Connection[NodeConnectionIndex].ToNode, TRUE, TRUE, &NodeConnectionCount, &NodeConnection); - } - - if (Status == MM_STATUS_SUCCESS) - { - for(Index = 0; Index < NodeConnectionCount; Index++) - { - // iterate recursively into the nodes - Status = MMixerGetTargetPinsByNodeConnectionIndex(MixerContext, NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], PinCount, Pins); - ASSERT(Status == MM_STATUS_SUCCESS); - } - // free node connection indexes - MixerContext->Free(NodeConnection); - } - - return Status; -} - -MIXER_STATUS -MMixerGetControlsFromPinByConnectionIndex( - IN PMIXER_CONTEXT MixerContext, - IN PKSMULTIPLE_ITEM NodeConnections, - IN PKSMULTIPLE_ITEM NodeTypes, - IN ULONG bUpDirection, - IN ULONG NodeConnectionIndex, - OUT PULONG Nodes) -{ - PKSTOPOLOGY_CONNECTION CurConnection; - LPGUID NodeType; - ULONG NodeIndex; - MIXER_STATUS Status; - ULONG NodeConnectionCount, Index; - PULONG NodeConnection; - - - /* get current connection */ - CurConnection = MMixerGetConnectionByIndex(NodeConnections, NodeConnectionIndex); - - if (bUpDirection) - NodeIndex = CurConnection->FromNode; - else - NodeIndex = CurConnection->ToNode; - - if (NodeIndex > NodeTypes->Count) - { - // reached end of pin connection - return MM_STATUS_SUCCESS; - } - - /* get target node type of current connection */ - NodeType = MMixerGetNodeType(NodeTypes, NodeIndex); - - if (IsEqualGUIDAligned(NodeType, &KSNODETYPE_SUM) || IsEqualGUIDAligned(NodeType, &KSNODETYPE_MUX)) - { - if (bUpDirection) - { - /* add the sum / mux node to destination line */ - Nodes[NodeIndex] = TRUE; - } - - return MM_STATUS_SUCCESS; - } - - /* now add the node */ - Nodes[NodeIndex] = TRUE; - - - /* get all node indexes referenced by that node */ - if (bUpDirection) - { - Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection); - } - else - { - Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection); - } - - if (Status == MM_STATUS_SUCCESS) - { - for(Index = 0; Index < NodeConnectionCount; Index++) - { - /* iterate recursively into the nodes */ - Status = MMixerGetControlsFromPinByConnectionIndex(MixerContext, NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Nodes); - ASSERT(Status == MM_STATUS_SUCCESS); - } - /* free node connection indexes */ - MixerContext->Free(NodeConnection); - } - - return Status; -} - MIXER_STATUS MMixerAddMixerControl( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo, - IN HANDLE hDevice, - IN PKSMULTIPLE_ITEM NodeTypes, + IN PTOPOLOGY Topology, IN ULONG NodeIndex, IN LPMIXERLINE_EXT MixerLine, OUT LPMIXERCONTROLW MixerControl) @@ -170,12 +28,12 @@ MMixerAddMixerControl( MixerControl->dwControlID = MixerInfo->ControlId; /* get node type */ - NodeType = MMixerGetNodeType(NodeTypes, NodeIndex); + NodeType = MMixerGetNodeTypeFromTopology(Topology, NodeIndex); /* store control type */ MixerControl->dwControlType = MMixerGetControlTypeFromTopologyNode(NodeType); - MixerControl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM; //FIXME - MixerControl->cMultipleItems = 0; //FIXME + MixerControl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM; /* FIXME */ + MixerControl->cMultipleItems = 0; /* FIXME */ if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) { @@ -186,7 +44,7 @@ MMixerAddMixerControl( { MixerControl->Bounds.dwMinimum = 0; MixerControl->Bounds.dwMaximum = 0xFFFF; - MixerControl->Metrics.cSteps = 0xC0; //FIXME + MixerControl->Metrics.cSteps = 0xC0; /* FIXME */ } /* setup request to retrieve name */ @@ -197,7 +55,7 @@ MMixerAddMixerControl( Node.Reserved = 0; /* get node name size */ - Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), NULL, 0, &BytesReturned); + Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), NULL, 0, &BytesReturned); if (Status == MM_STATUS_MORE_ENTRIES) { @@ -210,7 +68,7 @@ MMixerAddMixerControl( } /* get node name */ - Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned); + Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned); if (Status == MM_STATUS_SUCCESS) { @@ -268,7 +126,7 @@ MMixerAddMixerControl( Property.NodeProperty.Property.Set = KSPROPSETID_Audio; /* get node volume level info */ - Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned); + Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned); if (Status == MM_STATUS_SUCCESS) { @@ -324,236 +182,6 @@ MMixerAddMixerControl( return MM_STATUS_SUCCESS; } -MIXER_STATUS -MMixerAddMixerSourceLine( - IN PMIXER_CONTEXT MixerContext, - IN OUT LPMIXER_INFO MixerInfo, - IN HANDLE hDevice, - IN PKSMULTIPLE_ITEM NodeConnections, - IN PKSMULTIPLE_ITEM NodeTypes, - IN ULONG PinId, - IN ULONG bBridgePin, - IN ULONG bTargetPin) -{ - LPMIXERLINE_EXT SrcLine, DstLine; - MIXER_STATUS Status; - KSP_PIN Pin; - LPWSTR PinName; - GUID NodeType; - ULONG BytesReturned, ControlCount, Index; - LPGUID Node; - PULONG Nodes; - - if (!bTargetPin) - { - /* allocate src mixer line */ - SrcLine = (LPMIXERLINE_EXT)MixerContext->Alloc(sizeof(MIXERLINE_EXT)); - - if (!SrcLine) - return MM_STATUS_NO_MEMORY; - - /* zero struct */ - RtlZeroMemory(SrcLine, sizeof(MIXERLINE_EXT)); - - } - else - { - ASSERT(!IsListEmpty(&MixerInfo->LineList)); - SrcLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE); - } - - /* get destination line */ - DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE); - ASSERT(DstLine); - - - if (!bTargetPin) - { - /* initialize mixer src line */ - SrcLine->hDevice = hDevice; - SrcLine->PinId = PinId; - SrcLine->Line.cbStruct = sizeof(MIXERLINEW); - - /* initialize mixer destination line */ - SrcLine->Line.cbStruct = sizeof(MIXERLINEW); - SrcLine->Line.dwDestination = 0; - SrcLine->Line.dwSource = DstLine->Line.cConnections; - SrcLine->Line.dwLineID = (DstLine->Line.cConnections * 0x10000); - SrcLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE; - SrcLine->Line.dwUser = 0; - SrcLine->Line.cChannels = DstLine->Line.cChannels; - SrcLine->Line.cConnections = 0; - SrcLine->Line.Target.dwType = 1; - SrcLine->Line.Target.dwDeviceID = DstLine->Line.Target.dwDeviceID; - SrcLine->Line.Target.wMid = MixerInfo->MixCaps.wMid; - SrcLine->Line.Target.wPid = MixerInfo->MixCaps.wPid; - SrcLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion; - InitializeListHead(&SrcLine->LineControlsExtraData); - - ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == L'\0'); - wcscpy(SrcLine->Line.Target.szPname, MixerInfo->MixCaps.szPname); - - } - - /* allocate a node arrary */ - Nodes = (PULONG)MixerContext->Alloc(sizeof(ULONG) * NodeTypes->Count); - - if (!Nodes) - { - /* not enough memory */ - if (!bTargetPin) - { - MixerContext->Free(SrcLine); - } - return MM_STATUS_NO_MEMORY; - } - - Status = MMixerGetControlsFromPin(MixerContext, NodeConnections, NodeTypes, PinId, bTargetPin, Nodes); - if (Status != MM_STATUS_SUCCESS) - { - /* something went wrong */ - if (!bTargetPin) - { - MixerContext->Free(SrcLine); - } - MixerContext->Free(Nodes); - return Status; - } - - /* now count all nodes controlled by that pin */ - ControlCount = 0; - for(Index = 0; Index < NodeTypes->Count; Index++) - { - if (Nodes[Index]) - { - // get node type - Node = MMixerGetNodeType(NodeTypes, Index); - - if (MMixerGetControlTypeFromTopologyNode(Node)) - { - // found a node which can be resolved to a type - ControlCount++; - } - } - } - - /* now allocate the line controls */ - if (ControlCount) - { - SrcLine->LineControls = (LPMIXERCONTROLW)MixerContext->Alloc(sizeof(MIXERCONTROLW) * ControlCount); - - if (!SrcLine->LineControls) - { - /* no memory available */ - if (!bTargetPin) - { - MixerContext->Free(SrcLine); - } - MixerContext->Free(Nodes); - return MM_STATUS_NO_MEMORY; - } - - SrcLine->NodeIds = (PULONG)MixerContext->Alloc(sizeof(ULONG) * ControlCount); - if (!SrcLine->NodeIds) - { - /* no memory available */ - MixerContext->Free(SrcLine->LineControls); - if (!bTargetPin) - { - MixerContext->Free(SrcLine); - } - MixerContext->Free(Nodes); - return MM_STATUS_NO_MEMORY; - } - - /* zero line controls */ - RtlZeroMemory(SrcLine->LineControls, sizeof(MIXERCONTROLW) * ControlCount); - RtlZeroMemory(SrcLine->NodeIds, sizeof(ULONG) * ControlCount); - - ControlCount = 0; - for(Index = 0; Index < NodeTypes->Count; Index++) - { - if (Nodes[Index]) - { - // get node type - Node = MMixerGetNodeType(NodeTypes, Index); - - if (MMixerGetControlTypeFromTopologyNode(Node)) - { - /* store the node index for retrieving / setting details */ - SrcLine->NodeIds[ControlCount] = Index; - - Status = MMixerAddMixerControl(MixerContext, MixerInfo, hDevice, NodeTypes, Index, SrcLine, &SrcLine->LineControls[ControlCount]); - if (Status == MM_STATUS_SUCCESS) - { - /* increment control count on success */ - ControlCount++; - } - } - } - } - /* store control count */ - SrcLine->Line.cControls = ControlCount; - } - - /* release nodes array */ - MixerContext->Free(Nodes); - - /* get pin category */ - Pin.PinId = PinId; - Pin.Reserved = 0; - Pin.Property.Flags = KSPROPERTY_TYPE_GET; - Pin.Property.Set = KSPROPSETID_Pin; - Pin.Property.Id = KSPROPERTY_PIN_CATEGORY; - - /* try get pin category */ - Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (LPVOID)&NodeType, sizeof(GUID), &BytesReturned); - if (Status != MM_STATUS_SUCCESS) - { - //FIXME - //map component type - } - - /* retrieve pin name */ - Pin.PinId = PinId; - Pin.Reserved = 0; - Pin.Property.Flags = KSPROPERTY_TYPE_GET; - Pin.Property.Set = KSPROPSETID_Pin; - Pin.Property.Id = KSPROPERTY_PIN_NAME; - - /* try get pin name size */ - Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned); - - if (Status == MM_STATUS_MORE_ENTRIES) - { - PinName = (LPWSTR)MixerContext->Alloc(BytesReturned); - if (PinName) - { - /* try get pin name */ - Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (LPVOID)PinName, BytesReturned, &BytesReturned); - - if (Status == MM_STATUS_SUCCESS) - { - MixerContext->Copy(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR)); - SrcLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0'; - - MixerContext->Copy(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR)); - SrcLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0'; - } - MixerContext->Free(PinName); - } - } - - /* insert src line */ - if (!bTargetPin) - { - InsertTailList(&MixerInfo->LineList, &SrcLine->Entry); - DstLine->Line.cConnections++; - } - - return MM_STATUS_SUCCESS; -} - MIXER_STATUS MMixerCreateDestinationLine( IN PMIXER_CONTEXT MixerContext, @@ -563,11 +191,11 @@ MMixerCreateDestinationLine( { LPMIXERLINE_EXT DestinationLine; - // allocate a mixer destination line + /* allocate a mixer destination line */ DestinationLine = (LPMIXERLINE_EXT) MixerContext->Alloc(sizeof(MIXERLINE_EXT)); if (!MixerInfo) { - // no memory + /* no memory */ return MM_STATUS_NO_MEMORY; } @@ -578,7 +206,7 @@ MMixerCreateDestinationLine( DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE; DestinationLine->Line.dwUser = 0; DestinationLine->Line.dwComponentType = (bInputMixer == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN); - DestinationLine->Line.cChannels = 2; //FIXME + DestinationLine->Line.cChannels = 2; /* FIXME */ if (LineName) { @@ -589,12 +217,6 @@ MMixerCreateDestinationLine( DestinationLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0'; } - else - { - /* FIXME no name was found for pin */ - wcscpy(DestinationLine->Line.szShortName, L"Summe"); - wcscpy(DestinationLine->Line.szName, L"Summe"); - } DestinationLine->Line.Target.dwType = (bInputMixer == 0 ? MIXERLINE_TARGETTYPE_WAVEOUT : MIXERLINE_TARGETTYPE_WAVEIN); DestinationLine->Line.Target.dwDeviceID = !bInputMixer; @@ -605,214 +227,697 @@ MMixerCreateDestinationLine( ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == 0); wcscpy(DestinationLine->Line.Target.szPname, MixerInfo->MixCaps.szPname); - // initialize extra line + /* initialize extra line */ InitializeListHead(&DestinationLine->LineControlsExtraData); - // insert into mixer info + /* insert into mixer info */ InsertHeadList(&MixerInfo->LineList, &DestinationLine->Entry); - // done + /* done */ return MM_STATUS_SUCCESS; } MIXER_STATUS -MMixerGetControlsFromPin( +MMixerGetPinName( IN PMIXER_CONTEXT MixerContext, - IN PKSMULTIPLE_ITEM NodeConnections, - IN PKSMULTIPLE_ITEM NodeTypes, + IN LPMIXER_INFO MixerInfo, IN ULONG PinId, - IN ULONG bUpDirection, - OUT PULONG Nodes) + IN OUT LPWSTR * OutBuffer) { - ULONG NodeConnectionCount, Index; + KSP_PIN Pin; + ULONG BytesReturned; + LPWSTR Buffer; MIXER_STATUS Status; - PULONG NodeConnection; - /* sanity check */ - ASSERT(PinId != (ULONG)-1); + /* prepare pin */ + Pin.PinId = PinId; + Pin.Reserved = 0; + Pin.Property.Flags = KSPROPERTY_TYPE_GET; + Pin.Property.Set = KSPROPSETID_Pin; + Pin.Property.Id = KSPROPERTY_PIN_NAME; - /* get all node indexes referenced by that pin */ - if (bUpDirection) - Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, PinId, FALSE, FALSE, &NodeConnectionCount, &NodeConnection); - else - Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, PinId, FALSE, TRUE, &NodeConnectionCount, &NodeConnection); + /* try get pin name size */ + Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned); - for(Index = 0; Index < NodeConnectionCount; Index++) + /* check if buffer overflowed */ + if (Status == MM_STATUS_MORE_ENTRIES) { - /* get all associated controls */ - Status = MMixerGetControlsFromPinByConnectionIndex(MixerContext, NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Nodes); + /* allocate buffer */ + Buffer = (LPWSTR)MixerContext->Alloc(BytesReturned); + if (!Buffer) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + /* try get pin name */ + Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Buffer, BytesReturned, &BytesReturned); + if (Status != MM_STATUS_SUCCESS) + { + /* failed to get pin name */ + MixerContext->Free((PVOID)Buffer); + return Status; + } + + /* successfully obtained pin name */ + *OutBuffer = Buffer; + return MM_STATUS_SUCCESS; } - MixerContext->Free(NodeConnection); + /* failed to get pin name */ + return Status; +} + +MIXER_STATUS +MMixerBuildMixerDestinationLine( + IN PMIXER_CONTEXT MixerContext, + IN OUT LPMIXER_INFO MixerInfo, + IN ULONG PinId, + IN ULONG bInput) +{ + LPWSTR PinName; + MIXER_STATUS Status; + + /* try get pin name */ + Status = MMixerGetPinName(MixerContext, MixerInfo, PinId, &PinName); + if (Status == MM_STATUS_SUCCESS) + { + /* create mixer destination line */ + + Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, PinName); + + /* free pin name */ + MixerContext->Free(PinName); + } + else + { + /* create mixer destination line unlocalized */ + Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, L"No Name"); + } return Status; } +MIXER_STATUS +MMixerBuildTopology( + IN PMIXER_CONTEXT MixerContext, + IN LPMIXER_DATA MixerData, + OUT PTOPOLOGY * OutTopology) +{ + ULONG PinsCount; + PKSMULTIPLE_ITEM NodeTypes = NULL; + PKSMULTIPLE_ITEM NodeConnections = NULL; + MIXER_STATUS Status; + if (MixerData->Topology) + { + /* re-use existing topology */ + *OutTopology = MixerData->Topology; + return MM_STATUS_SUCCESS; + } + + /* get connected filter pin count */ + PinsCount = MMixerGetFilterPinCount(MixerContext, MixerData->hDevice); + + if (!PinsCount) + { + /* referenced filter does not have any pins */ + return MM_STATUS_UNSUCCESSFUL; + } + + /* get topology node types */ + Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes); + if (Status != MM_STATUS_SUCCESS) + { + /* failed to get topology node types */ + return Status; + } + + /* get topology connections */ + Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections); + if (Status != MM_STATUS_SUCCESS) + { + /* failed to get topology connections */ + MixerContext->Free(NodeTypes); + return Status; + } + + /* create a topology */ + Status = MMixerCreateTopology(MixerContext, PinsCount, NodeConnections, NodeTypes, OutTopology); + + /* free node types & connections */ + MixerContext->Free(NodeConnections); + MixerContext->Free(NodeTypes); + + if (Status == MM_STATUS_SUCCESS) + { + /* store topology object */ + MixerData->Topology = *OutTopology; + } + + /* done */ + return Status; +} + +MIXER_STATUS +MMixerCountMixerControls( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG PinId, + IN ULONG bUpStream, + OUT PULONG OutNodesCount, + OUT PULONG OutNodes, + OUT PULONG OutLineTerminator) +{ + PULONG Nodes; + ULONG NodesCount, NodeIndex, Count, bTerminator; + MIXER_STATUS Status; + + /* allocate an array to store all nodes which are upstream of this pin */ + Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes); + + if (Status != MM_STATUS_SUCCESS) + { + /* out of memory */ + return STATUS_NO_MEMORY; + } + + /* mark result array as zero */ + *OutNodesCount = 0; + + /* get next nodes */ + MMixerGetNextNodesFromPinIndex(MixerContext, Topology, PinId, bUpStream, &NodesCount, Nodes); + + /* assume no topology split before getting line terminator */ + ASSERT(NodesCount == 1); + + /* get first node */ + NodeIndex = Nodes[0]; + Count = 0; + + do + { + /* check if the node is a terminator */ + MMixerIsNodeTerminator(Topology, NodeIndex, &bTerminator); + + if (bTerminator) + { + /* found terminator */ + break; + } + + /* store node id */ + OutNodes[Count] = NodeIndex; + + /* increment node count */ + Count++; + + /* get next nodes upstream */ + MMixerGetNextNodesFromNodeIndex(MixerContext, Topology, NodeIndex, bUpStream, &NodesCount, Nodes); + + /* assume there is a node connected */ + ASSERT(NodesCount != 0); + ASSERT(NodesCount == 1); + + /* use first index */ + NodeIndex = Nodes[0]; + + }while(TRUE); + + /* free node index */ + MixerContext->Free(Nodes); + + /* store nodes count */ + *OutNodesCount = Count; + + /* store line terminator */ + *OutLineTerminator = NodeIndex; + + /* done */ + return MM_STATUS_SUCCESS; +} + +MIXER_STATUS +MMixerAddMixerControlsToMixerLineByNodeIndexArray( + IN PMIXER_CONTEXT MixerContext, + IN LPMIXER_INFO MixerInfo, + IN PTOPOLOGY Topology, + IN OUT LPMIXERLINE_EXT DstLine, + IN ULONG NodesCount, + IN PULONG Nodes) +{ + ULONG Index, Count, bReserved; + MIXER_STATUS Status; + + /* store nodes array */ + DstLine->NodeIds = Nodes; + + /* allocate MIXERCONTROLSW array */ + DstLine->LineControls = MixerContext->Alloc(NodesCount * sizeof(MIXERCONTROLW)); + + if (!DstLine->LineControls) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + /* initialize control count */ + Count = 0; + + for(Index = 0; Index < NodesCount; Index++) + { + /* check if the node has already been reserved to a line */ + MMixerIsTopologyNodeReserved(Topology, Nodes[Index], &bReserved); + + if (bReserved) + { + /* node is already used, skip it */ + continue; + } + + /* set node status as used */ + MMixerSetTopologyNodeReserved(Topology, Nodes[Index]); + + /* now add the mixer control */ + Status = MMixerAddMixerControl(MixerContext, MixerInfo, Topology, Nodes[Index], DstLine, &DstLine->LineControls[Count]); + + if (Status == MM_STATUS_SUCCESS) + { + /* increment control count */ + Count++; + } + } + + /* store control count */ + DstLine->Line.cControls = Count; + + /* done */ + return MM_STATUS_SUCCESS; +} + +MIXER_STATUS +MMixerBuildMixerSourceLine( + IN PMIXER_CONTEXT MixerContext, + IN OUT LPMIXER_INFO MixerInfo, + IN PTOPOLOGY Topology, + IN ULONG PinId, + IN ULONG NodesCount, + IN PULONG Nodes, + OUT LPMIXERLINE_EXT * OutSrcLine) +{ + LPMIXERLINE_EXT SrcLine, DstLine; + LPWSTR PinName; + MIXER_STATUS Status; + + /* construct source line */ + SrcLine = (LPMIXERLINE_EXT)MixerContext->Alloc(sizeof(MIXERLINE_EXT)); + + if (!SrcLine) + { + /* no memory */ + return MM_STATUS_NO_MEMORY; + } + + /* get destination line */ + DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE); + ASSERT(DstLine); + + /* initialize mixer src line */ + SrcLine->hDevice = MixerInfo->hMixer; + SrcLine->PinId = PinId; + SrcLine->NodeIds = Nodes; + + /* initialize mixer line */ + SrcLine->Line.cbStruct = sizeof(MIXERLINEW); + SrcLine->Line.dwDestination = 0; + SrcLine->Line.dwSource = DstLine->Line.cConnections; + SrcLine->Line.dwLineID = (DstLine->Line.cConnections * 0x10000); + SrcLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE; + SrcLine->Line.dwUser = 0; + SrcLine->Line.cChannels = DstLine->Line.cChannels; + SrcLine->Line.cConnections = 0; + SrcLine->Line.Target.dwType = 1; + SrcLine->Line.Target.dwDeviceID = DstLine->Line.Target.dwDeviceID; + SrcLine->Line.Target.wMid = MixerInfo->MixCaps.wMid; + SrcLine->Line.Target.wPid = MixerInfo->MixCaps.wPid; + SrcLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion; + InitializeListHead(&SrcLine->LineControlsExtraData); + + /* copy name */ + ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == L'\0'); + wcscpy(SrcLine->Line.Target.szPname, MixerInfo->MixCaps.szPname); + + /* get pin name */ + Status = MMixerGetPinName(MixerContext, MixerInfo, PinId, &PinName); + + if (Status == MM_STATUS_SUCCESS) + { + /* store pin name as line name */ + MixerContext->Copy(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR)); + SrcLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0'; + + MixerContext->Copy(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR)); + SrcLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0'; + + /* free pin name buffer */ + MixerContext->Free(PinName); + } + + /* add the controls to mixer line */ + Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, Topology, SrcLine, NodesCount, Nodes); + if (Status != MM_STATUS_SUCCESS) + { + /* failed */ + return Status; + } + + /* store result */ + *OutSrcLine = SrcLine; + + return MM_STATUS_SUCCESS; +} MIXER_STATUS MMixerAddMixerSourceLines( IN PMIXER_CONTEXT MixerContext, IN OUT LPMIXER_INFO MixerInfo, - IN HANDLE hDevice, - IN PKSMULTIPLE_ITEM NodeConnections, - IN PKSMULTIPLE_ITEM NodeTypes, - IN ULONG PinsCount, - IN ULONG BridgePinIndex, - IN ULONG TargetPinIndex, - IN PULONG Pins) + IN PTOPOLOGY Topology, + IN ULONG LineTerminator) { - ULONG Index; + PULONG AllNodes, AllPins, AllPinNodes; + ULONG AllNodesCount, AllPinsCount, AllPinNodesCount; + ULONG Index, SubIndex, PinId, CurNode, bConnected; + MIXER_STATUS Status; + LPMIXERLINE_EXT DstLine, SrcLine; - for(Index = PinsCount; Index > 0; Index--) + /* get destination line */ + DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE); + ASSERT(DstLine); + + /* allocate an array to store all nodes which are upstream of the line terminator */ + Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllNodes); + + /* check for success */ + if (Status != MM_STATUS_SUCCESS) { - DPRINT("MMixerAddMixerSourceLines Index %lu Pin %lu\n", Index-1, Pins[Index-1]); - if (Pins[Index-1]) - { - MMixerAddMixerSourceLine(MixerContext, MixerInfo, hDevice, NodeConnections, NodeTypes, Index-1, (Index -1 == BridgePinIndex), (Index -1 == TargetPinIndex)); - } + /* out of memory */ + return MM_STATUS_NO_MEMORY; } + + /* allocate an array to store all nodes which are downstream of a particular pin */ + Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllPinNodes); + + /* allocate an array to store all pins which are upstream of this pin */ + Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &AllPins); + + /* check for success */ + if (Status != MM_STATUS_SUCCESS) + { + /* out of memory */ + MixerContext->Free(AllNodes); + return MM_STATUS_NO_MEMORY; + } + + /* get all nodes which indirectly / directly connect to this node */ + AllNodesCount = 0; + MMixerGetAllUpOrDownstreamNodesFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllNodesCount, AllNodes); + + /* get all pins which indirectly / directly connect to this node */ + AllPinsCount = 0; + MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllPinsCount, AllPins); + + DPRINT("LineTerminator %lu\n", LineTerminator); + DPRINT("PinCount %lu\n", AllPinsCount); + DPRINT("AllNodesCount %lu\n", AllNodesCount); + + /* now construct the source lines which are attached to the destination line */ + Index = AllPinsCount; + + do + { + /* get current pin id */ + PinId = AllPins[Index - 1]; + + /* reset nodes count */ + AllPinNodesCount = 0; + + /* now scan all nodes and add them to AllPinNodes array when they are connected to this pin */ + for(SubIndex = 0; SubIndex < AllNodesCount; SubIndex++) + { + /* get current node index */ + CurNode = AllNodes[SubIndex]; + + if (CurNode != MAXULONG && CurNode != LineTerminator) + { + /* check if that node is connected in some way to the current pin */ + Status = MMixerIsNodeConnectedToPin(MixerContext, Topology, CurNode, PinId, TRUE, &bConnected); + + if (Status != MM_STATUS_SUCCESS) + break; + + if (bConnected) + { + /* it is connected */ + AllPinNodes[AllPinNodesCount] = CurNode; + AllPinNodesCount++; + + /* clear current index */ + AllNodes[SubIndex] = MAXULONG; + } + } + } + + /* decrement pin index */ + Index--; + + if (AllPinNodesCount) + { + /* now build the mixer source line */ + Status = MMixerBuildMixerSourceLine(MixerContext, MixerInfo, Topology, PinId, AllPinNodesCount, AllPinNodes, &SrcLine); + + if (Status == MM_STATUS_SUCCESS) + { + /* insert into line list */ + InsertTailList(&MixerInfo->LineList, &SrcLine->Entry); + + /* increment destination line count */ + DstLine->Line.cConnections++; + } + } + + }while(Index != 0); + return MM_STATUS_SUCCESS; } +MIXER_STATUS +MMixerAddMixerControlsToDestinationLine( + IN PMIXER_CONTEXT MixerContext, + IN OUT LPMIXER_INFO MixerInfo, + IN PTOPOLOGY Topology, + IN ULONG PinId, + IN ULONG bInput, + OUT PULONG OutLineTerminator) +{ + PULONG Nodes; + ULONG NodesCount, LineTerminator; + MIXER_STATUS Status; + LPMIXERLINE_EXT DstLine; + + /* allocate nodes index array */ + Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes); + + /* check for success */ + if (Status != MM_STATUS_SUCCESS) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + /* get all destination line controls */ + Status = MMixerCountMixerControls(MixerContext, Topology, PinId, TRUE, &NodesCount, Nodes, &LineTerminator); + + /* check for success */ + if (Status != MM_STATUS_SUCCESS) + { + /* failed to count controls */ + MixerContext->Free(Nodes); + return Status; + } + + /* get destination mixer line */ + DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE); + + /* sanity check */ + ASSERT(DstLine); + + if (NodesCount > 0) + { + /* add all nodes as mixer controls to the destination line */ + Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, Topology, DstLine, NodesCount, Nodes); + if (Status != MM_STATUS_SUCCESS) + { + /* failed to add controls */ + MixerContext->Free(Nodes); + return Status; + } + } + + /* store result */ + *OutLineTerminator = LineTerminator; + + /* return result */ + return Status; +} + +VOID +MMixerApplyOutputFilterHack( + IN PMIXER_CONTEXT MixerContext, + IN LPMIXER_DATA MixerData, + IN OUT PULONG PinsCount, + IN OUT PULONG Pins) +{ + ULONG Count = 0, Index; + MIXER_STATUS Status; + PKSPIN_PHYSICALCONNECTION Connection; + + for(Index = 0; Index < *PinsCount; Index++) + { + /* check if it has a physical connection */ + Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[Index], &Connection); + + if (Status == MM_STATUS_SUCCESS) + { + /* remove pin */ + MixerContext->Copy(&Pins[Index], &Pins[Index + 1], (*PinsCount - (Index + 1)) * sizeof(ULONG)); + + /* free physical connection */ + MixerContext->Free(Connection); + + /* decrement index */ + Index--; + + /* decrement pin count */ + (*PinsCount)--; + } + else + { + /* simple pin */ + Count++; + } + } + + /* store result */ + *PinsCount = Count; +} + MIXER_STATUS MMixerHandlePhysicalConnection( IN PMIXER_CONTEXT MixerContext, IN PMIXER_LIST MixerList, + IN LPMIXER_DATA MixerData, IN OUT LPMIXER_INFO MixerInfo, IN ULONG bInput, IN PKSPIN_PHYSICALCONNECTION OutConnection) { - PULONG PinsRef = NULL, PinConnectionIndex = NULL, PinsSrcRef; - ULONG PinsRefCount, Index, PinConnectionIndexCount; MIXER_STATUS Status; - PKSMULTIPLE_ITEM NodeTypes = NULL; - PKSMULTIPLE_ITEM NodeConnections = NULL; - PULONG MixerControls; - ULONG MixerControlsCount; - LPMIXER_DATA MixerData; + ULONG PinsCount, LineTerminator; + PULONG Pins; + PTOPOLOGY Topology; - - // open the connected filter + /* first try to open the connected filter */ OutConnection->SymbolicLinkName[1] = L'\\'; MixerData = MMixerGetDataByDeviceName(MixerList, OutConnection->SymbolicLinkName); - ASSERT(MixerData); - // store connected mixer handle + /* check if the linked connection is found */ + if (!MixerData) + { + /* filter references invalid physical connection */ + return MM_STATUS_UNSUCCESSFUL; + } + + DPRINT("Name %S, Pin %lu bInput %lu\n", OutConnection->SymbolicLinkName, OutConnection->Pin, bInput); + + /* store connected mixer handle */ MixerInfo->hMixer = MixerData->hDevice; - // get connected filter pin count - PinsRefCount = MMixerGetFilterPinCount(MixerContext, MixerData->hDevice); - ASSERT(PinsRefCount); - PinsRef = (PULONG)MixerContext->Alloc(sizeof(ULONG) * PinsRefCount); - if (!PinsRef) - { - // no memory - return MM_STATUS_UNSUCCESSFUL; - } - - // get topology node types - Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes); + Status = MMixerBuildTopology(MixerContext, MixerData, &Topology); if (Status != MM_STATUS_SUCCESS) { - MixerContext->Free(PinsRef); + /* failed to create topology */ return Status; } - // get topology connections - Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections); - if (Status != MM_STATUS_SUCCESS) - { - MixerContext->Free(PinsRef); - MixerContext->Free(NodeTypes); - return Status; - } - // gets connection index of the bridge pin which connects to a node - DPRINT("Pin %lu\n", OutConnection->Pin); + /* allocate pin index array which will hold all referenced pins */ + Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins); + ASSERT(Status == MM_STATUS_SUCCESS); - Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, OutConnection->Pin, FALSE, !bInput, &PinConnectionIndexCount, &PinConnectionIndex); - if (Status != MM_STATUS_SUCCESS) + if (!bInput) { - MixerContext->Free(PinsRef); - MixerContext->Free(NodeTypes); - MixerContext->Free(NodeConnections); - return Status; - } + /* the mixer is an output mixer + * find end pin of the node path + */ + PinsCount = 0; + Status = MMixerGetAllUpOrDownstreamPinsFromPinIndex(MixerContext, Topology, OutConnection->Pin, FALSE, &PinsCount, Pins); - /* there should be no split in the bridge pin */ - ASSERT(PinConnectionIndexCount == 1); - - /* find all target pins of this connection */ - Status = MMixerGetTargetPinsByNodeConnectionIndex(MixerContext, NodeConnections, NodeTypes, FALSE, PinConnectionIndex[0], PinsRefCount, PinsRef); - if (Status != MM_STATUS_SUCCESS) - { - MixerContext->Free(PinsRef); - MixerContext->Free(NodeTypes); - MixerContext->Free(NodeConnections); - MixerContext->Free(PinConnectionIndex); - return Status; - } - - for(Index = 0; Index < PinsRefCount; Index++) - { - DPRINT("PinsRefCount %lu Index %lu Value %lu\n", PinsRefCount, Index, PinsRef[Index]); - if (PinsRef[Index]) + /* check for success */ + if (Status != MM_STATUS_SUCCESS) { - // found a target pin, now get all references - Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, Index, FALSE, FALSE, &MixerControlsCount, &MixerControls); - if (Status != MM_STATUS_SUCCESS) - { - DPRINT("MMixerGetNodeIndexes failed with %u\n", Status); - break; - } + /* failed to get end pin */ + MixerContext->Free(Pins); + //MMixerFreeTopology(Topology); - /* sanity check */ - ASSERT(MixerControlsCount == 1); + /* return error code */ + return Status; + } + /* HACK: + * some topologies do not have strict boundaries + * WorkArround: remove all pin ids which have a physical connection + * because bridge pins may belong to different render paths + */ + MMixerApplyOutputFilterHack(MixerContext, MixerData, &PinsCount, Pins); - PinsSrcRef = (PULONG)MixerContext->Alloc(PinsRefCount * sizeof(ULONG)); - if (!PinsSrcRef) - { - /* no memory */ - MixerContext->Free(PinsRef); - MixerContext->Free(NodeTypes); - MixerContext->Free(NodeConnections); - MixerContext->Free(PinConnectionIndex); - MixerContext->Free(MixerControls); - return MM_STATUS_NO_MEMORY; - } + /* sanity checks */ + ASSERT(PinsCount != 0); + ASSERT(PinsCount == 1); - // now get all connected source pins - Status = MMixerGetTargetPinsByNodeConnectionIndex(MixerContext, NodeConnections, NodeTypes, TRUE, MixerControls[0], PinsRefCount, PinsSrcRef); - if (Status != MM_STATUS_SUCCESS) - { - // failed */ - MixerContext->Free(PinsRef); - MixerContext->Free(NodeTypes); - MixerContext->Free(NodeConnections); - MixerContext->Free(PinConnectionIndex); - MixerContext->Free(MixerControls); - MixerContext->Free(PinsSrcRef); - return Status; - } + /* create destination line */ + Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, Pins[0], bInput); - /* add pins from target line */ - if (!bInput) - { - // dont add bridge pin for input mixers - PinsSrcRef[Index] = TRUE; - PinsSrcRef[OutConnection->Pin] = TRUE; - } - PinsSrcRef[OutConnection->Pin] = TRUE; + if (Status != MM_STATUS_SUCCESS) + { + MixerContext->Free(Pins); + //MMixerFreeTopology(Topology); - Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, NodeConnections, NodeTypes, PinsRefCount, OutConnection->Pin, Index, PinsSrcRef); + /* return error code */ + return Status; + } - MixerContext->Free(MixerControls); - MixerContext->Free(PinsSrcRef); + /* add mixer controls to destination line */ + Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, Topology, Pins[0], bInput, &LineTerminator); + + if (Status == MM_STATUS_SUCCESS) + { + /* now add the rest of the source lines */ + Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, Topology, LineTerminator); } } + else + { + Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, Topology, OutConnection->Pin, bInput, &LineTerminator); + + if (Status == MM_STATUS_SUCCESS) + { + /* now add the rest of the source lines */ + Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, Topology, LineTerminator); + } + } + + /* free topology */ + //MMixerFreeTopology(Topology); return Status; } @@ -823,193 +928,139 @@ MMixerInitializeFilter( IN PMIXER_CONTEXT MixerContext, IN PMIXER_LIST MixerList, IN LPMIXER_DATA MixerData, - IN PKSMULTIPLE_ITEM NodeTypes, - IN PKSMULTIPLE_ITEM NodeConnections, - IN ULONG PinCount, + IN PTOPOLOGY Topology, IN ULONG NodeIndex, IN ULONG bInputMixer) { LPMIXER_INFO MixerInfo; MIXER_STATUS Status; PKSPIN_PHYSICALCONNECTION OutConnection; - ULONG Index; ULONG * Pins; - ULONG bUsed; - ULONG BytesReturned; - KSP_PIN Pin; - LPWSTR Buffer = NULL; - ULONG PinId; + ULONG PinsFound; - // allocate a mixer info struct + /* allocate a mixer info struct */ MixerInfo = (LPMIXER_INFO) MixerContext->Alloc(sizeof(MIXER_INFO)); if (!MixerInfo) { - // no memory + /* no memory */ return MM_STATUS_NO_MEMORY; } - // intialize mixer caps */ - MixerInfo->MixCaps.wMid = MM_MICROSOFT; //FIXME - MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; //FIXME - MixerInfo->MixCaps.vDriverVersion = 1; //FIXME + /* intialize mixer caps */ + MixerInfo->MixCaps.wMid = MM_MICROSOFT; /* FIXME */ + MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; /* FIXME */ + MixerInfo->MixCaps.vDriverVersion = 1; /* FIXME */ MixerInfo->MixCaps.fdwSupport = 0; MixerInfo->MixCaps.cDestinations = 1; MixerInfo->hMixer = MixerData->hDevice; - // get mixer name + /* get mixer name */ MMixerGetDeviceName(MixerContext, MixerInfo, MixerData->hDeviceInterfaceKey); - // initialize line list + /* initialize line list */ InitializeListHead(&MixerInfo->LineList); InitializeListHead(&MixerInfo->EventList); - // sanity check - ASSERT(PinCount); + /* now allocate an array which will receive the indices of the pin + * which has a ADC / DAC nodetype in its path + */ + Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins); + ASSERT(Status == MM_STATUS_SUCCESS); - // now allocate an array which will receive the indices of the pin - // which has a ADC / DAC nodetype in its path - Pins = (PULONG)MixerContext->Alloc(PinCount * sizeof(ULONG)); + PinsFound = 0; - if (!Pins) - { - // no memory - MMixerFreeMixerInfo(MixerContext, MixerInfo); - return MM_STATUS_NO_MEMORY; - } + /* now get all sink / source pins, which are attached to the ADC / DAC node + * For sink pins (wave out) search up stream + * For source pins (wave in) search down stream + * The search direction is always the opposite of the current mixer type + */ + PinsFound = 0; + MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, !bInputMixer, &PinsFound, Pins); - // now get the target pins of the ADC / DAC node - Status = MMixerGetTargetPins(MixerContext, NodeTypes, NodeConnections, NodeIndex, !bInputMixer, Pins, PinCount); - - // find a target pin with a name - PinId = PinCount +1; - for(Index = 0; Index < PinCount; Index++) - { - if (Pins[Index]) - { - // store index of pin - PinId = Index; - - /* retrieve pin name */ - Pin.PinId = Index; - Pin.Reserved = 0; - Pin.Property.Flags = KSPROPERTY_TYPE_GET; - Pin.Property.Set = KSPROPSETID_Pin; - Pin.Property.Id = KSPROPERTY_PIN_NAME; - - /* try get pin name size */ - Status = MixerContext->Control(MixerData->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned); - - if (Status == MM_STATUS_MORE_ENTRIES) - { - Buffer = (LPWSTR)MixerContext->Alloc(BytesReturned); - if (Buffer) - { - /* try get pin name */ - Status = MixerContext->Control(MixerData->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Buffer, BytesReturned, &BytesReturned); - if (Status != MM_STATUS_SUCCESS) - { - MixerContext->Free((PVOID)Buffer); - Buffer = NULL; - } - else - { - // found name, done - break; - } - } - } - } - } - - if (PinId < PinCount) - { - // create an wave info struct - MMixerInitializeWaveInfo(MixerContext, MixerList, MixerData, MixerInfo->MixCaps.szPname, bInputMixer, PinId); - } - - Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInputMixer, Buffer); - - if (Buffer) - { - // free name - MixerContext->Free(Buffer); - } + /* if there is now pin found, we have a broken topology */ + ASSERT(PinsFound != 0); + /* now create a wave info struct */ + Status = MMixerInitializeWaveInfo(MixerContext, MixerList, MixerData, MixerInfo->MixCaps.szPname, bInputMixer, PinsFound, Pins); if (Status != MM_STATUS_SUCCESS) { - // failed to create destination line + /* failed to create wave info struct */ MixerContext->Free(MixerInfo); MixerContext->Free(Pins); - return Status; } - RtlZeroMemory(Pins, sizeof(ULONG) * PinCount); - // now get the target pins of the ADC / DAC node - Status = MMixerGetTargetPins(MixerContext, NodeTypes, NodeConnections, NodeIndex, bInputMixer, Pins, PinCount); - - if (Status != MM_STATUS_SUCCESS) + if (bInputMixer) { - // failed to locate target pins - MixerContext->Free(Pins); - MMixerFreeMixerInfo(MixerContext, MixerInfo); - DPRINT("MMixerGetTargetPins failed with %u\n", Status); - return Status; - } + /* pre create the mixer destination line for input mixers */ + Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, Pins[0], bInputMixer); - // filter hasnt been used - bUsed = FALSE; - - // now check all pins and generate new lines for destination lines - for(Index = 0; Index < PinCount; Index++) - { - DPRINT("Index %lu TargetPin %lu\n", Index, Pins[Index]); - // is the current index a target pin - if (Pins[Index]) + if (Status != MM_STATUS_SUCCESS) { - // check if the pin has a physical connection - Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Index, &OutConnection); - if (Status == MM_STATUS_SUCCESS) - { - // the pin has a physical connection - Status = MMixerHandlePhysicalConnection(MixerContext, MixerList, MixerInfo, bInputMixer, OutConnection); - DPRINT("MMixerHandlePhysicalConnection status %u\n", Status); - MixerContext->Free(OutConnection); - bUsed = TRUE; - } - else - { - // filter exposes the topology on the same filter - MMixerAddMixerSourceLine(MixerContext, MixerInfo, MixerData->hDevice, NodeConnections, NodeTypes, Index, FALSE, FALSE); - bUsed = TRUE; - } + /* failed to create mixer destination line */ + return Status; } } + + + /* now get the bridge pin which is at the end of node path + * For sink pins (wave out) search down stream + * For source pins (wave in) search up stream + */ MixerContext->Free(Pins); + Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins); + ASSERT(Status == MM_STATUS_SUCCESS); - if (bUsed) + PinsFound = 0; + MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, bInputMixer, &PinsFound, Pins); + + /* if there is no pin found, we have a broken topology */ + ASSERT(PinsFound != 0); + + /* there should be exactly one bridge pin */ + ASSERT(PinsFound == 1); + + DPRINT("BridgePin %lu bInputMixer %lu\n", Pins[0], bInputMixer); + + /* does the pin have a physical connection */ + Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[0], &OutConnection); + + if (Status == MM_STATUS_SUCCESS) { - // store mixer info in list - if (!bInputMixer && MixerList->MixerListCount == 1) - { - //FIXME preferred device should be inserted at front - //windows always inserts output mixer in front - InsertHeadList(&MixerList->MixerList, &MixerInfo->Entry); - } - else - { - InsertTailList(&MixerList->MixerList, &MixerInfo->Entry); - } - MixerList->MixerListCount++; - DPRINT("New MixerCount %lu\n", MixerList->MixerListCount); + /* topology on the topoloy filter */ + Status = MMixerHandlePhysicalConnection(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, OutConnection); + + /* free physical connection data */ + MixerContext->Free(OutConnection); } else { - // failed to create a mixer topology - MMixerFreeMixerInfo(MixerContext, MixerInfo); + /* FIXME + * handle drivers which expose their topology on the same filter + */ + ASSERT(0); } - // done + /* free pins */ + MixerContext->Free(Pins); + + if (!bInputMixer && MixerList->MixerListCount == 1) + { + /* FIXME preferred device should be inserted at front + * windows always inserts output mixer in front + */ + InsertHeadList(&MixerList->MixerList, &MixerInfo->Entry); + } + else + { + /* insert at back */ + InsertTailList(&MixerList->MixerList, &MixerInfo->Entry); + } + + /* increment mixer count */ + MixerList->MixerListCount++; + + /* done */ return Status; } @@ -1020,71 +1071,64 @@ MMixerSetupFilter( IN LPMIXER_DATA MixerData, IN PULONG DeviceCount) { - PKSMULTIPLE_ITEM NodeTypes = NULL, NodeConnections = NULL; MIXER_STATUS Status; - ULONG PinCount; + PTOPOLOGY Topology; ULONG NodeIndex; - // get number of pins - PinCount = MMixerGetFilterPinCount(MixerContext, MixerData->hDevice); - ASSERT(PinCount); - DPRINT("NumOfPins: %lu\n", PinCount); - - // get filter node types - Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes); - if (Status != MM_STATUS_SUCCESS) + /* check if topology has already been built */ + if (MixerData->Topology == NULL) { - // failed - return Status; + /* build topology */ + Status = MMixerBuildTopology(MixerContext, MixerData, &Topology); + + if (Status != MM_STATUS_SUCCESS) + { + /* failed to build topology */ + return Status; + } + + /* store topology */ + MixerData->Topology = Topology; + } + else + { + /* re-use topology */ + Topology = MixerData->Topology; } - // get filter node connections - Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections); - if (Status != MM_STATUS_SUCCESS) - { - // failed - MixerContext->Free(NodeTypes); - return Status; - } - - // check if the filter has an wave out node - - NodeIndex = MMixerGetIndexOfGuid(NodeTypes, &KSNODETYPE_DAC); + /* check if the filter has an wave out node */ + NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_DAC); if (NodeIndex != MAXULONG) { - // it has - Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, NodeTypes, NodeConnections, PinCount, NodeIndex, FALSE); - DPRINT("MMixerInitializeFilter Status %u\n", Status); - // check for success + /* it has */ + Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, Topology, NodeIndex, FALSE); + + /* check for success */ if (Status == MM_STATUS_SUCCESS) { - // increment mixer count + /* increment mixer count */ (*DeviceCount)++; } } - // check if the filter has an wave in node - NodeIndex = MMixerGetIndexOfGuid(NodeTypes, &KSNODETYPE_ADC); + /* check if the filter has an wave in node */ + NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_ADC); if (NodeIndex != MAXULONG) { - // it has - Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, NodeTypes, NodeConnections, PinCount, NodeIndex, TRUE); - DPRINT("MMixerInitializeFilter Status %u\n", Status); - // check for success + /* it has */ + Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, Topology, NodeIndex, TRUE); + + /* check for success */ if (Status == MM_STATUS_SUCCESS) { - // increment mixer count + /* increment mixer count */ (*DeviceCount)++; } } - //free resources - MixerContext->Free((PVOID)NodeTypes); - MixerContext->Free((PVOID)NodeConnections); - - // done + /* done */ return Status; } @@ -1093,20 +1137,22 @@ MIXER_STATUS MMixerAddEvent( IN PMIXER_CONTEXT MixerContext, IN OUT LPMIXER_INFO MixerInfo, - IN ULONG NodeId) + IN PVOID MixerEventContext, + IN PMIXER_EVENT MixerEventRoutine) { - KSE_NODE Property; - LPEVENT_ITEM EventData; - ULONG BytesReturned; - MIXER_STATUS Status; + //KSE_NODE Property; + PEVENT_NOTIFICATION_ENTRY EventData; + //ULONG BytesReturned; + //MIXER_STATUS Status; - EventData = (LPEVENT_ITEM)MixerContext->AllocEventData(sizeof(LIST_ENTRY)); + EventData = (PEVENT_NOTIFICATION_ENTRY)MixerContext->AllocEventData(sizeof(EVENT_NOTIFICATION_ENTRY)); if (!EventData) { - // not enough memory + /* not enough memory */ return MM_STATUS_NO_MEMORY; } +#if 0 /* setup request */ Property.Event.Set = KSEVENTSETID_AudioControlChange; Property.Event.Flags = KSEVENT_TYPE_TOPOLOGY|KSEVENT_TYPE_ENABLE; @@ -1118,48 +1164,18 @@ MMixerAddEvent( Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_ENABLE_EVENT, (PVOID)&Property, sizeof(KSP_NODE), (PVOID)EventData, sizeof(KSEVENTDATA), &BytesReturned); if (Status != MM_STATUS_SUCCESS) { - // failed to add event + /* failed to add event */ MixerContext->FreeEventData(EventData); return Status; } +#endif - //store event + /* initialize notification entry */ + EventData->MixerEventContext = MixerEventContext; + EventData->MixerEventRoutine; + + /* store event */ InsertTailList(&MixerInfo->EventList, &EventData->Entry); - return Status; -} - -MIXER_STATUS -MMixerAddEvents( - IN PMIXER_CONTEXT MixerContext, - IN OUT LPMIXER_INFO MixerInfo) -{ - PKSMULTIPLE_ITEM NodeTypes; - ULONG Index; - MIXER_STATUS Status; - LPGUID Guid; - - // get filter node types - Status = MMixerGetFilterTopologyProperty(MixerContext, MixerInfo->hMixer, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes); - - if (Status != MM_STATUS_SUCCESS) - { - // failed - return Status; - } - - for(Index = 0; Index < NodeTypes->Count; Index++) - { - Guid = MMixerGetNodeType(NodeTypes, Index); - if (IsEqualGUID(&KSNODETYPE_VOLUME, Guid) || IsEqualGUID(&KSNODETYPE_MUTE, Guid)) - { - //add an event for volume / mute controls - //TODO: extra control types - MMixerAddEvent(MixerContext, MixerInfo, Index); - } - } - - // free node types - MixerContext->Free(NodeTypes); - return MM_STATUS_SUCCESS; } + diff --git a/lib/drivers/sound/mmixer/filter.c b/lib/drivers/sound/mmixer/filter.c index 955fd273ae9..9a2739e2992 100644 --- a/lib/drivers/sound/mmixer/filter.c +++ b/lib/drivers/sound/mmixer/filter.c @@ -19,15 +19,15 @@ MMixerGetFilterPinCount( MIXER_STATUS Status; ULONG NumPins, BytesReturned; - // setup property request + /* setup property request */ Pin.Flags = KSPROPERTY_TYPE_GET; Pin.Set = KSPROPSETID_Pin; Pin.Id = KSPROPERTY_PIN_CTYPES; - // query pin count + /* query pin count */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSPROPERTY), (PVOID)&NumPins, sizeof(ULONG), (PULONG)&BytesReturned); - // check for success + /* check for success */ if (Status != MM_STATUS_SUCCESS) return 0; @@ -46,43 +46,43 @@ MMixerGetFilterTopologyProperty( MIXER_STATUS Status; ULONG BytesReturned; - // setup property request + /* setup property request */ Property.Id = PropertyId; Property.Flags = KSPROPERTY_TYPE_GET; Property.Set = KSPROPSETID_Topology; - // query for the size + /* query for the size */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), NULL, 0, &BytesReturned); if (Status != MM_STATUS_MORE_ENTRIES) return Status; - //sanity check + /* sanity check */ ASSERT(BytesReturned); - // allocate an result buffer + /* allocate an result buffer */ MultipleItem = (PKSMULTIPLE_ITEM)MixerContext->Alloc(BytesReturned); if (!MultipleItem) { - // not enough memory + /* not enough memory */ return MM_STATUS_NO_MEMORY; } - // query again with allocated buffer + /* query again with allocated buffer */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), (PVOID)MultipleItem, BytesReturned, &BytesReturned); if (Status != MM_STATUS_SUCCESS) { - // failed + /* failed */ MixerContext->Free((PVOID)MultipleItem); return Status; } - // store result + /* store result */ *OutMultipleItem = MultipleItem; - // done + /* done */ return Status; } @@ -109,24 +109,23 @@ MMixerGetPhysicalConnection( if (Status == MM_STATUS_UNSUCCESSFUL) { - // pin does not have a physical connection + /* pin does not have a physical connection */ return Status; } DPRINT("Status %u BytesReturned %lu\n", Status, BytesReturned); Connection = (PKSPIN_PHYSICALCONNECTION)MixerContext->Alloc(BytesReturned); if (!Connection) { - // not enough memory + /* not enough memory */ return MM_STATUS_NO_MEMORY; } - // query the pin for the physical connection + /* query the pin for the physical connection */ Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Connection, BytesReturned, &BytesReturned); if (Status != MM_STATUS_SUCCESS) { - // failed to query the physical connection + /* failed to query the physical connection */ MixerContext->Free(Connection); - DPRINT("Status %u\n", Status); return Status; } @@ -141,73 +140,76 @@ MMixerGetControlTypeFromTopologyNode( { if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_AGC)) { - // automatic gain control + /* automatic gain control */ return MIXERCONTROL_CONTROLTYPE_ONOFF; } else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_LOUDNESS)) { - // loudness control + /* loudness control */ return MIXERCONTROL_CONTROLTYPE_LOUDNESS; } - else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUTE )) + else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUTE)) { - // mute control + /* mute control */ return MIXERCONTROL_CONTROLTYPE_MUTE; } else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_TONE)) { - // tpne control - //FIXME - // MIXERCONTROL_CONTROLTYPE_ONOFF if KSPROPERTY_AUDIO_BASS_BOOST is supported - // MIXERCONTROL_CONTROLTYPE_BASS if KSPROPERTY_AUDIO_BASS is supported - // MIXERCONTROL_CONTROLTYPE_TREBLE if KSPROPERTY_AUDIO_TREBLE is supported + /* tone control + * FIXME + * MIXERCONTROL_CONTROLTYPE_ONOFF if KSPROPERTY_AUDIO_BASS_BOOST is supported + * MIXERCONTROL_CONTROLTYPE_BASS if KSPROPERTY_AUDIO_BASS is supported + * MIXERCONTROL_CONTROLTYPE_TREBLE if KSPROPERTY_AUDIO_TREBLE is supported + */ UNIMPLEMENTED; return MIXERCONTROL_CONTROLTYPE_ONOFF; } else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_VOLUME)) { - // volume control + /* volume control */ return MIXERCONTROL_CONTROLTYPE_VOLUME; } else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_PEAKMETER)) { - // peakmeter control + /* peakmeter control */ return MIXERCONTROL_CONTROLTYPE_PEAKMETER; } else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUX)) { - // mux control + /* mux control */ return MIXERCONTROL_CONTROLTYPE_MUX; } else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUX)) { - // mux control + /* mux control */ return MIXERCONTROL_CONTROLTYPE_MUX; } else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_STEREO_WIDE)) { - // stero wide control + /* stero wide control */ return MIXERCONTROL_CONTROLTYPE_FADER; } else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_CHORUS)) { - // chorus control + /* chorus control */ return MIXERCONTROL_CONTROLTYPE_FADER; } else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_REVERB)) { - // reverb control + /* reverb control */ return MIXERCONTROL_CONTROLTYPE_FADER; } else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_SUPERMIX)) { - // supermix control - // MIXERCONTROL_CONTROLTYPE_MUTE if KSPROPERTY_AUDIO_MUTE is supported + /* supermix control + * MIXERCONTROL_CONTROLTYPE_MUTE if KSPROPERTY_AUDIO_MUTE is supported + */ UNIMPLEMENTED; return MIXERCONTROL_CONTROLTYPE_VOLUME; } - //TODO - //check for other supported node types + /* TODO + * check for other supported node types + */ //UNIMPLEMENTED return 0; } @@ -279,4 +281,3 @@ MMixerGetPinInstanceCount( ASSERT(Status == MM_STATUS_SUCCESS); return PinInstances.CurrentCount; } - diff --git a/lib/drivers/sound/mmixer/mixer.c b/lib/drivers/sound/mmixer/mixer.c index 4ef2ea9e62a..cd6b0a08a7d 100644 --- a/lib/drivers/sound/mmixer/mixer.c +++ b/lib/drivers/sound/mmixer/mixer.c @@ -17,16 +17,16 @@ MMixerGetCount( PMIXER_LIST MixerList; MIXER_STATUS Status; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } - // grab mixer list + /* grab mixer list */ MixerList = (PMIXER_LIST)MixerContext->MixerContext; // return number of mixers @@ -42,16 +42,16 @@ MMixerGetCapabilities( MIXER_STATUS Status; LPMIXER_INFO MixerInfo; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } - // get mixer info + /* get mixer info */ MixerInfo = MMixerGetMixerInfoByIndex(MixerContext, MixerIndex); if (!MixerInfo) @@ -76,36 +76,35 @@ MIXER_STATUS MMixerOpen( IN PMIXER_CONTEXT MixerContext, IN ULONG MixerId, - IN PVOID MixerEvent, + IN PVOID MixerEventContext, IN PMIXER_EVENT MixerEventRoutine, OUT PHANDLE MixerHandle) { MIXER_STATUS Status; LPMIXER_INFO MixerInfo; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } + /* get mixer info */ MixerInfo = (LPMIXER_INFO)MMixerGetMixerInfoByIndex(MixerContext, MixerId); if (!MixerInfo) { - // invalid mixer id + /* invalid mixer id */ return MM_STATUS_INVALID_PARAMETER; } - // FIXME - // handle event notification - - Status = MMixerAddEvents(MixerContext, MixerInfo); + /* add the event */ + Status = MMixerAddEvent(MixerContext, MixerInfo, MixerEventContext, MixerEventRoutine); - // store result + /* store result */ *MixerHandle = (HANDLE)MixerInfo; return MM_STATUS_SUCCESS; @@ -122,26 +121,26 @@ MMixerGetLineInfo( LPMIXER_INFO MixerInfo; LPMIXERLINE_EXT MixerLineSrc; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } - // clear hmixer from flags + /* clear hmixer from flags */ Flags &=~MIXER_OBJECTF_HMIXER; if (Flags == MIXER_GETLINEINFOF_DESTINATION) { - // cast to mixer info + /* cast to mixer info */ MixerInfo = (LPMIXER_INFO)MixerHandle; if (MixerLine->dwDestination != 0) { - // destination line member must be zero + /* destination line member must be zero */ return MM_STATUS_INVALID_PARAMETER; } @@ -153,7 +152,7 @@ MMixerGetLineInfo( } else if (Flags == MIXER_GETLINEINFOF_SOURCE) { - // cast to mixer info + /* cast to mixer info */ MixerInfo = (LPMIXER_INFO)MixerHandle; @@ -162,9 +161,9 @@ MMixerGetLineInfo( if (MixerLine->dwSource >= MixerLineSrc->Line.cConnections) { - DPRINT1("dwSource %u > Destinations %u\n", MixerLine->dwSource, MixerLineSrc->Line.cConnections); + DPRINT("dwSource %u > Destinations %u\n", MixerLine->dwSource, MixerLineSrc->Line.cConnections); - // invalid parameter + /* invalid parameter */ return MM_STATUS_INVALID_PARAMETER; } @@ -179,13 +178,13 @@ MMixerGetLineInfo( } else if (Flags == MIXER_GETLINEINFOF_LINEID) { - // cast to mixer info + /* cast to mixer info */ MixerInfo = (LPMIXER_INFO)MixerHandle; MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLine->dwLineID); if (!MixerLineSrc) { - // invalid parameter + /* invalid parameter */ return MM_STATUS_INVALID_PARAMETER; } @@ -195,7 +194,7 @@ MMixerGetLineInfo( } else if (Flags == MIXER_GETLINEINFOF_COMPONENTTYPE) { - // cast to mixer info + /* cast to mixer info */ MixerInfo = (LPMIXER_INFO)MixerHandle; MixerLineSrc = MMixerGetSourceMixerLineByComponentType(MixerInfo, MixerLine->dwComponentType); @@ -228,12 +227,12 @@ MMixerGetLineControls( MIXER_STATUS Status; ULONG Index; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } @@ -241,31 +240,31 @@ MMixerGetLineControls( if (Flags == MIXER_GETLINECONTROLSF_ALL) { - // cast to mixer info + /* cast to mixer info */ MixerInfo = (LPMIXER_INFO)MixerHandle; MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLineControls->dwLineID); if (!MixerLineSrc) { - // invalid line id + /* invalid line id */ return MM_STATUS_INVALID_PARAMETER; } - // copy line control(s) + /* copy line control(s) */ MixerContext->Copy(MixerLineControls->pamxctrl, MixerLineSrc->LineControls, min(MixerLineSrc->Line.cControls, MixerLineControls->cControls) * sizeof(MIXERCONTROLW)); return MM_STATUS_SUCCESS; } else if (Flags == MIXER_GETLINECONTROLSF_ONEBYTYPE) { - // cast to mixer info + /* cast to mixer info */ MixerInfo = (LPMIXER_INFO)MixerHandle; MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLineControls->dwLineID); if (!MixerLineSrc) { - // invalid line id + /* invalid line id */ return MM_STATUS_INVALID_PARAMETER; } @@ -277,7 +276,7 @@ MMixerGetLineControls( DPRINT("dwControlType %x\n", MixerLineSrc->LineControls[Index].dwControlType); if (MixerLineControls->dwControlType == MixerLineSrc->LineControls[Index].dwControlType) { - // found a control with that type + /* found a control with that type */ MixerContext->Copy(MixerLineControls->pamxctrl, &MixerLineSrc->LineControls[Index], sizeof(MIXERCONTROLW)); return MM_STATUS_SUCCESS; } @@ -287,18 +286,18 @@ MMixerGetLineControls( } else if (Flags == MIXER_GETLINECONTROLSF_ONEBYID) { - // cast to mixer info + /* cast to mixer info */ MixerInfo = (LPMIXER_INFO)MixerHandle; Status = MMixerGetMixerControlById(MixerInfo, MixerLineControls->dwControlID, NULL, &MixerControl, NULL); if (Status != MM_STATUS_SUCCESS) { - // invalid parameter + /* invalid parameter */ return MM_STATUS_INVALID_PARAMETER; } - // copy the controls + /* copy the controls */ MixerContext->Copy(MixerLineControls->pamxctrl, MixerControl, sizeof(MIXERCONTROLW)); return MM_STATUS_SUCCESS; } @@ -320,25 +319,25 @@ MMixerSetControlDetails( LPMIXERLINE_EXT MixerLine; LPMIXERCONTROLW MixerControl; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } - // get mixer info + /* get mixer info */ MixerInfo = (LPMIXER_INFO)MixerHandle; - // get mixer control + /* get mixer control */ Status = MMixerGetMixerControlById(MixerInfo, MixerControlDetails->dwControlID, &MixerLine, &MixerControl, &NodeId); - // check for success + /* check for success */ if (Status != MM_STATUS_SUCCESS) { - // failed to find control id + /* failed to find control id */ return MM_STATUS_INVALID_PARAMETER; } @@ -370,35 +369,35 @@ MMixerGetControlDetails( LPMIXERLINE_EXT MixerLine; LPMIXERCONTROLW MixerControl; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } - // get mixer info + /* get mixer info */ MixerInfo = (LPMIXER_INFO)MixerHandle; - // get mixer control + /* get mixer control */ Status = MMixerGetMixerControlById(MixerInfo, MixerControlDetails->dwControlID, &MixerLine, &MixerControl, &NodeId); - // check for success + /* check for success */ if (Status != MM_STATUS_SUCCESS) { - // failed to find control id + /* failed to find control id */ return MM_STATUS_INVALID_PARAMETER; } switch(MixerControl->dwControlType) { case MIXERCONTROL_CONTROLTYPE_MUTE: - Status = MMixerSetGetMuteControlDetails(MixerContext, MixerInfo->hMixer, NodeId, MixerLine->Line.dwLineID, MixerControlDetails, FALSE); + Status = MMixerSetGetMuteControlDetails(MixerContext, MixerInfo, NodeId, MixerLine->Line.dwLineID, MixerControlDetails, FALSE); break; case MIXERCONTROL_CONTROLTYPE_VOLUME: - Status = MMixerSetGetVolumeControlDetails(MixerContext, MixerInfo->hMixer, NodeId, FALSE, MixerControl, MixerControlDetails, MixerLine); + Status = MMixerSetGetVolumeControlDetails(MixerContext, MixerInfo, NodeId, FALSE, MixerControl, MixerControlDetails, MixerLine); break; default: Status = MM_STATUS_NOT_IMPLEMENTED; @@ -423,7 +422,7 @@ MMixerInitialize( if (!MixerContext || !EnumFunction || !EnumContext) { - // invalid parameter + /* invalid parameter */ return MM_STATUS_INVALID_PARAMETER; } @@ -431,19 +430,19 @@ MMixerInitialize( !MixerContext->AllocEventData || !MixerContext->FreeEventData || !MixerContext->Close || !MixerContext->OpenKey || !MixerContext->QueryKeyValue || !MixerContext->CloseKey) { - // invalid parameter + /* invalid parameter */ return MM_STATUS_INVALID_PARAMETER; } - // allocate a mixer list + /* allocate a mixer list */ MixerList = (PMIXER_LIST)MixerContext->Alloc(sizeof(MIXER_LIST)); if (!MixerList) { - // no memory + /* no memory */ return MM_STATUS_NO_MEMORY; } - //initialize mixer list + /* initialize mixer list */ MixerList->MixerListCount = 0; MixerList->MixerDataCount = 0; MixerList->WaveInListCount = 0; @@ -454,48 +453,49 @@ MMixerInitialize( InitializeListHead(&MixerList->WaveOutList); - // store mixer list + /* store mixer list */ MixerContext->MixerContext = (PVOID)MixerList; - // start enumerating all available devices + /* start enumerating all available devices */ Count = 0; DeviceIndex = 0; do { - // enumerate a device + /* enumerate a device */ Status = EnumFunction(EnumContext, DeviceIndex, &DeviceName, &hMixer, &hKey); if (Status != MM_STATUS_SUCCESS) { - //check error code + /* check error code */ if (Status == MM_STATUS_NO_MORE_DEVICES) { - // enumeration has finished + /* enumeration has finished */ break; } else { DPRINT1("Failed to enumerate device %lu\n", DeviceIndex); - // TODO cleanup + /* TODO cleanup */ return Status; } } else { - // create a mixer data entry + /* create a mixer data entry */ Status = MMixerCreateMixerData(MixerContext, MixerList, DeviceIndex, DeviceName, hMixer, hKey); if (Status != MM_STATUS_SUCCESS) break; } - // increment device index + /* increment device index */ DeviceIndex++; }while(TRUE); - //now all filters have been pre-opened - // lets enumerate the filters + /* now all filters have been pre-opened + * lets enumerate the filters + */ Entry = MixerList->MixerData.Flink; while(Entry != &MixerList->MixerData) { @@ -504,6 +504,6 @@ MMixerInitialize( Entry = Entry->Flink; } - // done + /* done */ return MM_STATUS_SUCCESS; } diff --git a/lib/drivers/sound/mmixer/mmixer.h b/lib/drivers/sound/mmixer/mmixer.h index 0cfc4acdc07..c7abbb69a52 100644 --- a/lib/drivers/sound/mmixer/mmixer.h +++ b/lib/drivers/sound/mmixer/mmixer.h @@ -48,7 +48,10 @@ typedef MIXER_STATUS(*PMIXER_CLOSEKEY)( IN HANDLE hKey); typedef VOID (*PMIXER_EVENT)( - IN PVOID MixerEvent); + IN PVOID MixerEventContext, + IN HANDLE hMixer, + IN ULONG NotificationType, + IN ULONG Value); typedef VOID (*PMIXER_COPY)( IN PVOID Dst, @@ -130,7 +133,7 @@ MIXER_STATUS MMixerOpen( IN PMIXER_CONTEXT MixerContext, IN ULONG MixerId, - IN PVOID MixerEvent, + IN PVOID MixerEventContext, IN PMIXER_EVENT MixerEventRoutine, OUT PHANDLE MixerHandle); diff --git a/lib/drivers/sound/mmixer/mmixer.rbuild b/lib/drivers/sound/mmixer/mmixer.rbuild index 582868ab1d6..21d65510061 100644 --- a/lib/drivers/sound/mmixer/mmixer.rbuild +++ b/lib/drivers/sound/mmixer/mmixer.rbuild @@ -8,4 +8,5 @@ mixer.c sup.c wave.c + topology.c diff --git a/lib/drivers/sound/mmixer/priv.h b/lib/drivers/sound/mmixer/priv.h index 1580c8a99a6..b854c2e70c2 100644 --- a/lib/drivers/sound/mmixer/priv.h +++ b/lib/drivers/sound/mmixer/priv.h @@ -17,11 +17,56 @@ #define YDEBUG #include +typedef struct __TOPOLOGY_NODE__ +{ + GUID NodeType; + ULONG NodeIndex; + + ULONG NodeConnectedToCount; + struct __TOPOLOGY_NODE__ ** NodeConnectedTo; + + ULONG NodeConnectedFromCount; + struct __TOPOLOGY_NODE__ ** NodeConnectedFrom; + + ULONG PinConnectedFromCount; + PULONG PinConnectedFrom; + + ULONG PinConnectedToCount; + PULONG PinConnectedTo; + + ULONG Visited; + ULONG Reserved; +}TOPOLOGY_NODE, *PTOPOLOGY_NODE; + typedef struct { - KSEVENTDATA EventData; - LIST_ENTRY Entry; -}EVENT_ITEM, *LPEVENT_ITEM; + ULONG PinId; + + ULONG NodesConnectedToCount; + PTOPOLOGY_NODE * NodesConnectedTo; + + ULONG NodesConnectedFromCount; + PTOPOLOGY_NODE * NodesConnectedFrom; + + ULONG PinConnectedFromCount; + PULONG PinConnectedFrom; + + ULONG PinConnectedToCount; + PULONG PinConnectedTo; + + ULONG Visited; +}PIN, *PPIN; + + +typedef struct +{ + ULONG TopologyPinsCount; + PPIN TopologyPins; + + ULONG TopologyNodesCount; + PTOPOLOGY_NODE TopologyNodes; + +}TOPOLOGY, *PTOPOLOGY; typedef struct { @@ -68,6 +113,7 @@ typedef struct HANDLE hDevice; HANDLE hDeviceInterfaceKey; LPWSTR DeviceName; + PTOPOLOGY Topology; }MIXER_DATA, *LPMIXER_DATA; typedef struct @@ -94,6 +140,14 @@ typedef struct LIST_ENTRY WaveOutList; }MIXER_LIST, *PMIXER_LIST; +typedef struct +{ + LIST_ENTRY Entry; + PVOID MixerEventContext; + PMIXER_EVENT MixerEventRoutine; + +}EVENT_NOTIFICATION_ENTRY, *PEVENT_NOTIFICATION_ENTRY; + #define DESTINATION_LINE 0xFFFF0000 ULONG @@ -215,7 +269,7 @@ MMixerGetMixerControlById( MIXER_STATUS MMixerSetGetMuteControlDetails( IN PMIXER_CONTEXT MixerContext, - IN HANDLE hMixer, + IN LPMIXER_INFO MixerInfo, IN ULONG NodeId, IN ULONG dwLineID, IN LPMIXERCONTROLDETAILS MixerControlDetails, @@ -224,7 +278,7 @@ MMixerSetGetMuteControlDetails( MIXER_STATUS MMixerSetGetVolumeControlDetails( IN PMIXER_CONTEXT MixerContext, - IN HANDLE hMixer, + IN LPMIXER_INFO MixerInfo, IN ULONG NodeId, IN ULONG bSet, LPMIXERCONTROLW MixerControl, @@ -273,9 +327,124 @@ MMixerInitializeWaveInfo( IN LPMIXER_DATA MixerData, IN LPWSTR DeviceName, IN ULONG bWaveIn, - IN ULONG PinId); + IN ULONG PinCount, + IN PULONG Pins); MIXER_STATUS -MMixerAddEvents( +MMixerAddEvent( IN PMIXER_CONTEXT MixerContext, - IN OUT LPMIXER_INFO MixerInfo); + IN OUT LPMIXER_INFO MixerInfo, + IN PVOID MixerEvent, + IN PMIXER_EVENT MixerEventRoutine); + +/* topology.c */ + +MIXER_STATUS +MMixerCreateTopology( + IN PMIXER_CONTEXT MixerContext, + IN ULONG PinCount, + IN PKSMULTIPLE_ITEM NodeConnections, + IN PKSMULTIPLE_ITEM NodeTypes, + OUT PTOPOLOGY *OutTopology); + +VOID +MMixerGetAllUpOrDownstreamPinsFromNodeIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + IN ULONG bUpStream, + OUT PULONG OutPinsCount, + OUT PULONG OutPins); + +MIXER_STATUS +MMixerGetAllUpOrDownstreamPinsFromPinIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG PinIndex, + IN ULONG bUpStream, + OUT PULONG OutPinsCount, + OUT PULONG OutPins); + +VOID +MMixerGetNextNodesFromPinIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG PinIndex, + IN ULONG bUpStream, + OUT PULONG OutNodesCount, + OUT PULONG OutNodes); + +MIXER_STATUS +MMixerAllocateTopologyPinArray( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + OUT PULONG * OutPins); + +MIXER_STATUS +MMixerAllocateTopologyNodeArray( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + OUT PULONG * OutPins); + +VOID +MMixerGetAllUpOrDownstreamNodesFromPinIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG PinIndex, + IN ULONG bUpStream, + OUT PULONG OutNodesCount, + OUT PULONG OutNodes); + +VOID +MMixerIsNodeTerminator( + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + OUT ULONG * bTerminator); + +VOID +MMixerGetNextNodesFromNodeIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + IN ULONG bUpStream, + OUT PULONG OutNodesCount, + OUT PULONG OutNodes); + +LPGUID +MMixerGetNodeTypeFromTopology( + IN PTOPOLOGY Topology, + IN ULONG NodeIndex); + +MIXER_STATUS +MMixerGetAllUpOrDownstreamNodesFromNodeIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + IN ULONG bUpStream, + OUT PULONG OutNodesCount, + OUT PULONG OutNodes); + +MIXER_STATUS +MMixerIsNodeConnectedToPin( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + IN ULONG PinId, + IN ULONG bUpStream, + OUT PULONG bConnected); + +ULONG +MMixerGetNodeIndexFromGuid( + IN PTOPOLOGY Topology, + IN const GUID *NodeType); + +VOID +MMixerSetTopologyNodeReserved( + IN PTOPOLOGY Topology, + IN ULONG NodeIndex); + +VOID +MMixerIsTopologyNodeReserved( + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + OUT PULONG bReserved); diff --git a/lib/drivers/sound/mmixer/sup.c b/lib/drivers/sound/mmixer/sup.c index e864d1db01d..a557cf97465 100644 --- a/lib/drivers/sound/mmixer/sup.c +++ b/lib/drivers/sound/mmixer/sup.c @@ -54,9 +54,10 @@ MMixerFreeMixerInfo( IN PMIXER_CONTEXT MixerContext, IN LPMIXER_INFO MixerInfo) { - //UNIMPLEMENTED - // FIXME - // free all lines + /* UNIMPLEMENTED + * FIXME + * free all lines + */ MixerContext->Free((PVOID)MixerInfo); } @@ -71,7 +72,7 @@ MMixerGetMixerInfoByIndex( PMIXER_LIST MixerList; ULONG Index = 0; - // get mixer list + /* get mixer list */ MixerList = (PMIXER_LIST)MixerContext->MixerContext; if (!MixerList->MixerListCount) @@ -86,7 +87,7 @@ MMixerGetMixerInfoByIndex( if (Index == MixerIndex) return MixerInfo; - // move to next mixer entry + /* move to next mixer entry */ Index++; Entry = Entry->Flink; } @@ -141,42 +142,6 @@ MMixerGetSourceMixerLineByLineId( return NULL; } -ULONG -MMixerGetIndexOfGuid( - PKSMULTIPLE_ITEM MultipleItem, - LPCGUID NodeType) -{ - ULONG Index; - LPGUID Guid; - - Guid = (LPGUID)(MultipleItem+1); - - /* iterate through node type array */ - for(Index = 0; Index < MultipleItem->Count; Index++) - { - if (IsEqualGUIDAligned(NodeType, Guid)) - { - /* found matching guid */ - return Index; - } - Guid++; - } - return MAXULONG; -} - -PKSTOPOLOGY_CONNECTION -MMixerGetConnectionByIndex( - IN PKSMULTIPLE_ITEM MultipleItem, - IN ULONG Index) -{ - PKSTOPOLOGY_CONNECTION Descriptor; - - ASSERT(Index < MultipleItem->Count); - - Descriptor = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1); - return &Descriptor[Index]; -} - LPGUID MMixerGetNodeType( IN PKSMULTIPLE_ITEM MultipleItem, @@ -190,183 +155,6 @@ MMixerGetNodeType( return &NodeType[Index]; } -MIXER_STATUS -MMixerGetNodeIndexes( - IN PMIXER_CONTEXT MixerContext, - IN PKSMULTIPLE_ITEM MultipleItem, - IN ULONG NodeIndex, - IN ULONG bNode, - IN ULONG bFrom, - OUT PULONG NodeReferenceCount, - OUT PULONG *NodeReference) -{ - ULONG Index, Count = 0; - PKSTOPOLOGY_CONNECTION Connection; - PULONG Refs; - - // KSMULTIPLE_ITEM is followed by several KSTOPOLOGY_CONNECTION - Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1); - - // first count all referenced nodes - for(Index = 0; Index < MultipleItem->Count; Index++) - { - if (bNode) - { - if (bFrom) - { - if (Connection->FromNode == NodeIndex) - { - // node id has a connection - Count++; - } - } - else - { - if (Connection->ToNode == NodeIndex) - { - // node id has a connection - Count++; - } - } - } - else - { - if (bFrom) - { - if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE) - { - // node id has a connection - Count++; - } - } - else - { - if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE) - { - // node id has a connection - Count++; - } - } - } - - - // move to next connection - Connection++; - } - - if (!Count) - { - *NodeReferenceCount = 0; - *NodeReference = NULL; - return MM_STATUS_SUCCESS; - } - - ASSERT(Count != 0); - - /* now allocate node index array */ - Refs = (PULONG)MixerContext->Alloc(sizeof(ULONG) * Count); - if (!Refs) - { - // not enough memory - return MM_STATUS_NO_MEMORY; - } - - Count = 0; - Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1); - for(Index = 0; Index < MultipleItem->Count; Index++) - { - if (bNode) - { - if (bFrom) - { - if (Connection->FromNode == NodeIndex) - { - /* node id has a connection */ - Refs[Count] = Index; - Count++; - } - } - else - { - if (Connection->ToNode == NodeIndex) - { - /* node id has a connection */ - Refs[Count] = Index; - Count++; - } - } - } - else - { - if (bFrom) - { - if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE) - { - /* node id has a connection */ - Refs[Count] = Index; - Count++; - } - } - else - { - if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE) - { - /* node id has a connection */ - Refs[Count] = Index; - Count++; - } - } - } - - /* move to next connection */ - Connection++; - } - - /* store result */ - *NodeReference = Refs; - *NodeReferenceCount = Count; - - return MM_STATUS_SUCCESS; -} - -MIXER_STATUS -MMixerGetTargetPins( - IN PMIXER_CONTEXT MixerContext, - IN PKSMULTIPLE_ITEM NodeTypes, - IN PKSMULTIPLE_ITEM NodeConnections, - IN ULONG NodeIndex, - IN ULONG bUpDirection, - OUT PULONG Pins, - IN ULONG PinCount) -{ - ULONG NodeConnectionCount, Index; - MIXER_STATUS Status; - PULONG NodeConnection; - - // sanity check */ - ASSERT(NodeIndex != (ULONG)-1); - - /* get all node indexes referenced by that pin */ - if (bUpDirection) - Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection); - else - Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection); - - //DPRINT("NodeIndex %u Status %x Count %u\n", NodeIndex, Status, NodeConnectionCount); - - if (Status == MM_STATUS_SUCCESS) - { - for(Index = 0; Index < NodeConnectionCount; Index++) - { - Status = MMixerGetTargetPinsByNodeConnectionIndex(MixerContext, NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], PinCount, Pins); - ASSERT(Status == STATUS_SUCCESS); - } - MixerContext->Free((PVOID)NodeConnection); - } - - return Status; -} - LPMIXERLINE_EXT MMixerGetSourceMixerLineByComponentType( LPMIXER_INFO MixerInfo, @@ -445,10 +233,38 @@ MMixerGetVolumeControlIndex( return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1); } +VOID +MMixerNotifyControlChange( + IN PMIXER_CONTEXT MixerContext, + IN LPMIXER_INFO MixerInfo, + IN ULONG NotificationType, + IN ULONG Value) +{ + PLIST_ENTRY Entry; + PEVENT_NOTIFICATION_ENTRY NotificationEntry; + + /* enumerate list and add a notification entry */ + Entry = MixerInfo->LineList.Flink; + while(Entry != &MixerInfo->EventList) + { + /* get notification entry offset */ + NotificationEntry = (PEVENT_NOTIFICATION_ENTRY)CONTAINING_RECORD(Entry, EVENT_NOTIFICATION_ENTRY, Entry); + + if (NotificationEntry->MixerEventRoutine) + { + /* now perform the callback */ + NotificationEntry->MixerEventRoutine(NotificationEntry->MixerEventContext, (HANDLE)MixerInfo, NotificationType, Value); + } + + /* move to next notification entry */ + Entry = Entry->Flink; + } +} + MIXER_STATUS MMixerSetGetMuteControlDetails( IN PMIXER_CONTEXT MixerContext, - IN HANDLE hMixer, + IN LPMIXER_INFO MixerInfo, IN ULONG NodeId, IN ULONG dwLineID, IN LPMIXERCONTROLDETAILS MixerControlDetails, @@ -469,7 +285,7 @@ MMixerSetGetMuteControlDetails( Value = Input->fValue; /* set control details */ - Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value); + Status = MMixerSetGetControlDetails(MixerContext, MixerInfo->hMixer, NodeId, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value); if (Status != MM_STATUS_SUCCESS) return Status; @@ -482,7 +298,8 @@ MMixerSetGetMuteControlDetails( } else { - // FIXME notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID + /* notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID */ + MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_LINE_CHANGE, dwLineID); } return Status; @@ -491,7 +308,7 @@ MMixerSetGetMuteControlDetails( MIXER_STATUS MMixerSetGetVolumeControlDetails( IN PMIXER_CONTEXT MixerContext, - IN HANDLE hMixer, + IN LPMIXER_INFO MixerInfo, IN ULONG NodeId, IN ULONG bSet, LPMIXERCONTROLW MixerControl, @@ -534,12 +351,12 @@ MMixerSetGetVolumeControlDetails( if (bSet) { /* TODO */ - Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value); - Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value); + Status = MMixerSetGetControlDetails(MixerContext, MixerInfo->hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value); + Status = MMixerSetGetControlDetails(MixerContext, MixerInfo->hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value); } else { - Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value); + Status = MMixerSetGetControlDetails(MixerContext, MixerInfo->hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value); } if (!bSet) @@ -551,6 +368,7 @@ MMixerSetGetVolumeControlDetails( else { /* notify clients of a line change MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */ + MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->dwControlID); } return Status; } @@ -590,7 +408,7 @@ MMixerGetDataByDeviceName( MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry); if (wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0) { - // found entry + /* found entry */ return MixerData; } Entry = Entry->Flink; @@ -617,6 +435,7 @@ MMixerCreateMixerData( MixerData->DeviceName = DeviceName; MixerData->hDevice = hDevice; MixerData->hDeviceInterfaceKey = hKey; + MixerData->Topology = NULL; InsertTailList(&MixerList->MixerData, &MixerData->Entry); MixerList->MixerDataCount++; @@ -638,16 +457,16 @@ MMixerGetDeviceName( Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type); if (Status == MM_STATUS_SUCCESS) { - // copy device name + /* copy device name */ MixerContext->Copy(MixerInfo->MixCaps.szPname, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR)); - // make sure its null terminated + /* make sure its null terminated */ MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] = L'\0'; - // free device name + /* free device name */ MixerContext->Free(Name); - // done + /* done */ return Status; } @@ -658,13 +477,13 @@ MMixerGetDeviceName( Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type); if (Status == MM_STATUS_SUCCESS) { - // copy device name + /* copy device name */ MixerContext->Copy(MixerInfo->MixCaps.szPname, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR)); - // make sure its null terminated + /* make sure its null terminated */ MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] = L'\0'; - // free device name + /* free device name */ MixerContext->Free(Name); } diff --git a/lib/drivers/sound/mmixer/topology.c b/lib/drivers/sound/mmixer/topology.c new file mode 100644 index 00000000000..6113d1b9cfd --- /dev/null +++ b/lib/drivers/sound/mmixer/topology.c @@ -0,0 +1,1212 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Kernel Streaming + * FILE: lib/drivers/sound/mmixer/topology.c + * PURPOSE: Topology Handling Functions + * PROGRAMMER: Johannes Anderwald + */ + +#include "priv.h" + +VOID +MMixerPrintTopology( + PTOPOLOGY Topology) +{ + ULONG Index, SubIndex; + + DPRINT1("Num Pins %lu NumNodes %lu\n", Topology->TopologyPinsCount, Topology->TopologyNodesCount); + + for(Index = 0; Index < Topology->TopologyPinsCount; Index++) + { + DPRINT1("PinId %lu NodesConnectedFromCount %lu NodesConnectedToCount %lu Visited %lu\n", Topology->TopologyPins[Index].PinId, + Topology->TopologyPins[Index].NodesConnectedFromCount, Topology->TopologyPins[Index].NodesConnectedToCount, Topology->TopologyPins[Index].Visited); + + for(SubIndex = 0; SubIndex < Topology->TopologyPins[Index].NodesConnectedFromCount; SubIndex++) + DPRINT1("NodesConnectedFrom Index %lu NodeId %lu\n", SubIndex, Topology->TopologyPins[Index].NodesConnectedFrom[SubIndex]->NodeIndex); + + for(SubIndex = 0; SubIndex < Topology->TopologyPins[Index].NodesConnectedToCount; SubIndex++) + DPRINT1("NodesConnectedTo Index %lu NodeId %lu\n", SubIndex, Topology->TopologyPins[Index].NodesConnectedTo[SubIndex]->NodeIndex); + } + + for(Index = 0; Index < Topology->TopologyNodesCount; Index++) + { + DPRINT1("NodeId %lu NodesConnectedFromCount %lu NodesConnectedToCount %lu Visited %lu PinConnectedFromCount %lu PinConnectedToCount %lu\n", Topology->TopologyNodes[Index].NodeIndex, + Topology->TopologyNodes[Index].NodeConnectedFromCount, Topology->TopologyNodes[Index].NodeConnectedToCount, Topology->TopologyNodes[Index].Visited, + Topology->TopologyNodes[Index].PinConnectedFromCount, Topology->TopologyNodes[Index].PinConnectedToCount); + } + + +} + + +MIXER_STATUS +MMixerAllocateTopology( + IN PMIXER_CONTEXT MixerContext, + IN ULONG NodesCount, + IN ULONG PinCount, + OUT PTOPOLOGY * OutTopology) +{ + PTOPOLOGY Topology; + + /* allocate topology */ + Topology = (PTOPOLOGY)MixerContext->Alloc(sizeof(TOPOLOGY)); + + if (!Topology) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + /* allocate topology pins */ + Topology->TopologyPins = (PPIN) MixerContext->Alloc(sizeof(PIN) * PinCount); + + if (!Topology->TopologyPins) + { + /* release memory */ + MixerContext->Free(Topology); + + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + /* allocate topology nodes */ + if (NodesCount) + { + Topology->TopologyNodes = (PTOPOLOGY_NODE) MixerContext->Alloc(sizeof(TOPOLOGY_NODE) * NodesCount); + + if (!Topology->TopologyNodes) + { + /* release memory */ + MixerContext->Free(Topology->TopologyPins); + MixerContext->Free(Topology); + + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + } + + /* initialize topology */ + Topology->TopologyPinsCount = PinCount; + Topology->TopologyNodesCount = NodesCount; + + /* store result */ + *OutTopology = Topology; + + /* done */ + return MM_STATUS_SUCCESS; +} + +VOID +MMixerResetTopologyVisitStatus( + IN OUT PTOPOLOGY Topology) +{ + ULONG Index; + + for(Index = 0; Index < Topology->TopologyNodesCount; Index++) + { + /* reset visited status */ + Topology->TopologyNodes[Index].Visited = FALSE; + } + + for(Index = 0; Index < Topology->TopologyPinsCount; Index++) + { + /* reset visited status */ + Topology->TopologyPins[Index].Visited = FALSE; + } +} + +VOID +MMixerInitializeTopologyNodes( + IN PMIXER_CONTEXT MixerContext, + IN PKSMULTIPLE_ITEM NodeTypes, + IN OUT PTOPOLOGY Topology) +{ + ULONG Index; + LPGUID Guids; + + /* sanity check */ + ASSERT(Topology->TopologyNodesCount == NodeTypes->Count); + + /* get topology node types */ + Guids = (LPGUID)(NodeTypes + 1); + + for(Index = 0; Index < Topology->TopologyNodesCount; Index++) + { + /* store node connection index */ + Topology->TopologyNodes[Index].NodeIndex = Index; + + /* store topology node type */ + MixerContext->Copy(&Topology->TopologyNodes[Index].NodeType, &Guids[Index], sizeof(GUID)); + } +} + +MIXER_STATUS +MMixerAddPinConnection( + IN PMIXER_CONTEXT MixerContext, + IN PPIN Pin, + IN PTOPOLOGY_NODE Node, + IN ULONG bPinToNode) +{ + ULONG Count; + PULONG NewPinsIndex, OldPinsIndex; + PTOPOLOGY_NODE * NewNodes, *OldNodes; + + if (bPinToNode) + { + /* get existing count */ + Count = Pin->NodesConnectedToCount; + OldNodes = Pin->NodesConnectedTo; + } + else + { + /* get existing count */ + Count = Pin->NodesConnectedFromCount; + OldNodes = Pin->NodesConnectedFrom; + } + + /* allocate new nodes array */ + NewNodes = MixerContext->Alloc(sizeof(PTOPOLOGY_NODE) * (Count + 1)); + + if (!NewNodes) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + if (Count) + { + /* copy existing nodes */ + MixerContext->Copy(NewNodes, OldNodes, sizeof(PTOPOLOGY) * Count); + + /* release old nodes array */ + MixerContext->Free(OldNodes); + } + + /* add new topology node */ + NewNodes[Count] = Node; + + if (bPinToNode) + { + /* replace old nodes array */ + Pin->NodesConnectedTo = NewNodes; + + /* increment nodes count */ + Pin->NodesConnectedToCount++; + + /* now enlarge PinConnectedFromCount*/ + Count = Node->PinConnectedFromCount; + + /* connected pin count for node */ + OldPinsIndex = Node->PinConnectedFrom; + } + else + { + /* replace old nodes array */ + Pin->NodesConnectedFrom = NewNodes; + + /* increment nodes count */ + Pin->NodesConnectedFromCount++; + + /* now enlarge PinConnectedFromCount*/ + Count = Node->PinConnectedToCount; + + /* connected pin count for node */ + OldPinsIndex = Node->PinConnectedTo; + } + + /* allocate pin connection index */ + NewPinsIndex = MixerContext->Alloc(sizeof(ULONG) * (Count + 1)); + + if (!NewPinsIndex) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + if (Count) + { + /* copy existing nodes */ + MixerContext->Copy(NewPinsIndex, OldPinsIndex, sizeof(ULONG) * Count); + + /* release old nodes array */ + MixerContext->Free(OldPinsIndex); + } + + /* add new topology node */ + NewPinsIndex[Count] = Pin->PinId; + + if (bPinToNode) + { + /* replace old nodes array */ + Node->PinConnectedFrom = NewPinsIndex; + + /* increment pin count */ + Node->PinConnectedFromCount++; + } + else + { + /* replace old nodes array */ + Node->PinConnectedTo = NewPinsIndex; + + /* increment pin count */ + Node->PinConnectedToCount++; + } + + /* done */ + return MM_STATUS_SUCCESS; +} + +MIXER_STATUS +MMixerHandleNodeToNodeConnection( + IN PMIXER_CONTEXT MixerContext, + IN PKSTOPOLOGY_CONNECTION Connection, + IN OUT PTOPOLOGY Topology) +{ + PTOPOLOGY_NODE InNode, OutNode; + PTOPOLOGY_NODE * NewNodes; + ULONG Count; + + /* sanity checks */ + ASSERT(Topology->TopologyNodesCount > Connection->ToNode); + ASSERT(Topology->TopologyNodesCount > Connection->FromNode); + + /* get node */ + InNode = &Topology->TopologyNodes[Connection->FromNode]; + OutNode = &Topology->TopologyNodes[Connection->ToNode]; + + /* get existing count */ + Count = OutNode->NodeConnectedFromCount; + + /* allocate new nodes array */ + NewNodes = MixerContext->Alloc(sizeof(PTOPOLOGY_NODE) * (Count + 1)); + + if (!NewNodes) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + if (Count) + { + /* copy existing nodes */ + MixerContext->Copy(NewNodes, OutNode->NodeConnectedFrom, sizeof(PTOPOLOGY) * Count); + + /* release old nodes array */ + MixerContext->Free(OutNode->NodeConnectedFrom); + } + + /* add new topology node */ + NewNodes[OutNode->NodeConnectedFromCount] = InNode; + + /* replace old nodes array */ + OutNode->NodeConnectedFrom = NewNodes; + + /* increment nodes count */ + OutNode->NodeConnectedFromCount++; + + /* get existing count */ + Count = InNode->NodeConnectedToCount; + + /* allocate new nodes array */ + NewNodes = MixerContext->Alloc(sizeof(PTOPOLOGY_NODE) * (Count + 1)); + + if (!NewNodes) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + if (Count) + { + /* copy existing nodes */ + MixerContext->Copy(NewNodes, InNode->NodeConnectedTo, sizeof(PTOPOLOGY) * Count); + + /* release old nodes array */ + MixerContext->Free(InNode->NodeConnectedTo); + } + + /* add new topology node */ + NewNodes[InNode->NodeConnectedToCount] = OutNode; + + /* replace old nodes array */ + InNode->NodeConnectedTo = NewNodes; + + /* increment nodes count */ + InNode->NodeConnectedToCount++; + + /* done */ + return MM_STATUS_SUCCESS; +} + +MIXER_STATUS +MMixerAddPinToPinConnection( + IN PMIXER_CONTEXT MixerContext, + IN OUT PPIN InPin, + IN OUT PPIN OutPin) +{ + ULONG Count; + PULONG NewPinsIndex; + + /* now enlarge PinConnectedTo */ + Count = InPin->PinConnectedToCount; + + /* allocate pin connection index */ + NewPinsIndex = MixerContext->Alloc(sizeof(ULONG) * (Count + 1)); + + if (!NewPinsIndex) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + if (Count) + { + /* copy existing nodes */ + MixerContext->Copy(NewPinsIndex, InPin->PinConnectedTo, sizeof(ULONG) * Count); + + /* release old nodes array */ + MixerContext->Free(InPin->PinConnectedTo); + } + + /* add new topology node */ + NewPinsIndex[Count] = OutPin->PinId; + + /* replace old nodes array */ + InPin->PinConnectedTo = NewPinsIndex; + + /* increment pin count */ + InPin->PinConnectedToCount++; + + /* now enlarge PinConnectedFrom */ + Count = OutPin->PinConnectedFromCount; + + /* allocate pin connection index */ + NewPinsIndex = MixerContext->Alloc(sizeof(ULONG) * (Count + 1)); + + if (!NewPinsIndex) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + if (Count) + { + /* copy existing nodes */ + MixerContext->Copy(NewPinsIndex, OutPin->PinConnectedFrom, sizeof(ULONG) * Count); + + /* release old nodes array */ + MixerContext->Free(OutPin->PinConnectedFrom); + } + + /* add new topology node */ + NewPinsIndex[Count] = InPin->PinId; + + /* replace old nodes array */ + OutPin->PinConnectedFrom = NewPinsIndex; + + /* increment pin count */ + OutPin->PinConnectedFromCount++; + + /* done */ + return MM_STATUS_SUCCESS; +} + +MIXER_STATUS +MMixerHandleNodePinConnection( + IN PMIXER_CONTEXT MixerContext, + IN PKSTOPOLOGY_CONNECTION Connection, + IN OUT PTOPOLOGY Topology) +{ + PPIN Pin; + PTOPOLOGY_NODE Node; + + /* check type */ + if (Connection->FromNode == KSFILTER_NODE && + Connection->ToNode == KSFILTER_NODE) + { + /* Pin -> Pin direction */ + + /* sanity checks */ + ASSERT(Topology->TopologyPinsCount > Connection->FromNodePin); + ASSERT(Topology->TopologyPinsCount > Connection->ToNodePin); + + /* add connection */ + return MMixerAddPinToPinConnection(MixerContext, + &Topology->TopologyPins[Connection->FromNodePin], + &Topology->TopologyPins[Connection->ToNodePin]); + + } + else if (Connection->FromNode == KSFILTER_NODE) + { + /* Pin -> Node direction */ + + /* sanity checks */ + ASSERT(Topology->TopologyPinsCount > Connection->FromNodePin); + ASSERT(Topology->TopologyNodesCount > Connection->ToNode); + ASSERT(Connection->ToNode != KSFILTER_NODE); + + /* get pin */ + Pin = &Topology->TopologyPins[Connection->FromNodePin]; + + /* get node */ + Node = &Topology->TopologyNodes[Connection->ToNode]; + + /* initialize pin */ + Pin->PinId = Connection->FromNodePin; + + /* mark as visited */ + Pin->Visited = TRUE; + Node->Visited = TRUE; + + /* add connection */ + return MMixerAddPinConnection(MixerContext, Pin, Node, TRUE); + } + else if (Connection->ToNode == KSFILTER_NODE) + { + /* Node -> Pin direction */ + + /* sanity checks */ + ASSERT(Topology->TopologyPinsCount > Connection->ToNodePin); + ASSERT(Topology->TopologyNodesCount > Connection->FromNode); + ASSERT(Connection->FromNode != KSFILTER_NODE); + + /* get pin */ + Pin = &Topology->TopologyPins[Connection->ToNodePin]; + + /* get node */ + Node = &Topology->TopologyNodes[Connection->FromNode]; + + /* initialize pin */ + Pin->PinId = Connection->ToNodePin; + + /* mark as visited */ + Pin->Visited = TRUE; + Node->Visited = TRUE; + + /* add connection */ + return MMixerAddPinConnection(MixerContext, Pin, Node, FALSE); + } + /* invalid call */ + ASSERT(0); + return MM_STATUS_INVALID_PARAMETER; +} + +MIXER_STATUS +MMixerExploreTopology( + IN PMIXER_CONTEXT MixerContext, + IN PKSMULTIPLE_ITEM NodeConnections, + IN PKSMULTIPLE_ITEM NodeTypes, + IN OUT PTOPOLOGY Topology) +{ + ULONG Index; + LPGUID Guids; + PKSTOPOLOGY_CONNECTION Connection; + MIXER_STATUS Status; + + /* sanity check */ + ASSERT(Topology->TopologyNodesCount == NodeTypes->Count); + + /* get topology node types */ + Guids = (LPGUID)(NodeTypes + 1); + + /* get node connections */ + Connection = (PKSTOPOLOGY_CONNECTION)(NodeConnections + 1); + + for(Index = 0; Index < NodeConnections->Count; Index++) + { + if (Connection[Index].FromNode == KSFILTER_NODE || + Connection[Index].ToNode == KSFILTER_NODE) + { + /* handle connection from Pin -> Node / Node->Pin */ + Status = MMixerHandleNodePinConnection(MixerContext, + &Connection[Index], + Topology); + + } + else + { + /* handle connection from Node -> Node */ + Status = MMixerHandleNodeToNodeConnection(MixerContext, + &Connection[Index], + Topology); + } + + if (Status != MM_STATUS_SUCCESS) + { + /* failed to handle connection */ + return Status; + } + } + + /* done */ + return MM_STATUS_SUCCESS; +} + +VOID +MMixerAddPinIndexToArray( + IN PMIXER_CONTEXT MixerContext, + IN ULONG PinId, + IN ULONG MaxPins, + OUT PULONG OutPinCount, + OUT PULONG OutPins) +{ + ULONG Index; + + for(Index = 0; Index < MaxPins; Index++) + { + if (OutPins[Index] != MAXULONG) + { + if (OutPins[Index] > PinId) + { + /* shift entries up */ + MixerContext->Copy(&OutPins[Index + 1], &OutPins[Index], (MaxPins - (Index + 1)) * sizeof(ULONG)); + + /* store pin id */ + OutPins[Index] = PinId; + + /* increment pin count */ + (*OutPinCount)++; + + /* done */ + return; + } + } + else + { + /* store pin id */ + OutPins[Index] = PinId; + + /* increment pin count */ + (*OutPinCount)++; + + /* done */ + return; + } + } +} + +VOID +MMixerGetUpOrDownStreamPins( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN PTOPOLOGY_NODE TopologyNode, + IN ULONG bUpStream, + OUT PULONG OutPinCount, + OUT PULONG OutPins) +{ + ULONG Index, TopologyNodesCount, PinsCount; + PTOPOLOGY_NODE *TopologyNodes; + PULONG Pins; + PPIN Pin; + + /* sanity check */ + ASSERT(TopologyNode->Visited == FALSE); + + if (bUpStream) + { + /* use pins to which a node is attached to */ + PinsCount = TopologyNode->PinConnectedFromCount; + Pins = TopologyNode->PinConnectedFrom; + + TopologyNodesCount = TopologyNode->NodeConnectedFromCount; + TopologyNodes = TopologyNode->NodeConnectedFrom; + } + else + { + /* use pins which are attached to a node */ + PinsCount = TopologyNode->PinConnectedToCount; + Pins = TopologyNode->PinConnectedTo; + + TopologyNodesCount = TopologyNode->NodeConnectedToCount; + TopologyNodes = TopologyNode->NodeConnectedTo; + } + + /* add all diretly connected pins */ + for(Index = 0; Index < PinsCount; Index++) + { + /* sanity check */ + ASSERT(Pins[Index] < Topology->TopologyPinsCount); + + /* get pin */ + Pin = &Topology->TopologyPins[Pins[Index]]; + + /* pin should not have been visited */ + ASSERT(Pin->Visited == FALSE); + ASSERT(Pins[Index] == Pin->PinId); + + /* FIXME support Pin -> Pin connections in iteration */ + if (bUpStream) + { + /* indicates a very broken topology Pin -> Pin -> Node <-... */ + ASSERT(Pin->PinConnectedFromCount == 0); + } + else + { + /* indicates a very broken topology -> Node -> Pin -> Pin */ + ASSERT(Pin->PinConnectedToCount == 0); + } + + /* add them to pin array */ + MMixerAddPinIndexToArray(MixerContext, Pin->PinId, Topology->TopologyPinsCount, OutPinCount, OutPins); + + /* mark pin as visited */ + Pin->Visited = TRUE; + } + + /* mark node as visited */ + TopologyNode->Visited = TRUE; + + /* now visit all connected nodes */ + for(Index = 0; Index < TopologyNodesCount; Index++) + { + /* recursively visit them */ + MMixerGetUpOrDownStreamPins(MixerContext, Topology, TopologyNodes[Index], bUpStream, OutPinCount, OutPins); + } + +} + +ULONG +MMixerGetNodeIndexFromGuid( + IN PTOPOLOGY Topology, + IN const GUID * NodeType) +{ + ULONG Index; + + for(Index = 0; Index < Topology->TopologyNodesCount; Index++) + { + if (IsEqualGUIDAligned(NodeType, &Topology->TopologyNodes[Index].NodeType)) + { + return Index; + } + } + + return MAXULONG; +} + + +VOID +MMixerGetAllUpOrDownstreamPinsFromNodeIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + IN ULONG bUpStream, + OUT PULONG OutPinsCount, + OUT PULONG OutPins) +{ + PTOPOLOGY_NODE TopologyNode; + + /* reset visited status */ + MMixerResetTopologyVisitStatus(Topology); + + /* sanity check */ + ASSERT(Topology->TopologyNodesCount > NodeIndex); + + /* get topology node */ + TopologyNode = &Topology->TopologyNodes[NodeIndex]; + + /* now visit all upstream pins & nodes */ + MMixerGetUpOrDownStreamPins(MixerContext, Topology, TopologyNode, bUpStream, OutPinsCount, OutPins); +} + +VOID +MMixerGetUpOrDownstreamNodes( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN PTOPOLOGY_NODE TopologyNode, + IN ULONG bUpStream, + OUT PULONG OutNodeCount, + OUT PULONG OutNodes) +{ + ULONG Index, TopologyNodesCount; + PTOPOLOGY_NODE Node, *TopologyNodes; + + if (bUpStream) + { + /* use nodes to which a node is attached to */ + TopologyNodesCount = TopologyNode->NodeConnectedFromCount; + TopologyNodes = TopologyNode->NodeConnectedFrom; + } + else + { + /* use nodes which are attached to a node */ + TopologyNodesCount = TopologyNode->NodeConnectedToCount; + TopologyNodes = TopologyNode->NodeConnectedTo; + } + + /* sanity check */ + ASSERT(TopologyNode->Visited == FALSE); + + /* add all connected nodes */ + for(Index = 0; Index < TopologyNodesCount; Index++) + { + /* get node */ + Node = TopologyNodes[Index]; + + /* node should not have been visited */ + ASSERT(Node->Visited == FALSE); + + /* add them to node array */ + MMixerAddPinIndexToArray(MixerContext, Node->NodeIndex, Topology->TopologyNodesCount, OutNodeCount, OutNodes); + + /* recursively visit them */ + MMixerGetUpOrDownstreamNodes(MixerContext, Topology, TopologyNodes[Index], bUpStream, OutNodeCount, OutNodes); + } + + /* mark node as visited */ + TopologyNode->Visited = TRUE; + +} + +MIXER_STATUS +MMixerGetAllUpOrDownstreamNodesFromNodeIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + IN ULONG bUpStream, + OUT PULONG OutNodesCount, + OUT PULONG OutNodes) +{ + PTOPOLOGY_NODE TopologyNode; + + /* reset visited status */ + MMixerResetTopologyVisitStatus(Topology); + + /* sanity check */ + ASSERT(Topology->TopologyNodesCount > NodeIndex); + + /* get topology node */ + TopologyNode = &Topology->TopologyNodes[NodeIndex]; + + /* now visit all upstream pins & nodes */ + MMixerGetUpOrDownstreamNodes(MixerContext, Topology, TopologyNode, bUpStream, OutNodesCount, OutNodes); + + /* done */ + return MM_STATUS_SUCCESS; + +} + +MIXER_STATUS +MMixerGetAllUpOrDownstreamPinsFromPinIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG PinIndex, + IN ULONG bUpStream, + OUT PULONG OutPinsCount, + OUT PULONG OutPins) +{ + ULONG Index, TopologyNodesCount, TopologyPinsCount; + PPIN Pin; + PTOPOLOGY_NODE *TopologyNodes; + PULONG TopologyPins; + + /* get pin */ + Pin = &Topology->TopologyPins[PinIndex]; + + if (bUpStream) + { + /* use nodes to which this pin is attached to */ + TopologyNodes = Pin->NodesConnectedFrom; + TopologyNodesCount = Pin->NodesConnectedFromCount; + + /* use pins to which this pin is attached to */ + TopologyPins = Pin->PinConnectedFrom; + TopologyPinsCount = Pin->PinConnectedFromCount; + + } + else + { + /* use nodes which are attached to a pin */ + TopologyNodes = Pin->NodesConnectedTo; + TopologyNodesCount = Pin->NodesConnectedToCount; + + /* use pins which are attached to this pin */ + TopologyPins = Pin->PinConnectedTo; + TopologyPinsCount = Pin->PinConnectedToCount; + } + + + /* reset visited status */ + MMixerResetTopologyVisitStatus(Topology); + + /* sanity check */ + ASSERT(Topology->TopologyPinsCount > PinIndex); + + /* add pins which are directly connected to this pin */ + for(Index = 0; Index < TopologyPinsCount; Index++) + { + /* add them to pin array */ + MMixerAddPinIndexToArray(MixerContext, TopologyPins[Index], Topology->TopologyPinsCount, OutPinsCount, OutPins); + } + + /* now visit all up / down stream pins & nodes */ + for(Index = 0; Index < TopologyNodesCount; Index++) + { + /* explore all connected pins with helper */ + MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, TopologyNodes[Index]->NodeIndex, bUpStream, OutPinsCount, OutPins); + } + + /* done */ + return MM_STATUS_SUCCESS; + +} + +VOID +MMixerGetAllUpOrDownstreamNodesFromPinIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG PinIndex, + IN ULONG bUpStream, + OUT PULONG OutNodesCount, + OUT PULONG OutNodes) +{ + ULONG Index, TopologyNodesCount; + PPIN Pin; + PTOPOLOGY_NODE *TopologyNodes; + + /* mark them as empty */ + *OutNodesCount = 0; + + /* get pin */ + Pin = &Topology->TopologyPins[PinIndex]; + + if (bUpStream) + { + /* use nodes to which a pin is attached to */ + TopologyNodes = Pin->NodesConnectedFrom; + TopologyNodesCount = Pin->NodesConnectedFromCount; + } + else + { + /* use nodes which are attached to a node */ + TopologyNodes = Pin->NodesConnectedTo; + TopologyNodesCount = Pin->NodesConnectedToCount; + } + + + /* reset visited status */ + MMixerResetTopologyVisitStatus(Topology); + + /* sanity check */ + ASSERT(Topology->TopologyPinsCount > PinIndex); + + /* now visit all up / down stream pins & nodes */ + for(Index = 0; Index < TopologyNodesCount; Index++) + { + /* add node to array */ + MMixerAddPinIndexToArray(MixerContext, TopologyNodes[Index]->NodeIndex, Topology->TopologyNodesCount, OutNodesCount, OutNodes); + + /* explore all connected nodes with helper */ + MMixerGetAllUpOrDownstreamNodesFromNodeIndex(MixerContext, Topology, TopologyNodes[Index]->NodeIndex, bUpStream, OutNodesCount, OutNodes); + } +} + + +VOID +MMixerGetNextNodesFromPinIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG PinIndex, + IN ULONG bUpStream, + OUT PULONG OutNodesCount, + OUT PULONG OutNodes) +{ + PPIN Pin; + TOPOLOGY_NODE **TopologyNodes; + ULONG TopologyNodesCount; + ULONG Index; + + /* sanity check */ + ASSERT(PinIndex < Topology->TopologyPinsCount); + + /* get pin */ + Pin = &Topology->TopologyPins[PinIndex]; + + if (bUpStream) + { + /* get up stream nodes */ + TopologyNodes = Pin->NodesConnectedFrom; + TopologyNodesCount = Pin->NodesConnectedFromCount; + } + else + { + /* get down stream nodes */ + TopologyNodes = Pin->NodesConnectedTo; + TopologyNodesCount = Pin->NodesConnectedToCount; + } + + /* store topology nodes ids */ + for(Index = 0; Index < TopologyNodesCount; Index++) + { + OutNodes[Index] = TopologyNodes[Index]->NodeIndex; + } + + /* store topology nodes count */ + *OutNodesCount = TopologyNodesCount; +} + +VOID +MMixerGetNextNodesFromNodeIndex( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + IN ULONG bUpStream, + OUT PULONG OutNodesCount, + OUT PULONG OutNodes) +{ + TOPOLOGY_NODE **TopologyNodes; + ULONG TopologyNodesCount; + ULONG Index; + + /* sanity check */ + ASSERT(NodeIndex < Topology->TopologyNodesCount); + + if (bUpStream) + { + /* get up stream nodes */ + TopologyNodes = Topology->TopologyNodes[NodeIndex].NodeConnectedFrom; + TopologyNodesCount = Topology->TopologyNodes[NodeIndex].NodeConnectedFromCount; + } + else + { + /* get down stream nodes */ + TopologyNodes = Topology->TopologyNodes[NodeIndex].NodeConnectedTo; + TopologyNodesCount = Topology->TopologyNodes[NodeIndex].NodeConnectedToCount; + } + + /* store topology nodes ids */ + for(Index = 0; Index < TopologyNodesCount; Index++) + { + OutNodes[Index] = TopologyNodes[Index]->NodeIndex; + } + + /* store topology nodes count */ + *OutNodesCount = TopologyNodesCount; +} + +MIXER_STATUS +MMixerAllocateTopologyPinArray( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + OUT PULONG * OutPins) +{ + PULONG Pins; + ULONG Index; + + /* sanity check */ + ASSERT(Topology->TopologyPinsCount != 0); + + /* allocate topology pins */ + Pins = MixerContext->Alloc(Topology->TopologyPinsCount * sizeof(ULONG)); + + if (!Pins) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + /* mark index as unused */ + for(Index = 0; Index < Topology->TopologyPinsCount; Index++) + Pins[Index] = MAXULONG; + + /* store result */ + *OutPins = Pins; + + /* done */ + return MM_STATUS_SUCCESS; +} + +MIXER_STATUS +MMixerAllocateTopologyNodeArray( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + OUT PULONG * OutNodes) +{ + PULONG Nodes; + ULONG Index; + + /* sanity check */ + ASSERT(Topology->TopologyNodesCount != 0); + + /* allocate topology pins */ + Nodes = MixerContext->Alloc(Topology->TopologyNodesCount * sizeof(ULONG)); + + if (!Nodes) + { + /* out of memory */ + return MM_STATUS_NO_MEMORY; + } + + /* mark index as unused */ + for(Index = 0; Index < Topology->TopologyNodesCount; Index++) + Nodes[Index] = MAXULONG; + + /* store result */ + *OutNodes = Nodes; + + /* done */ + return MM_STATUS_SUCCESS; +} + +VOID +MMixerIsNodeTerminator( + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + OUT ULONG * bTerminator) +{ + /* sanity check */ + ASSERT(NodeIndex < Topology->TopologyNodesCount); + + /* check if node has multiple parents */ + if (Topology->TopologyNodes[NodeIndex].NodeConnectedFromCount > 1) + { + /* node is connected to multiple other nodes */ + *bTerminator = TRUE; + + /* done */ + return; + } + + /* check if node is mux / sum node */ + if (IsEqualGUIDAligned(&Topology->TopologyNodes[NodeIndex].NodeType, &KSNODETYPE_SUM) || + IsEqualGUIDAligned(&Topology->TopologyNodes[NodeIndex].NodeType, &KSNODETYPE_MUX)) + { + /* classic terminator */ + *bTerminator = TRUE; + + /* done */ + return; + + } + + /* node is not a terminator */ + *bTerminator = FALSE; +} + +MIXER_STATUS +MMixerIsNodeConnectedToPin( + IN PMIXER_CONTEXT MixerContext, + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + IN ULONG PinId, + IN ULONG bUpStream, + OUT PULONG bConnected) +{ + MIXER_STATUS Status; + ULONG Index, PinsCount; + PULONG Pins; + + /* allocate pin index array */ + Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins); + + if (Status != MM_STATUS_SUCCESS) + { + /* failed to allocate */ + return Status; + } + + /* now get connected pins */ + PinsCount = 0; + MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, bUpStream, &PinsCount, Pins); + + /* set to false */ + *bConnected = FALSE; + + for(Index = 0; Index < PinsCount; Index++) + { + if (Pins[Index] == PinId) + { + /* pin is connected */ + *bConnected = TRUE; + break; + } + } + + /* free pin index array */ + MixerContext->Free(Pins); + + /* done */ + return MM_STATUS_SUCCESS; +} + +LPGUID +MMixerGetNodeTypeFromTopology( + IN PTOPOLOGY Topology, + IN ULONG NodeIndex) +{ + /* sanity check */ + ASSERT(NodeIndex < Topology->TopologyNodesCount); + + return &Topology->TopologyNodes[NodeIndex].NodeType; +} + +VOID +MMixerSetTopologyNodeReserved( + IN PTOPOLOGY Topology, + IN ULONG NodeIndex) +{ + /* sanity check */ + ASSERT(NodeIndex < Topology->TopologyNodesCount); + + /* set reserved */ + Topology->TopologyNodes[NodeIndex].Reserved = TRUE; +} + +VOID +MMixerIsTopologyNodeReserved( + IN PTOPOLOGY Topology, + IN ULONG NodeIndex, + OUT PULONG bReserved) +{ + /* sanity check */ + ASSERT(NodeIndex < Topology->TopologyNodesCount); + + /* get reserved status */ + *bReserved = Topology->TopologyNodes[NodeIndex].Reserved; +} + + +MIXER_STATUS +MMixerCreateTopology( + IN PMIXER_CONTEXT MixerContext, + IN ULONG PinCount, + IN PKSMULTIPLE_ITEM NodeConnections, + IN PKSMULTIPLE_ITEM NodeTypes, + OUT PTOPOLOGY *OutTopology) +{ + MIXER_STATUS Status; + PTOPOLOGY Topology; + + /* allocate topology */ + Status = MMixerAllocateTopology(MixerContext, NodeTypes->Count, PinCount, &Topology); + + if (Status != MM_STATUS_SUCCESS) + { + /* failed to allocate topology */ + return Status; + } + + /* initialize topology nodes */ + MMixerInitializeTopologyNodes(MixerContext, NodeTypes, Topology); + + /* explore topology */ + Status = MMixerExploreTopology(MixerContext, NodeConnections, NodeTypes, Topology); + + if (Status != MM_STATUS_SUCCESS) + { + /* failed to allocate topology */ + return Status; + } + + MMixerPrintTopology(Topology); + + /* store result */ + *OutTopology = Topology; + + /* done */ + return MM_STATUS_SUCCESS; +} + + + + + diff --git a/lib/drivers/sound/mmixer/wave.c b/lib/drivers/sound/mmixer/wave.c index af29f45bb14..c0d832af880 100644 --- a/lib/drivers/sound/mmixer/wave.c +++ b/lib/drivers/sound/mmixer/wave.c @@ -345,7 +345,8 @@ MMixerInitializeWaveInfo( IN LPMIXER_DATA MixerData, IN LPWSTR DeviceName, IN ULONG bWaveIn, - IN ULONG PinId) + IN ULONG PinCount, + IN PULONG Pins) { MIXER_STATUS Status; PKSMULTIPLE_ITEM MultipleItem; @@ -356,11 +357,18 @@ MMixerInitializeWaveInfo( if (!WaveInfo) return MM_STATUS_NO_MEMORY; + if (PinCount > 1) + { + /* FIXME support multiple pins for wave device */ + DPRINT1("Implement support for multiple pins\n"); + //ASSERT(PinCount == 1); + } + /* initialize wave info */ WaveInfo->DeviceId = MixerData->DeviceId; - WaveInfo->PinId = PinId; + WaveInfo->PinId = Pins[0]; - // sanity check + /* sanity check */ ASSERT(wcslen(DeviceName) < MAXPNAMELEN); /* copy device name */ @@ -388,7 +396,7 @@ MMixerInitializeWaveInfo( } /* get audio pin data ranges */ - Status = MMixerGetAudioPinDataRanges(MixerContext, MixerData->hDevice, PinId, &MultipleItem); + Status = MMixerGetAudioPinDataRanges(MixerContext, MixerData->hDevice, Pins[0], &MultipleItem); if (Status != MM_STATUS_SUCCESS) { /* failed to get audio pin data ranges */ @@ -451,21 +459,21 @@ MMixerOpenWave( LPWAVE_INFO WaveInfo; ACCESS_MASK DesiredAccess = 0; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } - // grab mixer list + /* grab mixer list */ MixerList = (PMIXER_LIST)MixerContext->MixerContext; if (WaveFormat->wFormatTag != WAVE_FORMAT_PCM) { - // not implemented + /* not implemented */ return MM_STATUS_NOT_IMPLEMENTED; } @@ -501,16 +509,16 @@ MMixerWaveInCapabilities( MIXER_STATUS Status; LPWAVE_INFO WaveInfo; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } - // grab mixer list + /* grab mixer list */ MixerList = (PMIXER_LIST)MixerContext->MixerContext; /* find destination wave */ @@ -521,7 +529,7 @@ MMixerWaveInCapabilities( return MM_STATUS_UNSUCCESSFUL; } - //copy capabilities + /* copy capabilities */ MixerContext->Copy(Caps, &WaveInfo->u.InCaps, sizeof(WAVEINCAPSW)); return MM_STATUS_SUCCESS; @@ -537,16 +545,16 @@ MMixerWaveOutCapabilities( MIXER_STATUS Status; LPWAVE_INFO WaveInfo; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } - // grab mixer list + /* grab mixer list */ MixerList = (PMIXER_LIST)MixerContext->MixerContext; /* find destination wave */ @@ -557,7 +565,7 @@ MMixerWaveOutCapabilities( return MM_STATUS_UNSUCCESSFUL; } - //copy capabilities + /* copy capabilities */ MixerContext->Copy(Caps, &WaveInfo->u.OutCaps, sizeof(WAVEOUTCAPSW)); return MM_STATUS_SUCCESS; @@ -570,16 +578,16 @@ MMixerGetWaveInCount( PMIXER_LIST MixerList; MIXER_STATUS Status; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed - return 0; + /* invalid context passed */ + return Status; } - // grab mixer list + /* grab mixer list */ MixerList = (PMIXER_LIST)MixerContext->MixerContext; return MixerList->WaveInListCount; @@ -592,16 +600,16 @@ MMixerGetWaveOutCount( PMIXER_LIST MixerList; MIXER_STATUS Status; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed - return 0; + /* invalid context passed */ + return Status; } - // grab mixer list + /* grab mixer list */ MixerList = (PMIXER_LIST)MixerContext->MixerContext; return MixerList->WaveOutListCount; @@ -637,16 +645,16 @@ MMixerGetWaveDevicePath( ULONG Length; MIXER_STATUS Status; - // verify mixer context + /* verify mixer context */ Status = MMixerVerifyContext(MixerContext); if (Status != MM_STATUS_SUCCESS) { - // invalid context passed + /* invalid context passed */ return Status; } - // grab mixer list + /* grab mixer list */ MixerList = (PMIXER_LIST)MixerContext->MixerContext; /* find destination wave */ diff --git a/lib/rtl/heap.c b/lib/rtl/heap.c index d3b0efce6d4..0e43ccbcf66 100644 --- a/lib/rtl/heap.c +++ b/lib/rtl/heap.c @@ -1638,6 +1638,23 @@ RtlSizeHeap( return ret; } +BOOLEAN NTAPI +RtlpValidateHeap(HEAP * Heap, BOOLEAN ForceValidation) +{ + return HEAP_IsRealArena(Heap, Heap->flags, NULL, TRUE); +} + +BOOLEAN NTAPI +RtlpValidateHeapHeaders(HEAP * Heap, BOOLEAN Recalculate) +{ + return TRUE; +} + +BOOLEAN NTAPI +RtlpValidateHeapEntry(HEAP * Heap, PVOID HeapEntry) +{ + return TRUE; +} /*********************************************************************** * RtlValidateHeap diff --git a/lib/rtl/heap.h b/lib/rtl/heap.h index 81500d1f77e..8291838c6de 100644 --- a/lib/rtl/heap.h +++ b/lib/rtl/heap.h @@ -54,6 +54,26 @@ /* Segment flags */ #define HEAP_USER_ALLOCATED 0x1 +/* A handy inline to distinguis normal heap, special "debug heap" and special "page heap" */ +FORCEINLINE BOOLEAN +RtlpHeapIsSpecial(ULONG Flags) +{ + if (Flags & HEAP_SKIP_VALIDATION_CHECKS) return FALSE; + + if (Flags & (HEAP_FLAG_PAGE_ALLOCS | + HEAP_VALIDATE_ALL_ENABLED | + HEAP_VALIDATE_PARAMETERS_ENABLED | + HEAP_CAPTURE_STACK_BACKTRACES | + HEAP_CREATE_ENABLE_TRACING)) + { + /* This is a special heap */ + return TRUE; + } + + /* No need for a special treatment */ + return FALSE; +} + /* Heap structures */ struct _HEAP_COMMON_ENTRY { @@ -178,6 +198,7 @@ typedef struct _HEAP USHORT SegmentAllocatorBackTraceIndex; USHORT Reserved; LIST_ENTRY UCRSegmentList; + ULONG Flags; ULONG ForceFlags; ULONG CompatibilityFlags; @@ -200,6 +221,7 @@ typedef struct _HEAP USHORT MaximumTagIndex; PHEAP_TAG_ENTRY TagEntries; LIST_ENTRY UCRList; + LIST_ENTRY UCRSegments; // FIXME: non-Vista ULONG AlignRound; ULONG AlignMask; LIST_ENTRY VirtualAllocdBlocks; @@ -252,6 +274,13 @@ typedef struct _HEAP_UCR_DESCRIPTOR ULONG Size; } HEAP_UCR_DESCRIPTOR, *PHEAP_UCR_DESCRIPTOR; +typedef struct _HEAP_UCR_SEGMENT +{ + LIST_ENTRY ListEntry; + SIZE_T ReservedSize; + SIZE_T CommittedSize; +} HEAP_UCR_SEGMENT, *PHEAP_UCR_SEGMENT; + typedef struct _HEAP_ENTRY_EXTRA { union @@ -293,7 +322,69 @@ RtlpCoalesceFreeBlocks (PHEAP Heap, PHEAP_ENTRY_EXTRA NTAPI RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry); +BOOLEAN NTAPI +RtlpValidateHeap(PHEAP Heap, BOOLEAN ForceValidation); + +BOOLEAN NTAPI +RtlpValidateHeapEntry(PHEAP Heap, PHEAP_ENTRY HeapEntry); + +BOOLEAN NTAPI +RtlpValidateHeapHeaders(PHEAP Heap, BOOLEAN Recalculate); + /* heapdbg.c */ +HANDLE NTAPI +RtlDebugCreateHeap(ULONG Flags, + PVOID Addr, + SIZE_T TotalSize, + SIZE_T CommitSize, + PVOID Lock, + PRTL_HEAP_PARAMETERS Parameters); + +BOOLEAN NTAPI +RtlDebugDestroyHeap(HANDLE HeapPtr); + +PVOID NTAPI +RtlDebugAllocateHeap(PVOID HeapPtr, + ULONG Flags, + SIZE_T Size); + +PVOID NTAPI +RtlDebugReAllocateHeap(HANDLE HeapPtr, + ULONG Flags, + PVOID Ptr, + SIZE_T Size); + +BOOLEAN NTAPI +RtlDebugFreeHeap(HANDLE HeapPtr, + ULONG Flags, + PVOID Ptr); + +BOOLEAN NTAPI +RtlDebugGetUserInfoHeap(PVOID HeapHandle, + ULONG Flags, + PVOID BaseAddress, + PVOID *UserValue, + PULONG UserFlags); + +BOOLEAN NTAPI +RtlDebugSetUserValueHeap(PVOID HeapHandle, + ULONG Flags, + PVOID BaseAddress, + PVOID UserValue); + +BOOLEAN +NTAPI +RtlDebugSetUserFlagsHeap(PVOID HeapHandle, + ULONG Flags, + PVOID BaseAddress, + ULONG UserFlagsReset, + ULONG UserFlagsSet); + +SIZE_T NTAPI +RtlDebugSizeHeap(HANDLE HeapPtr, + ULONG Flags, + PVOID Ptr); + HANDLE NTAPI RtlpPageHeapCreate(ULONG Flags, PVOID Addr, diff --git a/lib/rtl/heap_rewrite.c b/lib/rtl/heap_rewrite.c index fd3bf047b0c..2c28b6a76d7 100644 --- a/lib/rtl/heap_rewrite.c +++ b/lib/rtl/heap_rewrite.c @@ -104,7 +104,7 @@ RtlpInitializeHeap(PHEAP Heap, /* Prepare a list of UCRs */ InitializeListHead(&Heap->UCRList); - InitializeListHead(&Heap->UCRSegmentList); + InitializeListHead(&Heap->UCRSegments); UcrDescriptor = NextHeapBase; for (i=0; iHeap; + SIZE_T ReserveSize = 16 * PAGE_SIZE; + SIZE_T CommitSize = 1 * PAGE_SIZE; + NTSTATUS Status; DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment); /* Check if we have unused UCRs */ if (IsListEmpty(&Heap->UCRList)) { - ASSERT(FALSE); + /* Get a pointer to the first UCR segment */ + UcrSegment = CONTAINING_RECORD(&Heap->UCRSegments.Flink, HEAP_UCR_SEGMENT, ListEntry); + + /* Check the list of UCR segments */ + if (IsListEmpty(&Heap->UCRSegments) || + UcrSegment->ReservedSize == UcrSegment->CommittedSize) + { + /* We need to create a new one. Reserve 16 pages for it */ + UcrSegment = NULL; + Status = ZwAllocateVirtualMemory(NtCurrentProcess(), + (PVOID *)&UcrSegment, + 0, + &ReserveSize, + MEM_RESERVE, + PAGE_READWRITE); + + if (!NT_SUCCESS(Status)) return NULL; + + /* Commit one page */ + Status = ZwAllocateVirtualMemory(NtCurrentProcess(), + (PVOID *)&UcrSegment, + 0, + &CommitSize, + MEM_COMMIT, + PAGE_READWRITE); + + if (!NT_SUCCESS(Status)) + { + /* Release reserved memory */ + ZwFreeVirtualMemory(NtCurrentProcess(), + (PVOID *)&UcrDescriptor, + &ReserveSize, + MEM_RELEASE); + return NULL; + } + + /* Set it's data */ + UcrSegment->ReservedSize = ReserveSize; + UcrSegment->CommittedSize = CommitSize; + + /* Add it to the head of the list */ + InsertHeadList(&Heap->UCRSegments, &UcrSegment->ListEntry); + + /* Get a pointer to the first available UCR descriptor */ + UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)(UcrSegment + 1); + } + else + { + /* It's possible to use existing UCR segment. Commit one more page */ + UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)((PCHAR)UcrSegment + UcrSegment->CommittedSize); + Status = ZwAllocateVirtualMemory(NtCurrentProcess(), + (PVOID *)&UcrDescriptor, + 0, + &CommitSize, + MEM_COMMIT, + PAGE_READWRITE); + + if (!NT_SUCCESS(Status)) return NULL; + + /* Update sizes */ + UcrSegment->CommittedSize += CommitSize; + } + + /* There is a whole bunch of new UCR descriptors. Put them into the unused list */ + while ((PCHAR)UcrDescriptor < ((PCHAR)UcrSegment + UcrSegment->CommittedSize)) + { + InsertTailList(&Heap->UCRList, &UcrDescriptor->ListEntry); + UcrDescriptor++; + } } /* There are unused UCRs, just get the first one */ @@ -506,9 +578,10 @@ PHEAP_FREE_ENTRY NTAPI RtlpFindAndCommitPages(PHEAP Heap, PHEAP_SEGMENT Segment, PSIZE_T Size, - PVOID Address) + PVOID AddressRequested) { PLIST_ENTRY Current; + ULONG_PTR Address = 0; PHEAP_UCR_DESCRIPTOR UcrDescriptor, PreviousUcr = NULL; PHEAP_ENTRY FirstEntry, LastEntry, PreviousLastEntry; NTSTATUS Status; @@ -523,20 +596,20 @@ RtlpFindAndCommitPages(PHEAP Heap, /* Check if we can use that one right away */ if (UcrDescriptor->Size >= *Size && - (UcrDescriptor->Address == Address || !Address)) + (UcrDescriptor->Address == AddressRequested || !AddressRequested)) { /* Get the address */ - Address = UcrDescriptor->Address; + Address = (ULONG_PTR)UcrDescriptor->Address; /* Commit it */ if (Heap->CommitRoutine) { - Status = Heap->CommitRoutine(Heap, &Address, Size); + Status = Heap->CommitRoutine(Heap, (PVOID *)&Address, Size); } else { Status = ZwAllocateVirtualMemory(NtCurrentProcess(), - &Address, + (PVOID *)&Address, 0, Size, MEM_COMMIT, @@ -597,7 +670,7 @@ RtlpFindAndCommitPages(PHEAP Heap, LastEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY; /* Update UCR descriptor */ - UcrDescriptor->Address = (PUCHAR)UcrDescriptor->Address + *Size; + UcrDescriptor->Address = (PVOID)((ULONG_PTR)UcrDescriptor->Address + *Size); UcrDescriptor->Size -= *Size; DPRINT("Updating UcrDescriptor %p, new Address %p, size %d\n", @@ -644,6 +717,7 @@ RtlpFindAndCommitPages(PHEAP Heap, } /* Advance to the next descriptor */ + PreviousUcr = UcrDescriptor; Current = Current->Flink; } @@ -655,15 +729,16 @@ RtlpDeCommitFreeBlock(PHEAP Heap, PHEAP_FREE_ENTRY FreeEntry, SIZE_T Size) { -#if 0 PHEAP_SEGMENT Segment; PHEAP_ENTRY PrecedingInUseEntry = NULL, NextInUseEntry = NULL; PHEAP_FREE_ENTRY NextFreeEntry; PHEAP_UCR_DESCRIPTOR UcrDescriptor; ULONG PrecedingSize, NextSize, DecommitSize; - ULONG DecommitBase; + ULONG_PTR DecommitBase; NTSTATUS Status; + DPRINT("Decommitting %p %p %x\n", Heap, FreeEntry, Size); + /* We can't decommit if there is a commit routine! */ if (Heap->CommitRoutine) { @@ -692,7 +767,7 @@ RtlpDeCommitFreeBlock(PHEAP Heap, } /* Get the next entry */ - NextFreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + (Size >> HEAP_ENTRY_SHIFT)); + NextFreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + Size); DecommitSize = ROUND_DOWN(NextFreeEntry, PAGE_SIZE); NextSize = (PHEAP_ENTRY)NextFreeEntry - (PHEAP_ENTRY)DecommitSize; @@ -708,14 +783,17 @@ RtlpDeCommitFreeBlock(PHEAP Heap, NextInUseEntry = (PHEAP_ENTRY)NextFreeEntry; } - NextFreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)NextFreeEntry - NextSize); + NextFreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)NextFreeEntry - NextSize); + /* Calculate real decommit size */ if (DecommitSize > DecommitBase) + { DecommitSize -= DecommitBase; + } else { /* Nothing to decommit */ - RtlpInsertFreeBlock(Heap, FreeEntry, PrecedingSize); + RtlpInsertFreeBlock(Heap, FreeEntry, Size); return; } @@ -739,7 +817,7 @@ RtlpDeCommitFreeBlock(PHEAP Heap, if (!NT_SUCCESS(Status)) { - RtlpInsertFreeBlock(Heap, FreeEntry, PrecedingSize); + RtlpInsertFreeBlock(Heap, FreeEntry, Size); return; } @@ -753,18 +831,22 @@ RtlpDeCommitFreeBlock(PHEAP Heap, FreeEntry->Flags = HEAP_ENTRY_LAST_ENTRY; FreeEntry->Size = PrecedingSize; Heap->TotalFreeSize += PrecedingSize; + + /* Set last entry in the segment to this entry */ Segment->LastEntryInSegment = (PHEAP_ENTRY)FreeEntry; - RtlpInsertFreeBlockHelper(Heap, FreeEntry, PrecedingSize); + + /* Insert it into the free list */ + RtlpInsertFreeBlockHelper(Heap, FreeEntry, PrecedingSize, FALSE); } - else if (NextInUseEntry) + else if (PrecedingInUseEntry) { /* Adjust preceding in use entry */ PrecedingInUseEntry->Flags |= HEAP_ENTRY_LAST_ENTRY; Segment->LastEntryInSegment = PrecedingInUseEntry; - } - else if ((Segment->LastEntryInSegment >= (PHEAP_ENTRY)DecommitBase)) + } else if ((ULONG_PTR)Segment->LastEntryInSegment >= DecommitBase && + ((PCHAR)Segment->LastEntryInSegment < ((PCHAR)DecommitBase + DecommitSize))) { - /* Adjust last entry in the segment */ + /* Update this segment's last entry */ Segment->LastEntryInSegment = Segment->FirstEntry; } @@ -779,16 +861,13 @@ RtlpDeCommitFreeBlock(PHEAP Heap, ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)NextFreeEntry + NextSize))->PreviousSize = NextSize; - Heap->TotalFreeSize += PrecedingSize; - RtlpInsertFreeBlockHelper(Heap, NextFreeEntry, NextSize); + Heap->TotalFreeSize += NextSize; + RtlpInsertFreeBlockHelper(Heap, NextFreeEntry, NextSize, FALSE); } else if (NextInUseEntry) { NextInUseEntry->PreviousSize = 0; } -#else - RtlpInsertFreeBlock(Heap, FreeEntry, Size); -#endif } BOOLEAN NTAPI @@ -1047,7 +1126,7 @@ RtlpCoalesceFreeBlocks (PHEAP Heap, /* Advance FreeEntry and update sizes */ FreeEntry = CurrentEntry; - *FreeSize += CurrentEntry->Size; + *FreeSize = *FreeSize + CurrentEntry->Size; Heap->TotalFreeSize -= CurrentEntry->Size; FreeEntry->Size = *FreeSize; @@ -1086,7 +1165,7 @@ RtlpCoalesceFreeBlocks (PHEAP Heap, RtlpRemoveFreeBlock(Heap, NextEntry, FALSE, FALSE); /* Update sizes */ - *FreeSize += NextEntry->Size; + *FreeSize = *FreeSize + NextEntry->Size; Heap->TotalFreeSize -= NextEntry->Size; FreeEntry->Size = *FreeSize; @@ -1397,23 +1476,9 @@ RtlCreateHeap(ULONG Flags, TotalSize = ROUND_UP(CommitSize, 16 * PAGE_SIZE); } - if (RtlpGetMode() == UserMode) - { - /* TODO: Here should be a call to special "Debug" heap, which does parameters validation, - however we're just going to simulate setting correct flags here */ - if (Flags & (HEAP_VALIDATE_ALL_ENABLED | - HEAP_VALIDATE_PARAMETERS_ENABLED | - HEAP_CAPTURE_STACK_BACKTRACES | - HEAP_FLAG_PAGE_ALLOCS | - HEAP_CREATE_ENABLE_TRACING) && - !(Flags & HEAP_SKIP_VALIDATION_CHECKS)) - { - // RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters); - Flags |= HEAP_SKIP_VALIDATION_CHECKS | - HEAP_TAIL_CHECKING_ENABLED | - HEAP_FREE_CHECKING_ENABLED; - } - } + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters); /* Calculate header size */ HeaderSize = sizeof(HEAP); @@ -1648,7 +1713,7 @@ RtlDestroyHeap(HANDLE HeapPtr) /* [in] Handle of heap */ { PHEAP Heap = (PHEAP)HeapPtr; PLIST_ENTRY Current; - PHEAP_UCR_DESCRIPTOR UcrDescriptor; + PHEAP_UCR_SEGMENT UcrSegment; PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry; PVOID BaseAddress; SIZE_T Size; @@ -1657,7 +1722,11 @@ RtlDestroyHeap(HANDLE HeapPtr) /* [in] Handle of heap */ if (!HeapPtr) return NULL; - // TODO: Check for special heap + /* Call special heap */ + if (RtlpHeapIsSpecial(Heap->Flags)) + { + if (!RtlDebugDestroyHeap(Heap)) return HeapPtr; + } /* Check for a process heap */ if (RtlpGetMode() == UserMode && @@ -1695,27 +1764,23 @@ RtlDestroyHeap(HANDLE HeapPtr) /* [in] Handle of heap */ Heap->LockVariable = NULL; } - /* Go through heap's global uncommitted ranges list and free them */ - DPRINT1("HEAP: Freeing segment's UCRs is not yet implemented!\n"); - Current = Heap->UCRSegmentList.Flink; - while(Current != &Heap->UCRSegmentList) + /* Free UCR segments if any were created */ + Current = Heap->UCRSegments.Flink; + while(Current != &Heap->UCRSegments) { - UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, ListEntry); - - if (UcrDescriptor) - { - BaseAddress = UcrDescriptor->Address; - Size = 0; - - /* Release that memory */ - ZwFreeVirtualMemory(NtCurrentProcess(), - &BaseAddress, - &Size, - MEM_RELEASE); - } + UcrSegment = CONTAINING_RECORD(Current, HEAP_UCR_SEGMENT, ListEntry); /* Advance to the next descriptor */ Current = Current->Flink; + + BaseAddress = (PVOID)UcrSegment; + Size = 0; + + /* Release that memory */ + ZwFreeVirtualMemory(NtCurrentProcess(), + &BaseAddress, + &Size, + MEM_RELEASE); } /* Go through segments and destroy them */ @@ -2028,6 +2093,10 @@ RtlAllocateHeap(IN PVOID HeapPtr, /* Force flags */ Flags |= Heap->ForceFlags; + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugAllocateHeap(Heap, Flags, Size); + /* Check for the maximum size */ if (Size >= 0x80000000) { @@ -2036,12 +2105,8 @@ RtlAllocateHeap(IN PVOID HeapPtr, return NULL; } - if (Flags & ( - HEAP_VALIDATE_ALL_ENABLED | - HEAP_VALIDATE_PARAMETERS_ENABLED | - HEAP_FLAG_PAGE_ALLOCS | - HEAP_CREATE_ENABLE_TRACING | - HEAP_CREATE_ALIGN_16)) + if (Flags & (HEAP_CREATE_ENABLE_TRACING | + HEAP_CREATE_ALIGN_16)) { DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags); } @@ -2254,6 +2319,10 @@ BOOLEAN NTAPI RtlFreeHeap( Heap = (PHEAP)HeapPtr; Flags |= Heap->ForceFlags; + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugFreeHeap(Heap, Flags, Ptr); + /* Lock if necessary */ if (!(Flags & HEAP_NO_SERIALIZE)) { @@ -2658,7 +2727,9 @@ RtlReAllocateHeap(HANDLE HeapPtr, /* Force heap flags */ Flags |= Heap->ForceFlags; - // Check for special heap + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugReAllocateHeap(Heap, Flags, Ptr, Size); /* Make sure size is valid */ if (Size >= 0x80000000) @@ -3000,7 +3071,7 @@ RtlReAllocateHeap(HANDLE HeapPtr, if (Size > OldSize && (Flags & HEAP_ZERO_MEMORY)) { - RtlZeroMemory((PCHAR)NewBaseAddress + OldSize, Size - OldSize ); + RtlZeroMemory((PCHAR)NewBaseAddress + OldSize, Size - OldSize); } /* Free the old block */ @@ -3145,9 +3216,11 @@ RtlSizeHeap( } /* Force flags */ - Flags |= Heap->Flags; + Flags |= Heap->ForceFlags; - // FIXME Special heap + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugSizeHeap(Heap, Flags, Ptr); /* Get the heap entry pointer */ HeapEntry = (PHEAP_ENTRY)Ptr - 1; @@ -3701,7 +3774,7 @@ RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine, */ ULONG NTAPI RtlGetProcessHeaps(ULONG count, - HANDLE *heaps ) + HANDLE *heaps) { UNIMPLEMENTED; return 0; @@ -3750,6 +3823,10 @@ RtlSetUserValueHeap(IN PVOID HeapHandle, /* Force flags */ Flags |= Heap->Flags; + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugSetUserValueHeap(Heap, Flags, BaseAddress, UserValue); + /* Lock if it's lockable */ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) { @@ -3805,6 +3882,10 @@ RtlSetUserFlagsHeap(IN PVOID HeapHandle, /* Force flags */ Flags |= Heap->Flags; + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugSetUserFlagsHeap(Heap, Flags, BaseAddress, UserFlagsReset, UserFlagsSet); + /* Lock if it's lockable */ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) { @@ -3857,6 +3938,10 @@ RtlGetUserInfoHeap(IN PVOID HeapHandle, /* Force flags */ Flags |= Heap->Flags; + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugGetUserInfoHeap(Heap, Flags, BaseAddress, UserValue, UserFlags); + /* Lock if it's lockable */ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) { diff --git a/lib/rtl/heapdbg.c b/lib/rtl/heapdbg.c index 242dfacaf9b..b8d68bd31eb 100644 --- a/lib/rtl/heapdbg.c +++ b/lib/rtl/heapdbg.c @@ -22,6 +22,535 @@ WCHAR RtlpPageHeapTargetDlls[512]; /* FUNCTIONS ******************************************************************/ +HANDLE NTAPI +RtlDebugCreateHeap(ULONG Flags, + PVOID Addr, + SIZE_T ReserveSize, + SIZE_T CommitSize, + PVOID Lock, + PRTL_HEAP_PARAMETERS Parameters) +{ + MEMORY_BASIC_INFORMATION MemoryInfo; + NTSTATUS Status; + PHEAP Heap; + + /* Validate parameters */ + if (ReserveSize <= HEAP_ENTRY_SIZE) + { + DPRINT1("HEAP: Incorrect ReserveSize %x\n", ReserveSize); + return NULL; + } + + if (ReserveSize < CommitSize) + { + DPRINT1("HEAP: Incorrect CommitSize %x\n", CommitSize); + return NULL; + } + + if (Flags & HEAP_NO_SERIALIZE && Lock) + { + DPRINT1("HEAP: Can't specify Lock routine and have HEAP_NO_SERIALIZE flag set\n"); + return NULL; + } + + /* If the address is specified, check it's virtual memory */ + if (Addr) + { + Status = ZwQueryVirtualMemory(NtCurrentProcess(), + Addr, + MemoryBasicInformation, + &MemoryInfo, + sizeof(MemoryInfo), + NULL); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("HEAP: Specified heap base address %p is invalid, Status 0x%08X\n", Addr, Status); + return NULL; + } + + if (MemoryInfo.BaseAddress != Addr) + { + DPRINT1("HEAP: Specified heap base address %p is not really a base one %p\n", Addr, MemoryInfo.BaseAddress); + return NULL; + } + + if (MemoryInfo.State == MEM_FREE) + { + DPRINT1("HEAP: Specified heap base address %p is free\n", Addr); + return NULL; + } + } + + /* All validation performed, now call the real routine with skip validation check flag */ + Flags |= HEAP_SKIP_VALIDATION_CHECKS | + HEAP_TAIL_CHECKING_ENABLED | + HEAP_FREE_CHECKING_ENABLED; + + Heap = RtlCreateHeap(Flags, Addr, ReserveSize, CommitSize, Lock, Parameters); + if (!Heap) return NULL; + + // FIXME: Capture stack backtrace + + RtlpValidateHeapHeaders(Heap, TRUE); + + return Heap; +} + +BOOLEAN NTAPI +RtlDebugDestroyHeap(HANDLE HeapPtr) +{ + SIZE_T Size; + PHEAP Heap = (PHEAP)HeapPtr; + + if (Heap == RtlGetCurrentPeb()->ProcessHeap) + { + DPRINT1("HEAP: It's forbidden delete process heap!"); + return FALSE; + } + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + if (!RtlpValidateHeap(Heap, FALSE)) return FALSE; + + /* Make heap invalid by zeroing its signature */ + Heap->Signature = 0; + + /* Free validate headers copy if it was existing */ + if (Heap->HeaderValidateCopy) + { + ZwFreeVirtualMemory(NtCurrentProcess(), + &Heap->HeaderValidateCopy, + &Size, + MEM_RELEASE); + } + + return TRUE; +} + +PVOID NTAPI +RtlDebugAllocateHeap(PVOID HeapPtr, + ULONG Flags, + SIZE_T Size) +{ + PHEAP Heap = (PHEAP)HeapPtr; + SIZE_T AllocSize = 1; + BOOLEAN HeapLocked = FALSE; + PVOID Result; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlpPageHeapAllocateHeap(HeapPtr, Flags, Size); + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return NULL; + } + + /* Add settable user value flag */ + Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS; + + /* Calculate size */ + if (Size) AllocSize = Size; + AllocSize = ((AllocSize + Heap->AlignRound) & Heap->AlignMask) + sizeof(HEAP_ENTRY_EXTRA); + + /* Check if size didn't exceed max one */ + if (AllocSize < Size || + AllocSize > Heap->MaximumAllocationSize) + { + DPRINT1("HEAP: Too big allocation size %x (max allowed %x)\n", Size, Heap->MaximumAllocationSize); + return NULL; + } + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Call main routine to do the stuff */ + Result = RtlAllocateHeap(HeapPtr, Flags, Size); + + /* Validate heap headers */ + RtlpValidateHeapHeaders(Heap, TRUE); + + if (Result) + { + if (Heap->Flags & HEAP_VALIDATE_ALL_ENABLED) + RtlpValidateHeap(Heap, FALSE); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; +} + +PVOID NTAPI +RtlDebugReAllocateHeap(HANDLE HeapPtr, + ULONG Flags, + PVOID Ptr, + SIZE_T Size) +{ + PHEAP Heap = (PHEAP)HeapPtr; + SIZE_T AllocSize = 1; + BOOLEAN HeapLocked = FALSE; + PVOID Result = NULL; + PHEAP_ENTRY HeapEntry; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlpPageHeapReAllocateHeap(HeapPtr, Flags, Size); + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return NULL; + } + + /* Add settable user value flag */ + Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS; + + /* Calculate size */ + if (Size) AllocSize = Size; + AllocSize = ((AllocSize + Heap->AlignRound) & Heap->AlignMask) + sizeof(HEAP_ENTRY_EXTRA); + + /* Check if size didn't exceed max one */ + if (AllocSize < Size || + AllocSize > Heap->MaximumAllocationSize) + { + DPRINT1("HEAP: Too big allocation size %x (max allowed %x)\n", Size, Heap->MaximumAllocationSize); + return NULL; + } + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)Ptr - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* Call main routine to do the stuff */ + Result = RtlReAllocateHeap(HeapPtr, Flags, Ptr, Size); + + if (Result) + { + /* Validate heap headers and then heap itself */ + RtlpValidateHeapHeaders(Heap, TRUE); + RtlpValidateHeap(Heap, FALSE); + } + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; +} + +BOOLEAN NTAPI +RtlDebugFreeHeap(HANDLE HeapPtr, + ULONG Flags, + PVOID Ptr) +{ + PHEAP Heap = (PHEAP)HeapPtr; + BOOLEAN HeapLocked = FALSE; + PHEAP_ENTRY HeapEntry; + BOOLEAN Result = FALSE; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlpPageHeapFreeHeap(HeapPtr, Flags, Size); + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + /* Add skip validation flag */ + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)Ptr - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* If it succeeded - call the main routine */ + Result = RtlFreeHeap(HeapPtr, Flags, Ptr); + + /* Validate heap headers and then heap itself */ + RtlpValidateHeapHeaders(Heap, TRUE); + RtlpValidateHeap(Heap, FALSE); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; +} + +BOOLEAN NTAPI +RtlDebugGetUserInfoHeap(PVOID HeapHandle, + ULONG Flags, + PVOID BaseAddress, + PVOID *UserValue, + PULONG UserFlags) +{ + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN HeapLocked = FALSE; + PHEAP_ENTRY HeapEntry; + BOOLEAN Result = FALSE; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlpPageHeapGetUserInfoHeap(HeapPtr, Flags, Size); + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + /* Add skip validation flag */ + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)BaseAddress - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* If it succeeded - call the main routine */ + Result = RtlGetUserInfoHeap(HeapHandle, Flags, BaseAddress, UserValue, UserFlags); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; +} + +BOOLEAN NTAPI +RtlDebugSetUserValueHeap(PVOID HeapHandle, + ULONG Flags, + PVOID BaseAddress, + PVOID UserValue) +{ + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN HeapLocked = FALSE; + PHEAP_ENTRY HeapEntry; + BOOLEAN Result = FALSE; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlpPageHeapSetUserValueHeap(HeapPtr, Flags, Size); + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + /* Add skip validation flag */ + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)BaseAddress - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* If it succeeded - call the main routine */ + Result = RtlSetUserValueHeap(HeapHandle, Flags, BaseAddress, UserValue); + + /* Validate the heap */ + RtlpValidateHeap(Heap, FALSE); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; +} + +BOOLEAN +NTAPI +RtlDebugSetUserFlagsHeap(PVOID HeapHandle, + ULONG Flags, + PVOID BaseAddress, + ULONG UserFlagsReset, + ULONG UserFlagsSet) +{ + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN HeapLocked = FALSE; + PHEAP_ENTRY HeapEntry; + BOOLEAN Result = FALSE; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlPageHeapSetUserFlagsHeap(HeapPtr, Flags, BaseAddress, UserFlagsReset, UserFlagsSet); + + /* Check if this heap allows flags to be set at all */ + if (UserFlagsSet & ~HEAP_SETTABLE_USER_FLAGS || + UserFlagsReset & ~HEAP_SETTABLE_USER_FLAGS) + { + return FALSE; + } + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + /* Add skip validation flag */ + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)BaseAddress - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* If it succeeded - call the main routine */ + Result = RtlSetUserFlagsHeap(HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet); + + /* Validate the heap */ + RtlpValidateHeap(Heap, FALSE); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; +} + +SIZE_T NTAPI +RtlDebugSizeHeap(HANDLE HeapPtr, + ULONG Flags, + PVOID Ptr) +{ + PHEAP Heap = (PHEAP)HeapPtr; + BOOLEAN HeapLocked = FALSE; + PHEAP_ENTRY HeapEntry; + SIZE_T Result = ~(SIZE_T)0; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlPageHeapSizeHeap(HeapPtr, Flags, Ptr); + + /* Check heap signature */ + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + /* Add skip validation flag */ + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)Ptr - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* If it succeeded - call the main routine */ + Result = RtlSizeHeap(HeapPtr, Flags, Ptr); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; +} + + +// Page heap -> move to another file + HANDLE NTAPI RtlpPageHeapCreate(ULONG Flags, PVOID Addr, diff --git a/lib/rtl/ppb.c b/lib/rtl/ppb.c index 868f170b989..39fb9213ee9 100644 --- a/lib/rtl/ppb.c +++ b/lib/rtl/ppb.c @@ -53,9 +53,7 @@ RtlCreateProcessParameters(PRTL_USER_PROCESS_PARAMETERS *ProcessParameters, PUNICODE_STRING ShellInfo, PUNICODE_STRING RuntimeData) { - NTSTATUS Status = STATUS_SUCCESS; PRTL_USER_PROCESS_PARAMETERS Param = NULL; - SIZE_T RegionSize = 0; ULONG Length = 0; PWCHAR Dest; UNICODE_STRING EmptyString; @@ -121,23 +119,16 @@ RtlCreateProcessParameters(PRTL_USER_PROCESS_PARAMETERS *ProcessParameters, Length += ALIGN(RuntimeData->MaximumLength, sizeof(ULONG)); /* Calculate the required block size */ - RegionSize = ROUND_UP(Length, PAGE_SIZE); - - Status = ZwAllocateVirtualMemory(NtCurrentProcess(), - (PVOID*)&Param, - 0, - &RegionSize, - MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE); - if (!NT_SUCCESS(Status)) + Param = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Length); + if (!Param) { RtlReleasePebLock(); - return Status; + return STATUS_INSUFFICIENT_RESOURCES; } DPRINT ("Process parameters allocated\n"); - Param->MaximumLength = RegionSize; + Param->MaximumLength = Length; Param->Length = Length; Param->Flags = RTL_USER_PROCESS_PARAMETERS_NORMALIZED; Param->Environment = Environment; @@ -219,15 +210,12 @@ RtlCreateProcessParameters(PRTL_USER_PROCESS_PARAMETERS *ProcessParameters, /* * @implemented */ -NTSTATUS NTAPI -RtlDestroyProcessParameters(PRTL_USER_PROCESS_PARAMETERS ProcessParameters) +NTSTATUS +NTAPI +RtlDestroyProcessParameters(IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters) { - SIZE_T RegionSize = 0; - - return ZwFreeVirtualMemory (NtCurrentProcess (), - (PVOID)ProcessParameters, - &RegionSize, - MEM_RELEASE); + RtlFreeHeap(RtlGetProcessHeap(), 0, ProcessParameters); + return STATUS_SUCCESS; } /* diff --git a/lib/sdk/crt/math/powf.c b/lib/sdk/crt/math/powf.c new file mode 100644 index 00000000000..55eb92a2e3f --- /dev/null +++ b/lib/sdk/crt/math/powf.c @@ -0,0 +1,11 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#include + +float powf(float x, float y) +{ + return (float)pow((double)x, (double)y); +} diff --git a/media/doc/README.WINE b/media/doc/README.WINE index bc990508b44..d03bb636129 100644 --- a/media/doc/README.WINE +++ b/media/doc/README.WINE @@ -188,7 +188,7 @@ reactos/dll/win32/xmllite # Autosync ReactOS shares the following programs with Winehq. reactos/base/applications/cmdutils/xcopy # Autosync -reactos/base/applications/games/winemine # Out of sync +reactos/base/applications/games/winmine # Forked at Wine-1_3_5 reactos/base/applications/extrac32 # Autosync reactos/base/applications/iexplore # Autosync reactos/base/applications/notepad # Forked at Wine-20041201 diff --git a/ntoskrnl/cc/copy.c b/ntoskrnl/cc/copy.c index 628f1dcbed8..8e19ba82d36 100644 --- a/ntoskrnl/cc/copy.c +++ b/ntoskrnl/cc/copy.c @@ -41,7 +41,7 @@ CcInitCacheZeroPage(VOID) { NTSTATUS Status; - Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &CcZeroPage); + Status = MmRequestPageMemoryConsumer(MC_SYSTEM, TRUE, &CcZeroPage); if (!NT_SUCCESS(Status)) { DbgPrint("Can't allocate CcZeroPage.\n"); diff --git a/ntoskrnl/ex/init.c b/ntoskrnl/ex/init.c index 650de252895..5b448efbbf1 100644 --- a/ntoskrnl/ex/init.c +++ b/ntoskrnl/ex/init.c @@ -14,6 +14,14 @@ #include #include "ntstrsafe.h" +/* Temporary hack */ +BOOLEAN +NTAPI +MmArmInitSystem( + IN ULONG Phase, + IN PLOADER_PARAMETER_BLOCK LoaderBlock +); + typedef struct _INIT_BUFFER { WCHAR DebugBuffer[256]; @@ -1045,7 +1053,7 @@ ExpInitializeExecutive(IN ULONG Cpu, if (!ExInitSystem()) KeBugCheck(PHASE0_INITIALIZATION_FAILED); /* Initialize the memory manager at phase 0 */ - if (!MmInitSystem(0, LoaderBlock)) KeBugCheck(PHASE0_INITIALIZATION_FAILED); + if (!MmArmInitSystem(0, LoaderBlock)) KeBugCheck(PHASE0_INITIALIZATION_FAILED); /* Load boot symbols */ ExpLoadBootSymbols(LoaderBlock); @@ -1577,7 +1585,7 @@ Phase1InitializationDiscard(IN PVOID Context) if (!MmInitSystem(1, LoaderBlock)) KeBugCheck(MEMORY1_INITIALIZATION_FAILED); /* Create NLS section */ - ExpInitNls(KeLoaderBlock); + ExpInitNls(LoaderBlock); /* Initialize Cache Views */ if (!CcInitializeCacheManager()) KeBugCheck(CACHE_INITIALIZATION_FAILED); @@ -1862,8 +1870,8 @@ Phase1InitializationDiscard(IN PVOID Context) NtClose(OptionHandle); } - /* Unmap Low memory, and initialize the MPW and Balancer Thread */ - MmInitSystem(2, LoaderBlock); + /* FIXME: This doesn't do anything for now */ + MmArmInitSystem(2, LoaderBlock); /* Update progress bar */ InbvUpdateProgressBar(80); diff --git a/ntoskrnl/ex/sysinfo.c b/ntoskrnl/ex/sysinfo.c index ee585576967..483c92057ea 100644 --- a/ntoskrnl/ex/sysinfo.c +++ b/ntoskrnl/ex/sysinfo.c @@ -547,8 +547,7 @@ QSI_DEF(SystemPerformanceInformation) * Add up all the used "Committed" memory + pagefile. * Not sure this is right. 8^\ */ - Spi->CommittedPages = MiMemoryConsumers[MC_PPOOL].PagesUsed + - MiMemoryConsumers[MC_NPPOOL].PagesUsed + + Spi->CommittedPages = MiMemoryConsumers[MC_SYSTEM].PagesUsed + MiMemoryConsumers[MC_CACHE].PagesUsed + MiMemoryConsumers[MC_USER].PagesUsed + MiUsedSwapPages; @@ -574,10 +573,10 @@ QSI_DEF(SystemPerformanceInformation) Spi->MappedPagesWriteCount = 0; /* FIXME */ Spi->MappedWriteIoCount = 0; /* FIXME */ - Spi->PagedPoolPages = MiMemoryConsumers[MC_PPOOL].PagesUsed; + Spi->PagedPoolPages = 0; /* FIXME */ Spi->PagedPoolAllocs = 0; /* FIXME */ Spi->PagedPoolFrees = 0; /* FIXME */ - Spi->NonPagedPoolPages = MiMemoryConsumers[MC_NPPOOL].PagesUsed; + Spi->NonPagedPoolPages = 0; /* FIXME */ Spi->NonPagedPoolAllocs = 0; /* FIXME */ Spi->NonPagedPoolFrees = 0; /* FIXME */ @@ -592,7 +591,7 @@ QSI_DEF(SystemPerformanceInformation) Spi->Spare3Count = 0; /* FIXME */ Spi->ResidentSystemCachePage = MiMemoryConsumers[MC_CACHE].PagesUsed; - Spi->ResidentPagedPoolPage = MiMemoryConsumers[MC_PPOOL].PagesUsed; /* FIXME */ + Spi->ResidentPagedPoolPage = 0; /* FIXME */ Spi->ResidentSystemDriverPage = 0; /* FIXME */ Spi->CcFastReadNoWait = 0; /* FIXME */ diff --git a/ntoskrnl/fstub/fstubex.c b/ntoskrnl/fstub/fstubex.c index d04c29923e6..51b8ea48b52 100644 --- a/ntoskrnl/fstub/fstubex.c +++ b/ntoskrnl/fstub/fstubex.c @@ -14,9 +14,6 @@ /* PRIVATE FUNCTIONS *********************************************************/ -#define PARTITION_ENTRY_SIZE 128 -#define TAG_FSTUB 'BtsF' - typedef struct _DISK_INFORMATION { PDEVICE_OBJECT DeviceObject; @@ -26,6 +23,88 @@ typedef struct _DISK_INFORMATION ULONGLONG SectorCount; } DISK_INFORMATION, *PDISK_INFORMATION; +#include +typedef struct _EFI_PARTITION_HEADER +{ + ULONGLONG Signature; // 0 + ULONG Revision; // 8 + ULONG HeaderSize; // 12 + ULONG HeaderCRC32; // 16 + ULONG Reserved; // 20 + ULONGLONG MyLBA; // 24 + ULONGLONG AlternateLBA; // 32 + ULONGLONG FirstUsableLBA; // 40 + ULONGLONG LastUsableLBA; // 48 + GUID DiskGUID; // 56 + ULONGLONG PartitionEntryLBA; // 72 + ULONG NumberOfEntries; // 80 + ULONG SizeOfPartitionEntry; // 84 + ULONG PartitionEntryCRC32; // 88 +} EFI_PARTITION_HEADER, *PEFI_PARTITION_HEADER; +#include + +typedef struct _EFI_PARTITION_ENTRY +{ + GUID PartitionType; // 0 + GUID UniquePartition; // 16 + ULONGLONG StartingLBA; // 32 + ULONGLONG EndingLBA; // 40 + ULONGLONG Attributes; // 48 + WCHAR Name[0x24]; // 56 +} EFI_PARTITION_ENTRY, *PEFI_PARTITION_ENTRY; + +typedef struct _CREATE_DISK_MBR +{ + ULONG Signature; +} CREATE_DISK_MBR, *PCREATE_DISK_MBR; + +typedef struct _CREATE_DISK_GPT +{ + GUID DiskId; + ULONG MaxPartitionCount; +} CREATE_DISK_GPT, *PCREATE_DISK_GPT; + +typedef struct _CREATE_DISK +{ + PARTITION_STYLE PartitionStyle; + union + { + CREATE_DISK_MBR Mbr; + CREATE_DISK_GPT Gpt; + }; +} CREATE_DISK, *PCREATE_DISK; + +typedef struct _PARTITION_TABLE_ENTRY +{ + UCHAR BootIndicator; + UCHAR StartHead; + UCHAR StartSector; + UCHAR StartCylinder; + UCHAR SystemIndicator; + UCHAR EndHead; + UCHAR EndSector; + UCHAR EndCylinder; + ULONG SectorCountBeforePartition; + ULONG PartitionSectorCount; +} PARTITION_TABLE_ENTRY, *PPARTITION_TABLE_ENTRY; + +typedef struct _MASTER_BOOT_RECORD +{ + UCHAR MasterBootRecordCodeAndData[0x1B8]; // 0 + ULONG Signature; // 440 + USHORT Reserved; // 444 + PARTITION_TABLE_ENTRY PartitionTable[4]; // 446 + USHORT MasterBootRecordMagic; // 510 +} MASTER_BOOT_RECORD, *PMASTER_BOOT_RECORD; + +/* Tag for Fstub allocations */ +#define TAG_FSTUB 'BtsF' +/* Partition entry size (bytes) - FIXME: It's hardcoded as Microsoft does, but according to specs, it shouldn't be */ +#define PARTITION_ENTRY_SIZE 128 +/* Defines "EFI PART" */ +#define EFI_HEADER_SIGNATURE 0x5452415020494645ULL +/* Defines version 1.0 */ +#define EFI_HEADER_REVISION_1 0x00010000 /* Defines system type for MBR showing that a GPT is following */ #define EFI_PMBR_OSTYPE_EFI 0xEE @@ -42,6 +121,17 @@ FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry, IN ULONG PartitionNumber ); +NTSTATUS +NTAPI +FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk, + IN PARTITION_STYLE * PartitionStyle +); + +VOID +NTAPI +FstubFreeDiskInformation(IN PDISK_INFORMATION DiskBuffer +); + NTSTATUS NTAPI FstubGetDiskGeometry(IN PDEVICE_OBJECT DeviceObject, @@ -56,6 +146,67 @@ FstubReadSector(IN PDEVICE_OBJECT DeviceObject, OUT PUSHORT Buffer ); +NTSTATUS +NTAPI +FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk +); + +NTSTATUS +NTAPI +FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk, + IN GUID DiskGUID, + IN ULONG MaxPartitionCount, + IN ULONGLONG FirstUsableLBA, + IN ULONGLONG LastUsableLBA, + IN BOOLEAN WriteBackupTable, + IN ULONG PartitionCount, + IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL +); + +NTSTATUS +NTAPI +FstubWriteSector(IN PDEVICE_OBJECT DeviceObject, + IN ULONG SectorSize, + IN ULONGLONG StartingSector OPTIONAL, + IN PUSHORT Buffer +); + +VOID +NTAPI +FstubAdjustPartitionCount(IN ULONG SectorSize, + IN OUT PULONG PartitionCount) +{ + ULONG Count; + PAGED_CODE(); + + ASSERT(SectorSize); + ASSERT(PartitionCount); + + /* Get partition count */ + Count = *PartitionCount; + /* We need at least 128 entries */ + if (Count < 128) + { + Count = 128; + } + + /* Then, ensure that we will have a round value, + * ie, all sectors will be full of entries + * There won't be lonely entries + */ + Count = (Count * PARTITION_ENTRY_SIZE) / SectorSize; + Count = (Count * SectorSize) / PARTITION_ENTRY_SIZE; + ASSERT(*PartitionCount <= Count); + /* Return result */ + *PartitionCount = Count; + + /* One more sanity check */ + if (SectorSize == 512) + { + ASSERT(Count % 4 == 0); + } +} + NTSTATUS NTAPI FstubAllocateDiskInformation(IN PDEVICE_OBJECT DeviceObject, @@ -118,6 +269,265 @@ FstubAllocateDiskInformation(IN PDEVICE_OBJECT DeviceObject, return STATUS_SUCCESS; } +PDRIVE_LAYOUT_INFORMATION +NTAPI +FstubConvertExtendedToLayout(IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx) +{ + ULONG i; + PDRIVE_LAYOUT_INFORMATION DriveLayout; + PAGED_CODE(); + + ASSERT(LayoutEx); + + /* Check whether we're dealing with MBR partition table */ + if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR) + { + ASSERT(FALSE); + return NULL; + } + + /* Allocate needed buffer */ + DriveLayout = ExAllocatePoolWithTag(NonPagedPool, + FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) + + LayoutEx->PartitionCount * sizeof(PARTITION_INFORMATION), + 'BtsF'); + if (!DriveLayout) + { + return NULL; + } + + /* Convert information about partition table */ + DriveLayout->PartitionCount = LayoutEx->PartitionCount; + DriveLayout->Signature = LayoutEx->Mbr.Signature; + + /* Convert each partition */ + for (i = 0; i < LayoutEx->PartitionCount; i++) + { + DriveLayout->PartitionEntry[i].StartingOffset = LayoutEx->PartitionEntry[i].StartingOffset; + DriveLayout->PartitionEntry[i].PartitionLength = LayoutEx->PartitionEntry[i].PartitionLength; + DriveLayout->PartitionEntry[i].HiddenSectors = LayoutEx->PartitionEntry[i].Mbr.HiddenSectors; + DriveLayout->PartitionEntry[i].PartitionNumber = LayoutEx->PartitionEntry[i].PartitionNumber; + DriveLayout->PartitionEntry[i].PartitionType = LayoutEx->PartitionEntry[i].Mbr.PartitionType; + DriveLayout->PartitionEntry[i].BootIndicator = LayoutEx->PartitionEntry[i].Mbr.BootIndicator; + DriveLayout->PartitionEntry[i].RecognizedPartition = LayoutEx->PartitionEntry[i].Mbr.RecognizedPartition; + DriveLayout->PartitionEntry[i].RewritePartition = LayoutEx->PartitionEntry[i].RewritePartition; + } + + return DriveLayout; +} + +VOID +NTAPI +FstubCopyEntryEFI(OUT PEFI_PARTITION_ENTRY Entry, + IN PPARTITION_INFORMATION_EX Partition, + ULONG SectorSize) +{ + PAGED_CODE(); + + ASSERT(Entry); + ASSERT(Partition); + ASSERT(SectorSize); + + /* Just convert data to EFI partition entry type */ + Entry->PartitionType = Partition->Gpt.PartitionType; + Entry->UniquePartition = Partition->Gpt.PartitionId; + Entry->StartingLBA = Partition->StartingOffset.QuadPart / SectorSize; + Entry->EndingLBA = (Partition->StartingOffset.QuadPart + Partition->PartitionLength.QuadPart - 1) / SectorSize; + Entry->Attributes = Partition->Gpt.Attributes; + RtlCopyMemory(Entry->Name, Partition->Gpt.Name, sizeof(Entry->Name)); +} + +NTSTATUS +NTAPI +FstubCreateDiskMBR(IN PDEVICE_OBJECT DeviceObject, + IN PCREATE_DISK_MBR DiskInfo) +{ + NTSTATUS Status; + PDISK_INFORMATION Disk = NULL; + PMASTER_BOOT_RECORD MasterBootRecord; + PAGED_CODE(); + + ASSERT(DeviceObject); + + /* Allocate internal structure */ + Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Read previous MBR, if any */ + Status = FstubReadSector(Disk->DeviceObject, + Disk->SectorSize, + 0ULL, + Disk->Buffer); + if (!NT_SUCCESS(Status)) + { + FstubFreeDiskInformation(Disk); + return Status; + } + /* Fill the buffer with needed information, we won't overwrite boot code */ + MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer; + MasterBootRecord->Signature = DiskInfo->Signature; + RtlZeroMemory(MasterBootRecord->PartitionTable, sizeof(PARTITION_TABLE_ENTRY) * 4); + MasterBootRecord->MasterBootRecordMagic = BOOT_RECORD_SIGNATURE; + + /* Finally, write MBR */ + Status = FstubWriteSector(Disk->DeviceObject, + Disk->SectorSize, + 0ULL, + Disk->Buffer); + + /* Release internal structure and return */ + FstubFreeDiskInformation(Disk); + return Status; +} + +NTSTATUS +NTAPI +FstubCreateDiskEFI(IN PDEVICE_OBJECT DeviceObject, + IN PCREATE_DISK_GPT DiskInfo) +{ + NTSTATUS Status; + PDISK_INFORMATION Disk = NULL; + ULONGLONG FirstUsableLBA, LastUsableLBA; + ULONG MaxPartitionCount, SectorsForPartitions; + PAGED_CODE(); + + ASSERT(DeviceObject); + ASSERT(DiskInfo); + + /* Allocate internal structure */ + Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0); + if (!NT_SUCCESS(Status)) + { + return Status; + } + ASSERT(Disk); + + /* Write legacy MBR */ + Status = FstubWriteBootSectorEFI(Disk); + if (!NT_SUCCESS(Status)) + { + FstubFreeDiskInformation(Disk); + return Status; + } + + /* Get max entries and adjust its number */ + MaxPartitionCount = DiskInfo->MaxPartitionCount; + FstubAdjustPartitionCount(Disk->SectorSize, &MaxPartitionCount); + + /* Count number of sectors needed to store partitions */ + SectorsForPartitions = (MaxPartitionCount * PARTITION_ENTRY_SIZE) / Disk->SectorSize; + /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */ + FirstUsableLBA = SectorsForPartitions + 2; + /* Set last usable LBA: Last sector - GPT header - Partitions entries */ + LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1; + + /* First, write primary table */ + Status = FstubWritePartitionTableEFI(Disk, + DiskInfo->DiskId, + MaxPartitionCount, + FirstUsableLBA, + LastUsableLBA, + FALSE, + 0, + NULL); + /* Then, write backup table */ + if (NT_SUCCESS(Status)) + { + Status = FstubWritePartitionTableEFI(Disk, + DiskInfo->DiskId, + MaxPartitionCount, + FirstUsableLBA, + LastUsableLBA, + TRUE, + 0, + NULL); + } + + /* Release internal structure and return */ + FstubFreeDiskInformation(Disk); + return Status; +} + +NTSTATUS +NTAPI +FstubCreateDiskRaw(IN PDEVICE_OBJECT DeviceObject) +{ + NTSTATUS Status; + PDISK_INFORMATION Disk = NULL; + PARTITION_STYLE PartitionStyle; + PMASTER_BOOT_RECORD MasterBootRecord; + PAGED_CODE(); + + ASSERT(DeviceObject); + + /* Allocate internal structure */ + Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Detect current disk partition style */ + Status = FstubDetectPartitionStyle(Disk, &PartitionStyle); + if (!NT_SUCCESS(Status)) + { + FstubFreeDiskInformation(Disk); + return Status; + } + + /* Read MBR, if any */ + Status = FstubReadSector(Disk->DeviceObject, + Disk->SectorSize, + 0ULL, + Disk->Buffer); + if (!NT_SUCCESS(Status)) + { + FstubFreeDiskInformation(Disk); + return Status; + } + + /* Only zero useful stuff */ + MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer; + MasterBootRecord->Signature = 0; + RtlZeroMemory(MasterBootRecord->PartitionTable, sizeof(PARTITION_TABLE_ENTRY)); + MasterBootRecord->MasterBootRecordMagic = 0; + + /* Write back that destroyed MBR */ + Status = FstubWriteSector(Disk->DeviceObject, + Disk->SectorSize, + 0ULL, + Disk->Buffer); + /* If previous style wasn't GPT, we're done here */ + if (PartitionStyle != PARTITION_STYLE_GPT) + { + FstubFreeDiskInformation(Disk); + return Status; + } + + /* Otherwise, we've to zero the two GPT headers */ + RtlZeroMemory(Disk->Buffer, Disk->SectorSize); + /* Erase primary header */ + Status = FstubWriteSector(Disk->DeviceObject, + Disk->SectorSize, + 1ULL, + Disk->Buffer); + /* In case of success, erase backup header */ + if (NT_SUCCESS(Status)) + { + Status = FstubWriteSector(Disk->DeviceObject, + Disk->SectorSize, + Disk->SectorCount - 1ULL, + Disk->Buffer); + } + + /* Release internal structure and return */ + FstubFreeDiskInformation(Disk); + return Status; +} + PCHAR NTAPI FstubDbgGuidToString(IN PGUID Guid, @@ -220,6 +630,36 @@ FstubDbgPrintPartitionEx(IN PPARTITION_INFORMATION_EX PartitionEntry, } } +VOID +NTAPI +FstubDbgPrintSetPartitionEx(IN PSET_PARTITION_INFORMATION_EX PartitionEntry, + IN ULONG PartitionNumber) +{ + CHAR Guid[38]; + PAGED_CODE(); + + DPRINT1("FSTUB: SET_PARTITION_INFORMATION_EX: %p\n", PartitionEntry); + DPRINT1("Modifying partition %ld\n", PartitionNumber); + switch (PartitionEntry->PartitionStyle) + { + case PARTITION_STYLE_MBR: + DPRINT1(" PartitionType: %02x\n", PartitionEntry->Mbr.PartitionType); + + break; + case PARTITION_STYLE_GPT: + FstubDbgGuidToString(&(PartitionEntry->Gpt.PartitionType), Guid); + DPRINT1(" PartitionType: %s\n", Guid); + FstubDbgGuidToString(&(PartitionEntry->Gpt.PartitionId), Guid); + DPRINT1(" PartitionId: %s\n", Guid); + DPRINT1(" Attributes: %16x\n", PartitionEntry->Gpt.Attributes); + DPRINT1(" Name: %ws\n", PartitionEntry->Gpt.Name); + + break; + default: + DPRINT1(" Unsupported partition style: %ld\n", PartitionEntry[PartitionNumber].PartitionStyle); + } +} + NTSTATUS NTAPI FstubDetectPartitionStyle(IN PDISK_INFORMATION Disk, @@ -374,14 +814,281 @@ Cleanup: return Status; } +NTSTATUS +NTAPI +FstubReadHeaderEFI(IN PDISK_INFORMATION Disk, + IN BOOLEAN ReadBackupTable, + PEFI_PARTITION_HEADER HeaderBuffer) +{ + NTSTATUS Status; + PUCHAR Sector = NULL; + ULONGLONG StartingSector; + PEFI_PARTITION_HEADER EFIHeader; + ULONG i, HeaderCRC32, PreviousCRC32, SectoredPartitionEntriesSize, LonelyPartitions; + PAGED_CODE(); + + ASSERT(Disk); + ASSERT(IS_VALID_DISK_INFO(Disk)); + ASSERT(HeaderBuffer); + + /* In case we want to read backup table, we read last disk sector */ + if (ReadBackupTable) + { + StartingSector = Disk->SectorCount - 1ULL; + } + else + { + /* Otherwise we start at first sector (as sector 0 is the MBR) */ + StartingSector = 1ULL; + } + + Status = FstubReadSector(Disk->DeviceObject, + Disk->SectorSize, + StartingSector, + Disk->Buffer); + if (!NT_SUCCESS(Status)) + { + DPRINT("EFI::Failed reading header!\n"); + return Status; + } + /* Let's use read buffer as EFI_PARTITION_HEADER */ + EFIHeader = (PEFI_PARTITION_HEADER)Disk->Buffer; + + + /* First check signature + * Then, check version (we only support v1) + * Finally check header size + */ + if (EFIHeader->Signature != EFI_HEADER_SIGNATURE || + EFIHeader->Revision != EFI_HEADER_REVISION_1 || + EFIHeader->HeaderSize != sizeof(EFI_PARTITION_HEADER)) + { + DPRINT("EFI::Wrong signature/version/header size!\n"); + DPRINT("%I64x (expected: %I64x)\n", EFIHeader->Signature, EFI_HEADER_SIGNATURE); + DPRINT("%03x (expected: %03x)\n", EFIHeader->Revision, EFI_HEADER_REVISION_1); + DPRINT("%02x (expected: %02x)\n", EFIHeader->HeaderSize, sizeof(EFI_PARTITION_HEADER)); + return STATUS_DISK_CORRUPT_ERROR; + } + + /* Save current checksum */ + HeaderCRC32 = EFIHeader->HeaderCRC32; + /* Then zero the one in EFI header. This is needed to compute header checksum */ + EFIHeader->HeaderCRC32 = 0; + /* Compute header checksum and compare with the one present in partition table */ + if (RtlComputeCrc32(0, (PUCHAR)Disk->Buffer, sizeof(EFI_PARTITION_HEADER)) != HeaderCRC32) + { + DPRINT("EFI::Not matching header checksum!\n"); + return STATUS_DISK_CORRUPT_ERROR; + } + /* Put back removed checksum in header */ + EFIHeader->HeaderCRC32 = HeaderCRC32; + + /* Check if current LBA is matching with ours */ + if (EFIHeader->MyLBA != StartingSector) + { + DPRINT("EFI::Not matching starting sector!\n"); + return STATUS_DISK_CORRUPT_ERROR; + } + + /* Allocate a buffer to read a sector on the disk */ + Sector = ExAllocatePoolWithTag(NonPagedPool, + Disk->SectorSize, + 'BtsF'); + if (!Sector) + { + DPRINT("EFI::Lacking resources!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Count how much sectors we'll have to read to read the whole partition table */ + SectoredPartitionEntriesSize = (EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize; + /* Compute partition table checksum */ + for (i = 0, PreviousCRC32 = 0; i < SectoredPartitionEntriesSize; i++) + { + Status = FstubReadSector(Disk->DeviceObject, + Disk->SectorSize, + EFIHeader->PartitionEntryLBA + i, + (PUSHORT)Sector); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(Sector, 'BtsF'); + DPRINT("EFI::Failed reading sector for partition entry!\n"); + return Status; + } + + PreviousCRC32 = RtlComputeCrc32(PreviousCRC32, Sector, Disk->SectorSize); + } + + /* Check whether we have a last sector not full of partitions */ + LonelyPartitions = (EFIHeader->NumberOfEntries * PARTITION_ENTRY_SIZE) % Disk->SectorSize; + /* In such case, we have to complete checksum computation */ + if (LonelyPartitions != 0) + { + /* Read the sector that contains those partitions */ + Status = FstubReadSector(Disk->DeviceObject, + Disk->SectorSize, + EFIHeader->PartitionEntryLBA + i, + (PUSHORT)Sector); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(Sector, 'BtsF'); + DPRINT("EFI::Failed reading sector for partition entry!\n"); + return Status; + } + + /* Then complete checksum by computing on each partition */ + for (i = 0; i < LonelyPartitions; i++) + { + PreviousCRC32 = RtlComputeCrc32(PreviousCRC32, Sector + i * PARTITION_ENTRY_SIZE, PARTITION_ENTRY_SIZE); + } + } + + /* Finally, release memory */ + ExFreePoolWithTag(Sector, 'BtsF'); + + /* Compare checksums */ + if (PreviousCRC32 == EFIHeader->PartitionEntryCRC32) + { + /* In case of a success, return read header */ + *HeaderBuffer = *EFIHeader; + return STATUS_SUCCESS; + } + else + { + DPRINT("EFI::Not matching partition table checksum!\n"); + DPRINT("EFI::Expected: %x, received: %x\n", EFIHeader->PartitionEntryCRC32, PreviousCRC32); + return STATUS_DISK_CORRUPT_ERROR; + } +} + NTSTATUS NTAPI FstubReadPartitionTableEFI(IN PDISK_INFORMATION Disk, IN BOOLEAN ReadBackupTable, OUT struct _DRIVE_LAYOUT_INFORMATION_EX** DriveLayout) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + EFI_PARTITION_HEADER EfiHeader; + ULONGLONG SectorsForPartitions; + EFI_PARTITION_ENTRY PartitionEntry; + BOOLEAN UpdatedPartitionTable = FALSE; + PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx = NULL; + ULONG i, PartitionCount, PartitionIndex, PartitionsPerSector; + PAGED_CODE(); + + ASSERT(Disk); + + /* Zero output */ + *DriveLayout = NULL; + + /* Read EFI header */ + Status = FstubReadHeaderEFI(Disk, + ReadBackupTable, + &EfiHeader); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Allocate a DRIVE_LAYOUT_INFORMATION_EX struct big enough */ + DriveLayoutEx = ExAllocatePoolWithTag(NonPagedPool, + FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) + + EfiHeader.NumberOfEntries * sizeof(PARTITION_INFORMATION_EX), + TAG_FSTUB); + if (!DriveLayoutEx) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (ReadBackupTable) + { + /* If we read backup but if it doesn't match with current geometry */ + if ((Disk->SectorCount - 1ULL) != EfiHeader.AlternateLBA) + { + /* We'll update it. First, count number of sectors needed to store partitions */ + SectorsForPartitions = (EfiHeader.NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize; + /* Then set first usable LBA: Legacy MBR + GPT header + Partitions entries */ + EfiHeader.FirstUsableLBA = SectorsForPartitions + 2; + /* Then set last usable LBA: Last sector - GPT header - Partitions entries */ + EfiHeader.LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1; + /* Inform that we'll rewrite partition table */ + UpdatedPartitionTable = TRUE; + } + } + + DriveLayoutEx->PartitionStyle = PARTITION_STYLE_GPT; + /* Translate LBA -> Offset */ + DriveLayoutEx->Gpt.StartingUsableOffset.QuadPart = EfiHeader.FirstUsableLBA * Disk->SectorSize; + DriveLayoutEx->Gpt.UsableLength.QuadPart = EfiHeader.LastUsableLBA - EfiHeader.FirstUsableLBA * Disk->SectorSize; + DriveLayoutEx->Gpt.MaxPartitionCount = EfiHeader.NumberOfEntries; + DriveLayoutEx->Gpt.DiskId = EfiHeader.DiskGUID; + + /* Count number of partitions per sector */ + PartitionsPerSector = (Disk->SectorSize / PARTITION_ENTRY_SIZE); + /* Read all partitions and fill in structure */ + for (i = 0, PartitionCount = 0, PartitionIndex = PartitionsPerSector; + i < EfiHeader.NumberOfEntries; + i++) + { + /* Only read following sector if we finished with previous sector */ + if (PartitionIndex == PartitionsPerSector) + { + Status = FstubReadSector(Disk->DeviceObject, + Disk->SectorSize, + EfiHeader.PartitionEntryLBA + (i / PartitionsPerSector), + Disk->Buffer); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(DriveLayoutEx, TAG_FSTUB); + return Status; + } + + PartitionIndex = 0; + } + /* Read following partition */ + PartitionEntry = ((PEFI_PARTITION_ENTRY)Disk->Buffer)[PartitionIndex]; + PartitionIndex++; + + /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */ + if (PartitionEntry.PartitionType.Data1 == 0 && + PartitionEntry.PartitionType.Data2 == 0 && + PartitionEntry.PartitionType.Data3 == 0 && + ((PULONGLONG)PartitionEntry.PartitionType.Data4)[0] == 0) + { + continue; + } + + /* Write data to structure. Don't forget GPT is using sectors, Windows offsets */ + DriveLayoutEx->PartitionEntry[PartitionCount].StartingOffset.QuadPart = PartitionEntry.StartingLBA * Disk->SectorSize; + DriveLayoutEx->PartitionEntry[PartitionCount].PartitionLength.QuadPart = (PartitionEntry.EndingLBA - + PartitionEntry.StartingLBA + 1) * + Disk->SectorSize; + /* This number starts from 1 */ + DriveLayoutEx->PartitionEntry[PartitionCount].PartitionNumber = PartitionCount + 1; + DriveLayoutEx->PartitionEntry[PartitionCount].RewritePartition = FALSE; + DriveLayoutEx->PartitionEntry[PartitionCount].PartitionStyle = PARTITION_STYLE_GPT; + DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.PartitionType = PartitionEntry.PartitionType; + DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.PartitionId = PartitionEntry.UniquePartition; + DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.Attributes = PartitionEntry.Attributes; + RtlCopyMemory(DriveLayoutEx->PartitionEntry[PartitionCount].Gpt.Name, + PartitionEntry.Name, sizeof(PartitionEntry.Name)); + + /* Update partition count */ + PartitionCount++; + } + DriveLayoutEx->PartitionCount = PartitionCount; + + /* If we updated partition table using backup table, rewrite partition table */ + if (UpdatedPartitionTable) + { + IoWritePartitionTableEx(Disk->DeviceObject, + DriveLayoutEx); + } + + /* Finally, return read data */ + *DriveLayout = DriveLayoutEx; + + return Status; } NTSTATUS @@ -503,34 +1210,843 @@ FstubReadSector(IN PDEVICE_OBJECT DeviceObject, return Status; } +NTSTATUS +NTAPI +FstubSetPartitionInformationEFI(IN PDISK_INFORMATION Disk, + IN ULONG PartitionNumber, + IN SET_PARTITION_INFORMATION_GPT * PartitionInfo) +{ + NTSTATUS Status; + PDRIVE_LAYOUT_INFORMATION_EX Layout = NULL; + PAGED_CODE(); + + ASSERT(Disk); + ASSERT(PartitionInfo); + + /* Partition 0 isn't correct (should start at 1) */ + if (PartitionNumber == 0) + { + return STATUS_INVALID_PARAMETER; + } + + /* Read partition table */ + Status = IoReadPartitionTableEx(Disk->DeviceObject, &Layout); + if (!NT_SUCCESS(Status)) + { + return Status; + } + ASSERT(Layout); + + /* If our partition (started at 0 now) is higher than partition count, then, there's an issue */ + if (Layout->PartitionCount <= --PartitionNumber) + { + ExFreePool(Layout); + return STATUS_INVALID_PARAMETER; + } + + /* Erase actual partition entry data with provided ones */ + Layout->PartitionEntry[PartitionNumber].Gpt.PartitionType = PartitionInfo->PartitionType; + Layout->PartitionEntry[PartitionNumber].Gpt.PartitionId = PartitionInfo->PartitionId; + Layout->PartitionEntry[PartitionNumber].Gpt.Attributes = PartitionInfo->Attributes; + RtlCopyMemory(Layout->PartitionEntry[PartitionNumber].Gpt.Name, PartitionInfo->Name, sizeof(PartitionInfo->Name)); + + /* Rewrite the whole partition table to update the modified entry */ + Status = IoWritePartitionTableEx(Disk->DeviceObject, Layout); + + /* Free partition table and return */ + ExFreePool(Layout); + return Status; +} + +NTSTATUS +NTAPI +FstubVerifyPartitionTableEFI(IN PDISK_INFORMATION Disk, + IN BOOLEAN FixErrors) +{ + NTSTATUS Status; + PEFI_PARTITION_HEADER EFIHeader; + EFI_PARTITION_HEADER ReadEFIHeader; + BOOLEAN PrimaryValid = FALSE, BackupValid = FALSE; + PAGED_CODE(); + + EFIHeader = ExAllocatePoolWithTag(NonPagedPool, sizeof(EFI_PARTITION_HEADER), TAG_FSTUB); + if (!EFIHeader) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = FstubReadHeaderEFI(Disk, FALSE, &ReadEFIHeader); + if (NT_SUCCESS(Status)) + { + PrimaryValid = TRUE; + } + + Status = FstubReadHeaderEFI(Disk, TRUE, &ReadEFIHeader); + if (NT_SUCCESS(Status)) + { + BackupValid = TRUE; + } + + if (!PrimaryValid) + { + if (!BackupValid || !FixErrors) + { + ExFreePoolWithTag(EFIHeader, TAG_FSTUB); + return STATUS_DISK_CORRUPT_ERROR; + } + + DPRINT1("EFI::Partition table fixing not yet supported!\n"); + ExFreePoolWithTag(EFIHeader, TAG_FSTUB); + return STATUS_NOT_IMPLEMENTED; + } + else if (!BackupValid) + { + if (!PrimaryValid || !FixErrors) + { + ExFreePoolWithTag(EFIHeader, TAG_FSTUB); + return STATUS_DISK_CORRUPT_ERROR; + } + + DPRINT1("EFI::Partition table fixing not yet supported!\n"); + ExFreePoolWithTag(EFIHeader, TAG_FSTUB); + return STATUS_NOT_IMPLEMENTED; + } + else + { + ExFreePoolWithTag(EFIHeader, TAG_FSTUB); + return STATUS_SUCCESS; + } +} + +NTSTATUS +NTAPI +FstubWriteBootSectorEFI(IN PDISK_INFORMATION Disk) +{ + NTSTATUS Status; + ULONG Signature = 0; + PMASTER_BOOT_RECORD MasterBootRecord; + PAGED_CODE(); + + ASSERT(Disk); + ASSERT(IS_VALID_DISK_INFO(Disk)); + + /* Read if a MBR is already present */ + Status = FstubReadSector(Disk->DeviceObject, + Disk->SectorSize, + 0ULL, + Disk->Buffer); + MasterBootRecord = (PMASTER_BOOT_RECORD)Disk->Buffer; + /* If one has been found */ + if (NT_SUCCESS(Status) && MasterBootRecord->MasterBootRecordMagic == BOOT_RECORD_SIGNATURE) + { + /* Save its signature */ + Signature = MasterBootRecord->Signature; + } + + /* Reset the MBR */ + RtlZeroMemory(MasterBootRecord, Disk->SectorSize); + /* Then create a fake MBR matching those purposes: + * It must have only partition. Type of this partition + * has to be 0xEE to signal a GPT is following. + * This partition has to cover the whole disk. To prevent + * any disk modification by a program that wouldn't + * understand anything to GPT. + */ + MasterBootRecord->Signature = Signature; + MasterBootRecord->PartitionTable[0].StartSector = 2; + MasterBootRecord->PartitionTable[0].SystemIndicator = EFI_PMBR_OSTYPE_EFI; + MasterBootRecord->PartitionTable[0].EndHead = 0xFF; + MasterBootRecord->PartitionTable[0].EndSector = 0xFF; + MasterBootRecord->PartitionTable[0].EndCylinder = 0xFF; + MasterBootRecord->PartitionTable[0].SectorCountBeforePartition = 1; + MasterBootRecord->PartitionTable[0].PartitionSectorCount = 0xFFFFFFFF; + MasterBootRecord->MasterBootRecordMagic = BOOT_RECORD_SIGNATURE; + + /* Finally, write that MBR */ + return FstubWriteSector(Disk->DeviceObject, + Disk->SectorSize, + 0, + Disk->Buffer); +} + +NTSTATUS +NTAPI +FstubWriteEntryEFI(IN PDISK_INFORMATION Disk, + IN ULONG PartitionsSizeSector, + IN ULONG PartitionEntryNumber, + IN PEFI_PARTITION_ENTRY PartitionEntry, + IN BOOLEAN WriteBackupTable, + IN BOOLEAN ForceWrite, + OUT PULONG PartitionEntryCRC32 OPTIONAL) +{ + ULONG Offset; + ULONGLONG FirstEntryLBA; + NTSTATUS Status = STATUS_SUCCESS; + PAGED_CODE(); + + ASSERT(Disk); + ASSERT(IS_VALID_DISK_INFO(Disk)); + + /* Get the first LBA where the partition table is: + * On primary table, it's sector 2 (skip MBR & Header) + * On backup table, it's ante last sector (Header) minus partition table size + */ + if (!WriteBackupTable) + { + FirstEntryLBA = 2ULL; + } + else + { + FirstEntryLBA = Disk->SectorCount - PartitionsSizeSector - 1; + } + + /* Copy the entry at the proper place into the buffer + * That way, we don't erase previous entries + */ + RtlCopyMemory(Disk->Buffer + (((PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize) / sizeof(PUSHORT)), + PartitionEntry, + sizeof(EFI_PARTITION_ENTRY)); + /* Compute size of buffer */ + Offset = (PartitionEntryNumber * PARTITION_ENTRY_SIZE) % Disk->SectorSize + PARTITION_ENTRY_SIZE; + ASSERT(Offset <= Disk->SectorSize); + + /* If it's full of partition entries, or if call ask for it, write down the data */ + if (Offset == Disk->SectorSize || ForceWrite) + { + /* We will write at first entry LBA + a shift made by already present/written entries */ + Status = FstubWriteSector(Disk->DeviceObject, + Disk->SectorSize, + FirstEntryLBA + ((PartitionEntryNumber * PARTITION_ENTRY_SIZE) / Disk->SectorSize), + Disk->Buffer); + if (!NT_SUCCESS(Status)) + { + return Status; + } + /* We clean buffer */ + RtlZeroMemory(Disk->Buffer, Disk->SectorSize); + } + + /* If we have a buffer for CRC32, then compute it */ + if (PartitionEntryCRC32) + { + *PartitionEntryCRC32 = RtlComputeCrc32(*PartitionEntryCRC32, (PUCHAR)PartitionEntry, PARTITION_ENTRY_SIZE); + } + + return Status; +} + +NTSTATUS +NTAPI +FstubWriteHeaderEFI(IN PDISK_INFORMATION Disk, + IN ULONG PartitionsSizeSector, + IN GUID DiskGUID, + IN ULONG NumberOfEntries, + IN ULONGLONG FirstUsableLBA, + IN ULONGLONG LastUsableLBA, + IN ULONG PartitionEntryCRC32, + IN BOOLEAN WriteBackupTable) +{ + PEFI_PARTITION_HEADER EFIHeader; + PAGED_CODE(); + + ASSERT(Disk); + ASSERT(IS_VALID_DISK_INFO(Disk)); + + /* Let's use read buffer as EFI_PARTITION_HEADER */ + EFIHeader = (PEFI_PARTITION_HEADER)Disk->Buffer; + + /* Complete standard header information */ + EFIHeader->Signature = EFI_HEADER_SIGNATURE; + EFIHeader->Revision = EFI_HEADER_REVISION_1; + EFIHeader->HeaderSize = sizeof(EFI_PARTITION_HEADER); + /* Set no CRC32 checksum at the moment */ + EFIHeader->HeaderCRC32 = 0; + EFIHeader->Reserved = 0; + /* Check whether we're writing primary or backup + * That way, we can ajust LBA setting: + * Primary is on first sector + * Backup is on last sector + */ + if (!WriteBackupTable) + { + EFIHeader->MyLBA = 1ULL; + EFIHeader->AlternateLBA = Disk->SectorCount - 1ULL; + } + else + { + EFIHeader->MyLBA = Disk->SectorCount - 1ULL; + EFIHeader->AlternateLBA = 1ULL; + } + /* Fill in with received data */ + EFIHeader->FirstUsableLBA = FirstUsableLBA; + EFIHeader->LastUsableLBA = LastUsableLBA; + EFIHeader->DiskGUID = DiskGUID; + /* Check whether we're writing primary or backup + * That way, we can ajust LBA setting: + * On primary, partition entries are just after header, so sector 2 + * On backup, partition entries are just before header, so, last sector minus partition table size + */ + if (!WriteBackupTable) + { + EFIHeader->PartitionEntryLBA = EFIHeader->MyLBA + 1ULL; + } + else + { + EFIHeader->PartitionEntryLBA = EFIHeader->MyLBA - PartitionsSizeSector; + } + /* Complete filling in */ + EFIHeader->NumberOfEntries = NumberOfEntries; + EFIHeader->SizeOfPartitionEntry = PARTITION_ENTRY_SIZE; + EFIHeader->PartitionEntryCRC32 = PartitionEntryCRC32; + /* Finally, compute header checksum */ + EFIHeader->HeaderCRC32 = RtlComputeCrc32(0, (PUCHAR)EFIHeader, sizeof(EFI_PARTITION_HEADER)); + + /* Debug the way we'll break disk, to let user pray */ + DPRINT("FSTUB: About to write the following header for %s table\n", (WriteBackupTable ? "backup" : "primary")); + DPRINT(" Signature: %I64x\n Revision: %x\n HeaderSize: %x\n HeaderCRC32: %x\n", + EFIHeader->Signature, EFIHeader->Revision, EFIHeader->HeaderSize, EFIHeader->HeaderCRC32); + DPRINT(" MyLBA: %I64x\n AlternateLBA: %I64x\n FirstUsableLBA: %I64x\n LastUsableLBA: %I64x\n", + EFIHeader->MyLBA, EFIHeader->AlternateLBA, EFIHeader->FirstUsableLBA, EFIHeader->LastUsableLBA); + DPRINT(" PartitionEntryLBA: %I64x\n NumberOfEntries: %x\n SizeOfPartitionEntry: %x\n PartitionEntryCRC32: %x\n", + EFIHeader->PartitionEntryLBA, EFIHeader->NumberOfEntries, + EFIHeader->SizeOfPartitionEntry, EFIHeader->PartitionEntryCRC32); + + /* Write header to disk */ + return FstubWriteSector(Disk->DeviceObject, + Disk->SectorSize, + EFIHeader->MyLBA, + Disk->Buffer); +} + +NTSTATUS +NTAPI +FstubWritePartitionTableEFI(IN PDISK_INFORMATION Disk, + IN GUID DiskGUID, + IN ULONG MaxPartitionCount, + IN ULONGLONG FirstUsableLBA, + IN ULONGLONG LastUsableLBA, + IN BOOLEAN WriteBackupTable, + IN ULONG PartitionCount, + IN PPARTITION_INFORMATION_EX PartitionEntries OPTIONAL) +{ + NTSTATUS Status; + EFI_PARTITION_ENTRY Entry; + ULONG i, WrittenPartitions, SectoredPartitionEntriesSize, PartitionEntryCRC32; + PAGED_CODE(); + + ASSERT(Disk); + ASSERT(MaxPartitionCount >= 128); + ASSERT(PartitionCount <= MaxPartitionCount); + + PartitionEntryCRC32 = 0; + /* Count how much sectors we'll have to read to read the whole partition table */ + SectoredPartitionEntriesSize = (MaxPartitionCount * PARTITION_ENTRY_SIZE) / Disk->SectorSize; + + for (i = 0, WrittenPartitions = 0; i < PartitionCount; i++) + { + /* If partition GUID is 00000000-0000-0000-0000-000000000000, then it's unused, skip it */ + if (PartitionEntries[i].Gpt.PartitionType.Data1 == 0 && + PartitionEntries[i].Gpt.PartitionType.Data2 == 0 && + PartitionEntries[i].Gpt.PartitionType.Data3 == 0 && + ((PULONGLONG)PartitionEntries[i].Gpt.PartitionType.Data4)[0] == 0) + { + continue; + } + + /* Copy the entry in the partition entry format */ + FstubCopyEntryEFI(&Entry, &PartitionEntries[i], Disk->SectorSize); + /* Then write the entry to the disk */ + Status = FstubWriteEntryEFI(Disk, + SectoredPartitionEntriesSize, + WrittenPartitions, + &Entry, + WriteBackupTable, + FALSE, + &PartitionEntryCRC32); + if (!NT_SUCCESS(Status)) + { + return Status; + } + WrittenPartitions++; + } + + /* Zero the buffer to write zeros to the disk */ + RtlZeroMemory(&Entry, sizeof(EFI_PARTITION_ENTRY)); + /* Write the disks with zeros for every unused remaining partition entry */ + for (i = WrittenPartitions; i < MaxPartitionCount; i++) + { + Status = FstubWriteEntryEFI(Disk, + SectoredPartitionEntriesSize, + i, + &Entry, + WriteBackupTable, + FALSE, + &PartitionEntryCRC32); + if (!NT_SUCCESS(Status)) + { + return Status; + } + } + + /* Once we're done, write the GPT header */ + return FstubWriteHeaderEFI(Disk, + SectoredPartitionEntriesSize, + DiskGUID, + MaxPartitionCount, + FirstUsableLBA, + LastUsableLBA, + PartitionEntryCRC32, + WriteBackupTable); +} + +NTSTATUS +NTAPI +FstubWritePartitionTableMBR(IN PDISK_INFORMATION Disk, + IN PDRIVE_LAYOUT_INFORMATION_EX LayoutEx) +{ + NTSTATUS Status; + PDRIVE_LAYOUT_INFORMATION DriveLayout; + PAGED_CODE(); + + ASSERT(IS_VALID_DISK_INFO(Disk)); + ASSERT(LayoutEx); + + /* Convert data to the correct format */ + DriveLayout = FstubConvertExtendedToLayout(LayoutEx); + if (!DriveLayout) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Really write information */ + Status = IoWritePartitionTable(Disk->DeviceObject, + Disk->SectorSize, + Disk->DiskGeometry.Geometry.SectorsPerTrack, + Disk->DiskGeometry.Geometry.TracksPerCylinder, + DriveLayout); + + /* Free allocated structure and return */ + ExFreePool(DriveLayout); + return Status; +} + +NTSTATUS +NTAPI +FstubWriteSector(IN PDEVICE_OBJECT DeviceObject, + IN ULONG SectorSize, + IN ULONGLONG StartingSector OPTIONAL, + IN PUSHORT Buffer) +{ + PIRP Irp; + KEVENT Event; + NTSTATUS Status; + LARGE_INTEGER StartingOffset; + IO_STATUS_BLOCK IoStatusBlock; + PIO_STACK_LOCATION IoStackLocation; + PAGED_CODE(); + + ASSERT(DeviceObject); + ASSERT(Buffer); + ASSERT(SectorSize); + + /* Compute starting offset */ + StartingOffset.QuadPart = StartingSector * SectorSize; + + /* Initialize waiting event */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + /* Prepare IRP */ + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, + DeviceObject, + Buffer, + SectorSize, + &StartingOffset, + &Event, + &IoStatusBlock); + if (!Irp) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Override volume verify */ + IoStackLocation = IoGetNextIrpStackLocation(Irp); + IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME; + + /* Then call driver, and wait for completion if needed */ + Status = IoCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + return Status; +} + /* FUNCTIONS *****************************************************************/ /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI IoCreateDisk(IN PDEVICE_OBJECT DeviceObject, IN struct _CREATE_DISK* Disk) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + PARTITION_STYLE PartitionStyle; + PAGED_CODE(); + + ASSERT(DeviceObject); + + /* Get partition style. If caller didn't provided data, assume it's raw */ + PartitionStyle = ((Disk) ? Disk->PartitionStyle : PARTITION_STYLE_RAW); + /* Then, call appropriate internal function */ + switch (PartitionStyle) + { + case PARTITION_STYLE_MBR: + return FstubCreateDiskMBR(DeviceObject, &(Disk->Mbr)); + case PARTITION_STYLE_GPT: + return FstubCreateDiskEFI(DeviceObject, &(Disk->Gpt)); + case PARTITION_STYLE_RAW: + return FstubCreateDiskRaw(DeviceObject); + default: + return STATUS_NOT_SUPPORTED; + } } /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI IoGetBootDiskInformation(IN OUT PBOOTDISK_INFORMATION BootDiskInformation, IN ULONG Size) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + PIRP Irp; + KEVENT Event; + PLIST_ENTRY NextEntry; + PFILE_OBJECT FileObject; + DISK_GEOMETRY DiskGeometry; + PDEVICE_OBJECT DeviceObject; + UNICODE_STRING DeviceStringW; + IO_STATUS_BLOCK IoStatusBlock; + CHAR Buffer[128], ArcBuffer[128]; + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN SingleDisk, IsBootDiskInfoEx; + PARC_DISK_SIGNATURE ArcDiskSignature; + PARC_DISK_INFORMATION ArcDiskInformation; + PARTITION_INFORMATION_EX PartitionInformation; + PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = NULL; + ULONG DiskCount, DiskNumber, Signature, PartitionNumber; + ANSI_STRING ArcBootString, ArcSystemString, DeviceStringA, ArcNameStringA; + extern PLOADER_PARAMETER_BLOCK IopLoaderBlock; + PAGED_CODE(); + + /* Get loader block. If it's null, we come to late */ + if (!IopLoaderBlock) + { + return STATUS_TOO_LATE; + } + + /* Check buffer size */ + if (Size < sizeof(BOOTDISK_INFORMATION)) + { + return STATUS_INVALID_PARAMETER; + } + + /* Init some useful stuff: + * Get arc disks information + * Check whether we have a single disk + * Check received structure size (extended or not?) + * Init boot strings (system/boot) + * Finaly, get disk count + */ + ArcDiskInformation = IopLoaderBlock->ArcDiskInformation; + SingleDisk = IsListEmpty(&(ArcDiskInformation->DiskSignatureListHead)); + IsBootDiskInfoEx = (Size >= sizeof(BOOTDISK_INFORMATION_EX)); + RtlInitAnsiString(&ArcBootString, IopLoaderBlock->ArcBootDeviceName); + RtlInitAnsiString(&ArcSystemString, IopLoaderBlock->ArcHalDeviceName); + DiskCount = IoGetConfigurationInformation()->DiskCount; + + /* If no disk, return success */ + if (DiskCount == 0) + { + return STATUS_SUCCESS; + } + + /* Now, browse all disks */ + for (DiskNumber = 0; DiskNumber < DiskCount; DiskNumber++) + { + /* Create the device name */ + sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition0", DiskNumber); + RtlInitAnsiString(&DeviceStringA, Buffer); + Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE); + if (!NT_SUCCESS(Status)) + { + continue; + } + + /* Get its device object */ + Status = IoGetDeviceObjectPointer(&DeviceStringW, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + RtlFreeUnicodeString(&DeviceStringW); + if (!NT_SUCCESS(Status)) + { + continue; + } + + /* Prepare for getting disk geometry */ + Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY, + DeviceObject, + NULL, + 0, + &DiskGeometry, + sizeof(DISK_GEOMETRY), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + ObDereferenceObject(FileObject); + continue; + } + + /* Then, call the drive, and wait for it if needed */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Status = IoCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + if (!NT_SUCCESS(Status)) + { + ObDereferenceObject(FileObject); + continue; + } + + /* Read partition table */ + Status = IoReadPartitionTableEx(DeviceObject, + &DriveLayout); + + /* FileObject, you can go! */ + ObDereferenceObject(FileObject); + + if (!NT_SUCCESS(Status)) + { + continue; + } + + /* Ensure we have at least 512 bytes per sector */ + if (DiskGeometry.BytesPerSector < 512) + { + DiskGeometry.BytesPerSector = 512; + } + + /* Now, for each arc disk, try to find the matching */ + for (NextEntry = ArcDiskInformation->DiskSignatureListHead.Flink; + NextEntry != &ArcDiskInformation->DiskSignatureListHead; + NextEntry = NextEntry->Flink) + { + ArcDiskSignature = CONTAINING_RECORD(NextEntry, + ARC_DISK_SIGNATURE, + ListEntry); + /* If they matches, ie + * - There's only one disk for both BIOS and detected + * - Signatures are matching + * - This is MBR + * (We don't check checksums here) + */ + if (((SingleDisk && DiskCount == 1) || + (IopVerifyDiskSignature(DriveLayout, ArcDiskSignature, &Signature))) && + (DriveLayout->PartitionStyle == PARTITION_STYLE_MBR)) + { + /* Create arc name */ + sprintf(ArcBuffer, "\\ArcName\\%s", ArcDiskSignature->ArcName); + RtlInitAnsiString(&ArcNameStringA, ArcBuffer); + + /* Browse all partitions */ + for (PartitionNumber = 1; PartitionNumber <= DriveLayout->PartitionCount; PartitionNumber++) + { + /* Create its device name */ + sprintf(Buffer, "\\Device\\Harddisk%lu\\Partition%lu", DiskNumber, PartitionNumber); + RtlInitAnsiString(&DeviceStringA, Buffer); + Status = RtlAnsiStringToUnicodeString(&DeviceStringW, &DeviceStringA, TRUE); + if (!NT_SUCCESS(Status)) + { + continue; + } + + /* If IopVerifyDiskSignature returned no signature, take the one from DriveLayout */ + if (!Signature) + { + Signature = DriveLayout->Mbr.Signature; + } + + /* Create partial arc name */ + sprintf(ArcBuffer, "%spartition(%lu)", ArcDiskSignature->ArcName, PartitionNumber); + RtlInitAnsiString(&ArcNameStringA, ArcBuffer); + + /* If it's matching boot string */ + if (RtlEqualString(&ArcNameStringA, &ArcBootString, TRUE)) + { + /* Then, fill in information about boot device */ + BootDiskInformation->BootDeviceSignature = Signature; + + /* Get its device object */ + Status = IoGetDeviceObjectPointer(&DeviceStringW, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + RtlFreeUnicodeString(&DeviceStringW); + continue; + } + + /* And call the drive to get information about partition */ + Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX, + DeviceObject, + NULL, + 0, + &PartitionInformation, + sizeof(PARTITION_INFORMATION_EX), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + ObDereferenceObject(FileObject); + RtlFreeUnicodeString(&DeviceStringW); + continue; + } + + /* Call & wait if needed */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Status = IoCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + if (!NT_SUCCESS(Status)) + { + ObDereferenceObject(FileObject); + RtlFreeUnicodeString(&DeviceStringW); + continue; + } + + /* We get partition offset as demanded and return it */ + BootDiskInformation->BootPartitionOffset = PartitionInformation.StartingOffset.QuadPart; + + /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */ + if (IsBootDiskInfoEx) + { + /* Is PT MBR or GPT? */ + if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT) + { + ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceGuid = DriveLayout->Gpt.DiskId; + ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceIsGpt = TRUE; + } + else + { + ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->BootDeviceIsGpt = FALSE; + } + } + + /* Dereference FileObject */ + ObDereferenceObject(FileObject); + } + + /* If it's matching system string */ + if (RtlEqualString(&ArcNameStringA, &ArcSystemString, TRUE)) + { + /* Then, fill in information about the system device */ + BootDiskInformation->SystemDeviceSignature = Signature; + + /* Get its device object */ + Status = IoGetDeviceObjectPointer(&DeviceStringW, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + RtlFreeUnicodeString(&DeviceStringW); + continue; + } + + /* And call the drive to get information about partition */ + Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX, + DeviceObject, + NULL, + 0, + &PartitionInformation, + sizeof(PARTITION_INFORMATION_EX), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + ObDereferenceObject(FileObject); + RtlFreeUnicodeString(&DeviceStringW); + continue; + } + + /* Call & wait if needed */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Status = IoCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + if (!NT_SUCCESS(Status)) + { + ObDereferenceObject(FileObject); + RtlFreeUnicodeString(&DeviceStringW); + continue; + } + + /* We get partition offset as demanded and return it */ + BootDiskInformation->SystemPartitionOffset = PartitionInformation.StartingOffset.QuadPart; + + /* If called passed a BOOTDISK_INFORMATION_EX structure, give more intel */ + if (IsBootDiskInfoEx) + { + /* Is PT MBR or GPT? */ + if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT) + { + ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceGuid = DriveLayout->Gpt.DiskId; + ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceIsGpt = TRUE; + } + else + { + ((PBOOTDISK_INFORMATION_EX)BootDiskInformation)->SystemDeviceIsGpt = FALSE; + } + } + + /* Dereference FileObject */ + ObDereferenceObject(FileObject); + } + + /* Release device string */ + RtlFreeUnicodeString(&DeviceStringW); + } + } + } + + /* Finally, release drive layout structure */ + ExFreePool(DriveLayout); + } + + /* And return */ + return Status; } /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI @@ -538,8 +2054,101 @@ IoReadDiskSignature(IN PDEVICE_OBJECT DeviceObject, IN ULONG BytesPerSector, OUT PDISK_SIGNATURE Signature) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + PULONG Buffer; + NTSTATUS Status; + ULONG HeaderCRC32, i, CheckSum; + PEFI_PARTITION_HEADER EFIHeader; + PPARTITION_DESCRIPTOR PartitionDescriptor; + PAGED_CODE(); + + /* Ensure we'll read at least 512 bytes */ + if (BytesPerSector < 512) + { + BytesPerSector = 512; + } + + /* Allocate a buffer for reading operations */ + Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, BytesPerSector, TAG_FSTUB); + if (!Buffer) + { + return STATUS_NO_MEMORY; + } + + /* Read first sector (sector 0) for MBR */ + Status = FstubReadSector(DeviceObject, + BytesPerSector, + 0ULL, + (PUSHORT)Buffer); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + + /* Get the partition descriptor array */ + PartitionDescriptor = (PPARTITION_DESCRIPTOR) + &(Buffer[PARTITION_TABLE_OFFSET]); + /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */ + if (PartitionDescriptor[0].PartitionType == EFI_PMBR_OSTYPE_EFI && + PartitionDescriptor[1].PartitionType == 0 && + PartitionDescriptor[2].PartitionType == 0 && + PartitionDescriptor[3].PartitionType == 0) + { + /* If we have GPT, read second sector (sector 1) for GPT header */ + Status = FstubReadSector(DeviceObject, + BytesPerSector, + 1ULL, + (PUSHORT)Buffer); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + EFIHeader = (PEFI_PARTITION_HEADER)Buffer; + + /* First check signature + * Then, check version (we only support v1 + * Finally check header size + */ + if (EFIHeader->Signature != EFI_HEADER_SIGNATURE || + EFIHeader->Revision != EFI_HEADER_REVISION_1 || + EFIHeader->HeaderSize != sizeof(EFI_PARTITION_HEADER)) + { + Status = STATUS_DISK_CORRUPT_ERROR; + goto Cleanup; + } + + /* Save current checksum */ + HeaderCRC32 = EFIHeader->HeaderCRC32; + /* Then zero the one in EFI header. This is needed to compute header checksum */ + EFIHeader->HeaderCRC32 = 0; + /* Compute header checksum and compare with the one present in partition table */ + if (RtlComputeCrc32(0, (PUCHAR)Buffer, sizeof(EFI_PARTITION_HEADER)) != HeaderCRC32) + { + Status = STATUS_DISK_CORRUPT_ERROR; + goto Cleanup; + } + + /* Set partition table style to GPT and return disk GUID */ + Signature->PartitionStyle = PARTITION_STYLE_GPT; + Signature->Gpt.DiskId = EFIHeader->DiskGUID; + } + else + { + /* Compute MBR checksum */ + for (i = 0, CheckSum = 0; i < 512 / sizeof(ULONG) ; i++) + { + CheckSum += Buffer[i]; + } + + /* Set partition table style to MBR and return signature (offset 440) and checksum */ + Signature->PartitionStyle = PARTITION_STYLE_MBR; + Signature->Mbr.Signature = Buffer[PARTITION_TABLE_OFFSET / 2 - 1]; + Signature->Mbr.CheckSum = CheckSum; + } + +Cleanup: + /* Free buffer and return */ + ExFreePoolWithTag(Buffer, TAG_FSTUB); + return Status; } /* @@ -610,7 +2219,7 @@ IoReadPartitionTableEx(IN PDEVICE_OBJECT DeviceObject, } /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI @@ -618,32 +2227,207 @@ IoSetPartitionInformationEx(IN PDEVICE_OBJECT DeviceObject, IN ULONG PartitionNumber, IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PDISK_INFORMATION Disk; + PARTITION_STYLE PartitionStyle; + PAGED_CODE(); + + ASSERT(DeviceObject); + ASSERT(PartitionInfo); + + /* Debug given modifications */ + FstubDbgPrintSetPartitionEx(PartitionInfo, PartitionNumber); + + /* Allocate internal structure */ + Status = FstubAllocateDiskInformation(DeviceObject, &Disk, NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Get partition table style on disk */ + Status = FstubDetectPartitionStyle(Disk, &PartitionStyle); + if (!NT_SUCCESS(Status)) + { + FstubFreeDiskInformation(Disk); + return Status; + } + + /* If it's not matching partition style given in modifications, give up */ + if (PartitionInfo->PartitionStyle != PartitionStyle) + { + FstubFreeDiskInformation(Disk); + return STATUS_INVALID_PARAMETER; + } + + /* Finally, handle modifications using proper function */ + switch (PartitionStyle) + { + case PARTITION_STYLE_MBR: + Status = IoSetPartitionInformation(DeviceObject, + Disk->SectorSize, + PartitionNumber, + PartitionInfo->Mbr.PartitionType); + break; + case PARTITION_STYLE_GPT: + Status = FstubSetPartitionInformationEFI(Disk, + PartitionNumber, + &(PartitionInfo->Gpt)); + break; + default: + Status = STATUS_NOT_SUPPORTED; + } + + /* Release internal structure and return */ + FstubFreeDiskInformation(Disk); + return Status; } /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI IoVerifyPartitionTable(IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN FixErrors) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PDISK_INFORMATION Disk; + PARTITION_STYLE PartitionStyle; + PAGED_CODE(); + + ASSERT(DeviceObject); + + /* Allocate internal structure */ + Status = FstubAllocateDiskInformation(DeviceObject, &Disk, NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + ASSERT(Disk); + + /* Get partition table style on disk */ + Status = FstubDetectPartitionStyle(Disk, &PartitionStyle); + if (!NT_SUCCESS(Status)) + { + FstubFreeDiskInformation(Disk); + return Status; + } + + /* Action will depend on partition style */ + switch (PartitionStyle) + { + /* For MBR, assume it's always OK */ + case PARTITION_STYLE_MBR: + Status = STATUS_SUCCESS; + break; + /* For GPT, call internal function */ + case PARTITION_STYLE_GPT: + Status = FstubVerifyPartitionTableEFI(Disk, FixErrors); + break; + /* Otherwise, signal we can't work */ + default: + Status = STATUS_NOT_SUPPORTED; + } + + /* Release internal structure and return */ + FstubFreeDiskInformation(Disk); + return Status; } /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI IoWritePartitionTableEx(IN PDEVICE_OBJECT DeviceObject, - IN struct _DRIVE_LAYOUT_INFORMATION_EX* DriveLayfout) + IN struct _DRIVE_LAYOUT_INFORMATION_EX* DriveLayout) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PDISK_INFORMATION Disk; + ULONGLONG SectorsForPartitions; + EFI_PARTITION_HEADER EfiHeader; + PAGED_CODE(); + + ASSERT(DeviceObject); + ASSERT(DriveLayout); + + /* Debug partition table that must be written */ + FstubDbgPrintDriveLayoutEx(DriveLayout); + + /* Allocate internal structure */ + Status = FstubAllocateDiskInformation(DeviceObject, &Disk, 0); + if (!NT_SUCCESS(Status)) + { + return Status; + } + ASSERT(Disk); + + switch (DriveLayout->PartitionStyle) + { + case PARTITION_STYLE_MBR: + Status = FstubWritePartitionTableMBR(Disk, DriveLayout); + break; + + case PARTITION_STYLE_GPT: + /* Read primary table header */ + Status = FstubReadHeaderEFI(Disk, + FALSE, + &EfiHeader); + /* If it failed, try reading back table header */ + if (!NT_SUCCESS(Status)) + { + Status = FstubReadHeaderEFI(Disk, + TRUE, + &EfiHeader); + } + + /* We have a header! */ + if (NT_SUCCESS(Status)) + { + /* Check if there are enough places for the partitions to be written */ + if (DriveLayout->PartitionCount <= EfiHeader.NumberOfEntries) + { + /* Count number of sectors needed to store partitions */ + SectorsForPartitions = (EfiHeader.NumberOfEntries * PARTITION_ENTRY_SIZE) / Disk->SectorSize; + /* Set first usable LBA: Legacy MBR + GPT header + Partitions entries */ + EfiHeader.FirstUsableLBA = SectorsForPartitions + 2; + /* Set last usable LBA: Last sector - GPT header - Partitions entries */ + EfiHeader.LastUsableLBA = Disk->SectorCount - SectorsForPartitions - 1; + /* Write primary table */ + Status = FstubWritePartitionTableEFI(Disk, + EfiHeader.DiskGUID, + EfiHeader.NumberOfEntries, + EfiHeader.FirstUsableLBA, + EfiHeader.LastUsableLBA, + FALSE, + DriveLayout->PartitionCount, + DriveLayout->PartitionEntry); + /* If it succeed, also update backup table */ + if (NT_SUCCESS(Status)) + { + Status = FstubWritePartitionTableEFI(Disk, + EfiHeader.DiskGUID, + EfiHeader.NumberOfEntries, + EfiHeader.FirstUsableLBA, + EfiHeader.LastUsableLBA, + TRUE, + DriveLayout->PartitionCount, + DriveLayout->PartitionEntry); + } + } + } + break; + + default: + DPRINT("Unsupported partition style: %ld\n", DriveLayout->PartitionStyle); + Status = STATUS_NOT_SUPPORTED; + } + + /* It's over, internal structure not needed anymore */ + FstubFreeDiskInformation(Disk); + + return Status; } /* EOF */ diff --git a/ntoskrnl/include/internal/mm.h b/ntoskrnl/include/internal/mm.h index c11fe04d6cc..25360af7416 100644 --- a/ntoskrnl/include/internal/mm.h +++ b/ntoskrnl/include/internal/mm.h @@ -114,10 +114,8 @@ typedef ULONG SWAPENTRY; #define MC_CACHE (0) #define MC_USER (1) -#define MC_PPOOL (2) -#define MC_NPPOOL (3) -#define MC_SYSTEM (4) -#define MC_MAXIMUM (5) +#define MC_SYSTEM (2) +#define MC_MAXIMUM (3) #define PAGED_POOL_MASK 1 #define MUST_SUCCEED_POOL_MASK 2 @@ -279,6 +277,17 @@ typedef struct _MEMORY_AREA } Data; } MEMORY_AREA, *PMEMORY_AREA; +typedef struct _MM_RMAP_ENTRY +{ + struct _MM_RMAP_ENTRY* Next; + PEPROCESS Process; + PVOID Address; +#if DBG + PVOID Caller; +#endif +} +MM_RMAP_ENTRY, *PMM_RMAP_ENTRY; + // // These two mappings are actually used by Windows itself, based on the ASSERTS // @@ -1670,20 +1679,6 @@ MmCallDllInitialize( IN PLIST_ENTRY ListHead ); -/* ReactOS Mm Hacks */ -VOID -FASTCALL -MiSyncForProcessAttach( - IN PKTHREAD NextThread, - IN PEPROCESS Process -); - -VOID -FASTCALL -MiSyncForContextSwitch( - IN PKTHREAD Thread -); - extern PMMSUPPORT MmKernelAddressSpace; FORCEINLINE diff --git a/ntoskrnl/ke/i386/ctxswitch.S b/ntoskrnl/ke/i386/ctxswitch.S index 4af3f9b504d..716db3b3b11 100644 --- a/ntoskrnl/ke/i386/ctxswitch.S +++ b/ntoskrnl/ke/i386/ctxswitch.S @@ -584,10 +584,6 @@ CheckNext: #endif SwapContext: - /* ReactOS Mm Hack */ - mov ecx, esi - call @MiSyncForContextSwitch@4 - /* Swap context at APC_LEVEL */ mov ecx, APC_LEVEL call @KiSwapContextInternal@0 diff --git a/ntoskrnl/ke/procobj.c b/ntoskrnl/ke/procobj.c index f187ec1dd71..ce337feab2a 100644 --- a/ntoskrnl/ke/procobj.c +++ b/ntoskrnl/ke/procobj.c @@ -96,9 +96,6 @@ KiAttachProcess(IN PKTHREAD Thread, /* Release lock */ KiReleaseApcLockFromDpcLevel(ApcLock); - - /* Make sure that we are in the right page directory (ReactOS Mm Hack) */ - MiSyncForProcessAttach(Thread, (PEPROCESS)Process); /* Swap Processes */ KiSwapProcess(Process, SavedApcState->Process); @@ -450,9 +447,6 @@ KeAttachProcess(IN PKPROCESS Process) ASSERT_PROCESS(Process); ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); - /* Make sure that we are in the right page directory (ReactOS Mm Hack) */ - MiSyncForProcessAttach(Thread, (PEPROCESS)Process); - /* Check if we're already in that process */ if (Thread->ApcState.Process == Process) return; diff --git a/ntoskrnl/ke/thrdobj.c b/ntoskrnl/ke/thrdobj.c index e73f4ff9d29..b23741f6a28 100644 --- a/ntoskrnl/ke/thrdobj.c +++ b/ntoskrnl/ke/thrdobj.c @@ -810,9 +810,6 @@ KeInitThread(IN OUT PKTHREAD Thread, Thread->StackLimit = (ULONG_PTR)KernelStack - KERNEL_STACK_SIZE; Thread->KernelStackResident = TRUE; - /* Make sure that we are in the right page directory (ReactOS Mm Hack) */ - MiSyncForProcessAttach(Thread, (PEPROCESS)Process); - /* Enter SEH to avoid crashes due to user mode */ Status = STATUS_SUCCESS; _SEH2_TRY diff --git a/ntoskrnl/ke/thrdschd.c b/ntoskrnl/ke/thrdschd.c index 87246f0b778..040073cdb09 100644 --- a/ntoskrnl/ke/thrdschd.c +++ b/ntoskrnl/ke/thrdschd.c @@ -386,9 +386,6 @@ KiSwapThread(IN PKTHREAD CurrentThread, /* Save the wait IRQL */ WaitIrql = CurrentThread->WaitIrql; - /* REACTOS Mm Hack of Doom */ - MiSyncForContextSwitch(NextThread); - /* Swap contexts */ ApcState = KiSwapContext(CurrentThread, NextThread); @@ -756,9 +753,6 @@ NtYieldExecution(VOID) /* Sanity check */ ASSERT(OldIrql <= DISPATCH_LEVEL); - /* REACTOS Mm Hack of Doom */ - MiSyncForContextSwitch(NextThread); - /* Swap to new thread */ KiSwapContext(Thread, NextThread); Status = STATUS_SUCCESS; diff --git a/ntoskrnl/mm/ARM3/mdlsup.c b/ntoskrnl/mm/ARM3/mdlsup.c index c7dd00db38e..6d63db132b7 100644 --- a/ntoskrnl/mm/ARM3/mdlsup.c +++ b/ntoskrnl/mm/ARM3/mdlsup.c @@ -16,8 +16,11 @@ #define MODULE_INVOLVED_IN_ARM3 #include "../ARM3/miarm.h" +/* GLOBALS ********************************************************************/ + BOOLEAN MmTrackPtes; BOOLEAN MmTrackLockedPages; +SIZE_T MmSystemLockPagesCount; /* PUBLIC FUNCTIONS ***********************************************************/ @@ -248,34 +251,47 @@ MmFreePagesFromMdl(IN PMDL Mdl) // // Reached the last page // - if (*Pages == -1) break; - - // - // Sanity check - // - ASSERT(*Pages <= MmHighestPhysicalPage); - + if (*Pages == LIST_HEAD) break; + // // Get the page entry // Pfn1 = MiGetPfnEntry(*Pages); - ASSERT(Pfn1->u3.ReferenceCount == 1); + ASSERT(Pfn1); + ASSERT(Pfn1->u2.ShareCount == 1); + ASSERT(MI_IS_PFN_DELETED(Pfn1) == TRUE); + if (Pfn1->u4.PteFrame != 0x1FFEDCB) + { + /* Corrupted PFN entry or invalid free */ + KeBugCheckEx(MEMORY_MANAGEMENT, 0x1236, (ULONG_PTR)Mdl, (ULONG_PTR)Pages, *Pages); + } // // Clear it // Pfn1->u3.e1.StartOfAllocation = 0; Pfn1->u3.e1.EndOfAllocation = 0; + Pfn1->u2.ShareCount == 0; // // Dereference it // - MmDereferencePage(*Pages); + ASSERT(Pfn1->u3.e2.ReferenceCount != 0); + if (Pfn1->u3.e2.ReferenceCount != 1) + { + /* Just take off one reference */ + InterlockedDecrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount); + } + else + { + /* We'll be nuking the whole page */ + MiDecrementReferenceCount(Pfn1, *Pages); + } // // Clear this page and move on // - *Pages++ = -1; + *Pages++ = LIST_HEAD; } while (--NumberOfPages != 0); // @@ -411,7 +427,7 @@ MmMapLockedPagesSpecifyCache(IN PMDL Mdl, // // We're done here // - if (*MdlPages == -1) break; + if (*MdlPages == LIST_HEAD) break; // // Write the PTE @@ -573,13 +589,14 @@ MmProbeAndLockPages(IN PMDL Mdl, ULONG LockPages, TotalPages; NTSTATUS Status = STATUS_SUCCESS; PEPROCESS CurrentProcess; - PMMSUPPORT AddressSpace; NTSTATUS ProbeStatus; PMMPTE PointerPte, LastPte; PMMPDE PointerPde; PFN_NUMBER PageFrameIndex; BOOLEAN UsePfnLock; KIRQL OldIrql; + USHORT OldRefCount, RefCount; + PMMPFN Pfn1; DPRINT("Probing MDL: %p\n", Mdl); // @@ -608,8 +625,17 @@ MmProbeAndLockPages(IN PMDL Mdl, LockPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Address, Mdl->ByteCount); ASSERT(LockPages != 0); + /* Block invalid access */ + if ((AccessMode != KernelMode) && + ((LastAddress > (PVOID)MM_USER_PROBE_ADDRESS) || (Address >= LastAddress))) + { + /* Caller should be in SEH, raise the error */ + *MdlPages = LIST_HEAD; + ExRaiseStatus(STATUS_ACCESS_VIOLATION); + } + // - // Get theprocess + // Get the process // if (Address <= MM_HIGHEST_USER_ADDRESS) { @@ -632,6 +658,9 @@ MmProbeAndLockPages(IN PMDL Mdl, TotalPages = LockPages; StartAddress = Address; + /* Large pages not supported */ + ASSERT(!MI_IS_PHYSICAL_ADDRESS(Address)); + // // Now probe them // @@ -646,7 +675,7 @@ MmProbeAndLockPages(IN PMDL Mdl, // // Assume failure // - *MdlPages = -1; + *MdlPages = LIST_HEAD; // // Read @@ -668,8 +697,7 @@ MmProbeAndLockPages(IN PMDL Mdl, // // Next address... // - Address = (PVOID)((ULONG_PTR)Address + PAGE_SIZE); - Address = PAGE_ALIGN(Address); + Address = PAGE_ALIGN((ULONG_PTR)Address + PAGE_SIZE); // // Next page... @@ -711,6 +739,10 @@ MmProbeAndLockPages(IN PMDL Mdl, // PointerPte = MiAddressToPte(StartAddress); PointerPde = MiAddressToPde(StartAddress); +#if (_MI_PAGING_LEVELS >= 3) + DPRINT1("PAE/x64 Not Implemented\n"); + ASSERT(FALSE); +#endif // // Sanity check @@ -761,7 +793,6 @@ MmProbeAndLockPages(IN PMDL Mdl, // UsePfnLock = TRUE; OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); - AddressSpace = NULL; // Keep compiler happy } else { @@ -782,13 +813,10 @@ MmProbeAndLockPages(IN PMDL Mdl, // Mdl->Process = CurrentProcess; - // - // Use the process lock - // + /* Lock the process working set */ + MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); UsePfnLock = FALSE; - AddressSpace = &CurrentProcess->Vm; - MmLockAddressSpace(AddressSpace); - OldIrql = DISPATCH_LEVEL; // Keep compiler happy + OldIrql = MM_NOIRQL; } // @@ -804,7 +832,7 @@ MmProbeAndLockPages(IN PMDL Mdl, // // Assume failure and check for non-mapped pages // - *MdlPages = -1; + *MdlPages = LIST_HEAD; #if (_MI_PAGING_LEVELS >= 3) /* Should be checking the PPE and PXE */ ASSERT(FALSE); @@ -824,10 +852,8 @@ MmProbeAndLockPages(IN PMDL Mdl, } else { - // - // Release process address space lock - // - MmUnlockAddressSpace(AddressSpace); + /* Release process working set */ + MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); } // @@ -856,10 +882,8 @@ MmProbeAndLockPages(IN PMDL Mdl, } else { - // - // Use the address space lock - // - MmLockAddressSpace(AddressSpace); + /* Lock the process working set */ + MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); } } @@ -896,10 +920,8 @@ MmProbeAndLockPages(IN PMDL Mdl, } else { - // - // Release process address space lock - // - MmUnlockAddressSpace(AddressSpace); + /* Release process working set */ + MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); } // @@ -927,10 +949,8 @@ MmProbeAndLockPages(IN PMDL Mdl, } else { - // - // Use the address space lock - // - MmLockAddressSpace(AddressSpace); + /* Lock the process working set */ + MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); } // @@ -952,14 +972,55 @@ MmProbeAndLockPages(IN PMDL Mdl, // Grab the PFN // PageFrameIndex = PFN_FROM_PTE(PointerPte); - if (PageFrameIndex <= MmHighestPhysicalPage) + Pfn1 = MiGetPfnEntry(PageFrameIndex); + if (Pfn1) { + /* Either this is for kernel-mode, or the working set is held */ ASSERT((CurrentProcess == NULL) || (UsePfnLock == FALSE)); - // - // Now lock the page - // - MmReferencePage(PageFrameIndex); + /* No Physical VADs supported yet */ + if (CurrentProcess) ASSERT(CurrentProcess->PhysicalVadRoot == NULL); + + /* This address should already exist and be fully valid */ + ASSERT(Pfn1->u3.e2.ReferenceCount != 0); + if (MI_IS_ROS_PFN(Pfn1)) + { + /* ReactOS Mm doesn't track share count */ + ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid); + } + else + { + /* On ARM3 pages, we should see a valid share count */ + ASSERT((Pfn1->u2.ShareCount != 0) && (Pfn1->u3.e1.PageLocation == ActiveAndValid)); + + /* We don't support mapping a prototype page yet */ + ASSERT((Pfn1->u3.e1.PrototypePte == 0) && (Pfn1->OriginalPte.u.Soft.Prototype == 0)); + } + + /* More locked pages! */ + InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, 1); + + /* Loop trying to update the reference count */ + do + { + /* Get the current reference count, make sure it's valid */ + OldRefCount = Pfn1->u3.e2.ReferenceCount; + ASSERT(OldRefCount != 0); + ASSERT(OldRefCount < 2500); + + /* Bump it up by one */ + RefCount = InterlockedCompareExchange16((PSHORT)&Pfn1->u3.e2.ReferenceCount, + OldRefCount + 1, + OldRefCount); + ASSERT(RefCount != 0); + } while (OldRefCount != RefCount); + + /* Was this the first lock attempt? */ + if (OldRefCount != 1) + { + /* Someone else came through */ + InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, -1); + } } else { @@ -973,7 +1034,10 @@ MmProbeAndLockPages(IN PMDL Mdl, // Write the page and move on // *MdlPages++ = PageFrameIndex; - if (!((ULONG_PTR)(++PointerPte) & (PAGE_SIZE - 1))) PointerPde++; + PointerPte++; + + /* Check if we're on a PDE boundary */ + if (!((ULONG_PTR)PointerPte & (PD_SIZE - 1))) PointerPde++; } while (PointerPte <= LastPte); // @@ -988,10 +1052,8 @@ MmProbeAndLockPages(IN PMDL Mdl, } else { - // - // Release process address space lock - // - MmUnlockAddressSpace(AddressSpace); + /* Release process working set */ + MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); } // @@ -1018,10 +1080,8 @@ CleanupWithLock: } else { - // - // Release process address space lock - // - MmUnlockAddressSpace(AddressSpace); + /* Release process working set */ + MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); } Cleanup: // @@ -1048,6 +1108,8 @@ MmUnlockPages(IN PMDL Mdl) PVOID Base; ULONG Flags, PageCount; KIRQL OldIrql; + USHORT RefCount, OldRefCount; + PMMPFN Pfn1; DPRINT("Unlocking MDL: %p\n", Mdl); // @@ -1107,17 +1169,71 @@ MmUnlockPages(IN PMDL Mdl) // // Last page, break out // - if (*MdlPages == -1) break; + if (*MdlPages == LIST_HEAD) break; // // Check if this page is in the PFN database // - if (*MdlPages <= MmHighestPhysicalPage) + Pfn1 = MiGetPfnEntry(*MdlPages); + if (Pfn1); { - // - // Unlock and dereference - // - MmDereferencePage(*MdlPages); + /* Get the current entry and reference count */ + OldRefCount = Pfn1->u3.e2.ReferenceCount; + ASSERT(OldRefCount != 0); + + /* Is this already the last dereference */ + if (OldRefCount == 1) + { + /* It should be on a free list waiting for us */ + ASSERT(Pfn1->u3.e2.ReferenceCount == 1); + ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid); + ASSERT(Pfn1->u2.ShareCount == 0); + + /* Not supported yet */ + ASSERT(((Pfn1->u3.e1.PrototypePte == 0) && + (Pfn1->OriginalPte.u.Soft.Prototype == 0))); + + /* One less page */ + InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, -1); + + /* Do the last dereference, we're done here */ + MiDecrementReferenceCount(Pfn1, *MdlPages); + } + else + { + /* Loop decrementing one reference */ + do + { + /* Make sure it's still valid */ + OldRefCount = Pfn1->u3.e2.ReferenceCount; + ASSERT(OldRefCount != 0); + + /* Take off one reference */ + RefCount = InterlockedCompareExchange16((PSHORT)&Pfn1->u3.e2.ReferenceCount, + OldRefCount - 1, + OldRefCount); + ASSERT(RefCount != 0); + } while (OldRefCount != RefCount); + ASSERT(RefCount > 1); + + /* Are there only lock references left? */ + if (RefCount == 2) + { + /* And does the page still have users? */ + if (Pfn1->u2.ShareCount >= 1) + { + /* Then it should still be valid */ + ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid); + + /* Not supported yet */ + ASSERT(((Pfn1->u3.e1.PrototypePte == 0) && + (Pfn1->OriginalPte.u.Soft.Prototype == 0))); + + /* But there is one less "locked" page though */ + InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, -1); + } + } + } } } while (++MdlPages < LastPage); @@ -1169,7 +1285,7 @@ MmUnlockPages(IN PMDL Mdl) // // Last page reached // - if (*MdlPages == -1) + if (*MdlPages == LIST_HEAD) { // // Were there no pages at all? @@ -1190,10 +1306,9 @@ MmUnlockPages(IN PMDL Mdl) break; } - // - // Sanity check - // - ASSERT(*MdlPages <= MmHighestPhysicalPage); + /* Save the PFN entry instead for the secondary loop */ + *MdlPages = (PFN_NUMBER)MiGetPfnEntry(*MdlPages); + ASSERT((*MdlPages) != 0); } while (++MdlPages < LastPage); // @@ -1207,10 +1322,64 @@ MmUnlockPages(IN PMDL Mdl) OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); do { - // - // Unlock and dereference - // - MmDereferencePage(*MdlPages); + /* Get the current entry and reference count */ + Pfn1 = (PMMPFN)(*MdlPages); + OldRefCount = Pfn1->u3.e2.ReferenceCount; + ASSERT(OldRefCount != 0); + + /* Is this already the last dereference */ + if (OldRefCount == 1) + { + /* It should be on a free list waiting for us */ + ASSERT(Pfn1->u3.e2.ReferenceCount == 1); + ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid); + ASSERT(Pfn1->u2.ShareCount == 0); + + /* Not supported yet */ + ASSERT(((Pfn1->u3.e1.PrototypePte == 0) && + (Pfn1->OriginalPte.u.Soft.Prototype == 0))); + + /* One less page */ + InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, -1); + + /* Do the last dereference, we're done here */ + MiDecrementReferenceCount(Pfn1, *MdlPages); + } + else + { + /* Loop decrementing one reference */ + do + { + /* Make sure it's still valid */ + OldRefCount = Pfn1->u3.e2.ReferenceCount; + ASSERT(OldRefCount != 0); + + /* Take off one reference */ + RefCount = InterlockedCompareExchange16((PSHORT)&Pfn1->u3.e2.ReferenceCount, + OldRefCount - 1, + OldRefCount); + ASSERT(RefCount != 0); + } while (OldRefCount != RefCount); + ASSERT(RefCount > 1); + + /* Are there only lock references left? */ + if (RefCount == 2) + { + /* And does the page still have users? */ + if (Pfn1->u2.ShareCount >= 1) + { + /* Then it should still be valid */ + ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid); + + /* Not supported yet */ + ASSERT(((Pfn1->u3.e1.PrototypePte == 0) && + (Pfn1->OriginalPte.u.Soft.Prototype == 0))); + + /* But there is one less "locked" page though */ + InterlockedExchangeAddSizeT(&MmSystemLockPagesCount, -1); + } + } + } } while (++MdlPages < LastPage); // diff --git a/ntoskrnl/mm/ARM3/miarm.h b/ntoskrnl/mm/ARM3/miarm.h index e8439dc5e11..c736bc38c80 100644 --- a/ntoskrnl/mm/ARM3/miarm.h +++ b/ntoskrnl/mm/ARM3/miarm.h @@ -45,9 +45,7 @@ #define MM_HIGHEST_VAD_ADDRESS \ (PVOID)((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (16 * PAGE_SIZE)) - -/* The range 0x10000->0x7FEFFFFF is reserved for the ROSMM MAREA Allocator */ -#define MI_LOWEST_VAD_ADDRESS (PVOID)0x7FF00000 +#define MI_LOWEST_VAD_ADDRESS (PVOID)MM_LOWEST_USER_ADDRESS #endif /* !_M_AMD64 */ @@ -152,6 +150,7 @@ C_ASSERT(SYSTEM_PD_SIZE == PAGE_SIZE); #endif extern const ULONG MmProtectToPteMask[32]; +extern const ULONG MmProtectToValue[32]; // // Assertions for session images, addresses, and PTEs @@ -242,7 +241,7 @@ extern const ULONG MmProtectToPteMask[32]; // #define MiProtoPteToPte(x) \ (PMMPTE)((ULONG_PTR)MmPagedPoolStart + \ - ((x)->u.Proto.ProtoAddressHigh | (x)->u.Proto.ProtoAddressLow)) + (((x)->u.Proto.ProtoAddressHigh << 7) | (x)->u.Proto.ProtoAddressLow)) #endif // @@ -625,10 +624,11 @@ MI_MAKE_PROTOTYPE_PTE(IN PMMPTE NewPte, * lets us only use 28 bits for the adress of the PTE */ Offset = (ULONG_PTR)PointerPte - (ULONG_PTR)MmPagedPoolStart; - + /* 7 bits go in the "low", and the other 21 bits go in the "high" */ NewPte->u.Proto.ProtoAddressLow = Offset & 0x7F; - NewPte->u.Proto.ProtoAddressHigh = Offset & 0xFFFFF80; + NewPte->u.Proto.ProtoAddressHigh = (Offset & 0xFFFFFF80) >> 7; + ASSERT(MiProtoPteToPte(NewPte) == PointerPte); } #endif @@ -833,7 +833,23 @@ MiUnlockWorkingSet(IN PETHREAD Thread, KeLeaveGuardedRegion(); } -NTSTATUS +// +// Returns the ProtoPTE inside a VAD for the given VPN +// +FORCEINLINE +PMMPTE +MI_GET_PROTOTYPE_PTE_FOR_VPN(IN PMMVAD Vad, + IN ULONG_PTR Vpn) +{ + PMMPTE ProtoPte; + + /* Find the offset within the VAD's prototype PTEs */ + ProtoPte = Vad->FirstPrototypePte + (Vpn - Vad->StartingVpn); + ASSERT(ProtoPte <= Vad->LastContiguousPte); + return ProtoPte; +} + +BOOLEAN NTAPI MmArmInitSystem( IN ULONG Phase, @@ -1059,6 +1075,13 @@ MiDecrementShareCount( IN PFN_NUMBER PageFrameIndex ); +VOID +NTAPI +MiDecrementReferenceCount( + IN PMMPFN Pfn1, + IN PFN_NUMBER PageFrameIndex +); + PFN_NUMBER NTAPI MiRemoveAnyPage( @@ -1204,6 +1227,42 @@ MiMakeProtectionMask( IN ULONG Protect ); +VOID +NTAPI +MiDeleteVirtualAddresses( + IN ULONG_PTR Va, + IN ULONG_PTR EndingAddress, + IN PMMVAD Vad +); + +ULONG +NTAPI +MiMakeSystemAddressValid( + IN PVOID PageTableVirtualAddress, + IN PEPROCESS CurrentProcess +); + +ULONG +NTAPI +MiMakeSystemAddressValidPfn( + IN PVOID VirtualAddress, + IN KIRQL OldIrql +); + +VOID +NTAPI +MiRemoveMappedView( + IN PEPROCESS CurrentProcess, + IN PMMVAD Vad +); + +PSUBSECTION +NTAPI +MiLocateSubsection( + IN PMMVAD Vad, + IN ULONG_PTR Vpn +); + // // MiRemoveZeroPage will use inline code to zero out the page manually if only // free pages are available. In some scenarios, we don't/can't run that piece of @@ -1219,4 +1278,18 @@ MiRemoveZeroPageSafe(IN ULONG Color) return 0; } +// +// New ARM3<->RosMM PAGE Architecture +// +#define MI_GET_ROS_DATA(x) ((PMMROSPFN)(x->RosMmData)) +#define MI_IS_ROS_PFN(x) (((x)->u4.AweAllocation == TRUE) && (MI_GET_ROS_DATA(x) != NULL)) +#define ASSERT_IS_ROS_PFN(x) ASSERT(MI_IS_ROS_PFN(x) == TRUE); +typedef struct _MMROSPFN +{ + PMM_RMAP_ENTRY RmapListHead; + SWAPENTRY SwapEntry; +} MMROSPFN, *PMMROSPFN; + +#define RosMmData AweReferenceCount + /* EOF */ diff --git a/ntoskrnl/mm/ARM3/mminit.c b/ntoskrnl/mm/ARM3/mminit.c index 76df1586996..2a63047f837 100644 --- a/ntoskrnl/mm/ARM3/mminit.c +++ b/ntoskrnl/mm/ARM3/mminit.c @@ -353,33 +353,16 @@ SIZE_T MmAllocationFragment; SIZE_T MmTotalCommitLimit; SIZE_T MmTotalCommitLimitMaximum; -/* PRIVATE FUNCTIONS **********************************************************/ - -#ifndef _M_AMD64 -// -// In Bavaria, this is probably a hate crime -// -VOID -FASTCALL -MiSyncARM3WithROS(IN PVOID AddressStart, - IN PVOID AddressEnd) -{ - // - // Puerile piece of junk-grade carbonized horseshit puss sold to the lowest bidder - // - ULONG Pde = ADDR_TO_PDE_OFFSET(AddressStart); - while (Pde <= ADDR_TO_PDE_OFFSET(AddressEnd)) - { - // - // This both odious and heinous - // - extern ULONG MmGlobalKernelPageDirectory[1024]; - MmGlobalKernelPageDirectory[Pde] = ((PULONG)PDE_BASE)[Pde]; - Pde++; - } -} +/* Internal setting used for debugging memory descriptors */ +BOOLEAN MiDbgEnableMdDump = +#ifdef _ARM_ +TRUE; +#else +FALSE; #endif +/* PRIVATE FUNCTIONS **********************************************************/ + PFN_NUMBER NTAPI MxGetNextPage(IN PFN_NUMBER PageCount) @@ -1679,7 +1662,58 @@ MiBuildPagedPool(VOID) MiInitializeSystemSpaceMap(NULL); } -NTSTATUS +VOID +NTAPI +MiDbgDumpMemoryDescriptors(VOID) +{ + PLIST_ENTRY NextEntry; + PMEMORY_ALLOCATION_DESCRIPTOR Md; + ULONG TotalPages = 0; + PCHAR + MemType[] = + { + "ExceptionBlock ", + "SystemBlock ", + "Free ", + "Bad ", + "LoadedProgram ", + "FirmwareTemporary ", + "FirmwarePermanent ", + "OsloaderHeap ", + "OsloaderStack ", + "SystemCode ", + "HalCode ", + "BootDriver ", + "ConsoleInDriver ", + "ConsoleOutDriver ", + "StartupDpcStack ", + "StartupKernelStack", + "StartupPanicStack ", + "StartupPcrPage ", + "StartupPdrPage ", + "RegistryData ", + "MemoryData ", + "NlsData ", + "SpecialMemory ", + "BBTMemory ", + "LoaderReserve ", + "LoaderXIPRom " + }; + + DPRINT1("Base\t\tLength\t\tType\n"); + for (NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink; + NextEntry != &KeLoaderBlock->MemoryDescriptorListHead; + NextEntry = NextEntry->Flink) + { + Md = CONTAINING_RECORD(NextEntry, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry); + DPRINT1("%08lX\t%08lX\t%s\n", Md->BasePage, Md->PageCount, MemType[Md->MemoryType]); + TotalPages += Md->PageCount; + } + + DPRINT1("Total: %08lX (%d MB)\n", TotalPages, (TotalPages * PAGE_SIZE) / 1024 / 1024); +} + +BOOLEAN NTAPI MmArmInitSystem(IN ULONG Phase, IN PLOADER_PARAMETER_BLOCK LoaderBlock) @@ -1690,6 +1724,9 @@ MmArmInitSystem(IN ULONG Phase, PPHYSICAL_MEMORY_RUN Run; PFN_NUMBER PageCount; + /* Dump memory descriptors */ + if (MiDbgEnableMdDump) MiDbgDumpMemoryDescriptors(); + // // Instantiate memory that we don't consider RAM/usable // We use the same exclusions that Windows does, in order to try to be @@ -1892,14 +1929,7 @@ MmArmInitSystem(IN ULONG Phase, /* Initialize the platform-specific parts */ MiInitMachineDependent(LoaderBlock); - - // - // Sync us up with ReactOS Mm - // - MiSyncARM3WithROS(MmNonPagedSystemStart, (PVOID)((ULONG_PTR)MmNonPagedPoolEnd - 1)); - MiSyncARM3WithROS(MmPfnDatabase, (PVOID)((ULONG_PTR)MmNonPagedPoolStart + MmSizeOfNonPagedPoolInBytes - 1)); - MiSyncARM3WithROS((PVOID)HYPER_SPACE, (PVOID)(HYPER_SPACE + PAGE_SIZE - 1)); - + // // Build the physical memory block // @@ -2097,7 +2127,7 @@ MmArmInitSystem(IN ULONG Phase, // // Always return success for now // - return STATUS_SUCCESS; + return TRUE; } /* EOF */ diff --git a/ntoskrnl/mm/ARM3/pagfault.c b/ntoskrnl/mm/ARM3/pagfault.c index 5188988c5cd..50304dad896 100644 --- a/ntoskrnl/mm/ARM3/pagfault.c +++ b/ntoskrnl/mm/ARM3/pagfault.c @@ -805,21 +805,23 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction, /* Get the prototype PTE! */ ProtoPte = MiProtoPteToPte(&TempPte); } + else + { + // + // We don't implement transition PTEs + // + ASSERT(TempPte.u.Soft.Transition == 0); - // - // We don't implement transition PTEs - // - ASSERT(TempPte.u.Soft.Transition == 0); - - /* Check for no-access PTE */ - if (TempPte.u.Soft.Protection == MM_NOACCESS) - { - /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */ - KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, - (ULONG_PTR)Address, - StoreInstruction, - (ULONG_PTR)TrapInformation, - 1); + /* Check for no-access PTE */ + if (TempPte.u.Soft.Protection == MM_NOACCESS) + { + /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */ + KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, + (ULONG_PTR)Address, + StoreInstruction, + (ULONG_PTR)TrapInformation, + 1); + } } /* Check for demand page */ @@ -874,8 +876,6 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction, #endif /* First things first, is the PDE valid? */ -// DPRINT1("The PDE we faulted on: %lx %lx\n", PointerPde, MiAddressToPde(PTE_BASE)); - //ASSERT(PointerPde != MiAddressToPde(PTE_BASE)); ASSERT(PointerPde->u.Hard.LargePage == 0); if (PointerPde->u.Hard.Valid == 0) { @@ -918,7 +918,6 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction, if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)) { /* Resolve the fault */ - //DPRINT1("VAD demand-zero fault: %p\n", Address); MiResolveDemandZeroFault(Address, PointerPte, CurrentProcess, @@ -929,11 +928,37 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction, return STATUS_PAGE_FAULT_DEMAND_ZERO; } - /* Don't handle prototype PTEs yet -- only kernel demand zero PTEs */ + /* Get protection and check if it's a prototype PTE */ + ProtectionCode = TempPte.u.Soft.Protection; ASSERT(TempPte.u.Soft.Prototype == 0); - ASSERT(TempPte.u.Long == 0); + + /* Check for non-demand zero PTE */ + if (TempPte.u.Long != 0) + { + /* This is a page fault, check for valid protection */ + ASSERT(ProtectionCode != 0x100); + + /* FIXME: Run MiAccessCheck */ + + /* Dispatch the fault */ + Status = MiDispatchFault(StoreInstruction, + Address, + PointerPte, + NULL, + FALSE, + PsGetCurrentProcess(), + TrapInformation, + NULL); + + /* Return the status */ + ASSERT(NT_SUCCESS(Status)); + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); + return Status; + } /* Check if this address range belongs to a valid allocation (VAD) */ + ASSERT(TempPte.u.Long == 0); ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad); if (ProtectionCode == MM_NOACCESS) { diff --git a/ntoskrnl/mm/ARM3/pfnlist.c b/ntoskrnl/mm/ARM3/pfnlist.c index ae2813b4d8a..0a1d426637c 100644 --- a/ntoskrnl/mm/ARM3/pfnlist.c +++ b/ntoskrnl/mm/ARM3/pfnlist.c @@ -555,7 +555,7 @@ MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex) Pfn1->u4.PteFrame = MiGetPfnEntryIndex(Blink); /* If there is an original pte, it should be an old link, NOT a ReactOS RMAP */ - ASSERT(Blink->u3.e1.ParityError == FALSE); + ASSERT(Blink->u4.AweAllocation == FALSE); Blink->OriginalPte.u.Long = PageFrameIndex; } @@ -563,7 +563,7 @@ MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex) ColorTable->Blink = Pfn1; /* If there is an original pte, it should be an old link, NOT a ReactOS RMAP */ - ASSERT(Pfn1->u3.e1.ParityError == FALSE); + ASSERT(Pfn1->u4.AweAllocation == FALSE); Pfn1->OriginalPte.u.Long = LIST_HEAD; /* And increase the count in the colored list */ @@ -667,7 +667,7 @@ MiInsertPageInList(IN PMMPFNLIST ListHead, Flink = ColorHead->Flink; /* If there is an original pte, it should be an old link, NOT a ReactOS RMAP */ - ASSERT(Pfn1->u3.e1.ParityError == FALSE); + ASSERT(Pfn1->u4.AweAllocation == FALSE); /* Make this page point back to the list, and point forwards to the old head */ Pfn1->OriginalPte.u.Long = Flink; @@ -853,7 +853,7 @@ MiDecrementShareCount(IN PMMPFN Pfn1, * ways we shouldn't be seeing RMAP entries at this point */ ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0); - ASSERT(Pfn1->u3.e1.ParityError == FALSE); + ASSERT(Pfn1->u4.AweAllocation == FALSE); /* Mark the page temporarily as valid, we're going to make it free soon */ Pfn1->u3.e1.PageLocation = ActiveAndValid; @@ -869,6 +869,49 @@ MiDecrementShareCount(IN PMMPFN Pfn1, } } +VOID +NTAPI +MiDecrementReferenceCount(IN PMMPFN Pfn1, + IN PFN_NUMBER PageFrameIndex) +{ + /* PFN lock must be held */ + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + /* Sanity checks on the page */ + ASSERT(PageFrameIndex < MmHighestPhysicalPage); + ASSERT(Pfn1 == MiGetPfnEntry(PageFrameIndex)); + ASSERT(Pfn1->u3.e2.ReferenceCount != 0); + + /* Dereference the page, bail out if it's still alive */ + InterlockedDecrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount); + if (Pfn1->u3.e2.ReferenceCount) return; + + /* Nobody should still have reference to this page */ + if (Pfn1->u2.ShareCount != 0) + { + /* Otherwise something's really wrong */ + KeBugCheckEx(PFN_LIST_CORRUPT, 7, PageFrameIndex, Pfn1->u2.ShareCount, 0); + } + + /* And it should be lying on some page list */ + ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid); + + /* Did someone set the delete flag? */ + if (MI_IS_PFN_DELETED(Pfn1)) + { + /* Insert it into the free list, there's nothing left to do */ + MiInsertPageInFreeList(PageFrameIndex); + return; + } + + /* We don't have a modified list yet */ + ASSERT(Pfn1->u3.e1.Modified == 0); + ASSERT(Pfn1->u3.e1.RemovalRequested == 0); + + /* FIXME: Normally it would go on the standby list, but we're pushing it on the free list */ + MiInsertPageInFreeList(PageFrameIndex); +} + VOID NTAPI MiInitializePfnForOtherProcess(IN PFN_NUMBER PageFrameIndex, diff --git a/ntoskrnl/mm/ARM3/procsup.c b/ntoskrnl/mm/ARM3/procsup.c index f881922d54f..7fdca8d0d83 100644 --- a/ntoskrnl/mm/ARM3/procsup.c +++ b/ntoskrnl/mm/ARM3/procsup.c @@ -29,14 +29,14 @@ MiRosTakeOverPebTebRanges(IN PEPROCESS Process) NTSTATUS Status; PMEMORY_AREA MemoryArea; PHYSICAL_ADDRESS BoundaryAddressMultiple; - PVOID AllocatedBase = (PVOID)MI_LOWEST_VAD_ADDRESS; + PVOID AllocatedBase = (PVOID)USER_SHARED_DATA; BoundaryAddressMultiple.QuadPart = 0; Status = MmCreateMemoryArea(&Process->Vm, MEMORY_AREA_OWNED_BY_ARM3, &AllocatedBase, ((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - 1) - - (ULONG_PTR)MI_LOWEST_VAD_ADDRESS, + (ULONG_PTR)USER_SHARED_DATA, PAGE_READWRITE, &MemoryArea, TRUE, @@ -139,6 +139,10 @@ MiCreatePebOrTeb(IN PEPROCESS Process, /* Insert the VAD */ ASSERT(Vad->EndingVpn >= Vad->StartingVpn); Process->VadRoot.NodeHint = Vad; + Vad->ControlArea = NULL; // For Memory-Area hack + Vad->FirstPrototypePte = NULL; + DPRINT("VAD: %p\n", Vad); + DPRINT("Allocated PEB/TEB at: 0x%p for %16s\n", *Base, Process->ImageFileName); MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result); /* Release the working set */ @@ -148,7 +152,6 @@ MiCreatePebOrTeb(IN PEPROCESS Process, KeReleaseGuardedMutex(&Process->AddressCreationLock); /* Return the status */ - DPRINT("Allocated PEB/TEB at: 0x%p for %16s\n", *Base, Process->ImageFileName); return Status; } @@ -258,7 +261,7 @@ MmDeleteKernelStack(IN PVOID StackBase, MiDecrementShareCount(Pfn2, PageTableFrameNumber); #endif /* Set the special pending delete marker */ - Pfn1->PteAddress = (PMMPTE)((ULONG_PTR)Pfn1->PteAddress | 1); + MI_SET_PFN_DELETED(Pfn1); /* And now delete the actual stack page */ MiDecrementShareCount(Pfn1, PageFrameNumber); @@ -370,9 +373,6 @@ MmCreateKernelStack(IN BOOLEAN GuiStack, MI_WRITE_VALID_PTE(PointerPte, TempPte); } - // Bug #4835 - (VOID)InterlockedExchangeAddUL(&MiMemoryConsumers[MC_NPPOOL].PagesUsed, StackPages); - // // Release the PFN lock // @@ -1174,7 +1174,6 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process) /* Enumerate the VADs */ VadTree = &Process->VadRoot; - DPRINT("Cleaning up VADs: %d\n", VadTree->NumberGenericTableElements); while (VadTree->NumberGenericTableElements) { /* Grab the current VAD */ @@ -1186,14 +1185,26 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process) /* Remove this VAD from the tree */ ASSERT(VadTree->NumberGenericTableElements >= 1); MiRemoveNode((PMMADDRESS_NODE)Vad, VadTree); - DPRINT("Moving on: %d\n", VadTree->NumberGenericTableElements); - /* Only PEB/TEB VADs supported for now */ - ASSERT(Vad->u.VadFlags.PrivateMemory == 1); + /* Only regular VADs supported for now */ ASSERT(Vad->u.VadFlags.VadType == VadNone); - /* Release the working set */ - MiUnlockProcessWorkingSet(Process, Thread); + /* Check if this is a section VAD */ + if (!(Vad->u.VadFlags.PrivateMemory) && (Vad->ControlArea)) + { + /* Remove the view */ + MiRemoveMappedView(Process, Vad); + } + else + { + /* Delete the addresses */ + MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT, + (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1), + Vad); + + /* Release the working set */ + MiUnlockProcessWorkingSet(Process, Thread); + } /* Skip ARM3 fake VADs, they'll be freed by MmDeleteProcessAddresSpace */ if (Vad->u.VadFlags.Spare == 1) diff --git a/ntoskrnl/mm/ARM3/section.c b/ntoskrnl/mm/ARM3/section.c index 822c5c524d1..30f6b4c0a03 100644 --- a/ntoskrnl/mm/ARM3/section.c +++ b/ntoskrnl/mm/ARM3/section.c @@ -393,6 +393,188 @@ MiCheckPurgeAndUpMapCount(IN PCONTROL_AREA ControlArea, return STATUS_SUCCESS; } +PSUBSECTION +NTAPI +MiLocateSubsection(IN PMMVAD Vad, + IN ULONG_PTR Vpn) +{ + PSUBSECTION Subsection; + PCONTROL_AREA ControlArea; + ULONG PteOffset; + + /* Get the control area */ + ControlArea = Vad->ControlArea; + ASSERT(ControlArea->u.Flags.Rom == 0); + ASSERT(ControlArea->u.Flags.Image == 0); + ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); + + /* Get the subsection */ + Subsection = (PSUBSECTION)(ControlArea + 1); + + /* We only support single-subsection segments */ + ASSERT(Subsection->SubsectionBase != NULL); + ASSERT(Vad->FirstPrototypePte >= Subsection->SubsectionBase); + ASSERT(Vad->FirstPrototypePte < &Subsection->SubsectionBase[Subsection->PtesInSubsection]); + + /* Compute the PTE offset */ + PteOffset = (ULONG_PTR)Vpn - Vad->StartingVpn; + PteOffset += Vad->FirstPrototypePte - Subsection->SubsectionBase; + + /* Again, we only support single-subsection segments */ + ASSERT(PteOffset < 0xF0000000); + ASSERT(PteOffset < Subsection->PtesInSubsection); + + /* Return the subsection */ + return Subsection; +} + +VOID +NTAPI +MiSegmentDelete(IN PSEGMENT Segment) +{ + PCONTROL_AREA ControlArea; + SEGMENT_FLAGS SegmentFlags; + PSUBSECTION Subsection; + PMMPTE PointerPte, LastPte, PteForProto; + MMPTE TempPte; + KIRQL OldIrql; + + /* Capture data */ + SegmentFlags = Segment->SegmentFlags; + ControlArea = Segment->ControlArea; + + /* Make sure control area is on the right delete path */ + ASSERT(ControlArea->u.Flags.BeingDeleted == 1); + ASSERT(ControlArea->WritableUserReferences == 0); + + /* These things are not supported yet */ + ASSERT(ControlArea->DereferenceList.Flink == NULL); + ASSERT(!(ControlArea->u.Flags.Image) & !(ControlArea->u.Flags.File)); + ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); + ASSERT(ControlArea->u.Flags.Rom == 0); + + /* Get the subsection and PTEs for this segment */ + Subsection = (PSUBSECTION)(ControlArea + 1); + PointerPte = Subsection->SubsectionBase; + LastPte = PointerPte + Segment->NonExtendedPtes; + + /* Lock the PFN database */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Check if the master PTE is invalid */ + PteForProto = MiAddressToPte(PointerPte); + if (!PteForProto->u.Hard.Valid) + { + /* Fault it in */ + MiMakeSystemAddressValidPfn(PointerPte, OldIrql); + } + + /* Loop all the segment PTEs */ + while (PointerPte < LastPte) + { + /* Check if it's time to switch master PTEs if we passed a PDE boundary */ + if (!((ULONG_PTR)PointerPte & (PD_SIZE - 1)) && + (PointerPte != Subsection->SubsectionBase)) + { + /* Check if the master PTE is invalid */ + PteForProto = MiAddressToPte(PointerPte); + if (!PteForProto->u.Hard.Valid) + { + /* Fault it in */ + MiMakeSystemAddressValidPfn(PointerPte, OldIrql); + } + } + + /* This should be a prototype PTE */ + TempPte = *PointerPte; + ASSERT(SegmentFlags.LargePages == 0); + ASSERT(TempPte.u.Hard.Valid == 0); + ASSERT(TempPte.u.Soft.Prototype == 1); + + /* Zero the PTE and keep going */ + PointerPte->u.Long = 0; + PointerPte++; + } + + /* Release the PFN lock */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Free the structures */ + ExFreePool(ControlArea); + ExFreePool(Segment); +} + +VOID +NTAPI +MiCheckControlArea(IN PCONTROL_AREA ControlArea, + IN KIRQL OldIrql) +{ + BOOLEAN DeleteSegment = FALSE; + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + /* Check if this is the last reference or view */ + if (!(ControlArea->NumberOfMappedViews) && + !(ControlArea->NumberOfSectionReferences)) + { + /* There should be no more user references either */ + ASSERT(ControlArea->NumberOfUserReferences == 0); + + /* Not yet supported */ + ASSERT(ControlArea->FilePointer == NULL); + + /* The control area is being destroyed */ + ControlArea->u.Flags.BeingDeleted = TRUE; + DeleteSegment = TRUE; + } + + /* Release the PFN lock */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Delete the segment if needed */ + if (DeleteSegment) + { + /* No more user write references at all */ + ASSERT(ControlArea->WritableUserReferences == 0); + MiSegmentDelete(ControlArea->Segment); + } +} + +VOID +NTAPI +MiRemoveMappedView(IN PEPROCESS CurrentProcess, + IN PMMVAD Vad) +{ + KIRQL OldIrql; + PCONTROL_AREA ControlArea; + + /* Get the control area */ + ControlArea = Vad->ControlArea; + + /* We only support non-extendable, non-image, pagefile-backed regular sections */ + ASSERT(Vad->u.VadFlags.VadType == VadNone); + ASSERT(Vad->u2.VadFlags2.ExtendableFile == FALSE); + ASSERT(ControlArea); + ASSERT(ControlArea->FilePointer == NULL); + + /* Delete the actual virtual memory pages */ + MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT, + (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1), + Vad); + + /* Release the working set */ + MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); + + /* Lock the PFN database */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Remove references */ + ControlArea->NumberOfMappedViews--; + ControlArea->NumberOfUserReferences--; + + /* Check if it should be destroyed */ + MiCheckControlArea(ControlArea, OldIrql); +} + NTSTATUS NTAPI MiMapViewInSystemSpace(IN PVOID Section, @@ -505,7 +687,6 @@ MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea, /* These flags/parameters are not supported */ ASSERT((AllocationType & MEM_DOS_LIM) == 0); ASSERT((AllocationType & MEM_RESERVE) == 0); - ASSERT((AllocationType & MEM_TOP_DOWN) == 0); ASSERT(Process->VmTopDown == 0); ASSERT(Section->u.Flags.CopyOnWrite == FALSE); ASSERT(ZeroBits == 0); @@ -553,13 +734,28 @@ MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea, /* Did the caller specify an address? */ if (!(*BaseAddress)) { - /* No, find an address bottom-up */ - Status = MiFindEmptyAddressRangeInTree(*ViewSize, - _64K, - &Process->VadRoot, - (PMMADDRESS_NODE*)&Process->VadFreeHint, - &StartAddress); - ASSERT(NT_SUCCESS(Status)); + /* Which way should we search? */ + if (AllocationType & MEM_TOP_DOWN) + { + /* No, find an address top-down */ + Status = MiFindEmptyAddressRangeDownTree(*ViewSize, + (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS, + _64K, + &Process->VadRoot, + &StartAddress, + (PMMADDRESS_NODE*)&Process->VadFreeHint); + ASSERT(NT_SUCCESS(Status)); + } + else + { + /* No, find an address bottom-up */ + Status = MiFindEmptyAddressRangeInTree(*ViewSize, + _64K, + &Process->VadRoot, + (PMMADDRESS_NODE*)&Process->VadFreeHint, + &StartAddress); + ASSERT(NT_SUCCESS(Status)); + } } else { @@ -1179,8 +1375,11 @@ NtCreateSection(OUT PHANDLE SectionHandle, SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE | SEC_NO_CHANGE))) { - DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes); - return STATUS_INVALID_PARAMETER_6; + if (!(AllocationAttributes & 1)) + { + DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes); + return STATUS_INVALID_PARAMETER_6; + } } /* Check for no allocation type */ diff --git a/ntoskrnl/mm/ARM3/vadnode.c b/ntoskrnl/mm/ARM3/vadnode.c index 46222b9c7aa..d62a02c8a73 100644 --- a/ntoskrnl/mm/ARM3/vadnode.c +++ b/ntoskrnl/mm/ARM3/vadnode.c @@ -100,6 +100,44 @@ MiInsertNode(IN PMM_AVL_TABLE Table, { /* Insert it into the tree */ RtlpInsertAvlTreeNode(Table, NewNode, Parent, Result); + + /* Now insert an ARM3 MEMORY_AREA for this node, unless the insert was already from the MEMORY_AREA code */ + PMMVAD Vad = (PMMVAD)NewNode; + if (Vad->u.VadFlags.Spare == 0) + { + NTSTATUS Status; + PMEMORY_AREA MemoryArea; + PHYSICAL_ADDRESS BoundaryAddressMultiple; + SIZE_T Size; + PEPROCESS Process = CONTAINING_RECORD(Table, EPROCESS, VadRoot); + PVOID AllocatedBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT); + BoundaryAddressMultiple.QuadPart = 0; + Size = ((Vad->EndingVpn + 1) - Vad->StartingVpn) << PAGE_SHIFT; + Status = MmCreateMemoryArea(&Process->Vm, + MEMORY_AREA_OWNED_BY_ARM3, + &AllocatedBase, + Size, + PAGE_READWRITE, + &MemoryArea, + TRUE, + 0, + BoundaryAddressMultiple); + ASSERT(NT_SUCCESS(Status)); + + /* Check if this is VM VAD */ + if (Vad->ControlArea == NULL) + { + /* We store the reactos MEMORY_AREA here */ + DPRINT("Storing %p in %p\n", MemoryArea, Vad); + Vad->FirstPrototypePte = (PMMPTE)MemoryArea; + } + else + { + /* This is a section VAD. Store the MAREA here for now */ + DPRINT("Storing %p in %p\n", MemoryArea, Vad); + Vad->ControlArea->WaitingForDeletion = (PVOID)MemoryArea; + } + } } VOID @@ -121,28 +159,6 @@ MiInsertVad(IN PMMVAD Vad, /* Do the actual insert operation */ MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result); - - /* Now insert an ARM3 MEMORY_AREA for this node, unless the insert was already from the MEMORY_AREA code */ - if (Vad->u.VadFlags.Spare == 0) - { - NTSTATUS Status; - PMEMORY_AREA MemoryArea; - PHYSICAL_ADDRESS BoundaryAddressMultiple; - SIZE_T Size; - PVOID AllocatedBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT); - BoundaryAddressMultiple.QuadPart = 0; - Size = ((Vad->EndingVpn + 1) - Vad->StartingVpn) << PAGE_SHIFT; - Status = MmCreateMemoryArea(&Process->Vm, - MEMORY_AREA_OWNED_BY_ARM3, - &AllocatedBase, - Size, - PAGE_READWRITE, - &MemoryArea, - TRUE, - 0, - BoundaryAddressMultiple); - ASSERT(NT_SUCCESS(Status)); - } } VOID @@ -163,6 +179,40 @@ MiRemoveNode(IN PMMADDRESS_NODE Node, if (!Table->NumberGenericTableElements) Table->NodeHint = NULL; else Table->NodeHint = Table->BalancedRoot.RightChild; } + + /* Free the node from ReactOS view as well */ + PMMVAD Vad = (PMMVAD)Node; + if (Vad->u.VadFlags.Spare == 0) + { + PMEMORY_AREA MemoryArea; + PEPROCESS Process; + + /* Check if this is VM VAD */ + if (Vad->ControlArea == NULL) + { + /* We store the ReactOS MEMORY_AREA here */ + MemoryArea = (PMEMORY_AREA)Vad->FirstPrototypePte; + } + else + { + /* This is a section VAD. We store the ReactOS MEMORY_AREA here */ + MemoryArea = (PMEMORY_AREA)Vad->ControlArea->WaitingForDeletion; + } + + /* Make sure one actually still exists */ + if (MemoryArea) + { + /* Get the process */ + Process = CONTAINING_RECORD(Table, EPROCESS, VadRoot); + + /* We only create fake memory-areas for ARM3 VADs */ + ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3); + ASSERT(MemoryArea->Vad == NULL); + + /* Free it */ + MmFreeMemoryArea(&Process->Vm, MemoryArea, NULL, NULL); + } + } } PMMADDRESS_NODE diff --git a/ntoskrnl/mm/ARM3/virtual.c b/ntoskrnl/mm/ARM3/virtual.c index 53ac25023f8..dd0f818e0ca 100644 --- a/ntoskrnl/mm/ARM3/virtual.c +++ b/ntoskrnl/mm/ARM3/virtual.c @@ -29,6 +29,85 @@ MiProtectVirtualMemory(IN PEPROCESS Process, /* PRIVATE FUNCTIONS **********************************************************/ +ULONG +NTAPI +MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress, + IN PEPROCESS CurrentProcess) +{ + NTSTATUS Status; + BOOLEAN LockChange = FALSE; + + /* Must be a non-pool page table, since those are double-mapped already */ + ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS); + ASSERT((PageTableVirtualAddress < MmPagedPoolStart) || + (PageTableVirtualAddress > MmPagedPoolEnd)); + + /* Working set lock or PFN lock should be held */ + ASSERT(KeAreAllApcsDisabled() == TRUE); + + /* Check if the page table is valid */ + while (!MmIsAddressValid(PageTableVirtualAddress)) + { + /* Fault it in */ + Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL); + if (!NT_SUCCESS(Status)) + { + /* This should not fail */ + KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR, + 1, + Status, + (ULONG_PTR)CurrentProcess, + (ULONG_PTR)PageTableVirtualAddress); + } + + /* This flag will be useful later when we do better locking */ + LockChange = TRUE; + } + + /* Let caller know what the lock state is */ + return LockChange; +} + +ULONG +NTAPI +MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress, + IN KIRQL OldIrql) +{ + NTSTATUS Status; + BOOLEAN LockChange = FALSE; + + /* Must be e kernel address */ + ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS); + + /* Check if the page is valid */ + while (!MmIsAddressValid(VirtualAddress)) + { + /* Release the PFN database */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Fault it in */ + Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL); + if (!NT_SUCCESS(Status)) + { + /* This should not fail */ + KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR, + 3, + Status, + 0, + (ULONG_PTR)VirtualAddress); + } + + /* This flag will be useful later when we do better locking */ + LockChange = TRUE; + + /* Lock the PFN database */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + } + + /* Let caller know what the lock state is */ + return LockChange; +} + PFN_NUMBER NTAPI MiDeleteSystemPageableVm(IN PMMPTE PointerPte, @@ -126,6 +205,266 @@ MiDeleteSystemPageableVm(IN PMMPTE PointerPte, return ActualPages; } +VOID +NTAPI +MiDeletePte(IN PMMPTE PointerPte, + IN PVOID VirtualAddress, + IN PEPROCESS CurrentProcess, + IN PMMPTE PrototypePte) +{ + PMMPFN Pfn1; + MMPTE TempPte; + PFN_NUMBER PageFrameIndex; + PMMPDE PointerPde; + + /* PFN lock must be held */ + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + /* Capture the PTE */ + TempPte = *PointerPte; + + /* We only support valid PTEs for now */ + ASSERT(TempPte.u.Hard.Valid == 1); + if (TempPte.u.Hard.Valid == 0) + { + /* Invalid PTEs not supported yet */ + ASSERT(TempPte.u.Soft.Prototype == 0); + ASSERT(TempPte.u.Soft.Transition == 0); + } + + /* Get the PFN entry */ + PageFrameIndex = PFN_FROM_PTE(&TempPte); + Pfn1 = MiGetPfnEntry(PageFrameIndex); + + /* Check if this is a valid, prototype PTE */ + if (Pfn1->u3.e1.PrototypePte == 1) + { + /* Get the PDE and make sure it's faulted in */ + PointerPde = MiAddressToPde(PointerPte); + if (PointerPde->u.Hard.Valid == 0) + { +#if (_MI_PAGING_LEVELS == 2) + /* Could be paged pool access from a new process -- synchronize the page directories */ + if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress))) + { +#endif + /* The PDE must be valid at this point */ + KeBugCheckEx(MEMORY_MANAGEMENT, + 0x61940, + (ULONG_PTR)PointerPte, + PointerPte->u.Long, + (ULONG_PTR)VirtualAddress); + } +#if (_MI_PAGING_LEVELS == 2) + } +#endif + /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */ + //MiDecrementShareCount(MiGetPfnEntry(PFN_FROM_PTE(PointerPde)), PFN_FROM_PDE(PointerPde)); + + /* Drop the share count */ + MiDecrementShareCount(Pfn1, PageFrameIndex); + + /* No fork yet */ + if (PointerPte <= MiHighestUserPte) ASSERT(PrototypePte == Pfn1->PteAddress); + } + else + { + /* Make sure the saved PTE address is valid */ + if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte) + { + /* The PFN entry is illegal, or invalid */ + KeBugCheckEx(MEMORY_MANAGEMENT, + 0x401, + (ULONG_PTR)PointerPte, + PointerPte->u.Long, + (ULONG_PTR)Pfn1->PteAddress); + } + + /* There should only be 1 shared reference count */ + ASSERT(Pfn1->u2.ShareCount == 1); + + /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */ + //MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame); + + /* Mark the PFN for deletion and dereference what should be the last ref */ + MI_SET_PFN_DELETED(Pfn1); + MiDecrementShareCount(Pfn1, PageFrameIndex); + + /* We should eventually do this */ + //CurrentProcess->NumberOfPrivatePages--; + } + + /* Destroy the PTE and flush the TLB */ + PointerPte->u.Long = 0; + KeFlushCurrentTb(); +} + +VOID +NTAPI +MiDeleteVirtualAddresses(IN ULONG_PTR Va, + IN ULONG_PTR EndingAddress, + IN PMMVAD Vad) +{ + PMMPTE PointerPte, PointerPde, PrototypePte, LastPrototypePte; + MMPTE TempPte; + PEPROCESS CurrentProcess; + KIRQL OldIrql; + BOOLEAN AddressGap = FALSE; + PSUBSECTION Subsection; + + /* Get out if this is a fake VAD, RosMm will free the marea pages */ + if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return; + + /* Grab the process and PTE/PDE for the address being deleted */ + CurrentProcess = PsGetCurrentProcess(); + PointerPde = MiAddressToPde(Va); + PointerPte = MiAddressToPte(Va); + + /* Check if this is a section VAD or a VM VAD */ + if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte)) + { + /* Don't worry about prototypes */ + PrototypePte = LastPrototypePte = NULL; + } + else + { + /* Get the prototype PTE */ + PrototypePte = Vad->FirstPrototypePte; + LastPrototypePte = Vad->FirstPrototypePte + 1; + } + + /* In all cases, we don't support fork() yet */ + ASSERT(CurrentProcess->CloneRoot == NULL); + + /* Loop the PTE for each VA */ + while (TRUE) + { + /* First keep going until we find a valid PDE */ + while (!PointerPde->u.Long) + { + /* There are gaps in the address space */ + AddressGap = TRUE; + + /* Still no valid PDE, try the next 4MB (or whatever) */ + PointerPde++; + + /* Update the PTE on this new boundary */ + PointerPte = MiPteToAddress(PointerPde); + + /* Check if all the PDEs are invalid, so there's nothing to free */ + Va = (ULONG_PTR)MiPteToAddress(PointerPte); + if (Va > EndingAddress) return; + } + + /* Now check if the PDE is mapped in */ + if (!PointerPde->u.Hard.Valid) + { + /* It isn't, so map it in */ + PointerPte = MiPteToAddress(PointerPde); + MiMakeSystemAddressValid(PointerPte, CurrentProcess); + } + + /* Now we should have a valid PDE, mapped in, and still have some VA */ + ASSERT(PointerPde->u.Hard.Valid == 1); + ASSERT(Va <= EndingAddress); + + /* Check if this is a section VAD with gaps in it */ + if ((AddressGap) && (LastPrototypePte)) + { + /* We need to skip to the next correct prototype PTE */ + PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT); + + /* And we need the subsection to skip to the next last prototype PTE */ + Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT); + if (Subsection) + { + /* Found it! */ + LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; + } + else + { + /* No more subsections, we are done with prototype PTEs */ + PrototypePte = NULL; + } + } + + /* Lock the PFN Database while we delete the PTEs */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + do + { + /* Capture the PDE and make sure it exists */ + TempPte = *PointerPte; + if (TempPte.u.Long) + { + /* Check if the PTE is actually mapped in */ + if (TempPte.u.Long & 0xFFFFFC01) + { + /* Are we dealing with section VAD? */ + if ((LastPrototypePte) && (PrototypePte > LastPrototypePte)) + { + /* We need to skip to the next correct prototype PTE */ + PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT); + + /* And we need the subsection to skip to the next last prototype PTE */ + Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT); + if (Subsection) + { + /* Found it! */ + LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; + } + else + { + /* No more subsections, we are done with prototype PTEs */ + PrototypePte = NULL; + } + } + + /* Check for prototype PTE */ + if ((TempPte.u.Hard.Valid == 0) && + (TempPte.u.Soft.Prototype == 1)) + { + /* Just nuke it */ + PointerPte->u.Long = 0; + } + else + { + /* Delete the PTE proper */ + MiDeletePte(PointerPte, + (PVOID)Va, + CurrentProcess, + PrototypePte); + } + } + else + { + /* The PTE was never mapped, just nuke it here */ + PointerPte->u.Long = 0; + } + } + + /* Update the address and PTE for it */ + Va += PAGE_SIZE; + PointerPte++; + PrototypePte++; + + /* Making sure the PDE is still valid */ + ASSERT(PointerPde->u.Hard.Valid == 1); + } + while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress)); + + /* The PDE should still be valid at this point */ + ASSERT(PointerPde->u.Hard.Valid == 1); + + /* Release the lock and get out if we're done */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + if (Va > EndingAddress) return; + + /* Otherwise, we exited because we hit a new PDE boundary, so start over */ + PointerPde = MiAddressToPde(Va); + AddressGap = FALSE; + } +} + LONG MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo, OUT PBOOLEAN HaveBadAddress, @@ -709,6 +1048,109 @@ MmFlushVirtualMemory(IN PEPROCESS Process, return STATUS_SUCCESS; } +ULONG +NTAPI +MiQueryAddressState(IN PVOID Va, + IN PMMVAD Vad, + IN PEPROCESS TargetProcess, + OUT PULONG ReturnedProtect, + OUT PVOID *NextVa) +{ + + PMMPTE PointerPte, PointerPde; + MMPTE TempPte; + BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE; + ULONG State = MEM_RESERVE, Protect = 0, LockChange; + ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) && + (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT))); + + /* Only normal VADs supported */ + ASSERT(Vad->u.VadFlags.VadType == VadNone); + + /* Get the PDE and PTE for the address */ + PointerPde = MiAddressToPde(Va); + PointerPte = MiAddressToPte(Va); + + /* Return the next range */ + *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE); + + /* Loop to make sure the PDE is valid */ + do + { + /* Try again */ + LockChange = 0; + + /* Is the PDE empty? */ + if (!PointerPde->u.Long) + { + /* No address in this range used yet, move to the next PDE range */ + *NextVa = MiPteToAddress(MiPteToAddress(PointerPde + 1)); + break; + } + + /* The PDE is empty, but is it faulted in? */ + if (!PointerPde->u.Hard.Valid) + { + /* It isn't, go ahead and do the fault */ + LockChange = MiMakeSystemAddressValid(MiPteToAddress(PointerPde), + TargetProcess); + } + + /* Check if the PDE was faulted in, making the PTE readable */ + if (!LockChange) ValidPte = TRUE; + } while (LockChange); + + /* Is it safe to try reading the PTE? */ + if (ValidPte) + { + /* FIXME: watch out for large pages */ + + /* Capture the PTE */ + TempPte = *PointerPte; + if (TempPte.u.Long) + { + /* The PTE is valid, so it's not zeroed out */ + DemandZeroPte = FALSE; + + /* Check if it's valid or has a valid protection mask */ + ASSERT(TempPte.u.Soft.Prototype == 0); + if ((TempPte.u.Soft.Protection != MM_DECOMMIT) || + (TempPte.u.Hard.Valid == 1)) + { + /* This means it's committed */ + State = MEM_COMMIT; + + /* For now, we lie about the protection */ + Protect = PAGE_EXECUTE_READWRITE; + } + else + { + /* Otherwise our defaults should hold */ + ASSERT(Protect == 0); + ASSERT(State == MEM_RESERVE); + } + } + } + + /* Check if this was a demand-zero PTE, since we need to find the state */ + if (DemandZeroPte) + { + /* Check if the VAD is for committed memory */ + if (Vad->u.VadFlags.MemCommit) + { + /* This is committed memory */ + State = MEM_COMMIT; + + /* Convert the protection */ + Protect = MmProtectToValue[Vad->u.VadFlags.Protection]; + } + } + + /* Return the protection code */ + *ReturnedProtect = Protect; + return State; +} + /* PUBLIC FUNCTIONS ***********************************************************/ /* @@ -1851,4 +2293,155 @@ NtResetWriteWatch(IN HANDLE ProcessHandle, return STATUS_SUCCESS; } +NTSTATUS +NTAPI +NtQueryVirtualMemory(IN HANDLE ProcessHandle, + IN PVOID BaseAddress, + IN MEMORY_INFORMATION_CLASS MemoryInformationClass, + OUT PVOID MemoryInformation, + IN SIZE_T MemoryInformationLength, + OUT PSIZE_T ReturnLength) +{ + PEPROCESS TargetProcess; + NTSTATUS Status; + PMMVAD Vad = NULL; + PVOID Address, NextAddress; + BOOLEAN Found; + ULONG NewProtect, NewState, BaseVpn; + MEMORY_BASIC_INFORMATION MemoryInfo; + KAPC_STATE ApcState; + DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress); + + /* Only this class is supported for now */ + ASSERT(MemoryInformationClass == MemoryBasicInformation); + + /* Validate the size information of the class */ + if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION)) + { + /* The size is invalid */ + return STATUS_INFO_LENGTH_MISMATCH; + } + + /* Bail out if the address is invalid */ + if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; + + /* Check for illegal addresses in user-space, or the shared memory area */ + if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) || + (PAGE_ALIGN(BaseAddress) == (PVOID)USER_SHARED_DATA)) + { + /* FIXME: We should return some bogus info structure */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Check if this is for a local or remote process */ + if (ProcessHandle == NtCurrentProcess()) + { + TargetProcess = PsGetCurrentProcess(); + } + else + { + /* Reference the target process */ + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_QUERY_INFORMATION, + PsProcessType, + ExGetPreviousMode(), + (PVOID*)&TargetProcess, + NULL); + if (!NT_SUCCESS(Status)) return Status; + + /* Attach to it now */ + KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); + } + + /* Loop the VADs */ + ASSERT(TargetProcess->VadRoot.NumberGenericTableElements); + if (TargetProcess->VadRoot.NumberGenericTableElements) + { + /* Scan on the right */ + Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild; + BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT; + while (Vad) + { + /* Check if this VAD covers the allocation range */ + if ((BaseVpn >= Vad->StartingVpn) && + (BaseVpn <= Vad->EndingVpn)) + { + /* We're done */ + Found = TRUE; + break; + } + + /* Check if this VAD is too high */ + if (BaseVpn < Vad->StartingVpn) + { + /* Search on the left next */ + Vad = Vad->LeftChild; + } + else + { + /* Then this VAD is too low, keep searching on the right */ + ASSERT(BaseVpn > Vad->EndingVpn); + Vad = Vad->RightChild; + } + } + } + + /* Was a VAD found? */ + if (!Found) + { + /* We don't handle this yet */ + UNIMPLEMENTED; + while (TRUE); + } + + /* This must be a VM VAD */ + ASSERT(Vad->u.VadFlags.PrivateMemory); + + /* Build the initial information block */ + Address = PAGE_ALIGN(BaseAddress); + MemoryInfo.BaseAddress = Address; + MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT); + MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection]; + MemoryInfo.Type = MEM_PRIVATE; + + /* Find the largest chunk of memory which has the same state and protection mask */ + MemoryInfo.State = MiQueryAddressState(Address, + Vad, + TargetProcess, + &MemoryInfo.Protect, + &NextAddress); + Address = NextAddress; + while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn) + { + /* Keep going unless the state or protection mask changed */ + NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress); + if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break; + Address = NextAddress; + } + + /* Now that we know the last VA address, calculate hte region size */ + MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress); + + /* Check if we were attached */ + if (ProcessHandle != NtCurrentProcess()) + { + /* Detach and derefernece the process */ + KeUnstackDetachProcess(&ApcState); + ObDereferenceObject(TargetProcess); + } + + /* Return the data (FIXME: Use SEH) */ + *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; + if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); + + /* All went well */ + DPRINT("Base: %p AllocBase: %p Protect: %lx AllocProtect: %lx " + "State: %lx Type: %lx Size: %lx\n", + MemoryInfo.BaseAddress, MemoryInfo.AllocationBase, + MemoryInfo.AllocationProtect, MemoryInfo.Protect, + MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize); + return STATUS_SUCCESS; +} + /* EOF */ diff --git a/ntoskrnl/mm/anonmem.c b/ntoskrnl/mm/anonmem.c index 0564c7ac71c..5f44fc745fd 100644 --- a/ntoskrnl/mm/anonmem.c +++ b/ntoskrnl/mm/anonmem.c @@ -516,99 +516,6 @@ MmModifyAttributes(PMMSUPPORT AddressSpace, } } -NTSTATUS FASTCALL -MiQueryVirtualMemory(IN HANDLE ProcessHandle, - IN PVOID Address, - IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass, - OUT PVOID VirtualMemoryInformation, - IN SIZE_T Length, - OUT PSIZE_T ResultLength) -{ - NTSTATUS Status; - PEPROCESS Process; - MEMORY_AREA* MemoryArea; - PMMSUPPORT AddressSpace; - - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_QUERY_INFORMATION, - NULL, - UserMode, - (PVOID*)(&Process), - NULL); - - if (!NT_SUCCESS(Status)) - { - DPRINT("NtQueryVirtualMemory() = %x\n",Status); - return(Status); - } - - AddressSpace = &Process->Vm; - - MmLockAddressSpace(AddressSpace); - MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address); - switch(VirtualMemoryInformationClass) - { - case MemoryBasicInformation: - { - PMEMORY_BASIC_INFORMATION Info = - (PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation; - if (Length != sizeof(MEMORY_BASIC_INFORMATION)) - { - MmUnlockAddressSpace(AddressSpace); - ObDereferenceObject(Process); - return(STATUS_INFO_LENGTH_MISMATCH); - } - - if (MemoryArea == NULL) - { - Info->Type = 0; - Info->State = MEM_FREE; - Info->Protect = PAGE_NOACCESS; - Info->AllocationProtect = 0; - Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address); - Info->AllocationBase = NULL; - Info->RegionSize = MmFindGapAtAddress(AddressSpace, Info->BaseAddress); - Status = STATUS_SUCCESS; - *ResultLength = sizeof(MEMORY_BASIC_INFORMATION); - } - else - { - switch(MemoryArea->Type) - { - case MEMORY_AREA_VIRTUAL_MEMORY: - Status = MmQueryAnonMem(MemoryArea, Address, Info, - ResultLength); - break; - - case MEMORY_AREA_SECTION_VIEW: - Status = MmQuerySectionView(MemoryArea, Address, Info, - ResultLength); - break; - - default: - DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea->Type); - Status = STATUS_UNSUCCESSFUL; - *ResultLength = 0; - } - } - break; - } - - default: - { - DPRINT1("Unsupported or unimplemented class: %lx\n", VirtualMemoryInformationClass); - Status = STATUS_INVALID_INFO_CLASS; - *ResultLength = 0; - break; - } - } - - MmUnlockAddressSpace(AddressSpace); - ObDereferenceObject(Process); - - return Status; -} - NTSTATUS NTAPI MiProtectVirtualMemory(IN PEPROCESS Process, IN OUT PVOID *BaseAddress, @@ -1032,171 +939,6 @@ NtAllocateVirtualMemory(IN HANDLE ProcessHandle, return(STATUS_SUCCESS); } - -NTSTATUS NTAPI -NtQueryVirtualMemory(IN HANDLE ProcessHandle, - IN PVOID Address, - IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass, - OUT PVOID VirtualMemoryInformation, - IN SIZE_T Length, - OUT PSIZE_T UnsafeResultLength) -{ - NTSTATUS Status; - SIZE_T ResultLength = 0; - KPROCESSOR_MODE PreviousMode; - WCHAR ModuleFileNameBuffer[MAX_PATH] = {0}; - UNICODE_STRING ModuleFileName; - PMEMORY_SECTION_NAME SectionName = NULL; - PEPROCESS Process; - union - { - MEMORY_BASIC_INFORMATION BasicInfo; - } - VirtualMemoryInfo; - - DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, " - "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, " - "Length %lu ResultLength %x)\n",ProcessHandle,Address, - VirtualMemoryInformationClass,VirtualMemoryInformation, - Length,ResultLength); - - PreviousMode = ExGetPreviousMode(); - - if (PreviousMode != KernelMode) - { - _SEH2_TRY - { - ProbeForWrite(VirtualMemoryInformation, - Length, - sizeof(ULONG_PTR)); - - if (UnsafeResultLength) ProbeForWriteSize_t(UnsafeResultLength); - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - /* Return the exception code */ - _SEH2_YIELD(return _SEH2_GetExceptionCode()); - } - _SEH2_END; - } - - if (Address >= MmSystemRangeStart) - { - DPRINT1("Invalid parameter\n"); - return STATUS_INVALID_PARAMETER; - } - - /* FIXME: Move this inside MiQueryVirtualMemory */ - if (VirtualMemoryInformationClass == MemorySectionName) - { - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_QUERY_INFORMATION, - NULL, - PreviousMode, - (PVOID*)(&Process), - NULL); - - if (!NT_SUCCESS(Status)) - { - DPRINT("NtQueryVirtualMemory() = %x\n",Status); - return(Status); - } - - RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer)); - Status = MmGetFileNameForAddress(Address, &ModuleFileName); - - if (NT_SUCCESS(Status)) - { - SectionName = VirtualMemoryInformation; - if (PreviousMode != KernelMode) - { - _SEH2_TRY - { - RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer); - SectionName->SectionFileName.MaximumLength = Length; - RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName); - - if (UnsafeResultLength != NULL) - { - *UnsafeResultLength = ModuleFileName.Length; - } - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - Status = _SEH2_GetExceptionCode(); - } - _SEH2_END; - } - else - { - RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer); - SectionName->SectionFileName.MaximumLength = Length; - RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName); - - if (UnsafeResultLength != NULL) - { - *UnsafeResultLength = ModuleFileName.Length; - } - } - } - ObDereferenceObject(Process); - return Status; - } - else - { - Status = MiQueryVirtualMemory(ProcessHandle, - Address, - VirtualMemoryInformationClass, - &VirtualMemoryInfo, - Length, - &ResultLength); - } - - if (NT_SUCCESS(Status)) - { - if (PreviousMode != KernelMode) - { - _SEH2_TRY - { - if (ResultLength > 0) - { - ProbeForWrite(VirtualMemoryInformation, - ResultLength, - 1); - RtlCopyMemory(VirtualMemoryInformation, - &VirtualMemoryInfo, - ResultLength); - } - if (UnsafeResultLength != NULL) - { - *UnsafeResultLength = ResultLength; - } - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - Status = _SEH2_GetExceptionCode(); - } - _SEH2_END; - } - else - { - if (ResultLength > 0) - { - RtlCopyMemory(VirtualMemoryInformation, - &VirtualMemoryInfo, - ResultLength); - } - - if (UnsafeResultLength != NULL) - { - *UnsafeResultLength = ResultLength; - } - } - } - - return(Status); -} - static VOID MmFreeVirtualMemoryPage(PVOID Context, MEMORY_AREA* MemoryArea, diff --git a/ntoskrnl/mm/balance.c b/ntoskrnl/mm/balance.c index 1303c013f70..17edb8e97c3 100644 --- a/ntoskrnl/mm/balance.c +++ b/ntoskrnl/mm/balance.c @@ -47,14 +47,6 @@ static LONG MiBalancerWork = 0; /* FUNCTIONS ****************************************************************/ -VOID MmPrintMemoryStatistic(VOID) -{ - DbgPrint("MC_CACHE %d, MC_USER %d, MC_PPOOL %d, MC_NPPOOL %d, MmAvailablePages %d\n", - MiMemoryConsumers[MC_CACHE].PagesUsed, MiMemoryConsumers[MC_USER].PagesUsed, - MiMemoryConsumers[MC_PPOOL].PagesUsed, MiMemoryConsumers[MC_NPPOOL].PagesUsed, - MmAvailablePages); -} - VOID INIT_FUNCTION NTAPI @@ -80,13 +72,7 @@ MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages) { MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 8; } - MiMemoryConsumers[MC_USER].PagesTarget = - NrAvailablePages - MiMinimumAvailablePages; - MiMemoryConsumers[MC_PPOOL].PagesTarget = NrAvailablePages / 2; - MiMemoryConsumers[MC_NPPOOL].PagesTarget = 0xFFFFFFFF; - MiMemoryConsumers[MC_NPPOOL].PagesUsed = NrSystemPages; - MiMemoryConsumers[MC_SYSTEM].PagesTarget = 0xFFFFFFFF; - MiMemoryConsumers[MC_SYSTEM].PagesUsed = 0; + MiMemoryConsumers[MC_USER].PagesTarget = NrAvailablePages - MiMinimumAvailablePages; } VOID @@ -261,7 +247,7 @@ MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait, /* * Allocate always memory for the non paged pool and for the pager thread. */ - if ((Consumer == MC_NPPOOL) || (Consumer == MC_SYSTEM) || MiIsBalancerThread()) + if ((Consumer == MC_SYSTEM) || MiIsBalancerThread()) { OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MmAllocPage(Consumer); diff --git a/ntoskrnl/mm/freelist.c b/ntoskrnl/mm/freelist.c index d2a36d06a50..e7bf276600a 100644 --- a/ntoskrnl/mm/freelist.c +++ b/ntoskrnl/mm/freelist.c @@ -23,13 +23,7 @@ /* GLOBALS ****************************************************************/ -// -// // ReactOS to NT Physical Page Descriptor Entry Legacy Mapping Definitions -// -// REACTOS NT -// -#define RmapListHead AweReferenceCount #define PHYSICAL_PAGE MMPFN #define PPHYSICAL_PAGE PMMPFN @@ -159,7 +153,7 @@ MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress, KIRQL OldIrql; PPHYSICAL_PAGE Pfn1; INT LookForZeroedPages; - ASSERT (KeGetCurrentIrql() <= APC_LEVEL); + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); // // Convert the low address into a PFN @@ -283,6 +277,7 @@ MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress, if ((Pfn1->u3.e1.PageLocation == ZeroedPageList) != LookForZeroedPages) continue; /* Remove the page from the free or zero list */ + ASSERT(Pfn1->u3.e1.ReadInProgress == 0); MiUnlinkFreeOrZeroedPage(Pfn1); // @@ -294,9 +289,13 @@ MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress, // Now setup the page and mark it // Pfn1->u3.e2.ReferenceCount = 1; + Pfn1->u2.ShareCount = 1; + MI_SET_PFN_DELETED(Pfn1); + Pfn1->u4.PteFrame = 0x1FFEDCB; Pfn1->u3.e1.StartOfAllocation = 1; Pfn1->u3.e1.EndOfAllocation = 1; - + Pfn1->u4.VerifierAllocation = 0; + // // Save this page into the MDL // @@ -338,7 +337,7 @@ MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress, // // Terminate the MDL array if there's certain missing pages // - if (PagesFound != PageCount) *MdlPage = -1; + if (PagesFound != PageCount) *MdlPage = LIST_HEAD; // // Now go back and loop over all the MDL pages @@ -351,7 +350,7 @@ MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress, // Check if we've reached the end // Page = *MdlPage++; - if (Page == (PFN_NUMBER)-1) break; + if (Page == LIST_HEAD) break; // // Get the PFN entry for the page and check if we should zero it out @@ -374,77 +373,29 @@ VOID NTAPI MmDumpPfnDatabase(VOID) { - ULONG i; - PPHYSICAL_PAGE Pfn1; - PCHAR State = "????", Type = "Unknown"; - KIRQL OldIrql; - ULONG Totals[5] = {0}, FreePages = 0; - - KeRaiseIrql(HIGH_LEVEL, &OldIrql); - - // - // Loop the PFN database - // - for (i = 0; i <= MmHighestPhysicalPage; i++) - { - Pfn1 = MiGetPfnEntry(i); - if (!Pfn1) continue; - - // - // Get the type - // - if (MiIsPfnInUse(Pfn1)) - { - State = "Used"; - } - else - { - State = "Free"; - Type = "Free"; - FreePages++; - break; - } - - // - // Pretty-print the page - // - DbgPrint("0x%08p:\t%04s\t%20s\t(%02d) [%08p])\n", - i << PAGE_SHIFT, - State, - Type, - Pfn1->u3.e2.ReferenceCount, - Pfn1->RmapListHead); - } - - DbgPrint("Nonpaged Pool: %d pages\t[%d KB]\n", Totals[MC_NPPOOL], (Totals[MC_NPPOOL] << PAGE_SHIFT) / 1024); - DbgPrint("Paged Pool: %d pages\t[%d KB]\n", Totals[MC_PPOOL], (Totals[MC_PPOOL] << PAGE_SHIFT) / 1024); - DbgPrint("File System Cache: %d pages\t[%d KB]\n", Totals[MC_CACHE], (Totals[MC_CACHE] << PAGE_SHIFT) / 1024); - DbgPrint("Process Working Set: %d pages\t[%d KB]\n", Totals[MC_USER], (Totals[MC_USER] << PAGE_SHIFT) / 1024); - DbgPrint("System: %d pages\t[%d KB]\n", Totals[MC_SYSTEM], (Totals[MC_SYSTEM] << PAGE_SHIFT) / 1024); - DbgPrint("Free: %d pages\t[%d KB]\n", FreePages, (FreePages << PAGE_SHIFT) / 1024); - - KeLowerIrql(OldIrql); + /* Pretty useless for now, to be improved later */ + return; } VOID NTAPI -MmSetRmapListHeadPage(PFN_NUMBER Pfn, struct _MM_RMAP_ENTRY* ListHead) +MmSetRmapListHeadPage(PFN_NUMBER Pfn, PMM_RMAP_ENTRY ListHead) { KIRQL oldIrql; PMMPFN Pfn1; oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Pfn1 = MiGetPfnEntry(Pfn); + ASSERT(Pfn1); + ASSERT_IS_ROS_PFN(Pfn1); + if (ListHead) { /* Should not be trying to insert an RMAP for a non-active page */ ASSERT(MiIsPfnInUse(Pfn1) == TRUE); /* Set the list head address */ - Pfn1->RmapListHead = (LONG)ListHead; - - /* Mark that the page has an actual RMAP, not a residual color link */ - Pfn1->u3.e1.ParityError = TRUE; + MI_GET_ROS_DATA(Pfn1)->RmapListHead = ListHead; } else { @@ -452,23 +403,20 @@ MmSetRmapListHeadPage(PFN_NUMBER Pfn, struct _MM_RMAP_ENTRY* ListHead) ASSERT(MiIsPfnInUse(Pfn1) == TRUE); /* In this case, the RMAP is actually being removed, so clear field */ - Pfn1->RmapListHead = 0; - - /* Mark that the page has no RMAP, not a residual color link */ - Pfn1->u3.e1.ParityError = FALSE; - + MI_GET_ROS_DATA(Pfn1)->RmapListHead = NULL; + /* ReactOS semantics will now release the page, which will make it free and enter a colored list */ } KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); } -struct _MM_RMAP_ENTRY* +PMM_RMAP_ENTRY NTAPI MmGetRmapListHeadPage(PFN_NUMBER Pfn) { KIRQL oldIrql; - struct _MM_RMAP_ENTRY* ListHead; + PMM_RMAP_ENTRY ListHead; PMMPFN Pfn1; /* Lock PFN database */ @@ -476,15 +424,11 @@ MmGetRmapListHeadPage(PFN_NUMBER Pfn) /* Get the entry */ Pfn1 = MiGetPfnEntry(Pfn); - - /* Check if the page doesn't really have an RMAP */ - if (Pfn1->u3.e1.ParityError == FALSE) - { - KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); - return NULL; - } - - ListHead = (struct _MM_RMAP_ENTRY*)Pfn1->RmapListHead; + ASSERT(Pfn1); + ASSERT_IS_ROS_PFN(Pfn1); + + /* Get the list head */ + ListHead = MI_GET_ROS_DATA(Pfn1)->RmapListHead; /* Should not have an RMAP for a non-active page */ ASSERT(MiIsPfnInUse(Pfn1) == TRUE); @@ -499,9 +443,14 @@ NTAPI MmSetSavedSwapEntryPage(PFN_NUMBER Pfn, SWAPENTRY SwapEntry) { KIRQL oldIrql; - + PPHYSICAL_PAGE Page; + + Page = MiGetPfnEntry(Pfn); + ASSERT(Page); + ASSERT_IS_ROS_PFN(Page); + oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); - MiGetPfnEntry(Pfn)->u1.WsIndex = SwapEntry; + MI_GET_ROS_DATA(Page)->SwapEntry = SwapEntry; KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); } @@ -511,9 +460,14 @@ MmGetSavedSwapEntryPage(PFN_NUMBER Pfn) { SWAPENTRY SwapEntry; KIRQL oldIrql; + PPHYSICAL_PAGE Page; + + Page = MiGetPfnEntry(Pfn); + ASSERT(Page); + ASSERT_IS_ROS_PFN(Page); oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); - SwapEntry = MiGetPfnEntry(Pfn)->u1.WsIndex; + SwapEntry = MI_GET_ROS_DATA(Page)->SwapEntry; KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); return(SwapEntry); @@ -534,7 +488,8 @@ MmReferencePage(PFN_NUMBER Pfn) Page = MiGetPfnEntry(Pfn); ASSERT(Page); - + ASSERT_IS_ROS_PFN(Page); + Page->u3.e2.ReferenceCount++; } @@ -551,6 +506,7 @@ MmGetReferenceCountPage(PFN_NUMBER Pfn) oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MiGetPfnEntry(Pfn); ASSERT(Page); + ASSERT_IS_ROS_PFN(Page); RCount = Page->u3.e2.ReferenceCount; @@ -570,17 +526,22 @@ NTAPI MmDereferencePage(PFN_NUMBER Pfn) { PPHYSICAL_PAGE Page; - DPRINT("MmDereferencePage(PhysicalAddress %x)\n", Pfn << PAGE_SHIFT); Page = MiGetPfnEntry(Pfn); ASSERT(Page); - + ASSERT_IS_ROS_PFN(Page); + Page->u3.e2.ReferenceCount--; if (Page->u3.e2.ReferenceCount == 0) { /* Mark the page temporarily as valid, we're going to make it free soon */ Page->u3.e1.PageLocation = ActiveAndValid; + + /* It's not a ROS PFN anymore */ + Page->u4.AweAllocation = FALSE; + ExFreePool(MI_GET_ROS_DATA(Page)); + Page->RosMmData = 0; /* Bring it back into the free list */ DPRINT("Legacy free: %lx\n", Pfn); @@ -595,14 +556,7 @@ MmAllocPage(ULONG Type) PFN_NUMBER PfnOffset; PMMPFN Pfn1; - if (Type != MC_SYSTEM) - { - PfnOffset = MiRemoveZeroPage(MI_GET_NEXT_COLOR()); - } - else - { - PfnOffset = MiRemoveAnyPage(MI_GET_NEXT_COLOR()); - } + PfnOffset = MiRemoveZeroPage(MI_GET_NEXT_COLOR()); if (!PfnOffset) { @@ -614,6 +568,17 @@ MmAllocPage(ULONG Type) Pfn1 = MiGetPfnEntry(PfnOffset); Pfn1->u3.e2.ReferenceCount = 1; Pfn1->u3.e1.PageLocation = ActiveAndValid; + + /* This marks the PFN as a ReactOS PFN */ + Pfn1->u4.AweAllocation = TRUE; + + /* Allocate the extra ReactOS Data and zero it out */ + Pfn1->RosMmData = (LONG)ExAllocatePoolWithTag(NonPagedPool, sizeof(MMROSPFN), 'RsPf'); + ASSERT(MI_GET_ROS_DATA(Pfn1) != NULL); + ASSERT_IS_ROS_PFN(Pfn1); + MI_GET_ROS_DATA(Pfn1)->SwapEntry = 0; + MI_GET_ROS_DATA(Pfn1)->RmapListHead = NULL; + return PfnOffset; } diff --git a/ntoskrnl/mm/i386/page.c b/ntoskrnl/mm/i386/page.c index a93860a0342..e096fcf3f56 100644 --- a/ntoskrnl/mm/i386/page.c +++ b/ntoskrnl/mm/i386/page.c @@ -109,6 +109,43 @@ MmProtectToPteMask[32] = PTE_EXECUTE_WRITECOPY | PTE_WRITECOMBINED_CACHE, }; +const +ULONG MmProtectToValue[32] = +{ + PAGE_NOACCESS, + PAGE_READONLY, + PAGE_EXECUTE, + PAGE_EXECUTE_READ, + PAGE_READWRITE, + PAGE_WRITECOPY, + PAGE_EXECUTE_READWRITE, + PAGE_EXECUTE_WRITECOPY, + PAGE_NOACCESS, + PAGE_NOCACHE | PAGE_READONLY, + PAGE_NOCACHE | PAGE_EXECUTE, + PAGE_NOCACHE | PAGE_EXECUTE_READ, + PAGE_NOCACHE | PAGE_READWRITE, + PAGE_NOCACHE | PAGE_WRITECOPY, + PAGE_NOCACHE | PAGE_EXECUTE_READWRITE, + PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY, + PAGE_NOACCESS, + PAGE_GUARD | PAGE_READONLY, + PAGE_GUARD | PAGE_EXECUTE, + PAGE_GUARD | PAGE_EXECUTE_READ, + PAGE_GUARD | PAGE_READWRITE, + PAGE_GUARD | PAGE_WRITECOPY, + PAGE_GUARD | PAGE_EXECUTE_READWRITE, + PAGE_GUARD | PAGE_EXECUTE_WRITECOPY, + PAGE_NOACCESS, + PAGE_WRITECOMBINE | PAGE_READONLY, + PAGE_WRITECOMBINE | PAGE_EXECUTE, + PAGE_WRITECOMBINE | PAGE_EXECUTE_READ, + PAGE_WRITECOMBINE | PAGE_READWRITE, + PAGE_WRITECOMBINE | PAGE_WRITECOPY, + PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE, + PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY +}; + /* FUNCTIONS ***************************************************************/ BOOLEAN MmUnmapPageTable(PULONG Pt); @@ -186,7 +223,7 @@ MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create) MmDeleteHyperspaceMapping(PageDir); return NULL; } - Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn); + Status = MmRequestPageMemoryConsumer(MC_SYSTEM, FALSE, &Pfn); if (!NT_SUCCESS(Status) || Pfn == 0) { KeBugCheck(MEMORY_MANAGEMENT); @@ -194,7 +231,7 @@ MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create) Entry = InterlockedCompareExchangePte(&PageDir[PdeOffset], PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0); if (Entry != 0) { - MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn); + MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn); Pfn = PTE_TO_PFN(Entry); } } @@ -243,7 +280,7 @@ MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create) { return NULL; } - Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn); + Status = MmRequestPageMemoryConsumer(MC_SYSTEM, FALSE, &Pfn); if (!NT_SUCCESS(Status) || Pfn == 0) { KeBugCheck(MEMORY_MANAGEMENT); @@ -251,7 +288,7 @@ MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create) Entry = InterlockedCompareExchangePte(PageDir, PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0); if (Entry != 0) { - MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn); + MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn); } } } @@ -412,7 +449,7 @@ MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage, if (FreePage && WasValid) { - MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn); + MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn); } /* @@ -872,54 +909,6 @@ MmGetPhysicalAddress(PVOID vaddr) return p; } -VOID -NTAPI -MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size) -{ - ULONG StartOffset, EndOffset, Offset; - PULONG Pde; - - // - // Check if the process isn't there anymore - // This is probably a bad sign, since it means the caller is setting cr3 to - // 0 or something... - // - if ((PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]) == 0) && (Process != PsGetCurrentProcess())) - { - DPRINT1("Process: %16s is dead: %p\n", Process->ImageFileName, Process->Pcb.DirectoryTableBase[0]); - ASSERT(FALSE); - return; - } - - if (Address < MmSystemRangeStart) - { - KeBugCheck(MEMORY_MANAGEMENT); - } - - StartOffset = ADDR_TO_PDE_OFFSET(Address); - EndOffset = ADDR_TO_PDE_OFFSET((PVOID)((ULONG_PTR)Address + Size)); - - if (Process != NULL && Process != PsGetCurrentProcess()) - { - Pde = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0])); - } - else - { - Pde = (PULONG)PAGEDIRECTORY_MAP; - } - for (Offset = StartOffset; Offset <= EndOffset; Offset++) - { - if (Offset != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP)) - { - InterlockedCompareExchangePte(&Pde[Offset], MmGlobalKernelPageDirectory[Offset], 0); - } - } - if (Pde != (PULONG)PAGEDIRECTORY_MAP) - { - MmDeleteHyperspaceMapping(Pde); - } -} - VOID INIT_FUNCTION NTAPI diff --git a/ntoskrnl/mm/marea.c b/ntoskrnl/mm/marea.c index 92aacb8b822..2c7567417ce 100644 --- a/ntoskrnl/mm/marea.c +++ b/ntoskrnl/mm/marea.c @@ -356,6 +356,12 @@ NTAPI MiInsertVad(IN PMMVAD Vad, IN PEPROCESS Process); +ULONG +NTAPI +MiMakeProtectionMask( + IN ULONG Protect +); + static VOID MmInsertMemoryArea( PMMSUPPORT AddressSpace, @@ -389,6 +395,7 @@ MmInsertMemoryArea( } Vad->u.VadFlags.Spare = 1; Vad->u.VadFlags.PrivateMemory = 1; + Vad->u.VadFlags.Protection = MiMakeProtectionMask(marea->Protect); MiInsertVad(Vad, MmGetAddressSpaceOwner(AddressSpace)); marea->Vad = Vad; } diff --git a/ntoskrnl/mm/mmfault.c b/ntoskrnl/mm/mmfault.c index 269a1659fe6..bf8b2e014c7 100644 --- a/ntoskrnl/mm/mmfault.c +++ b/ntoskrnl/mm/mmfault.c @@ -17,37 +17,6 @@ /* PRIVATE FUNCTIONS **********************************************************/ -VOID -FASTCALL -MiSyncForProcessAttach(IN PKTHREAD Thread, - IN PEPROCESS Process) -{ - PETHREAD Ethread = CONTAINING_RECORD(Thread, ETHREAD, Tcb); - - /* Hack Sync because Mm is broken */ - MmUpdatePageDir(Process, Ethread, sizeof(ETHREAD)); - MmUpdatePageDir(Process, Ethread->ThreadsProcess, sizeof(EPROCESS)); - MmUpdatePageDir(Process, - (PVOID)Thread->StackLimit, - Thread->LargeStack ? - KERNEL_LARGE_STACK_SIZE : KERNEL_STACK_SIZE); -} - -VOID -FASTCALL -MiSyncForContextSwitch(IN PKTHREAD Thread) -{ - PVOID Process = PsGetCurrentProcess(); - PETHREAD Ethread = CONTAINING_RECORD(Thread, ETHREAD, Tcb); - - /* Hack Sync because Mm is broken */ - MmUpdatePageDir(Process, Ethread->ThreadsProcess, sizeof(EPROCESS)); - MmUpdatePageDir(Process, - (PVOID)Thread->StackLimit, - Thread->LargeStack ? - KERNEL_LARGE_STACK_SIZE : KERNEL_STACK_SIZE); -} - NTSTATUS NTAPI MmpAccessFault(KPROCESSOR_MODE Mode, @@ -230,7 +199,7 @@ MmAccessFault(IN BOOLEAN StoreInstruction, IN KPROCESSOR_MODE Mode, IN PVOID TrapInformation) { - PMEMORY_AREA MemoryArea; + PMEMORY_AREA MemoryArea = NULL; /* Cute little hack for ROS */ if ((ULONG_PTR)Address >= (ULONG_PTR)MmSystemRangeStart) @@ -245,27 +214,24 @@ MmAccessFault(IN BOOLEAN StoreInstruction, #endif } - /* - * Check if this is an ARM3 memory area or if there's no memory area at all. - * The latter can happen early in the boot cycle when ARM3 paged pool is in - * use before having defined the memory areas proper. - * A proper fix would be to define memory areas in the ARM3 code, but we want - * to avoid adding this ReactOS-specific construct to ARM3 code. - * Either way, in the future, as ReactOS-paged pool is eliminated, this hack - * can go away. - */ - MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), Address); - if (!(MemoryArea) && (Address <= MM_HIGHEST_USER_ADDRESS)) + /* Is there a ReactOS address space yet? */ + if (MmGetKernelAddressSpace()) { - /* Could this be a VAD fault from user-mode? */ - MemoryArea = MmLocateMemoryAreaByAddress(MmGetCurrentAddressSpace(), Address); + /* Check if this is an ARM3 memory area */ + MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), Address); + if (!(MemoryArea) && (Address <= MM_HIGHEST_USER_ADDRESS)) + { + /* Could this be a VAD fault from user-mode? */ + MemoryArea = MmLocateMemoryAreaByAddress(MmGetCurrentAddressSpace(), Address); + } } - if ((!(MemoryArea) && ((ULONG_PTR)Address >= (ULONG_PTR)MmPagedPoolStart)) || - ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3))) + + /* Is this an ARM3 memory area, or is there no address space yet? */ + if (((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3)) || + (!(MemoryArea) && ((ULONG_PTR)Address >= (ULONG_PTR)MmPagedPoolStart)) || + (!MmGetKernelAddressSpace())) { - // - // Hand it off to more competent hands... - // + /* This is an ARM3 fault */ DPRINT("ARM3 fault %p\n", MemoryArea); return MmArmAccessFault(StoreInstruction, Address, Mode, TrapInformation); } diff --git a/ntoskrnl/mm/mminit.c b/ntoskrnl/mm/mminit.c index 60ea7f70cc6..df2140c9cc7 100644 --- a/ntoskrnl/mm/mminit.c +++ b/ntoskrnl/mm/mminit.c @@ -19,37 +19,6 @@ VOID NTAPI MiInitializeUserPfnBitmap(VOID); -PCHAR -MemType[] = -{ - "ExceptionBlock ", - "SystemBlock ", - "Free ", - "Bad ", - "LoadedProgram ", - "FirmwareTemporary ", - "FirmwarePermanent ", - "OsloaderHeap ", - "OsloaderStack ", - "SystemCode ", - "HalCode ", - "BootDriver ", - "ConsoleInDriver ", - "ConsoleOutDriver ", - "StartupDpcStack ", - "StartupKernelStack", - "StartupPanicStack ", - "StartupPcrPage ", - "StartupPdrPage ", - "RegistryData ", - "MemoryData ", - "NlsData ", - "SpecialMemory ", - "BBTMemory ", - "LoaderReserve ", - "LoaderXIPRom " -}; - HANDLE MpwThreadHandle; KEVENT MpwThreadEvent; @@ -61,12 +30,6 @@ ULONG MmReadClusterSize; UCHAR MmDisablePagingExecutive = 1; // Forced to off PMMPTE MmSharedUserDataPte; PMMSUPPORT MmKernelAddressSpace; -BOOLEAN MiDbgEnableMdDump = -#ifdef _ARM_ -TRUE; -#else -FALSE; -#endif /* PRIVATE FUNCTIONS *********************************************************/ @@ -314,27 +277,6 @@ MiDbgDumpAddressSpace(VOID) "Non Paged Pool Expansion PTE Space"); } -VOID -NTAPI -MiDbgDumpMemoryDescriptors(VOID) -{ - PLIST_ENTRY NextEntry; - PMEMORY_ALLOCATION_DESCRIPTOR Md; - ULONG TotalPages = 0; - - DPRINT1("Base\t\tLength\t\tType\n"); - for (NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink; - NextEntry != &KeLoaderBlock->MemoryDescriptorListHead; - NextEntry = NextEntry->Flink) - { - Md = CONTAINING_RECORD(NextEntry, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry); - DPRINT1("%08lX\t%08lX\t%s\n", Md->BasePage, Md->PageCount, MemType[Md->MemoryType]); - TotalPages += Md->PageCount; - } - - DPRINT1("Total: %08lX (%d MB)\n", TotalPages, (TotalPages * PAGE_SIZE) / 1024 / 1024); -} - NTSTATUS NTAPI MmMpwThreadMain(PVOID Ignored) { @@ -428,75 +370,65 @@ MmInitSystem(IN ULONG Phase, MMPTE TempPte = ValidKernelPte; PFN_NUMBER PageFrameNumber; - if (Phase == 0) - { - /* Initialize the kernel address space */ - KeInitializeGuardedMutex(&PsGetCurrentProcess()->AddressCreationLock); - MmKernelAddressSpace = MmGetCurrentAddressSpace(); - MmInitGlobalKernelPageDirectory(); - - /* Dump memory descriptors */ - if (MiDbgEnableMdDump) MiDbgDumpMemoryDescriptors(); - - /* Initialize ARMツウ in phase 0 */ - MmArmInitSystem(0, KeLoaderBlock); + /* Initialize the kernel address space */ + ASSERT(Phase == 1); + KeInitializeGuardedMutex(&PsIdleProcess->AddressCreationLock); + MmKernelAddressSpace = &PsIdleProcess->Vm; - /* Intialize system memory areas */ - MiInitSystemMemoryAreas(); + /* Intialize system memory areas */ + MiInitSystemMemoryAreas(); - /* Dump the address space */ - MiDbgDumpAddressSpace(); - } - else if (Phase == 1) - { - MiInitializeUserPfnBitmap(); - MmInitializeMemoryConsumer(MC_USER, MmTrimUserMemory); - MmInitializeRmapList(); - MmInitializePageOp(); - MmInitSectionImplementation(); - MmInitPagingFile(); - - // - // Create a PTE to double-map the shared data section. We allocate it - // from paged pool so that we can't fault when trying to touch the PTE - // itself (to map it), since paged pool addresses will already be mapped - // by the fault handler. - // - MmSharedUserDataPte = ExAllocatePoolWithTag(PagedPool, - sizeof(MMPTE), - ' mM'); - if (!MmSharedUserDataPte) return FALSE; - - // - // Now get the PTE for shared data, and read the PFN that holds it - // - PointerPte = MiAddressToPte((PVOID)KI_USER_SHARED_DATA); - ASSERT(PointerPte->u.Hard.Valid == 1); - PageFrameNumber = PFN_FROM_PTE(PointerPte); - - /* Build the PTE and write it */ - MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte, - PointerPte, - MM_READONLY, - PageFrameNumber); - *MmSharedUserDataPte = TempPte; - - /* Setup the memory threshold events */ - if (!MiInitializeMemoryEvents()) return FALSE; - - /* - * Unmap low memory - */ - MiInitBalancerThread(); - - /* - * Initialise the modified page writer. - */ - MmInitMpwThread(); - - /* Initialize the balance set manager */ - MmInitBsmThread(); - } + /* Dump the address space */ + MiDbgDumpAddressSpace(); + + MmInitGlobalKernelPageDirectory(); + MiInitializeUserPfnBitmap(); + MmInitializeMemoryConsumer(MC_USER, MmTrimUserMemory); + MmInitializeRmapList(); + MmInitializePageOp(); + MmInitSectionImplementation(); + MmInitPagingFile(); + + // + // Create a PTE to double-map the shared data section. We allocate it + // from paged pool so that we can't fault when trying to touch the PTE + // itself (to map it), since paged pool addresses will already be mapped + // by the fault handler. + // + MmSharedUserDataPte = ExAllocatePoolWithTag(PagedPool, + sizeof(MMPTE), + ' mM'); + if (!MmSharedUserDataPte) return FALSE; + + // + // Now get the PTE for shared data, and read the PFN that holds it + // + PointerPte = MiAddressToPte((PVOID)KI_USER_SHARED_DATA); + ASSERT(PointerPte->u.Hard.Valid == 1); + PageFrameNumber = PFN_FROM_PTE(PointerPte); + + /* Build the PTE and write it */ + MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte, + PointerPte, + MM_READONLY, + PageFrameNumber); + *MmSharedUserDataPte = TempPte; + + /* Setup the memory threshold events */ + if (!MiInitializeMemoryEvents()) return FALSE; + + /* + * Unmap low memory + */ + MiInitBalancerThread(); + + /* + * Initialise the modified page writer. + */ + MmInitMpwThread(); + + /* Initialize the balance set manager */ + MmInitBsmThread(); return TRUE; } diff --git a/ntoskrnl/mm/rmap.c b/ntoskrnl/mm/rmap.c index 8d6b7a6aab4..aa2f96ee3b4 100644 --- a/ntoskrnl/mm/rmap.c +++ b/ntoskrnl/mm/rmap.c @@ -19,17 +19,6 @@ /* TYPES ********************************************************************/ -typedef struct _MM_RMAP_ENTRY -{ - struct _MM_RMAP_ENTRY* Next; - PEPROCESS Process; - PVOID Address; -#if DBG - PVOID Caller; -#endif -} -MM_RMAP_ENTRY, *PMM_RMAP_ENTRY; - /* GLOBALS ******************************************************************/ static FAST_MUTEX RmapListLock; diff --git a/ntoskrnl/mm/section.c b/ntoskrnl/mm/section.c index 3b00025d6d1..5dad201e343 100644 --- a/ntoskrnl/mm/section.c +++ b/ntoskrnl/mm/section.c @@ -4902,7 +4902,7 @@ MmCreateSection (OUT PVOID * Section, PROS_SECTION_OBJECT *SectionObject = (PROS_SECTION_OBJECT *)Section; /* Check if an ARM3 section is being created instead */ - if (AllocationAttributes & 0xC0000000) + if (AllocationAttributes & 1) { DPRINT1("arm 3 path\n"); return MmCreateArm3Section(Section, @@ -4910,7 +4910,7 @@ MmCreateSection (OUT PVOID * Section, ObjectAttributes, MaximumSize, SectionPageProtection, - AllocationAttributes &~ 0xC0000000, + AllocationAttributes &~ 1, FileHandle, File); } diff --git a/subsystems/win32/csrss/csrsrv/init.c b/subsystems/win32/csrss/csrsrv/init.c index d700219bc05..fa8d6273765 100644 --- a/subsystems/win32/csrss/csrsrv/init.c +++ b/subsystems/win32/csrss/csrsrv/init.c @@ -107,7 +107,7 @@ InitializeVideoAddressSpace(VOID) &BaseAddress, 0, &ViewSize, - MEM_COMMIT, + MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!NT_SUCCESS(Status)) { diff --git a/subsystems/win32/win32k/include/hook.h b/subsystems/win32/win32k/include/hook.h index d09304b4895..ce6d64e1b9f 100644 --- a/subsystems/win32/win32k/include/hook.h +++ b/subsystems/win32/win32k/include/hook.h @@ -1,17 +1,10 @@ #pragma once #define HOOK_THREAD_REFERENCED (0x1) -#define NB_HOOKS (WH_MAXHOOK-WH_MINHOOK+1) #define HOOKID_TO_INDEX(HookId) (HookId - WH_MINHOOK) #define HOOKID_TO_FLAG(HookId) (1 << ((HookId) + 1)) #define ISITHOOKED(HookId) (((PTHREADINFO)PsGetCurrentThreadWin32Thread())->fsHooks & HOOKID_TO_FLAG(HookId)) -typedef struct tagHOOKTABLE -{ - LIST_ENTRY Hooks[NB_HOOKS]; /* array of hook chains */ - UINT Counts[NB_HOOKS]; /* use counts for each hook chain */ -} HOOKTABLE, *PHOOKTABLE; - typedef struct tagEVENTHOOK { THROBJHEAD head; @@ -32,11 +25,22 @@ typedef struct tagEVENTTABLE UINT Counts; } EVENTTABLE, *PEVENTTABLE; +typedef struct _NOTIFYEVENT +{ + DWORD event; + LONG idObject; + LONG idChild; + DWORD flags; +} NOTIFYEVENT, *PNOTIFYEVENT; + +LRESULT FASTCALL co_CallHook(INT HookId, INT Code, WPARAM wParam, LPARAM lParam); LRESULT FASTCALL co_HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam); LRESULT FASTCALL co_EVENT_CallEvents(DWORD, HWND, UINT_PTR, LONG_PTR); VOID FASTCALL HOOK_DestroyThreadHooks(PETHREAD Thread); +VOID FASTCALL EVENT_DestroyThreadEvents(PETHREAD Thread); PHOOK FASTCALL IntGetHookObject(HHOOK); PHOOK FASTCALL IntGetNextHook(PHOOK Hook); LRESULT FASTCALL UserCallNextHookEx( PHOOK pHook, int Code, WPARAM wParam, LPARAM lParam, BOOL Ansi); +BOOL FASTCALL IntUnhookWindowsHook(int,HOOKPROC); /* EOF */ diff --git a/subsystems/win32/win32k/include/msgqueue.h b/subsystems/win32/win32k/include/msgqueue.h index e35ab0f5a15..569f1028d83 100644 --- a/subsystems/win32/win32k/include/msgqueue.h +++ b/subsystems/win32/win32k/include/msgqueue.h @@ -87,9 +87,6 @@ typedef struct _USER_MESSAGE_QUEUE /* Caret information for this queue */ PTHRDCARETINFO CaretInfo; - /* Window hooks */ - PHOOKTABLE Hooks; - /* queue state tracking */ WORD WakeMask; WORD QueueBits; @@ -212,9 +209,6 @@ BOOL APIENTRY IntInitMessagePumpHook(); BOOL APIENTRY IntUninitMessagePumpHook(); #define MAKE_LONG(x, y) ((((y) & 0xFFFF) << 16) | ((x) & 0xFFFF)) -PHOOKTABLE FASTCALL MsqGetHooks(PUSER_MESSAGE_QUEUE Queue); -VOID FASTCALL MsqSetHooks(PUSER_MESSAGE_QUEUE Queue, PHOOKTABLE Hooks); - LPARAM FASTCALL MsqSetMessageExtraInfo(LPARAM lParam); LPARAM FASTCALL MsqGetMessageExtraInfo(VOID); VOID APIENTRY MsqRemoveWindowMessagesFromQueue(PVOID pWindow); /* F*(&$ headers, will be gone in the rewrite! */ diff --git a/subsystems/win32/win32k/include/win32.h b/subsystems/win32/win32k/include/win32.h index 63dfba76fe2..ed1e645a0a6 100644 --- a/subsystems/win32/win32k/include/win32.h +++ b/subsystems/win32/win32k/include/win32.h @@ -96,6 +96,7 @@ typedef struct _THREADINFO LIST_ENTRY PtiLink; POINT ptLast; + LIST_ENTRY aphkStart[NB_HOOKS]; CLIENTTHREADINFO cti; // Used only when no Desktop or pcti NULL. /* ReactOS */ LIST_ENTRY WindowListHead; @@ -150,6 +151,8 @@ typedef struct _W32PROCESS LIST_ENTRY GDIBrushAttrFreeList; } W32PROCESS, *PW32PROCESS; +#define CLIBS 32 + typedef struct _PROCESSINFO { W32PROCESS; @@ -158,11 +161,15 @@ typedef struct _PROCESSINFO struct _DESKTOP* rpdeskStartup; PCLS pclsPrivateList; PCLS pclsPublicList; - + DWORD dwhmodLibLoadedMask; + HANDLE ahmodLibLoaded[CLIBS]; + struct _WINSTATION_OBJECT *prpwinsta; + HWINSTA hwinsta; + ACCESS_MASK amwinsta; + DWORD dwHotkey; HMONITOR hMonitor; - + LUID luidSession; USERSTARTUPINFO usi; - ULONG Flags; DWORD dwLayout; DWORD dwRegisteredClasses; /* ReactOS */ diff --git a/subsystems/win32/win32k/include/window.h b/subsystems/win32/win32k/include/window.h index a91183a0153..39a55b26e00 100644 --- a/subsystems/win32/win32k/include/window.h +++ b/subsystems/win32/win32k/include/window.h @@ -104,7 +104,7 @@ IntShowOwnedPopups( PWND owner, BOOL fShow ); LRESULT FASTCALL IntDefWindowProc( PWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, BOOL Ansi); -VOID FASTCALL IntNotifyWinEvent(DWORD, PWND, LONG, LONG); +VOID FASTCALL IntNotifyWinEvent(DWORD, PWND, LONG, LONG, DWORD); PWND FASTCALL co_UserCreateWindowEx(CREATESTRUCTW*, PUNICODE_STRING, PLARGE_STRING); WNDPROC FASTCALL IntGetWindowProc(PWND,BOOL); diff --git a/subsystems/win32/win32k/main/dllmain.c b/subsystems/win32/win32k/main/dllmain.c index 3d5e0aa4e95..b12d96fa8ba 100644 --- a/subsystems/win32/win32k/main/dllmain.c +++ b/subsystems/win32/win32k/main/dllmain.c @@ -173,6 +173,7 @@ Win32kThreadCallback(struct _ETHREAD *Thread, { struct _EPROCESS *Process; PTHREADINFO Win32Thread; + int i; DECLARE_RETURN(NTSTATUS); DPRINT("Enter Win32kThreadCallback\n"); @@ -212,6 +213,10 @@ Win32kThreadCallback(struct _ETHREAD *Thread, InitializeListHead(&Win32Thread->WindowListHead); InitializeListHead(&Win32Thread->W32CallbackListHead); InitializeListHead(&Win32Thread->PtiLink); + for (i = 0; i < NB_HOOKS; i++) + { + InitializeListHead(&Win32Thread->aphkStart[i]); + } /* * inherit the thread desktop and process window station (if not yet inherited) from the process startup @@ -288,6 +293,7 @@ Win32kThreadCallback(struct _ETHREAD *Thread, Win32Thread->TIF_flags |= TIF_INCLEANUP; DceFreeThreadDCE(Win32Thread); HOOK_DestroyThreadHooks(Thread); + EVENT_DestroyThreadEvents(Thread); /* Cleanup timers */ DestroyTimersForThread(Win32Thread); KeSetEvent(Win32Thread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE); diff --git a/subsystems/win32/win32k/ntuser/callback.c b/subsystems/win32/win32k/ntuser/callback.c index 6d6a7529ea0..997cd4fb8b5 100644 --- a/subsystems/win32/win32k/ntuser/callback.c +++ b/subsystems/win32/win32k/ntuser/callback.c @@ -324,20 +324,22 @@ co_IntCallHookProc(INT HookId, PUNICODE_STRING ModuleName) { ULONG ArgumentLength; - PVOID Argument; + PVOID Argument = NULL; LRESULT Result = 0; NTSTATUS Status; PVOID ResultPointer; ULONG ResultLength; PHOOKPROC_CALLBACK_ARGUMENTS Common; - CBT_CREATEWNDW *CbtCreateWnd =NULL; + CBT_CREATEWNDW *CbtCreateWnd = NULL; PCHAR Extra; PHOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS CbtCreatewndExtra = NULL; - UNICODE_STRING WindowName; - UNICODE_STRING ClassName; - PANSI_STRING asWindowName; - PANSI_STRING asClassName; + UNICODE_STRING WindowName, ClassName; + ANSI_STRING asWindowName, asClassName; PTHREADINFO pti; + PWND pWnd; + BOOL Hit = FALSE; + + ASSERT(Proc); pti = PsGetCurrentThreadWin32Thread(); if (pti->TIF_flags & TIF_INCLEANUP) @@ -354,33 +356,67 @@ co_IntCallHookProc(INT HookId, switch(Code) { case HCBT_CREATEWND: + pWnd = UserGetWindowObject((HWND) wParam); + if (!pWnd) + { + DPRINT1("WH_CBT HCBT_CREATEWND wParam bad hWnd!\n"); + goto Fault_Exit; + } + CbtCreateWnd = (CBT_CREATEWNDW *) lParam; ArgumentLength += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); - asWindowName = (PANSI_STRING)&WindowName; - asClassName = (PANSI_STRING)&ClassName; - if (Ansi) { - RtlInitAnsiString(asWindowName, (PCSZ)CbtCreateWnd->lpcs->lpszName); - ArgumentLength += WindowName.Length + sizeof(CHAR); + RtlInitAnsiString(&asWindowName, NULL); + _SEH2_TRY + { + ProbeForRead(CbtCreateWnd->lpcs->lpszName, sizeof(CHAR), 1); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Hit = TRUE; + } + _SEH2_END; + if (Hit) // Client is at deaths door. + goto Fault_Exit; + if (CbtCreateWnd->lpcs->lpszName) + RtlInitAnsiString(&asWindowName, (PCSZ)CbtCreateWnd->lpcs->lpszName); + ArgumentLength += asWindowName.Length + sizeof(CHAR); } else { - RtlInitUnicodeString(&WindowName, CbtCreateWnd->lpcs->lpszName); + RtlInitUnicodeString(&WindowName, NULL); + _SEH2_TRY + { + ProbeForRead(CbtCreateWnd->lpcs->lpszName, sizeof(WCHAR), 1); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Hit = TRUE; + } + _SEH2_END; + if (Hit) + goto Fault_Exit; + if (CbtCreateWnd->lpcs->lpszName) + RtlInitUnicodeString(&WindowName, CbtCreateWnd->lpcs->lpszName); ArgumentLength += WindowName.Length + sizeof(WCHAR); } - if (! IS_ATOM(CbtCreateWnd->lpcs->lpszClass)) + if (!IS_ATOM(CbtCreateWnd->lpcs->lpszClass)) { if (Ansi) { - RtlInitAnsiString(asClassName, (PCSZ)CbtCreateWnd->lpcs->lpszClass); - ArgumentLength += ClassName.Length + sizeof(CHAR); + RtlInitAnsiString(&asClassName, NULL); + if (CbtCreateWnd->lpcs->lpszClass) + RtlInitAnsiString(&asClassName, (PCSZ)CbtCreateWnd->lpcs->lpszClass); + ArgumentLength += asClassName.Length + sizeof(CHAR); } else { - RtlInitUnicodeString(&ClassName, CbtCreateWnd->lpcs->lpszClass); + RtlInitUnicodeString(&ClassName, NULL); + if (CbtCreateWnd->lpcs->lpszClass) + RtlInitUnicodeString(&ClassName, CbtCreateWnd->lpcs->lpszClass); ArgumentLength += ClassName.Length + sizeof(WCHAR); } } @@ -406,7 +442,7 @@ co_IntCallHookProc(INT HookId, break; default: DPRINT1("Trying to call unsupported CBT hook %d\n", Code); - return 0; + goto Fault_Exit; } break; case WH_KEYBOARD_LL: @@ -435,14 +471,14 @@ co_IntCallHookProc(INT HookId, break; default: DPRINT1("Trying to call unsupported window hook %d\n", HookId); - return 0; + goto Fault_Exit; } Argument = IntCbAllocateMemory(ArgumentLength); if (NULL == Argument) { DPRINT1("HookProc callback failed: out of memory\n"); - return 0; + goto Fault_Exit; } Common = (PHOOKPROC_CALLBACK_ARGUMENTS) Argument; Common->HookId = HookId; @@ -452,7 +488,8 @@ co_IntCallHookProc(INT HookId, Common->Proc = Proc; Common->Ansi = Ansi; Common->ModuleNameLength = ModuleName->Length; - memcpy(Common->ModuleName, ModuleName->Buffer, ModuleName->Length); + if (ModuleName->Buffer) + RtlCopyMemory(Common->ModuleName, ModuleName->Buffer, ModuleName->Length); Extra = (PCHAR) Common->ModuleName + Common->ModuleNameLength; switch(HookId) @@ -466,32 +503,53 @@ co_IntCallHookProc(INT HookId, RtlCopyMemory( &CbtCreatewndExtra->Cs, CbtCreateWnd->lpcs, sizeof(CREATESTRUCTW) ); CbtCreatewndExtra->WndInsertAfter = CbtCreateWnd->hwndInsertAfter; Extra = (PCHAR) (CbtCreatewndExtra + 1); - RtlCopyMemory(Extra, WindowName.Buffer, WindowName.Length); - CbtCreatewndExtra->Cs.lpszName = (LPCWSTR) (Extra - (PCHAR) CbtCreatewndExtra); - CbtCreatewndExtra->Cs.lpszClass = ClassName.Buffer; - Extra += WindowName.Length; + CbtCreatewndExtra->Cs.lpszClass = ClassName.Buffer; // if Atom + if (Ansi) { + if (asWindowName.Buffer) + RtlCopyMemory(Extra, asWindowName.Buffer, asWindowName.Length); + CbtCreatewndExtra->Cs.lpszName = (LPCWSTR) (Extra - (PCHAR) CbtCreatewndExtra); + Extra += asWindowName.Length; + *((CHAR *) Extra) = '\0'; Extra += sizeof(CHAR); } else { + if (asWindowName.Buffer) + RtlCopyMemory(Extra, WindowName.Buffer, WindowName.Length); + CbtCreatewndExtra->Cs.lpszName = (LPCWSTR) (Extra - (PCHAR) CbtCreatewndExtra); + Extra += WindowName.Length; + *((WCHAR *) Extra) = L'\0'; Extra += sizeof(WCHAR); } - if (! IS_ATOM(ClassName.Buffer)) + if (!IS_ATOM(ClassName.Buffer)) { - RtlCopyMemory(Extra, ClassName.Buffer, ClassName.Length); - CbtCreatewndExtra->Cs.lpszClass = - (LPCWSTR)(ULONG_PTR) MAKELONG(Extra - (PCHAR) CbtCreatewndExtra, 1); - Extra += ClassName.Length; - if (Ansi) + { + if (asClassName.Buffer) + RtlCopyMemory(Extra, asClassName.Buffer, asClassName.Length); + CbtCreatewndExtra->Cs.lpszClass = + (LPCWSTR)(ULONG_PTR) MAKELONG(Extra - (PCHAR) CbtCreatewndExtra, 1); + Extra += asClassName.Length; + *((CHAR *) Extra) = '\0'; + Extra += sizeof(CHAR); + } else + { + if (ClassName.Buffer) + RtlCopyMemory(Extra, ClassName.Buffer, ClassName.Length); + CbtCreatewndExtra->Cs.lpszClass = + (LPCWSTR)(ULONG_PTR) MAKELONG(Extra - (PCHAR) CbtCreatewndExtra, 1); + Extra += ClassName.Length; + *((WCHAR *) Extra) = L'\0'; + Extra += sizeof(WCHAR); + } } break; case HCBT_CLICKSKIPPED: @@ -533,7 +591,6 @@ co_IntCallHookProc(INT HookId, case WH_GETMESSAGE: RtlCopyMemory(Extra, (PVOID) lParam, sizeof(MSG)); Common->lParam = (LPARAM) (Extra - (PCHAR) Common); -// DPRINT1("KHOOK Memory: %x\n",Common); break; case WH_FOREGROUNDIDLE: case WH_KEYBOARD: @@ -563,37 +620,47 @@ co_IntCallHookProc(INT HookId, _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Result = 0; + Hit = TRUE; } _SEH2_END; if (!NT_SUCCESS(Status)) { - return 0; + goto Fault_Exit; } - - if (HookId == WH_CBT && Code == HCBT_CREATEWND) + /* Support write backs... SEH is in UserCallNextHookEx. */ + switch (HookId) { - if (CbtCreatewndExtra) - { - _SEH2_TRY - { /* - The parameters could have been changed, include the coordinates - and dimensions of the window. We copy it back. - */ - CbtCreateWnd->hwndInsertAfter = CbtCreatewndExtra->WndInsertAfter; - CbtCreateWnd->lpcs->x = CbtCreatewndExtra->Cs.x; - CbtCreateWnd->lpcs->y = CbtCreatewndExtra->Cs.y; - CbtCreateWnd->lpcs->cx = CbtCreatewndExtra->Cs.cx; - CbtCreateWnd->lpcs->cy = CbtCreatewndExtra->Cs.cy; - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + case WH_CBT: + if (Code == HCBT_CREATEWND) { - Result = 0; + if (CbtCreatewndExtra) + {/* + The parameters could have been changed, include the coordinates + and dimensions of the window. We copy it back. + */ + CbtCreateWnd->hwndInsertAfter = CbtCreatewndExtra->WndInsertAfter; + CbtCreateWnd->lpcs->x = CbtCreatewndExtra->Cs.x; + CbtCreateWnd->lpcs->y = CbtCreatewndExtra->Cs.y; + CbtCreateWnd->lpcs->cx = CbtCreatewndExtra->Cs.cx; + CbtCreateWnd->lpcs->cy = CbtCreatewndExtra->Cs.cy; + } } - _SEH2_END; - } + break; + // "The GetMsgProc hook procedure can examine or modify the message." + case WH_GETMESSAGE: + if (lParam) + { + RtlCopyMemory((PVOID) lParam, Extra, sizeof(MSG)); + } + break; } +Fault_Exit: + if (Hit) + { + DPRINT1("Exception CallHookProc HookId %d Code %d\n",HookId,Code); + } if (Argument) IntCbFreeMemory(Argument); return Result; diff --git a/subsystems/win32/win32k/ntuser/caret.c b/subsystems/win32/win32k/ntuser/caret.c index 3ebae7ae11b..76d58a7120b 100644 --- a/subsystems/win32/win32k/ntuser/caret.c +++ b/subsystems/win32/win32k/ntuser/caret.c @@ -30,10 +30,13 @@ static BOOL FASTCALL co_IntHideCaret(PTHRDCARETINFO CaretInfo) { + PWND pWnd; if(CaretInfo->hWnd && CaretInfo->Visible && CaretInfo->Showing) { + pWnd = UserGetWindowObject(CaretInfo->hWnd); co_IntSendMessage(CaretInfo->hWnd, WM_SYSTIMER, IDCARETTIMER, 0); CaretInfo->Showing = 0; + IntNotifyWinEvent(EVENT_OBJECT_HIDE, pWnd, OBJID_CARET, CHILDID_SELF, 0); return TRUE; } return FALSE; @@ -43,17 +46,20 @@ BOOL FASTCALL co_IntDestroyCaret(PTHREADINFO Win32Thread) { PUSER_MESSAGE_QUEUE ThreadQueue; + PWND pWnd; ThreadQueue = (PUSER_MESSAGE_QUEUE)Win32Thread->MessageQueue; if(!ThreadQueue || !ThreadQueue->CaretInfo) return FALSE; + pWnd = UserGetWindowObject(ThreadQueue->CaretInfo->hWnd); co_IntHideCaret(ThreadQueue->CaretInfo); ThreadQueue->CaretInfo->Bitmap = (HBITMAP)0; ThreadQueue->CaretInfo->hWnd = (HWND)0; ThreadQueue->CaretInfo->Size.cx = ThreadQueue->CaretInfo->Size.cy = 0; ThreadQueue->CaretInfo->Showing = 0; ThreadQueue->CaretInfo->Visible = 0; + IntNotifyWinEvent(EVENT_OBJECT_DESTROY, pWnd, OBJID_CARET, CHILDID_SELF, 0); return TRUE; } @@ -176,6 +182,7 @@ BOOL FASTCALL co_IntSetCaretPos(int X, int Y) { PTHREADINFO pti; + PWND pWnd; PUSER_MESSAGE_QUEUE ThreadQueue; pti = PsGetCurrentThreadWin32Thread(); @@ -183,6 +190,7 @@ co_IntSetCaretPos(int X, int Y) if(ThreadQueue->CaretInfo->hWnd) { + pWnd = UserGetWindowObject(ThreadQueue->CaretInfo->hWnd); if(ThreadQueue->CaretInfo->Pos.x != X || ThreadQueue->CaretInfo->Pos.y != Y) { co_IntHideCaret(ThreadQueue->CaretInfo); @@ -191,6 +199,7 @@ co_IntSetCaretPos(int X, int Y) ThreadQueue->CaretInfo->Pos.y = Y; co_IntSendMessage(ThreadQueue->CaretInfo->hWnd, WM_SYSTIMER, IDCARETTIMER, 0); IntSetTimer(UserGetWindowObject(ThreadQueue->CaretInfo->hWnd), IDCARETTIMER, IntGetCaretBlinkTime(), NULL, TMRF_SYSTEM); + IntNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, pWnd, OBJID_CARET, CHILDID_SELF, 0); } return TRUE; } @@ -277,6 +286,7 @@ BOOL FASTCALL co_UserHideCaret(PWND Window OPTIONAL) BOOL FASTCALL co_UserShowCaret(PWND Window OPTIONAL) { PTHREADINFO pti; + PWND pWnd; PUSER_MESSAGE_QUEUE ThreadQueue; if (Window) ASSERT_REFS_CO(Window); @@ -301,11 +311,12 @@ BOOL FASTCALL co_UserShowCaret(PWND Window OPTIONAL) ThreadQueue->CaretInfo->Visible = 1; if(!ThreadQueue->CaretInfo->Showing) { + pWnd = UserGetWindowObject(ThreadQueue->CaretInfo->hWnd); co_IntSendMessage(ThreadQueue->CaretInfo->hWnd, WM_SYSTIMER, IDCARETTIMER, 0); + IntNotifyWinEvent(EVENT_OBJECT_SHOW, pWnd, OBJID_CARET, OBJID_CARET, 0); } IntSetTimer(UserGetWindowObject(ThreadQueue->CaretInfo->hWnd), IDCARETTIMER, IntGetCaretBlinkTime(), NULL, TMRF_SYSTEM); } - return TRUE; } @@ -370,7 +381,7 @@ NtUserCreateCaret( } ThreadQueue->CaretInfo->Visible = 0; ThreadQueue->CaretInfo->Showing = 0; - + IntNotifyWinEvent(EVENT_OBJECT_CREATE, Window, OBJID_CARET, CHILDID_SELF, 0); RETURN(TRUE); CLEANUP: diff --git a/subsystems/win32/win32k/ntuser/desktop.c b/subsystems/win32/win32k/ntuser/desktop.c index 5967f8abc79..894245d219b 100644 --- a/subsystems/win32/win32k/ntuser/desktop.c +++ b/subsystems/win32/win32k/ntuser/desktop.c @@ -881,6 +881,7 @@ NtUserCreateDesktop( LARGE_STRING WindowName; PWND pWnd = NULL; CREATESTRUCTW Cs; + INT i; DECLARE_RETURN(HDESK); DPRINT("Enter NtUserCreateDesktop: %wZ\n", lpszDesktopName); @@ -1023,6 +1024,11 @@ NtUserCreateDesktop( /* Initialize some local (to win32k) desktop state. */ InitializeListHead(&DesktopObject->PtiList); DesktopObject->ActiveMessageQueue = NULL; + /* Setup Global Hooks. */ + for (i = 0; i < NB_HOOKS; i++) + { + InitializeListHead(&DesktopObject->pDeskInfo->aphkStart[i]); + } ExFreePoolWithTag(DesktopName.Buffer, TAG_STRING); if (! NT_SUCCESS(Status)) diff --git a/subsystems/win32/win32k/ntuser/event.c b/subsystems/win32/win32k/ntuser/event.c index 02d2ffeb2a2..2c5a91e53c1 100644 --- a/subsystems/win32/win32k/ntuser/event.c +++ b/subsystems/win32/win32k/ntuser/event.c @@ -2,7 +2,7 @@ * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * PURPOSE: Window event handlers - * FILE: subsystem/win32/win32k/ntuser/event.c + * FILE: subsystems/win32/win32k/ntuser/event.c * PROGRAMER: James Tabor (james.tabor@rectos.org) */ @@ -103,12 +103,15 @@ IntCallLowLevelEvent( PEVENTHOOK pEH, LONG idChild) { NTSTATUS Status; - ULONG_PTR uResult; - EVENTPACK EP; + PEVENTPACK pEP; + ULONG_PTR uResult = 0; - EP.pEH = pEH; - EP.idObject = idObject; - EP.idChild = idChild; + pEP = ExAllocatePoolWithTag(NonPagedPool, sizeof(EVENTPACK), TAG_HOOK); + if (!pEP) return 0; + + pEP->pEH = pEH; + pEP->idObject = idObject; + pEP->idChild = idChild; /* FIXME should get timeout from * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */ @@ -116,16 +119,18 @@ IntCallLowLevelEvent( PEVENTHOOK pEH, hwnd, event, 0, - (LPARAM)&EP, + (LPARAM)pEP, 5000, TRUE, MSQ_ISEVENT, &uResult); - + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(pEP, TAG_HOOK); + } return NT_SUCCESS(Status) ? uResult : 0; } - static BOOL FASTCALL @@ -145,14 +150,49 @@ IntRemoveEvent(PEVENTHOOK pEH) return FALSE; } +VOID +FASTCALL +EVENT_DestroyThreadEvents(PETHREAD Thread) +{ + PTHREADINFO pti; + PEVENTHOOK pEH; + PLIST_ENTRY pLE; + + pti = Thread->Tcb.Win32Thread; + if (!pti) return; + + if (!GlobalEvents || !GlobalEvents->Counts) return; + + pLE = GlobalEvents->Events.Flink; + if (IsListEmpty(pLE)) return; + + pEH = CONTAINING_RECORD(pLE, EVENTHOOK, Chain); + do + { + if (IsListEmpty(pLE)) break; + if (!pEH) break; + pLE = pEH->Chain.Flink; + if (pEH->head.pti == pti) + { + IntRemoveEvent(pEH); + } + pEH = CONTAINING_RECORD(pLE, EVENTHOOK, Chain); + } while (pLE != &GlobalEvents->Events); + + return; +} + /* FUNCTIONS *****************************************************************/ +// +// Dispatch MsgQueue Event Call processor! +// LRESULT FASTCALL co_EVENT_CallEvents( DWORD event, - HWND hwnd, - UINT_PTR idObject, - LONG_PTR idChild) + HWND hwnd, + UINT_PTR idObject, + LONG_PTR idChild) { PEVENTHOOK pEH; LRESULT Result; @@ -165,9 +205,11 @@ co_EVENT_CallEvents( DWORD event, hwnd, pEP->idObject, pEP->idChild, - (DWORD_PTR)(NtCurrentTeb()->ClientId).UniqueThread, + PtrToUint(NtCurrentTeb()->ClientId.UniqueThread), (DWORD)EngGetTickCount(), pEH->Proc); + + ExFreePoolWithTag(pEP, TAG_HOOK); return Result; } @@ -177,55 +219,66 @@ IntNotifyWinEvent( DWORD Event, PWND pWnd, LONG idObject, - LONG idChild) + LONG idChild, + DWORD flags) { PEVENTHOOK pEH; PLIST_ENTRY pLE; + PTHREADINFO pti, ptiCurrent; DPRINT("IntNotifyWinEvent GlobalEvents = 0x%x pWnd 0x%x\n",GlobalEvents, pWnd); - if (!pWnd) return; + if (!GlobalEvents || !GlobalEvents->Counts) return; if (pWnd && pWnd->state & WNDS_DESTROYED) return; - if (!GlobalEvents || !GlobalEvents->Counts) return; + ptiCurrent = PsGetCurrentThreadWin32Thread(); + + if (pWnd && flags & WEF_SETBYWNDPTI) + pti = pWnd->head.pti; + else + pti = ptiCurrent; pLE = GlobalEvents->Events.Flink; pEH = CONTAINING_RECORD(pLE, EVENTHOOK, Chain); do - { + { + if (!pEH) break; UserReferenceObject(pEH); // Must be inside the event window. if ( (pEH->eventMin <= Event) && (pEH->eventMax >= Event)) { - if (pEH->head.pti->pEThread != PsGetCurrentThread()) - { // if all process || all thread || other thread same process - if (!(pEH->idProcess) || !(pEH->idThread) || - (NtCurrentTeb()->ClientId.UniqueProcess == (PVOID)(DWORD_PTR)pEH->idProcess)) +// if all process || all thread || other thread same process +// if ^skip own thread && ((Pid && CPid == Pid && ^skip own process) || all process) + if ( (!pEH->idProcess || pEH->idProcess == PtrToUint(pti->pEThread->Cid.UniqueProcess)) && + (!(pEH->Flags & WINEVENT_SKIPOWNPROCESS) || pEH->head.pti->ppi != pti->ppi) && + (!pEH->idThread || pEH->idThread == PtrToUint(pti->pEThread->Cid.UniqueThread)) && + (!(pEH->Flags & WINEVENT_SKIPOWNTHREAD) || pEH->head.pti != pti) && + pEH->head.pti->rpdesk == ptiCurrent->rpdesk ) // Same as hooks. + { + // Send message to the thread if pEH is not current. + if (pEH->head.pti != ptiCurrent) { + DPRINT1("Global Event 0x%x, idObject %d\n", Event, idObject); IntCallLowLevelEvent( pEH, Event, UserHMGetHandle(pWnd), idObject, idChild); } - }// if ^skip own thread && ((Pid && CPid == Pid && ^skip own process) || all process) - else if ( !(pEH->Flags & WINEVENT_SKIPOWNTHREAD) && - ( ((pEH->idProcess && - NtCurrentTeb()->ClientId.UniqueProcess == (PVOID)(DWORD_PTR)pEH->idProcess) && - !(pEH->Flags & WINEVENT_SKIPOWNPROCESS)) || - !pEH->idProcess ) ) - { - // What in the deuce is this right-aligned formatting? - co_IntCallEventProc( UserHMGetHandle(pEH), - Event, - UserHMGetHandle(pWnd), - idObject, - idChild, - PtrToUint(NtCurrentTeb()->ClientId.UniqueThread), - (DWORD)EngGetTickCount(), - pEH->Proc); - } + else + { + DPRINT1("Local Event 0x%x, idObject %d\n", Event, idObject); + co_IntCallEventProc( UserHMGetHandle(pEH), + Event, + UserHMGetHandle(pWnd), + idObject, + idChild, + PtrToUint(NtCurrentTeb()->ClientId.UniqueThread), + (DWORD)EngGetTickCount(), + pEH->Proc); + } + } } UserDereferenceObject(pEH); pLE = pEH->Chain.Flink; @@ -255,7 +308,7 @@ NtUserNotifyWinEvent( if (gpsi->dwInstalledEventHooks & GetMaskFromEvent(Event)) { UserRefObjectCo(Window, &Ref); - IntNotifyWinEvent( Event, Window, idObject, idChild); + IntNotifyWinEvent( Event, Window, idObject, idChild, WEF_SETBYWNDPTI); UserDerefObjectCo(Window); } UserLeave(); @@ -322,7 +375,7 @@ NtUserSetWinEventHook( goto SetEventExit; } } - + // Creator, pti is set here. pEH = UserCreateObject(gHandleTable, NULL, &Handle, otEvent, sizeof(EVENTHOOK)); if (pEH) { @@ -330,16 +383,18 @@ NtUserSetWinEventHook( GlobalEvents->Counts++; UserHMGetHandle(pEH) = Handle; - if (Thread) - pEH->head.pti = Thread->Tcb.Win32Thread; - else - pEH->head.pti = GetW32ThreadInfo(); pEH->eventMin = eventMin; pEH->eventMax = eventMax; - pEH->idProcess = idProcess; - pEH->idThread = idThread; + pEH->idProcess = idProcess; // These are cmp'ed + pEH->idThread = idThread; // " pEH->Flags = dwflags; - + /* + If WINEVENT_INCONTEXT, set offset from hmod and proc. Save ihmod from + the atom index table where the hmod data is saved to be recalled later + if fSync set by WINEVENT_INCONTEXT. + If WINEVENT_OUTOFCONTEXT just use proc.. + Do this instead.... + */ if (NULL != hmodWinEventProc) { pEH->offPfn = (ULONG_PTR)((char *)lpfnWinEventProc - (char *)hmodWinEventProc); diff --git a/subsystems/win32/win32k/ntuser/focus.c b/subsystems/win32/win32k/ntuser/focus.c index 5832eaf41ad..414b1523bf7 100644 --- a/subsystems/win32/win32k/ntuser/focus.c +++ b/subsystems/win32/win32k/ntuser/focus.c @@ -165,6 +165,7 @@ co_IntSendKillFocusMessages(HWND hWndPrev, HWND hWnd) { if (hWndPrev) { + IntNotifyWinEvent(EVENT_OBJECT_FOCUS, NULL, OBJID_CLIENT, CHILDID_SELF, 0); co_IntPostOrSendMessage(hWndPrev, WM_KILLFOCUS, (WPARAM)hWnd, 0); } } @@ -174,6 +175,8 @@ co_IntSendSetFocusMessages(HWND hWndPrev, HWND hWnd) { if (hWnd) { + PWND pWnd = UserGetWindowObject(hWnd); + IntNotifyWinEvent(EVENT_OBJECT_FOCUS, pWnd, OBJID_CLIENT, CHILDID_SELF, 0); co_IntPostOrSendMessage(hWnd, WM_SETFOCUS, (WPARAM)hWndPrev, 0); } } @@ -356,8 +359,10 @@ co_IntSetActiveWindow(PWND Wnd OPTIONAL) cbt.fMouse = FALSE; cbt.hWndActive = hWndPrev; if (co_HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hWnd, (LPARAM)&cbt)) + { + DPRINT1("SetActiveWindow WH_CBT Call Hook return!\n"); return 0; - + } ThreadQueue->ActiveWindow = hWnd; co_IntSendDeactivateMessages(hWndPrev, hWnd); @@ -392,9 +397,11 @@ co_IntSetFocusWindow(PWND Window OPTIONAL) return hWndPrev; } - if (co_HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)Window->head.h, (LPARAM)hWndPrev)) - return 0; - + if (co_HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)Window->head.h, (LPARAM)hWndPrev)) + { + DPRINT1("SetFocusWindow 1 WH_CBT Call Hook return!\n"); + return 0; + } ThreadQueue->FocusWindow = Window->head.h; co_IntSendKillFocusMessages(hWndPrev, Window->head.h); @@ -403,9 +410,11 @@ co_IntSetFocusWindow(PWND Window OPTIONAL) else { ThreadQueue->FocusWindow = 0; - - if (co_HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)0, (LPARAM)hWndPrev)) - return 0; + if (co_HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)0, (LPARAM)hWndPrev)) + { + DPRINT1("SetFocusWindow 2 WH_CBT Call Hook return!\n"); + return 0; + } co_IntSendKillFocusMessages(hWndPrev, 0); } @@ -533,7 +542,7 @@ NtUserSetCapture(HWND hWnd) { PTHREADINFO pti; PUSER_MESSAGE_QUEUE ThreadQueue; - PWND Window; + PWND Window, pWnd; HWND hWndPrev; DECLARE_RETURN(HWND); @@ -553,13 +562,23 @@ NtUserSetCapture(HWND hWnd) hWndPrev = MsqSetStateWindow(ThreadQueue, MSQ_STATE_CAPTURE, hWnd); + if (hWndPrev) + { + pWnd = UserGetWindowObject(hWndPrev); + if (pWnd) + IntNotifyWinEvent(EVENT_SYSTEM_CAPTUREEND, pWnd, OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI); + } + /* also remove other windows if not capturing anymore */ - if(hWnd == NULL) + if (hWnd == NULL) { MsqSetStateWindow(ThreadQueue, MSQ_STATE_MENUOWNER, NULL); MsqSetStateWindow(ThreadQueue, MSQ_STATE_MOVESIZE, NULL); } + if (Window) + IntNotifyWinEvent(EVENT_SYSTEM_CAPTURESTART, Window, OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI); + co_IntPostOrSendMessage(hWndPrev, WM_CAPTURECHANGED, 0, (LPARAM)hWnd); ThreadQueue->CaptureWindow = hWnd; diff --git a/subsystems/win32/win32k/ntuser/hook.c b/subsystems/win32/win32k/ntuser/hook.c index 625bd3abbcb..d83c8c90c31 100644 --- a/subsystems/win32/win32k/ntuser/hook.c +++ b/subsystems/win32/win32k/ntuser/hook.c @@ -2,8 +2,10 @@ * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * PURPOSE: Window hooks - * FILE: subsystem/win32/win32k/ntuser/hook.c + * FILE: subsystems/win32/win32k/ntuser/hook.c * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net) + * James Tabor (james.tabor@rectos.org) + * * REVISION HISTORY: * 06-06-2001 CSH Created * NOTE: Most of this code was adapted from Wine, @@ -15,472 +17,117 @@ #define NDEBUG #include -static PHOOKTABLE GlobalHooks; - +typedef struct _HOOKPACK +{ + PHOOK pHk; + LPARAM lParam; +} HOOKPACK, *PHOOKPACK; /* PRIVATE FUNCTIONS *********************************************************/ - -/* create a new hook table */ -static -PHOOKTABLE -IntAllocHookTable(void) -{ - PHOOKTABLE Table; - UINT i; - - Table = ExAllocatePoolWithTag(PagedPool, sizeof(HOOKTABLE), TAG_HOOK); - if (NULL != Table) - { - for (i = 0; i < NB_HOOKS; i++) - { - InitializeListHead(&Table->Hooks[i]); - Table->Counts[i] = 0; - } - } - - return Table; -} - - -PHOOK -FASTCALL -IntGetHookObject(HHOOK hHook) -{ - PHOOK Hook; - - if (!hHook) - { - SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE); - return NULL; - } - - Hook = (PHOOK)UserGetObject(gHandleTable, hHook, otHook); - if (!Hook) - { - SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE); - return NULL; - } - - ASSERT(Hook->head.cLockObj >= 0); - - Hook->head.cLockObj++; - - return Hook; -} - - - -/* create a new hook and add it to the specified table */ -static -PHOOK -IntAddHook(PETHREAD Thread, int HookId, BOOLEAN Global, PWINSTATION_OBJECT WinStaObj) -{ - PTHREADINFO W32Thread; - PHOOK Hook; - PHOOKTABLE Table = Global ? GlobalHooks : MsqGetHooks(((PTHREADINFO)Thread->Tcb.Win32Thread)->MessageQueue); - HANDLE Handle; - - if (NULL == Table) - { - Table = IntAllocHookTable(); - if (NULL == Table) - { - return NULL; - } - if (Global) - { - GlobalHooks = Table; - } - else - { - MsqSetHooks(((PTHREADINFO)Thread->Tcb.Win32Thread)->MessageQueue, Table); - } - } - - Hook = UserCreateObject(gHandleTable, NULL, &Handle, otHook, sizeof(HOOK)); - if (NULL == Hook) - { - return NULL; - } - - Hook->Thread = Thread; - Hook->HookId = HookId; - - if (Thread) - { - W32Thread = ((PTHREADINFO)Thread->Tcb.Win32Thread); - ASSERT(W32Thread != NULL); - W32Thread->fsHooks |= HOOKID_TO_FLAG(HookId); - - if (W32Thread->pClientInfo) - W32Thread->pClientInfo->fsHooks = W32Thread->fsHooks; - - if (W32Thread->pDeskInfo) // Do this for now. - W32Thread->pDeskInfo->fsHooks= W32Thread->fsHooks; - - Hook->head.pti = W32Thread; - Hook->head.rpdesk = W32Thread->rpdesk; - } - - RtlInitUnicodeString(&Hook->ModuleName, NULL); - - InsertHeadList(&Table->Hooks[HOOKID_TO_INDEX(HookId)], &Hook->Chain); - - return Hook; -} - -/* get the hook table that a given hook belongs to */ -static -PHOOKTABLE -FASTCALL -IntGetTable(PHOOK Hook) -{ - if (NULL == Hook->Thread || WH_KEYBOARD_LL == Hook->HookId || - WH_MOUSE_LL == Hook->HookId) - { - return GlobalHooks; - } - - return MsqGetHooks(((PTHREADINFO)Hook->Thread->Tcb.Win32Thread)->MessageQueue); -} - -/* get the first hook in the chain */ -static -PHOOK -FASTCALL -IntGetFirstHook(PHOOKTABLE Table, int HookId) -{ - PLIST_ENTRY Elem = Table->Hooks[HOOKID_TO_INDEX(HookId)].Flink; - - return Elem == &Table->Hooks[HOOKID_TO_INDEX(HookId)] - ? NULL : CONTAINING_RECORD(Elem, HOOK, Chain); -} - -/* find the first non-deleted hook in the chain */ -static -PHOOK -FASTCALL -IntGetFirstValidHook(PHOOKTABLE Table, int HookId) -{ - PHOOK Hook; - PLIST_ENTRY Elem; - - Hook = IntGetFirstHook(Table, HookId); - - while (NULL != Hook && NULL == Hook->Proc) - { - Elem = Hook->Chain.Flink; - Hook = (Elem == &Table->Hooks[HOOKID_TO_INDEX(HookId)] - ? NULL : CONTAINING_RECORD(Elem, HOOK, Chain)); - } - - return Hook; -} - -/* find the next hook in the chain, skipping the deleted ones */ -PHOOK -FASTCALL -IntGetNextHook(PHOOK Hook) -{ - PHOOKTABLE Table = IntGetTable(Hook); - int HookId = Hook->HookId; - PLIST_ENTRY Elem; - - Elem = Hook->Chain.Flink; - while (Elem != &Table->Hooks[HOOKID_TO_INDEX(HookId)]) - { - Hook = CONTAINING_RECORD(Elem, HOOK, Chain); - if (NULL != Hook->Proc) - { - return Hook; - } - } - - if (NULL != GlobalHooks && Table != GlobalHooks) /* now search through the global table */ - { - return IntGetFirstValidHook(GlobalHooks, HookId); - } - - return NULL; -} - -/* free a hook, removing it from its chain */ -static -VOID -FASTCALL -IntFreeHook(PHOOKTABLE Table, PHOOK Hook, PWINSTATION_OBJECT WinStaObj) -{ - RemoveEntryList(&Hook->Chain); - RtlFreeUnicodeString(&Hook->ModuleName); - - /* Dereference thread if required */ - if (Hook->Flags & HOOK_THREAD_REFERENCED) - { - ObDereferenceObject(Hook->Thread); - } - - /* Close handle */ - UserDeleteObject(UserHMGetHandle(Hook), otHook); -} - -/* remove a hook, freeing it if the chain is not in use */ -static -VOID -IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj, BOOL TableAlreadyLocked) -{ - PTHREADINFO W32Thread; - PHOOKTABLE Table = IntGetTable(Hook); - - ASSERT(NULL != Table); // At this point this should not be null! - - W32Thread = ((PTHREADINFO)Hook->Thread->Tcb.Win32Thread); - ASSERT(W32Thread != NULL); - W32Thread->fsHooks &= ~HOOKID_TO_FLAG(Hook->HookId); - - GetWin32ClientInfo()->fsHooks = W32Thread->fsHooks; - - if (W32Thread->pDeskInfo) // Do this for now. - W32Thread->pDeskInfo->fsHooks= W32Thread->fsHooks; - - if (0 != Table->Counts[HOOKID_TO_INDEX(Hook->HookId)]) - { - Hook->Proc = NULL; /* chain is in use, just mark it and return */ - } - else - { - IntFreeHook(Table, Hook, WinStaObj); - } -} - -/* release a hook chain, removing deleted hooks if the use count drops to 0 */ -static -VOID -FASTCALL -IntReleaseHookChain(PHOOKTABLE Table, int HookId, PWINSTATION_OBJECT WinStaObj) -{ - PLIST_ENTRY Elem; - PHOOK HookObj; - - if (NULL == Table) - { - return; - } - - /* use count shouldn't already be 0 */ - ASSERT(0 != Table->Counts[HOOKID_TO_INDEX(HookId)]); - - if (0 == Table->Counts[HOOKID_TO_INDEX(HookId)]) - { - return; - } - - if (0 == --Table->Counts[HOOKID_TO_INDEX(HookId)]) - { - Elem = Table->Hooks[HOOKID_TO_INDEX(HookId)].Flink; - - while (Elem != &Table->Hooks[HOOKID_TO_INDEX(HookId)]) - { - HookObj = CONTAINING_RECORD(Elem, HOOK, Chain); - Elem = Elem->Flink; - - if (NULL == HookObj->Proc) - { - IntFreeHook(Table, HookObj, WinStaObj); - } - } - } -} - static LRESULT FASTCALL -IntCallLowLevelHook(PHOOK Hook, INT Code, WPARAM wParam, LPARAM lParam) +IntCallLowLevelHook( PHOOK Hook, + INT Code, + WPARAM wParam, + LPARAM lParam) { NTSTATUS Status; - ULONG_PTR uResult; + PTHREADINFO pti; + PHOOKPACK pHP; + ULONG_PTR uResult = 0; + + if (Hook->Thread) + pti = Hook->Thread->Tcb.Win32Thread; + else + pti = Hook->head.pti; + + pHP = ExAllocatePoolWithTag(NonPagedPool, sizeof(HOOKPACK), TAG_HOOK); + if (!pHP) return 0; + + pHP->pHk = Hook; + pHP->lParam = lParam; /* FIXME should get timeout from * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */ - Status = co_MsqSendMessage(((PTHREADINFO)Hook->Thread->Tcb.Win32Thread)->MessageQueue, - IntToPtr(Code), - Hook->HookId, + Status = co_MsqSendMessage( pti->MessageQueue, + IntToPtr(Code), // hWnd + Hook->HookId, // Msg wParam, - lParam, + (LPARAM)pHP, 5000, TRUE, MSQ_ISHOOK, - &uResult); - - return NT_SUCCESS(Status) ? uResult : 0; -} - -/* - Called from inside kernel space. - */ -LRESULT -FASTCALL -co_HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam) -{ - PHOOK Hook, SaveHook; - PTHREADINFO pti; - PCLIENTINFO ClientInfo; - PHOOKTABLE Table; - LRESULT Result; - PWINSTATION_OBJECT WinStaObj; - NTSTATUS Status; - - ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK); - - /* FIXME! Check pDeskInfo->fsHooks for global hooks! */ - if (!ISITHOOKED(HookId)) - { - return 0; - } - - pti = PsGetCurrentThreadWin32Thread(); - if (!pti) - { - Table = NULL; - } - else - { - Table = MsqGetHooks(pti->MessageQueue); - } - - if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId))) - { - /* try global table */ - Table = GlobalHooks; - if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId))) - { - return 0; /* no hook set */ - } - } - - if ((Hook->Thread != PsGetCurrentThread()) && (Hook->Thread != NULL)) - { - DPRINT1("\nHook found by Id and posted to Thread! %d\n",HookId ); - /* Post it in message queue. */ - return IntCallLowLevelHook(Hook, Code, wParam, lParam); - } - - Table->Counts[HOOKID_TO_INDEX(HookId)]++; - if (Table != GlobalHooks && GlobalHooks != NULL) - { - GlobalHooks->Counts[HOOKID_TO_INDEX(HookId)]++; - } - - ClientInfo = GetWin32ClientInfo(); - SaveHook = ClientInfo->phkCurrent; - ClientInfo->phkCurrent = Hook; /* Load the call. */ - - Result = co_IntCallHookProc(HookId, - Code, - wParam, - lParam, - Hook->Proc, - Hook->Ansi, - &Hook->ModuleName); - - ClientInfo->phkCurrent = SaveHook; - - Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation, - KernelMode, - 0, - &WinStaObj); - + &uResult); if (!NT_SUCCESS(Status)) { - DPRINT1("Invalid window station????\n"); - } - else - { - IntReleaseHookChain(MsqGetHooks(pti->MessageQueue), HookId, WinStaObj); - IntReleaseHookChain(GlobalHooks, HookId, WinStaObj); - ObDereferenceObject(WinStaObj); - } - - return Result; -} - -VOID -FASTCALL -HOOK_DestroyThreadHooks(PETHREAD Thread) -{ - int HookId; - PLIST_ENTRY Elem; - PHOOK HookObj; - PWINSTATION_OBJECT WinStaObj; - NTSTATUS Status; - - if (NULL != GlobalHooks) - { - Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation, - KernelMode, - 0, - &WinStaObj); - - if (!NT_SUCCESS(Status)) - { - DPRINT1("Invalid window station????\n"); - return; - } - - for (HookId = WH_MINHOOK; HookId <= WH_MAXHOOK; HookId++) - { - /* only low-level keyboard/mouse global hooks can be owned by a thread */ - switch(HookId) - { - case WH_KEYBOARD_LL: - case WH_MOUSE_LL: - Elem = GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)].Flink; - - while (Elem != &GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)]) - { - HookObj = CONTAINING_RECORD(Elem, HOOK, Chain); - Elem = Elem->Flink; - - if (HookObj->Thread == Thread) - { - IntRemoveHook(HookObj, WinStaObj, TRUE); - } - } - break; - } - } + DPRINT1("Error Hook Call SendMsg. %d Status: 0x%x\n", Hook->HookId, Status); + ExFreePoolWithTag(pHP, TAG_HOOK); } + return NT_SUCCESS(Status) ? uResult : 0; } static LRESULT FASTCALL -co_HOOK_CallHookNext(PHOOK Hook, INT Code, WPARAM wParam, LPARAM lParam) +co_HOOK_CallHookNext( PHOOK Hook, + INT Code, + WPARAM wParam, + LPARAM lParam) { - if ((Hook->Thread != PsGetCurrentThread()) && (Hook->Thread != NULL)) + if ( (Hook->Thread != PsGetCurrentThread()) && (Hook->Thread != NULL) ) { - DPRINT1("CALLING HOOK from another Thread. %d\n", Hook->HookId); + DPRINT1("Calling Next HOOK from another Thread. %d\n", Hook->HookId); return IntCallLowLevelHook(Hook, Code, wParam, lParam); } - DPRINT("CALLING HOOK %d\n", Hook->HookId); + DPRINT("Calling Next HOOK %d\n", Hook->HookId); - return co_IntCallHookProc(Hook->HookId, - Code, - wParam, - lParam, - Hook->Proc, - Hook->Ansi, + return co_IntCallHookProc( Hook->HookId, + Code, + wParam, + lParam, + Hook->Proc, + Hook->Ansi, &Hook->ModuleName); } +// +// Dispatch MsgQueue Hook Call processor! +// +LRESULT +FASTCALL +co_CallHook( INT HookId, + INT Code, + WPARAM wParam, + LPARAM lParam) +{ + LRESULT Result; + PHOOK phk; + PHOOKPACK pHP = (PHOOKPACK)lParam; + + phk = pHP->pHk; + /* The odds are high for this to be a Global call. */ + Result = co_IntCallHookProc( HookId, + Code, + wParam, + pHP->lParam, + phk->Proc, + phk->Ansi, + &phk->ModuleName); + + ExFreePoolWithTag(pHP, TAG_HOOK); + return Result; +} LRESULT FASTCALL -IntCallDebugHook(PHOOK Hook, - int Code, - WPARAM wParam, - LPARAM lParam) +IntCallDebugHook( PHOOK Hook, + int Code, + WPARAM wParam, + LPARAM lParam, + BOOL Ansi) { LRESULT lResult = 0; ULONG Size; @@ -606,16 +253,13 @@ IntCallDebugHook(PHOOK Hook, return lResult; } -/* - Called from user space via CallNextHook. - */ LRESULT FASTCALL -UserCallNextHookEx(PHOOK Hook, - int Code, - WPARAM wParam, - LPARAM lParam, - BOOL Ansi) +UserCallNextHookEx( PHOOK Hook, + int Code, + WPARAM wParam, + LPARAM lParam, + BOOL Ansi) { LRESULT lResult = 0; BOOL BadChk = FALSE; @@ -1000,7 +644,7 @@ UserCallNextHookEx(PHOOK Hook, } case WH_DEBUG: - lResult = IntCallDebugHook(Hook, Code, wParam, lParam); + lResult = IntCallDebugHook(Hook, Code, wParam, lParam, Ansi); break; /* @@ -1016,70 +660,454 @@ UserCallNextHookEx(PHOOK Hook, DPRINT1("Unsupported HOOK Id -> %d\n",Hook->HookId); break; } - + if (Hook->HookId == WH_JOURNALPLAYBACK && lResult == 0) + lResult = -1; return lResult; } +PHOOK +FASTCALL +IntGetHookObject(HHOOK hHook) +{ + PHOOK Hook; + + if (!hHook) + { + SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE); + return NULL; + } + + Hook = (PHOOK)UserGetObject(gHandleTable, hHook, otHook); + if (!Hook) + { + SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE); + return NULL; + } + + UserReferenceObject(Hook); + + return Hook; +} + +/* get the first hook in the chain */ +static +PHOOK +FASTCALL +IntGetFirstHook(PLIST_ENTRY Table) +{ + PLIST_ENTRY Elem = Table->Flink; + + if (IsListEmpty(Table)) return NULL; + + return Elem == Table ? NULL : CONTAINING_RECORD(Elem, HOOK, Chain); +} + +/* find the next hook in the chain */ +PHOOK +FASTCALL +IntGetNextHook(PHOOK Hook) +{ + int HookId = Hook->HookId; + PLIST_ENTRY Elem; + PTHREADINFO pti; + + if (Hook->Thread) + { + pti = ((PTHREADINFO)Hook->Thread->Tcb.Win32Thread); + + Elem = Hook->Chain.Flink; + if (Elem != &pti->aphkStart[HOOKID_TO_INDEX(HookId)]) + return CONTAINING_RECORD(Elem, HOOK, Chain); + } + else + { + pti = PsGetCurrentThreadWin32Thread(); + + Elem = Hook->Chain.Flink; + if (Elem != &pti->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)]) + return CONTAINING_RECORD(Elem, HOOK, Chain); + } + return NULL; +} + +/* free a hook, removing it from its chain */ +static +VOID +FASTCALL +IntFreeHook(PHOOK Hook) +{ + RemoveEntryList(&Hook->Chain); + if (Hook->ModuleName.Buffer) + { + ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK); + Hook->ModuleName.Buffer = NULL; + } + /* Close handle */ + UserDeleteObject(UserHMGetHandle(Hook), otHook); +} + +/* remove a hook, freeing it from the chain */ +static +BOOL +FASTCALL +IntRemoveHook(PHOOK Hook) +{ + INT HookId; + PTHREADINFO pti; + + HookId = Hook->HookId; + + if (Hook->Thread) // Local + { + pti = ((PTHREADINFO)Hook->Thread->Tcb.Win32Thread); + + IntFreeHook( Hook); + + if ( IsListEmpty(&pti->aphkStart[HOOKID_TO_INDEX(HookId)]) ) + { + pti->fsHooks &= ~HOOKID_TO_FLAG(HookId); + _SEH2_TRY + { + GetWin32ClientInfo()->fsHooks = pti->fsHooks; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + } + _SEH2_END; + return TRUE; + } + } + else // Global + { + IntFreeHook( Hook); + + pti = PsGetCurrentThreadWin32Thread(); + + if ( pti->pDeskInfo && + IsListEmpty(&pti->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)]) ) + { + pti->pDeskInfo->fsHooks &= ~HOOKID_TO_FLAG(HookId); + return TRUE; + } + } + return FALSE; +} + +VOID +FASTCALL +HOOK_DestroyThreadHooks(PETHREAD Thread) +{ + PTHREADINFO pti; + int HookId; + PHOOK HookObj; + PLIST_ENTRY pElem; + + pti = Thread->Tcb.Win32Thread; + if (!pti || !pti->pDeskInfo) return; + +// Local Thread cleanup. + if (pti->fsHooks) + { + for (HookId = WH_MINHOOK; HookId <= WH_MAXHOOK; HookId++) + { + PLIST_ENTRY pLLE = &pti->aphkStart[HOOKID_TO_INDEX(HookId)]; + + if (IsListEmpty(pLLE)) continue; + + pElem = pLLE->Flink; + HookObj = CONTAINING_RECORD(pElem, HOOK, Chain); + do + { + if (!HookObj) break; + if (IntRemoveHook(HookObj)) break; + pElem = HookObj->Chain.Flink; + HookObj = CONTAINING_RECORD(pElem, HOOK, Chain); + } + while (pElem != pLLE); + } + pti->fsHooks = 0; + } +// Global search based on Thread and cleanup. + if (pti->pDeskInfo->fsHooks) + { + for (HookId = WH_MINHOOK; HookId <= WH_MAXHOOK; HookId++) + { + PLIST_ENTRY pGLE = &pti->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)]; + + if (IsListEmpty(pGLE)) continue; + + pElem = pGLE->Flink; + HookObj = CONTAINING_RECORD(pElem, HOOK, Chain); + do + { + if (!HookObj) break; + if (HookObj->head.pti == pti) + { + if (IntRemoveHook(HookObj)) break; + } + pElem = HookObj->Chain.Flink; + HookObj = CONTAINING_RECORD(pElem, HOOK, Chain); + } + while (pElem != pGLE); + } + } + return; +} + +/* + Win32k Kernel Space Hook Caller. + */ +LRESULT +FASTCALL +co_HOOK_CallHooks( INT HookId, + INT Code, + WPARAM wParam, + LPARAM lParam) +{ + PHOOK Hook, SaveHook; + PTHREADINFO pti; + PCLIENTINFO ClientInfo; + PLIST_ENTRY pLLE, pGLE; + BOOL Local = FALSE, Global = FALSE; + LRESULT Result = 0; + + ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK); + + pti = GetW32ThreadInfo(); // Need to call this! + + if (!pti || !pti->pDeskInfo) + goto Exit; // Must have a desktop running for hooks. + + if ( pti->TIF_flags & TIF_INCLEANUP) + goto Exit; + + if ( ISITHOOKED(HookId) ) + { + DPRINT("Local Hooker %d\n", HookId); + Local = TRUE; + } + + if ( pti->pDeskInfo->fsHooks & HOOKID_TO_FLAG(HookId) ) + { + DPRINT("Global Hooker %d\n", HookId); + Global = TRUE; + } + + if ( !Local && !Global ) goto Exit; // No work! + + pLLE = &pti->aphkStart[HOOKID_TO_INDEX(HookId)]; + pGLE = &pti->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)]; + Hook = NULL; + + /* SetWindowHookEx sorts out the Thread issue by placing the Hook to + the correct Thread if not NULL. + */ + if ( Local ) + { + Hook = IntGetFirstHook(pLLE); + if (!Hook) + { + DPRINT1("No Local Hook Found!\n"); + goto Exit; + } + ObReferenceObject(Hook->Thread); + + ClientInfo = pti->pClientInfo; + SaveHook = pti->sphkCurrent; + + /* Load it for the next call. */ + pti->sphkCurrent = Hook; + Hook->phkNext = IntGetNextHook(Hook); + if (ClientInfo) + { + _SEH2_TRY + { + ClientInfo->phkCurrent = Hook; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ClientInfo = NULL; // Don't bother next run. + } + _SEH2_END; + } + Result = co_IntCallHookProc( HookId, + Code, + wParam, + lParam, + Hook->Proc, + Hook->Ansi, + &Hook->ModuleName); + if (ClientInfo) + { + _SEH2_TRY + { + ClientInfo->phkCurrent = SaveHook; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + } + _SEH2_END; + } + pti->sphkCurrent = SaveHook; + Hook->phkNext = NULL; + ObDereferenceObject(Hook->Thread); + } + + if ( Global ) + { + PTHREADINFO ptiHook; + + Hook = IntGetFirstHook(pGLE); + if (!Hook) + { + DPRINT1("No Global Hook Found!\n"); + goto Exit; + } + /* Performance goes down the drain. If more hooks are associated to this + * hook ID, this will have to post to each of the thread message queues + * or make a direct call. + */ + do + { + /* Hook->Thread is null, we hax around this with Hook->head.pti. */ + ptiHook = Hook->head.pti; + + /* "Global hook monitors messages for all threads in the same desktop + * as the calling thread." + */ + if ( ptiHook->TIF_flags & TIF_INCLEANUP || + ptiHook->rpdesk != pti->rpdesk) + { + Hook = IntGetNextHook(Hook); + if (!Hook) break; + continue; + } + // Lockup the thread while this links through user world. + ObReferenceObject(ptiHook->pEThread); + if (ptiHook != pti ) + { + DPRINT("\nGlobal Hook posting to another Thread! %d\n",HookId ); + Result = IntCallLowLevelHook(Hook, Code, wParam, lParam); + } + else + { /* Make the direct call. */ + Result = co_IntCallHookProc( HookId, + Code, + wParam, + lParam, + Hook->Proc, + Hook->Ansi, + &Hook->ModuleName); + } + ObDereferenceObject(ptiHook->pEThread); + Hook = IntGetNextHook(Hook); + } + while ( Hook ); + DPRINT("Ret: Global HookId %d Result 0x%x\n", HookId,Result); + } +Exit: + if (HookId == WH_JOURNALPLAYBACK && Result == 0) + Result = -1; + return Result; +} + +BOOL +FASTCALL +IntUnhookWindowsHook(int HookId, HOOKPROC pfnFilterProc) +{ + PHOOK Hook; + PLIST_ENTRY pLLE, pLE; + PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); + + if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId ) + { + SetLastWin32Error(ERROR_INVALID_HOOK_FILTER); + return FALSE; + } + + if (pti->fsHooks) + { + pLLE = &pti->aphkStart[HOOKID_TO_INDEX(HookId)]; + + if (IsListEmpty(pLLE)) return FALSE; + + pLE = pLLE->Flink; + Hook = CONTAINING_RECORD(pLE, HOOK, Chain); + do + { + if (!Hook) break; + if (Hook->Proc == pfnFilterProc) + { + if (Hook->head.pti == pti) + { + IntRemoveHook(Hook); + UserDereferenceObject(Hook); + return TRUE; + } + else + { + SetLastWin32Error(ERROR_ACCESS_DENIED); + return FALSE; + } + } + pLE = Hook->Chain.Flink; + Hook = CONTAINING_RECORD(pLE, HOOK, Chain); + } + while (pLE != pLLE); + } + return FALSE; +} + +/* + * Support for compatibility only? Global hooks are processed in kernel space. + * This is very thread specific! Never seeing applications with more than one + * hook per thread installed. Most of the applications are Global hookers and + * associated with just one hook Id. Maybe it's for diagnostic testing or a + * throw back to 3.11? + */ LRESULT APIENTRY -NtUserCallNextHookEx(int Code, - WPARAM wParam, - LPARAM lParam, - BOOL Ansi) +NtUserCallNextHookEx( int Code, + WPARAM wParam, + LPARAM lParam, + BOOL Ansi) { + PTHREADINFO pti; PHOOK HookObj, NextObj; PCLIENTINFO ClientInfo; - PWINSTATION_OBJECT WinStaObj; - NTSTATUS Status; + LRESULT lResult = 0; DECLARE_RETURN(LRESULT); DPRINT("Enter NtUserCallNextHookEx\n"); UserEnterExclusive(); - Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation, - KernelMode, - 0, - &WinStaObj); - if (!NT_SUCCESS(Status)) - { - SetLastNtError(Status); - RETURN( 0); - } + pti = GetW32ThreadInfo(); - ObDereferenceObject(WinStaObj); - - ClientInfo = GetWin32ClientInfo(); - - if (!ClientInfo) RETURN( 0); - - HookObj = ClientInfo->phkCurrent; + HookObj = pti->sphkCurrent; if (!HookObj) RETURN( 0); - /* Check that the first hook in the chain is not this hook */ - NextObj = IntGetFirstHook(IntGetTable(HookObj), HookObj->HookId); + NextObj = HookObj->phkNext; - /* Its the same so it has already been called */ - if (HookObj == NextObj) RETURN(0); - - UserReferenceObject(HookObj); - - Ansi = HookObj->Ansi; - - if (NULL != HookObj->Thread && (HookObj->Thread != PsGetCurrentThread())) + pti->sphkCurrent = NextObj; + ClientInfo = pti->pClientInfo; + _SEH2_TRY { - DPRINT1("Thread mismatch\n"); - UserDereferenceObject(HookObj); - SetLastWin32Error(ERROR_INVALID_HANDLE); - RETURN( 0); + ClientInfo->phkCurrent = NextObj; } - - NextObj = IntGetNextHook(HookObj); - ClientInfo->phkCurrent = NextObj; /* Preset next hook from list. */ - UserCallNextHookEx( HookObj, Code, wParam, lParam, Ansi); - UserDereferenceObject(HookObj); + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ClientInfo = NULL; + } + _SEH2_END; - RETURN( (LRESULT)NextObj); + /* Now in List run down. */ + if (ClientInfo && NextObj) + { + NextObj->phkNext = IntGetNextHook(NextObj); + lResult = UserCallNextHookEx( NextObj, Code, wParam, lParam, NextObj->Ansi); + } + RETURN( lResult); CLEANUP: DPRINT("Leave NtUserCallNextHookEx, ret=%i\n",_ret_); @@ -1089,43 +1117,51 @@ CLEANUP: HHOOK APIENTRY -NtUserSetWindowsHookAW(int idHook, - HOOKPROC lpfn, - BOOL Ansi) +NtUserSetWindowsHookAW( int idHook, + HOOKPROC lpfn, + BOOL Ansi) { + DWORD ThreadId; UNICODE_STRING USModuleName; RtlInitUnicodeString(&USModuleName, NULL); + ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread); - return NtUserSetWindowsHookEx(NULL, &USModuleName, 0, idHook, lpfn, Ansi); + return NtUserSetWindowsHookEx( NULL, + &USModuleName, + ThreadId, + idHook, + lpfn, + Ansi); } HHOOK APIENTRY -NtUserSetWindowsHookEx(HINSTANCE Mod, - PUNICODE_STRING UnsafeModuleName, - DWORD ThreadId, - int HookId, - HOOKPROC HookProc, - BOOL Ansi) +NtUserSetWindowsHookEx( HINSTANCE Mod, + PUNICODE_STRING UnsafeModuleName, + DWORD ThreadId, + int HookId, + HOOKPROC HookProc, + BOOL Ansi) { PWINSTATION_OBJECT WinStaObj; - PCLIENTINFO ClientInfo; - BOOLEAN Global; - PETHREAD Thread; PHOOK Hook; UNICODE_STRING ModuleName; NTSTATUS Status; HHOOK Handle; - BOOLEAN ThreadReferenced = FALSE; + PETHREAD Thread = NULL; + PTHREADINFO ptiCurrent, pti = NULL; + BOOL Hit = FALSE; DECLARE_RETURN(HHOOK); DPRINT("Enter NtUserSetWindowsHookEx\n"); UserEnterExclusive(); + ptiCurrent = GetW32ThreadInfo(); + if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId ) { - SetLastWin32Error(ERROR_INVALID_PARAMETER); + SetLastWin32Error(ERROR_INVALID_HOOK_FILTER); RETURN( NULL); } @@ -1135,173 +1171,211 @@ NtUserSetWindowsHookEx(HINSTANCE Mod, RETURN( NULL); } - ClientInfo = GetWin32ClientInfo(); - if (ThreadId) /* thread-local hook */ { - if (HookId == WH_JOURNALRECORD || + if ( HookId == WH_JOURNALRECORD || HookId == WH_JOURNALPLAYBACK || HookId == WH_KEYBOARD_LL || HookId == WH_MOUSE_LL || HookId == WH_SYSMSGFILTER) - { - /* these can only be global */ - SetLastWin32Error(ERROR_INVALID_PARAMETER); - RETURN( NULL); - } + { + /* these can only be global */ + SetLastWin32Error(ERROR_GLOBAL_ONLY_HOOK); + RETURN( NULL); + } - Mod = NULL; - Global = FALSE; + if (!NT_SUCCESS(PsLookupThreadByThreadId((HANDLE)(DWORD_PTR) ThreadId, &Thread))) + { + DPRINT1("Invalid thread id 0x%x\n", ThreadId); + SetLastWin32Error(ERROR_INVALID_PARAMETER); + RETURN( NULL); + } - if (!NT_SUCCESS(PsLookupThreadByThreadId((HANDLE)(DWORD_PTR) ThreadId, &Thread))) - { - DPRINT1("Invalid thread id 0x%x\n", ThreadId); - SetLastWin32Error(ERROR_INVALID_PARAMETER); - RETURN( NULL); - } + pti = Thread->Tcb.Win32Thread; - /* Thread was referenced */ - ThreadReferenced = TRUE; - if (Thread->ThreadsProcess != PsGetCurrentProcess()) - { - ObDereferenceObject(Thread); - DPRINT1("Can't specify thread belonging to another process\n"); - SetLastWin32Error(ERROR_INVALID_PARAMETER); - RETURN( NULL); - } + ObDereferenceObject(Thread); + + if ( pti->rpdesk != ptiCurrent->rpdesk) // gptiCurrent->rpdesk) + { + SetLastWin32Error(ERROR_ACCESS_DENIED); + RETURN( NULL); + } + + if (Thread->ThreadsProcess != PsGetCurrentProcess()) + { + if ( !Mod && + (HookId == WH_GETMESSAGE || + HookId == WH_CALLWNDPROC || + HookId == WH_CBT || + HookId == WH_HARDWARE || + HookId == WH_DEBUG || + HookId == WH_SHELL || + HookId == WH_FOREGROUNDIDLE || + HookId == WH_CALLWNDPROCRET) ) + { + SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD); + RETURN( NULL); + } + + if ( (pti->TIF_flags & (TIF_CSRSSTHREAD|TIF_SYSTEMTHREAD)) && + (HookId == WH_GETMESSAGE || + HookId == WH_CALLWNDPROC || + HookId == WH_CBT || + HookId == WH_HARDWARE || + HookId == WH_DEBUG || + HookId == WH_SHELL || + HookId == WH_FOREGROUNDIDLE || + HookId == WH_CALLWNDPROCRET) ) + { + SetLastWin32Error(ERROR_HOOK_TYPE_NOT_ALLOWED); + RETURN( NULL); + } + } } else /* system-global hook */ { - if (HookId == WH_KEYBOARD_LL || HookId == WH_MOUSE_LL) - { - Mod = NULL; - Thread = PsGetCurrentThread(); - - Status = ObReferenceObjectByPointer(Thread, - THREAD_ALL_ACCESS, - PsThreadType, - KernelMode); - - if (!NT_SUCCESS(Status)) - { - SetLastNtError(Status); - RETURN( (HANDLE) NULL); - } - - /* Thread was referenced */ - ThreadReferenced = TRUE; - } - else if (NULL == Mod) - { - SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD); - RETURN( NULL); - } - else - { - Thread = NULL; - } - Global = TRUE; + pti = ptiCurrent; // gptiCurrent; + if ( !Mod && + (HookId == WH_GETMESSAGE || + HookId == WH_CALLWNDPROC || + HookId == WH_CBT || + HookId == WH_SYSMSGFILTER || + HookId == WH_HARDWARE || + HookId == WH_DEBUG || + HookId == WH_SHELL || + HookId == WH_FOREGROUNDIDLE || + HookId == WH_CALLWNDPROCRET) ) + { + SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD); + RETURN( NULL); + } } - if ((Global && (HookId != WH_KEYBOARD_LL && HookId != WH_MOUSE_LL)) || - WH_DEBUG == HookId || - WH_JOURNALPLAYBACK == HookId || - WH_JOURNALRECORD == HookId) - { -#if 0 /* Removed to get winEmbed working again */ - UNIMPLEMENTED -#else - DPRINT1("Not implemented: HookId %d Global %s\n", HookId, Global ? "TRUE" : "FALSE"); -#endif - - /* Dereference thread if needed */ - if (ThreadReferenced) ObDereferenceObject(Thread); - SetLastWin32Error(ERROR_NOT_SUPPORTED); - RETURN( NULL); - } - - Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation, - KernelMode, - 0, + Status = IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation, + KernelMode, + 0, &WinStaObj); if (!NT_SUCCESS(Status)) { - /* Dereference thread if needed */ - if (ThreadReferenced) ObDereferenceObject(Thread); - SetLastNtError(Status); - RETURN( (HANDLE) NULL); + SetLastNtError(Status); + RETURN( NULL); + } + ObDereferenceObject(WinStaObj); + + Hook = UserCreateObject(gHandleTable, NULL, &Handle, otHook, sizeof(HOOK)); + + if (!Hook) + { + RETURN( NULL); } - Hook = IntAddHook(Thread, HookId, Global, WinStaObj); - if (NULL == Hook) + Hook->ihmod = (INT)Mod; // Module Index from atom table, Do this for now. + Hook->Thread = Thread; /* Set Thread, Null is Global. */ + Hook->HookId = HookId; + Hook->rpdesk = pti->rpdesk; + Hook->phkNext = NULL; /* Dont use as a chain! Use link lists for chaining. */ + Hook->Proc = HookProc; + Hook->Ansi = Ansi; + + if (ThreadId) /* thread-local hook */ { - /* Dereference thread if needed */ - if (ThreadReferenced) ObDereferenceObject(Thread); - ObDereferenceObject(WinStaObj); - RETURN( NULL); - } + InsertHeadList(&pti->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain); + pti->sphkCurrent = NULL; + Hook->ptiHooked = pti; + pti->fsHooks |= HOOKID_TO_FLAG(HookId); - /* Let IntFreeHook now that this thread needs a dereference */ - if (ThreadReferenced) - { - Hook->Flags |= HOOK_THREAD_REFERENCED; - } - - if (NULL != Mod) - { - Status = MmCopyFromCaller(&ModuleName, UnsafeModuleName, sizeof(UNICODE_STRING)); - if (!NT_SUCCESS(Status)) - { - UserDereferenceObject(Hook); - IntRemoveHook(Hook, WinStaObj, FALSE); - ObDereferenceObject(WinStaObj); - SetLastNtError(Status); - RETURN( NULL); - } - - Hook->ModuleName.Buffer = ExAllocatePoolWithTag(PagedPool, - ModuleName.MaximumLength, - TAG_HOOK); - if (NULL == Hook->ModuleName.Buffer) - { - UserDereferenceObject(Hook); - IntRemoveHook(Hook, WinStaObj, FALSE); - ObDereferenceObject(WinStaObj); - SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); - RETURN( NULL); - } - - Hook->ModuleName.MaximumLength = ModuleName.MaximumLength; - Status = MmCopyFromCaller(Hook->ModuleName.Buffer, - ModuleName.Buffer, - ModuleName.MaximumLength); - if (!NT_SUCCESS(Status)) - { - ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK); - UserDereferenceObject(Hook); - IntRemoveHook(Hook, WinStaObj, FALSE); - ObDereferenceObject(WinStaObj); - SetLastNtError(Status); - RETURN( NULL); - } - - Hook->ModuleName.Length = ModuleName.Length; - /* make proc relative to the module base */ - Hook->Proc = (void *)((char *)HookProc - (char *)Mod); + if (pti->pClientInfo) + { + if ( pti->ppi == ptiCurrent->ppi) /* gptiCurrent->ppi) */ + { + _SEH2_TRY + { + pti->pClientInfo->fsHooks = pti->fsHooks; + pti->pClientInfo->phkCurrent = 0; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Hit = TRUE; + } + _SEH2_END; + if (Hit) + { + DPRINT1("Problem writing to Local ClientInfo!\n"); + } + } + else + { + KeAttachProcess(&pti->ppi->peProcess->Pcb); + _SEH2_TRY + { + pti->pClientInfo->fsHooks = pti->fsHooks; + pti->pClientInfo->phkCurrent = 0; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Hit = TRUE; + } + _SEH2_END; + KeDetachProcess(); + if (Hit) + { + DPRINT1("Problem writing to Remote ClientInfo!\n"); + } + } + } } else - Hook->Proc = HookProc; + { + InsertHeadList(&pti->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain); + Hook->ptiHooked = NULL; + //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId); + pti->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId); + } - Hook->Ansi = Ansi; - Handle = UserHMGetHandle(Hook); + RtlInitUnicodeString(&Hook->ModuleName, NULL); - /* Clear the client threads next hook. */ - ClientInfo->phkCurrent = 0; + if (Mod) + { + Status = MmCopyFromCaller(&ModuleName, + UnsafeModuleName, + sizeof(UNICODE_STRING)); + if (!NT_SUCCESS(Status)) + { + IntRemoveHook(Hook); + SetLastNtError(Status); + RETURN( NULL); + } - UserDereferenceObject(Hook); + Hook->ModuleName.Buffer = ExAllocatePoolWithTag( PagedPool, + ModuleName.MaximumLength, + TAG_HOOK); + if (NULL == Hook->ModuleName.Buffer) + { + IntRemoveHook(Hook); + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + RETURN( NULL); + } - ObDereferenceObject(WinStaObj); + Hook->ModuleName.MaximumLength = ModuleName.MaximumLength; + Status = MmCopyFromCaller( Hook->ModuleName.Buffer, + ModuleName.Buffer, + ModuleName.MaximumLength); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK); + Hook->ModuleName.Buffer = NULL; + IntRemoveHook(Hook); + SetLastNtError(Status); + RETURN( NULL); + } + + Hook->ModuleName.Length = ModuleName.Length; + /* make proc relative to the module base */ + Hook->offPfn = (ULONG_PTR)((char *)HookProc - (char *)Mod); + } + else + Hook->offPfn = 0; RETURN( Handle); @@ -1311,46 +1385,28 @@ CLEANUP: END_CLEANUP; } - BOOL APIENTRY NtUserUnhookWindowsHookEx(HHOOK Hook) { - PWINSTATION_OBJECT WinStaObj; PHOOK HookObj; - NTSTATUS Status; DECLARE_RETURN(BOOL); DPRINT("Enter NtUserUnhookWindowsHookEx\n"); UserEnterExclusive(); - Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation, - KernelMode, - 0, - &WinStaObj); - - if (!NT_SUCCESS(Status)) - { - SetLastNtError(Status); - RETURN( FALSE); - } - - /* Status = UserReferenceObjectByHandle(gHandleTable, Hook, - otHookProc, (PVOID *) &HookObj); */ if (!(HookObj = IntGetHookObject(Hook))) { DPRINT1("Invalid handle passed to NtUserUnhookWindowsHookEx\n"); - ObDereferenceObject(WinStaObj); /* SetLastNtError(Status); */ RETURN( FALSE); } ASSERT(Hook == UserHMGetHandle(HookObj)); - IntRemoveHook(HookObj, WinStaObj, FALSE); + IntRemoveHook(HookObj); UserDereferenceObject(HookObj); - ObDereferenceObject(WinStaObj); RETURN( TRUE); diff --git a/subsystems/win32/win32k/ntuser/message.c b/subsystems/win32/win32k/ntuser/message.c index 83376cb8947..afec69e5cd1 100644 --- a/subsystems/win32/win32k/ntuser/message.c +++ b/subsystems/win32/win32k/ntuser/message.c @@ -368,9 +368,10 @@ IntDispatchMessage(PMSG pMsg) { LARGE_INTEGER TickCount; LONG Time; - LRESULT retval; + LRESULT retval = 0; PMSGMEMORY MsgMemoryEntry; INT lParamBufferSize; + PTHREADINFO pti; LPARAM lParamPacked; PWND Window = NULL; @@ -380,17 +381,20 @@ IntDispatchMessage(PMSG pMsg) if (!Window) return 0; } + pti = PsGetCurrentThreadWin32Thread(); + if (((pMsg->message == WM_SYSTIMER) || (pMsg->message == WM_TIMER)) && (pMsg->lParam) ) { if (pMsg->message == WM_TIMER) { - if (ValidateTimerCallback(PsGetCurrentThreadWin32Thread(),pMsg->lParam)) + ObReferenceObject(pti->pEThread); + if (ValidateTimerCallback(pti,pMsg->lParam)) { KeQueryTickCount(&TickCount); Time = MsqCalculateMessageTime(&TickCount); - return co_IntCallWindowProc((WNDPROC)pMsg->lParam, + retval = co_IntCallWindowProc((WNDPROC)pMsg->lParam, TRUE, pMsg->hwnd, WM_TIMER, @@ -398,7 +402,8 @@ IntDispatchMessage(PMSG pMsg) (LPARAM)Time, sizeof(LPARAM)); } - return 0; + ObDereferenceObject(pti->pEThread); + return retval; } else { @@ -431,7 +436,7 @@ IntDispatchMessage(PMSG pMsg) DPRINT1("Failed to pack message parameters\n"); return 0; } - + ObReferenceObject(pti->pEThread); retval = co_IntCallWindowProc( Window->lpfnWndProc, !Window->Unicode, pMsg->hwnd, @@ -452,6 +457,7 @@ IntDispatchMessage(PMSG pMsg) co_UserGetUpdateRgn( Window, hrgn, TRUE ); REGION_FreeRgnByHandle( hrgn ); } + ObDereferenceObject(pti->pEThread); return retval; } @@ -679,10 +685,85 @@ co_IntTranslateMouseMessage( return FALSE; } -BOOL ProcessMouseMessage(MSG* Msg, USHORT HitTest, UINT RemoveMsg) +BOOL ProcessMouseMessage(MSG* Msg, BOOLEAN RemoveMessages) { MOUSEHOOKSTRUCT MHook; EVENTMSG Event; + PTHREADINFO pti; + PUSER_MESSAGE_QUEUE ThreadQueue; + USER_REFERENCE_ENTRY Ref; + USHORT HitTest = HTNOWHERE; + + pti = PsGetCurrentThreadWin32Thread(); + ThreadQueue = pti->MessageQueue; + + if(RemoveMessages) + { + PWND MsgWindow = NULL; + + /* Mouse message process */ + + if( Msg->hwnd && + ( MsgWindow = UserGetWindowObject(Msg->hwnd) ) && + Msg->message >= WM_MOUSEFIRST && + Msg->message <= WM_MOUSELAST ) + { + USHORT HitTest; + + UserRefObjectCo(MsgWindow, &Ref); + + if ( co_IntTranslateMouseMessage( ThreadQueue, + Msg, + &HitTest, + TRUE)) + /* FIXME - check message filter again, if the message doesn't match anymore, + search again */ + { + UserDerefObjectCo(MsgWindow); + /* eat the message, search again */ + return FALSE; + } + + if(ThreadQueue->CaptureWindow == NULL) + { + co_IntSendHitTestMessages(ThreadQueue, Msg); + + if ( ( Msg->message != WM_MOUSEMOVE && + Msg->message != WM_NCMOUSEMOVE ) && + IS_BTN_MESSAGE(Msg->message, DOWN) && + co_IntActivateWindowMouse(ThreadQueue, Msg, MsgWindow, &HitTest) ) + { + UserDerefObjectCo(MsgWindow); + /* eat the message, search again */ + return FALSE; + } + } + + UserDerefObjectCo(MsgWindow); + } + else + { + co_IntSendHitTestMessages(ThreadQueue, Msg); + } + + return TRUE; + } + + if ( ( Msg->hwnd && + Msg->message >= WM_MOUSEFIRST && + Msg->message <= WM_MOUSELAST ) && + co_IntTranslateMouseMessage( ThreadQueue, + Msg, + &HitTest, + FALSE) ) + /* FIXME - check message filter again, if the message doesn't match anymore, + search again */ + { + /* eat the message, search again */ + return FALSE; + } + + pti->rpdesk->htEx = HitTest; /* Now set the capture hit. */ Event.message = Msg->message; Event.time = Msg->time; @@ -697,7 +778,7 @@ BOOL ProcessMouseMessage(MSG* Msg, USHORT HitTest, UINT RemoveMsg) MHook.wHitTestCode = HitTest; MHook.dwExtraInfo = 0; if (co_HOOK_CallHooks( WH_MOUSE, - RemoveMsg ? HC_ACTION : HC_NOREMOVE, + RemoveMessages ? HC_ACTION : HC_NOREMOVE, Msg->message, (LPARAM)&MHook )) { @@ -709,13 +790,14 @@ BOOL ProcessMouseMessage(MSG* Msg, USHORT HitTest, UINT RemoveMsg) HCBT_CLICKSKIPPED, Msg->message, (LPARAM)&MHook); + DPRINT1("MouseMessage WH_CBT Call Hook return!\n"); return FALSE; } return TRUE; } -BOOL ProcessKeyboardMessage(MSG* Msg, UINT RemoveMsg) +BOOL ProcessKeyboardMessage(MSG* Msg, BOOLEAN RemoveMessages) { EVENTMSG Event; @@ -728,7 +810,7 @@ BOOL ProcessKeyboardMessage(MSG* Msg, UINT RemoveMsg) co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event); if (co_HOOK_CallHooks( WH_KEYBOARD, - RemoveMsg ? HC_ACTION : HC_NOREMOVE, + RemoveMessages ? HC_ACTION : HC_NOREMOVE, LOWORD(Msg->wParam), Msg->lParam)) { @@ -737,10 +819,31 @@ BOOL ProcessKeyboardMessage(MSG* Msg, UINT RemoveMsg) HCBT_KEYSKIPPED, LOWORD(Msg->wParam), Msg->lParam ); + DPRINT1("KeyboardMessage WH_CBT Call Hook return!\n"); return FALSE; } return TRUE; } + +BOOL ProcessHardwareMessage(MSG* Msg, BOOLEAN RemoveMessages) +{ + if ( IS_MOUSE_MESSAGE(Msg->message)) + { + if (!ProcessMouseMessage(Msg, RemoveMessages)) + { + return FALSE; + } + } + else if ( IS_KBD_MESSAGE(Msg->message)) + { + if(!ProcessKeyboardMessage(Msg, RemoveMessages)) + { + return FALSE; + } + } + + return TRUE; +} /* * Internal version of PeekMessage() doing all the work */ @@ -755,44 +858,15 @@ co_IntPeekMessage( PUSER_MESSAGE Msg, LARGE_INTEGER LargeTickCount; PUSER_MESSAGE_QUEUE ThreadQueue; PUSER_MESSAGE Message; - BOOL Present, RemoveMessages; - USER_REFERENCE_ENTRY Ref; - USHORT HitTest; - - /* The queues and order in which they are checked are documented in the MSDN - article on GetMessage() */ + BOOL RemoveMessages; pti = PsGetCurrentThreadWin32Thread(); ThreadQueue = pti->MessageQueue; - /* Inspect RemoveMsg flags */ - /* Note: - The only flag we process is PM_REMOVE. - Processing (High word) PM_QS_Xx Is needed. This and MsgFilterXxx can result - with QS_Xx flags to be used to isolate which message check to test for. - ATM, we look at all messages and the filters are sent to co_MsqFindMessage - and there, it is cross checked. - Example: Wine server/queue.c is_keyboard_msg, check_msg_filter and - filter_contains_hw_range. - */ RemoveMessages = RemoveMsg & PM_REMOVE; -/* - If no filter is specified, messages are processed in the following order: - - * Sent messages - * Posted messages - * Input (hardware) messages and system internal events - * Sent messages (again) - * WM_PAINT messages - * WM_TIMER messages - */ -CheckMessages: - - HitTest = HTNOWHERE; - - Present = FALSE; - + do + { KeQueryTickCount(&LargeTickCount); ThreadQueue->LastMsgRead = LargeTickCount.u.LowPart; @@ -814,43 +888,46 @@ CheckMessages: { ThreadQueue->QuitPosted = FALSE; } - goto MsgExit; + + return TRUE; } /* Now check for normal messages. */ - Present = co_MsqFindMessage( ThreadQueue, + if (co_MsqFindMessage( ThreadQueue, FALSE, RemoveMessages, Window, MsgFilterMin, MsgFilterMax, - &Message ); - if (Present) + &Message )) { RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE)); if (RemoveMessages) { MsqDestroyMessage(Message); } - goto MessageFound; + break; } /* Check for hardware events. */ - Present = co_MsqFindMessage( ThreadQueue, + if(co_MsqFindMessage( ThreadQueue, TRUE, RemoveMessages, Window, MsgFilterMin, MsgFilterMax, - &Message ); - if (Present) + &Message )) { RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE)); if (RemoveMessages) { MsqDestroyMessage(Message); } - goto MessageFound; + + if(!ProcessHardwareMessage(&Msg->Msg, RemoveMessages)) + continue; + + break; } /* Check for sent messages again. */ @@ -858,120 +935,30 @@ CheckMessages: ; /* Check for paint messages. */ - if ( IntGetPaintMessage( Window, + if( IntGetPaintMessage( Window, MsgFilterMin, MsgFilterMax, pti, &Msg->Msg, RemoveMessages)) { - goto MsgExit; + break; } if (PostTimerMessages(Window)) - goto CheckMessages; - - if(Present) { -MessageFound: - - if(RemoveMessages) - { - PWND MsgWindow = NULL; - - /* Mouse message process */ - - if( Msg->Msg.hwnd && - ( MsgWindow = UserGetWindowObject(Msg->Msg.hwnd) ) && - Msg->Msg.message >= WM_MOUSEFIRST && - Msg->Msg.message <= WM_MOUSELAST ) - { - USHORT HitTest; - - UserRefObjectCo(MsgWindow, &Ref); - - if ( co_IntTranslateMouseMessage( ThreadQueue, - &Msg->Msg, - &HitTest, - TRUE)) - /* FIXME - check message filter again, if the message doesn't match anymore, - search again */ - { - UserDerefObjectCo(MsgWindow); - /* eat the message, search again */ - goto CheckMessages; + continue; } - if(ThreadQueue->CaptureWindow == NULL) - { - co_IntSendHitTestMessages(ThreadQueue, &Msg->Msg); - - if ( ( Msg->Msg.message != WM_MOUSEMOVE && - Msg->Msg.message != WM_NCMOUSEMOVE ) && - IS_BTN_MESSAGE(Msg->Msg.message, DOWN) && - co_IntActivateWindowMouse(ThreadQueue, &Msg->Msg, MsgWindow, &HitTest) ) - { - UserDerefObjectCo(MsgWindow); - /* eat the message, search again */ - goto CheckMessages; - } - } - - UserDerefObjectCo(MsgWindow); - } - else - { - co_IntSendHitTestMessages(ThreadQueue, &Msg->Msg); - } - -// if(MsgWindow) -// { -// UserDereferenceObject(MsgWindow); -// } - - goto MsgExit; - } - - if ( ( Msg->Msg.hwnd && - Msg->Msg.message >= WM_MOUSEFIRST && - Msg->Msg.message <= WM_MOUSELAST ) && - co_IntTranslateMouseMessage( ThreadQueue, - &Msg->Msg, - &HitTest, - FALSE) ) - /* FIXME - check message filter again, if the message doesn't match anymore, - search again */ - { - /* eat the message, search again */ - goto CheckMessages; - } - -MsgExit: - pti->rpdesk->htEx = HitTest; /* Now set the capture hit. */ - - if ( IS_MOUSE_MESSAGE(Msg->Msg.message)) - { - if (!ProcessMouseMessage(&Msg->Msg, HitTest, RemoveMsg)) - { return FALSE; } - } + while (TRUE); - if ( IS_KBD_MESSAGE(Msg->Msg.message)) - { - if(!ProcessKeyboardMessage(&Msg->Msg, RemoveMsg)) - { - return FALSE; - } - } // The WH_GETMESSAGE hook enables an application to monitor messages about to // be returned by the GetMessage or PeekMessage function. co_HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, RemoveMsg & PM_REMOVE, (LPARAM)&Msg->Msg); return TRUE; - } - - return Present; } static NTSTATUS FASTCALL @@ -1344,13 +1331,13 @@ co_IntSendMessageTimeoutSingle( HWND hWnd, UINT uTimeout, ULONG_PTR *uResult ) { - ULONG_PTR Result; NTSTATUS Status; PWND Window = NULL; PMSGMEMORY MsgMemoryEntry; INT lParamBufferSize; LPARAM lParamPacked; PTHREADINFO Win32Thread; + ULONG_PTR Result = 0; DECLARE_RETURN(LRESULT); USER_REFERENCE_ENTRY Ref; @@ -1391,6 +1378,7 @@ co_IntSendMessageTimeoutSingle( HWND hWnd, RETURN( FALSE); } + ObReferenceObject(Win32Thread->pEThread); Result = (ULONG_PTR)co_IntCallWindowProc( Window->lpfnWndProc, !Window->Unicode, hWnd, @@ -1403,6 +1391,8 @@ co_IntSendMessageTimeoutSingle( HWND hWnd, *uResult = Result; } + ObDereferenceObject(Win32Thread->pEThread); + IntCallWndProcRet( Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult); if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE))) @@ -1600,7 +1590,7 @@ co_IntSendMessageWithCallBack( HWND hWnd, /* If this is not a callback and it can be sent now, then send it. */ if ((Window->head.pti->MessageQueue == Win32Thread->MessageQueue) && (CompletionCallback == NULL)) { - + ObReferenceObject(Win32Thread->pEThread); Result = (ULONG_PTR)co_IntCallWindowProc( Window->lpfnWndProc, !Window->Unicode, hWnd, @@ -1612,6 +1602,7 @@ co_IntSendMessageWithCallBack( HWND hWnd, { *uResult = Result; } + ObDereferenceObject(Win32Thread->pEThread); } IntCallWndProcRet( Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult); @@ -2579,12 +2570,13 @@ NtUserMessageCall( 2000, &RetVal); } + Ret = RetVal; } else if (parm.flags & BSF_POSTMESSAGE) { Ret = UserPostMessage(HWND_BROADCAST, Msg, wParam, lParam); } - else if ( parm.flags & BSF_SENDNOTIFYMESSAGE) + else //Everything else,,,, if ( parm.flags & BSF_SENDNOTIFYMESSAGE) { Ret = UserSendNotifyMessage(HWND_BROADCAST, Msg, wParam, lParam); } @@ -2610,21 +2602,31 @@ NtUserMessageCall( case FNID_CALLWNDPROC: case FNID_CALLWNDPROCRET: { - PCLIENTINFO ClientInfo = GetWin32ClientInfo(); - PHOOK NextObj, Hook = ClientInfo->phkCurrent; + PTHREADINFO pti; + PCLIENTINFO ClientInfo; + PHOOK NextObj, Hook; - if (!ClientInfo || !Hook) break; + pti = GetW32ThreadInfo(); - UserReferenceObject(Hook); + Hook = pti->sphkCurrent; - if (Hook->Thread && (Hook->Thread != PsGetCurrentThread())) + if (!Hook) break; + + NextObj = Hook->phkNext; + ClientInfo = pti->pClientInfo; + _SEH2_TRY { - UserDereferenceObject(Hook); - break; + ClientInfo->phkCurrent = NextObj; } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ClientInfo = NULL; + } + _SEH2_END; - NextObj = IntGetNextHook(Hook); - ClientInfo->phkCurrent = NextObj; + if (!ClientInfo || !NextObj) break; + + NextObj->phkNext = IntGetNextHook(NextObj); if ( Hook->HookId == WH_CALLWNDPROC) { @@ -2660,8 +2662,6 @@ NtUserMessageCall( Hook->Ansi, &Hook->ModuleName); } - UserDereferenceObject(Hook); - lResult = (LRESULT) NextObj; } break; } diff --git a/subsystems/win32/win32k/ntuser/msgqueue.c b/subsystems/win32/win32k/ntuser/msgqueue.c index 5d0bee88eba..04694b46286 100644 --- a/subsystems/win32/win32k/ntuser/msgqueue.c +++ b/subsystems/win32/win32k/ntuser/msgqueue.c @@ -948,26 +948,25 @@ co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue) &Message->ListEntry); if (Message->HookMessage == MSQ_ISHOOK) - { - Result = co_HOOK_CallHooks(Message->Msg.message, - (INT)(INT_PTR)Message->Msg.hwnd, - Message->Msg.wParam, - Message->Msg.lParam); + { // Direct Hook Call processor + Result = co_CallHook( Message->Msg.message, // HookId + (INT)(INT_PTR)Message->Msg.hwnd, // Code + Message->Msg.wParam, + Message->Msg.lParam); } else if (Message->HookMessage == MSQ_ISEVENT) - { + { // Direct Event Call processor Result = co_EVENT_CallEvents( Message->Msg.message, Message->Msg.hwnd, Message->Msg.wParam, Message->Msg.lParam); } else - { - /* Call the window procedure. */ - Result = co_IntSendMessage(Message->Msg.hwnd, - Message->Msg.message, - Message->Msg.wParam, - Message->Msg.lParam); + { /* Call the window procedure. */ + Result = co_IntSendMessage( Message->Msg.hwnd, + Message->Msg.message, + Message->Msg.wParam, + Message->Msg.lParam); } /* remove the message from the local dispatching list, because it doesn't need @@ -1004,7 +1003,9 @@ co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue) KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE); } - /* Call the callback if the message was sent with SendMessageCallback */ + /* Call the callback if the message wa + + s sent with SendMessageCallback */ if (Message->CompletionCallback != NULL) { co_IntCallSentMessageCallback(Message->CompletionCallback, @@ -1133,10 +1134,10 @@ co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue, PUSER_SENT_MESSAGE Message; KEVENT CompletionEvent; NTSTATUS WaitStatus; - LRESULT Result; PUSER_MESSAGE_QUEUE ThreadQueue; LARGE_INTEGER Timeout; PLIST_ENTRY Entry; + LRESULT Result = 0; //// Result could be trashed. //// if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG))) { @@ -1154,7 +1155,6 @@ co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue, /* FIXME - increase reference counter of sender's message queue here */ - Result = 0; Message->Msg.hwnd = Wnd; Message->Msg.message = Msg; Message->Msg.wParam = wParam; @@ -1164,6 +1164,7 @@ co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue, Message->SenderQueue = ThreadQueue; IntReferenceMessageQueue(ThreadQueue); Message->CompletionCallback = NULL; + Message->CompletionCallbackContext = 0; Message->HookMessage = HookMessage; Message->HasPackedLParam = FALSE; @@ -1632,18 +1633,6 @@ MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue) IntDereferenceMessageQueue(MessageQueue); } -PHOOKTABLE FASTCALL -MsqGetHooks(PUSER_MESSAGE_QUEUE Queue) -{ - return Queue->Hooks; -} - -VOID FASTCALL -MsqSetHooks(PUSER_MESSAGE_QUEUE Queue, PHOOKTABLE Hooks) -{ - Queue->Hooks = Hooks; -} - LPARAM FASTCALL MsqSetMessageExtraInfo(LPARAM lParam) { diff --git a/subsystems/win32/win32k/ntuser/object.c b/subsystems/win32/win32k/ntuser/object.c index 0c3fb075d7e..222b36cb6b8 100644 --- a/subsystems/win32/win32k/ntuser/object.c +++ b/subsystems/win32/win32k/ntuser/object.c @@ -330,8 +330,8 @@ UserCreateObject( PUSER_HANDLE_TABLE ht, { case otWindow: // case otMenu: -// case otHook: -// case otCallProc: + case otHook: + case otCallProc: case otInputContext: Object = DesktopHeapAlloc(rpdesk, size); dt = TRUE; @@ -420,8 +420,8 @@ UserDereferenceObject(PVOID object) { case otWindow: // case otMenu: -// case otHook: -// case otCallProc: + case otHook: + case otCallProc: case otInputContext: return DesktopHeapFree(((PTHRDESKHEAD)object)->rpdesk, object); diff --git a/subsystems/win32/win32k/ntuser/simplecall.c b/subsystems/win32/win32k/ntuser/simplecall.c index af977096c8a..29d35958e44 100644 --- a/subsystems/win32/win32k/ntuser/simplecall.c +++ b/subsystems/win32/win32k/ntuser/simplecall.c @@ -228,21 +228,6 @@ NtUserCallOneParam( RETURN (ret); } - case ONEPARAM_ROUTINE_ISWINDOWINDESTROY: - { - PWND Window; - DWORD_PTR Result; - - if(!(Window = UserGetWindowObject((HWND)Param))) - { - RETURN( FALSE); - } - - Result = (DWORD_PTR)IntIsWindowInDestroy(Window); - - RETURN( Result); - } - case ONEPARAM_ROUTINE_ENABLEPROCWNDGHSTING: { BOOL Enable; @@ -473,6 +458,8 @@ NtUserCallTwoParam( case TWOPARAM_ROUTINE_SETCURSORPOS: RETURN( (DWORD_PTR)UserSetCursorPos((int)Param1, (int)Param2, FALSE)); + case TWOPARAM_ROUTINE_UNHOOKWINDOWSHOOK: + RETURN( IntUnhookWindowsHook((int)Param1, (HOOKPROC)Param2)); } DPRINT1("Calling invalid routine number 0x%x in NtUserCallTwoParam(), Param1=0x%x Parm2=0x%x\n", Routine, Param1, Param2); @@ -713,6 +700,21 @@ NtUserCallHwndParam( UserLeave(); return 0; } + + case HWNDPARAM_ROUTINE_ROS_NOTIFYWINEVENT: + { + PWND pWnd; + PNOTIFYEVENT pne; + UserEnterExclusive(); + pne = (PNOTIFYEVENT)Param; + if (hWnd) + pWnd = UserGetWindowObject(hWnd); + else + pWnd = NULL; + IntNotifyWinEvent(pne->event, pWnd, pne->idObject, pne->idChild, pne->flags); + UserLeave(); + return 0; + } } UNIMPLEMENTED; diff --git a/subsystems/win32/win32k/ntuser/window.c b/subsystems/win32/win32k/ntuser/window.c index 0b270cf8c0b..f6e588fa1d8 100644 --- a/subsystems/win32/win32k/ntuser/window.c +++ b/subsystems/win32/win32k/ntuser/window.c @@ -163,7 +163,6 @@ IntIsWindow(HWND hWnd) } - PWND FASTCALL IntGetParent(PWND Wnd) { @@ -179,7 +178,6 @@ IntGetParent(PWND Wnd) return NULL; } - /* * IntWinListChildren * @@ -348,7 +346,7 @@ static LRESULT co_UserFreeWindow(PWND Window, Window->state2 |= WNDS2_INDESTROY; Window->style &= ~WS_VISIBLE; - IntNotifyWinEvent(EVENT_OBJECT_DESTROY, Window, OBJID_WINDOW, 0); + IntNotifyWinEvent(EVENT_OBJECT_DESTROY, Window, OBJID_WINDOW, CHILDID_SELF, 0); /* remove the window already at this point from the thread window list so we don't get into trouble when destroying the thread windows while we're still @@ -399,7 +397,9 @@ static LRESULT co_UserFreeWindow(PWND Window, if(BelongsToThreadData) co_IntSendMessage(Window->head.h, WM_NCDESTROY, 0, 0); } + DestroyTimersForWindow(ThreadData, Window); + HOOK_DestroyThreadHooks(ThreadData->pEThread); // This is needed here too! /* flush the message queue */ @@ -1168,6 +1168,7 @@ co_IntSetParent(PWND Wnd, PWND WndNewParent) } + IntNotifyWinEvent(EVENT_OBJECT_PARENTCHANGE, Wnd ,OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI); /* * SetParent additionally needs to make hwnd the top window * in the z-order and send the expected WM_WINDOWPOSCHANGING and @@ -1227,13 +1228,6 @@ IntUnlinkWindow(PWND Wnd) Wnd->spwndPrev = Wnd->spwndNext = NULL; } -BOOL FASTCALL -IntIsWindowInDestroy(PWND Window) -{ - return ((Window->state2 & WNDS2_INDESTROY) == WNDS2_INDESTROY); -} - - BOOL FASTCALL IntGetWindowPlacement(PWND Wnd, WINDOWPLACEMENT *lpwndpl) @@ -1635,6 +1629,33 @@ PWND FASTCALL IntCreateWindow(CREATESTRUCTW* Cs, pti = PsGetCurrentThreadWin32Thread(); + if (!(Cs->dwExStyle & WS_EX_LAYOUTRTL)) + { + if (ParentWindow) + { + if ( (Cs->style & (WS_CHILD|WS_POPUP)) == WS_CHILD && + ParentWindow->ExStyle & WS_EX_LAYOUTRTL && + !(ParentWindow->ExStyle & WS_EX_NOINHERITLAYOUT) ) + Cs->dwExStyle |= WS_EX_LAYOUTRTL; + } + else + {/* + Note from MSDN http://msdn.microsoft.com/en-us/library/aa913269.aspx : + + Dialog boxes and message boxes do not inherit layout, so you must + set the layout explicitly. + */ + if ( Class && Class->fnid != FNID_DIALOG) + { + PPROCESSINFO ppi = PsGetCurrentProcessWin32Process(); + if (ppi->dwLayout & LAYOUT_RTL) + { + Cs->dwExStyle |= WS_EX_LAYOUTRTL; + } + } + } + } + /* Automatically add WS_EX_WINDOWEDGE */ if ((Cs->dwExStyle & WS_EX_DLGMODALFRAME) || ((!(Cs->dwExStyle & WS_EX_STATICEDGE)) && @@ -1877,17 +1898,19 @@ co_UserCreateWindowEx(CREATESTRUCTW* Cs, PLARGE_STRING WindowName) { PWND Window = NULL, ParentWindow = NULL, OwnerWindow; - HWND hWnd, hWndParent, hWndOwner; + HWND hWnd, hWndParent, hWndOwner, hwndInsertAfter; DWORD dwStyle; PWINSTATION_OBJECT WinSta; PCLS Class = NULL; SIZE Size; POINT MaxPos; - CBT_CREATEWNDW CbtCreate; + CBT_CREATEWNDW * pCbtCreate; LRESULT Result; USER_REFERENCE_ENTRY ParentRef, Ref; PTHREADINFO pti; + ANSI_STRING asClassName; DWORD dwShowMode = SW_SHOW; + CREATESTRUCTW *pCsw; DECLARE_RETURN(PWND); /* Get the current window station and reference it */ @@ -1900,6 +1923,10 @@ co_UserCreateWindowEx(CREATESTRUCTW* Cs, WinSta = pti->rpdesk->rpwinstaParent; ObReferenceObjectByPointer(WinSta, KernelMode, ExWindowStationObjectType, 0); + pCsw = NULL; + pCbtCreate = NULL; + RtlInitAnsiString(&asClassName, NULL); + /* Get the class and reference it*/ Class = IntGetAndReferenceClass(ClassName, Cs->hInstance); if(!Class) @@ -1956,22 +1983,57 @@ co_UserCreateWindowEx(CREATESTRUCTW* Cs, RETURN(0); } - hWnd = Window->head.h; + hWnd = UserHMGetHandle(Window); UserRefObjectCo(Window, &Ref); ObDereferenceObject(WinSta); - /* Call the WH_CBT hook */ - dwStyle = Cs->style; - Cs->style = Window->style; /* HCBT_CREATEWND needs the real window style */ - CbtCreate.lpcs = Cs; - CbtCreate.hwndInsertAfter = HWND_TOP; + //// Call the WH_CBT hook //// - if (co_HOOK_CallHooks(WH_CBT, HCBT_CREATEWND, (WPARAM) hWnd, (LPARAM) &CbtCreate)) + // Allocate the calling structures Justin Case this goes Global. + pCsw = ExAllocatePoolWithTag(NonPagedPool, sizeof(CREATESTRUCTW), TAG_HOOK); + pCbtCreate = ExAllocatePoolWithTag(NonPagedPool, sizeof(CBT_CREATEWNDW), TAG_HOOK); + + /* Fill the new CREATESTRUCTW */ + pCsw->lpCreateParams = Cs->lpCreateParams; + pCsw->hInstance = Cs->hInstance; + pCsw->hMenu = Cs->hMenu; + pCsw->hwndParent = Cs->hwndParent; + pCsw->cx = Cs->cx; + pCsw->cy = Cs->cy; + pCsw->x = Cs->x; + pCsw->y = Cs->y; + pCsw->dwExStyle = Cs->dwExStyle; + dwStyle = Cs->style; // Save it anyway. + pCsw->style = Window->style; /* HCBT_CREATEWND needs the real window style */ + + pCsw->lpszName = (LPCWSTR) WindowName->Buffer; + pCsw->lpszClass = (LPCWSTR) ClassName->Buffer; + + if (Window->state & WNDS_ANSICREATOR) { - DPRINT1("HCBT_CREATEWND hook failed!\n"); + if (!IS_ATOM(ClassName->Buffer)) + { + RtlUnicodeStringToAnsiString(&asClassName, ClassName, TRUE); + pCsw->lpszClass = (LPCWSTR) asClassName.Buffer; + } + } + + pCbtCreate->lpcs = pCsw; + pCbtCreate->hwndInsertAfter = HWND_TOP; + + Result = co_HOOK_CallHooks(WH_CBT, HCBT_CREATEWND, (WPARAM) hWnd, (LPARAM) pCbtCreate); + if (Result != 0) + { + DPRINT1("WH_CBT HCBT_CREATEWND hook failed! 0x%x\n", Result); RETURN( (PWND) NULL); } + // Write back changes. + Cs->cx = pCsw->cx; + Cs->cy = pCsw->cy; + Cs->x = pCsw->x; + Cs->y = pCsw->y; + hwndInsertAfter = pCbtCreate->hwndInsertAfter; Cs->style = dwStyle; /* NCCREATE and WM_NCCALCSIZE need the original values*/ @@ -2014,7 +2076,7 @@ co_UserCreateWindowEx(CREATESTRUCTW* Cs, } /* Send the NCCREATE message */ - Result = co_IntSendMessage(Window->head.h, WM_NCCREATE, 0, (LPARAM) Cs); + Result = co_IntSendMessage(UserHMGetHandle(Window), WM_NCCREATE, 0, (LPARAM) Cs); if (!Result) { DPRINT1("co_UserCreateWindowEx(): NCCREATE message failed\n"); @@ -2032,7 +2094,7 @@ co_UserCreateWindowEx(CREATESTRUCTW* Cs, /* Send the WM_CREATE message. */ - Result = co_IntSendMessage(Window->head.h, WM_CREATE, 0, (LPARAM) Cs); + Result = co_IntSendMessage(UserHMGetHandle(Window), WM_CREATE, 0, (LPARAM) Cs); if (Result == (LRESULT)-1) { DPRINT1("co_UserCreateWindowEx(): WM_CREATE message failed\n"); @@ -2040,7 +2102,7 @@ co_UserCreateWindowEx(CREATESTRUCTW* Cs, } /* Send the EVENT_OBJECT_CREATE event*/ - IntNotifyWinEvent(EVENT_OBJECT_CREATE, Window, OBJID_WINDOW, 0); + IntNotifyWinEvent(EVENT_OBJECT_CREATE, Window, OBJID_WINDOW, CHILDID_SELF, 0); /* By setting the flag below it can be examined to determine if the window was created successfully and a valid pwnd was passed back to caller since @@ -2102,7 +2164,7 @@ co_UserCreateWindowEx(CREATESTRUCTW* Cs, if (Window->ExStyle & WS_EX_MDICHILD) { - co_IntSendMessage(ParentWindow->head.h, WM_MDIREFRESHMENU, 0, 0); + co_IntSendMessage(UserHMGetHandle(ParentWindow), WM_MDIREFRESHMENU, 0, 0); /* ShowWindow won't activate child windows */ co_WinPosSetWindowPos(Window, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); } @@ -2121,6 +2183,10 @@ CLEANUP: IntDereferenceClass(Class, pti->pDeskInfo, pti->ppi); } + if (pCsw) ExFreePoolWithTag(pCsw, TAG_HOOK); + if (pCbtCreate) ExFreePoolWithTag(pCbtCreate, TAG_HOOK); + RtlFreeAnsiString(&asClassName); + if (Window) { UserDerefObjectCo(Window); @@ -2280,7 +2346,7 @@ NtUserCreateWindowEx( /* Call the internal function */ pwnd = co_UserCreateWindowEx(&Cs, &ustrClassName, plstrWindowName); - if(!pwnd) + if(!pwnd) { DPRINT1("co_UserCreateWindowEx failed!\n"); } @@ -2343,7 +2409,11 @@ BOOLEAN FASTCALL co_UserDestroyWindow(PWND Window) /* If window was created successfully and it is hooked */ if ((Window->state2 & WNDS2_WMCREATEMSGPROCESSED)) { - if (co_HOOK_CallHooks(WH_CBT, HCBT_DESTROYWND, (WPARAM) hWnd, 0)) return FALSE; + if (co_HOOK_CallHooks(WH_CBT, HCBT_DESTROYWND, (WPARAM) hWnd, 0)) + { + DPRINT1("Destroy Window WH_CBT Call Hook return!\n"); + return FALSE; + } } /* Inform the parent */ diff --git a/subsystems/win32/win32k/ntuser/winpos.c b/subsystems/win32/win32k/ntuser/winpos.c index 7a9562613c4..04c0fd7263f 100644 --- a/subsystems/win32/win32k/ntuser/winpos.c +++ b/subsystems/win32/win32k/ntuser/winpos.c @@ -299,8 +299,10 @@ co_WinPosMinMaximize(PWND Wnd, UINT ShowFlag, RECT* NewPos) WinPosInitInternalPos(Wnd, &Size, &Wnd->rcWindow); if (co_HOOK_CallHooks( WH_CBT, HCBT_MINMAX, (WPARAM)Wnd->head.h, ShowFlag)) + { + DPRINT1("WinPosMinMaximize WH_CBT Call Hook return!\n"); return SWP_NOSIZE | SWP_NOMOVE; - + } if (Wnd->style & WS_MINIMIZE) { if (!co_IntSendMessageNoWait(Wnd->head.h, WM_QUERYOPEN, 0, 0)) @@ -385,7 +387,6 @@ co_WinPosMinMaximize(PWND Wnd, UINT ShowFlag, RECT* NewPos) } } } - return(SwpFlags); } @@ -1358,6 +1359,14 @@ co_WinPosSetWindowPos( co_IntSendMessageNoWait(WinPos.hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM) &WinPos); } + if ( WinPos.flags & SWP_FRAMECHANGED || WinPos.flags & SWP_STATECHANGED || + !(WinPos.flags & SWP_NOCLIENTSIZE) || !(WinPos.flags & SWP_NOCLIENTMOVE) ) + { + PWND pWnd = UserGetWindowObject(WinPos.hwnd); + if (pWnd) + IntNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, pWnd, OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI); + } + return TRUE; } diff --git a/subsystems/win32/win32k/objects/cliprgn.c b/subsystems/win32/win32k/objects/cliprgn.c index a4acba496fd..8698bb3ee98 100644 --- a/subsystems/win32/win32k/objects/cliprgn.c +++ b/subsystems/win32/win32k/objects/cliprgn.c @@ -541,7 +541,7 @@ NEW_CLIPPING_UpdateGCRegion(PDC pDC) CLIPOBJ * co; /* Must have VisRgn set to a valid state! */ - if (!pDC->prgnVis) return ERROR; + ASSERT (pDC->prgnVis); if (pDC->prgnAPI) {