/*********************************************************************** Copyright 2002 Ben Rudiak-Gould. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, or visit . ***********************************************************************/ #define WIN32_LEAN_AND_MEAN #define _WIN32_IE 0x300 // winbase.h defines an unrelated struct called "DCB". Rather than rename // the kernel one I'm hacking around the problem, at least for now. #define DCB SOME_OTHER_DCB #include #undef DCB #include #include "resource.h" #include "../include/dvdsynth-device.h" #include "kernel-io.h" #include HINSTANCE g_hinstance; DvsDockingBayGlobal* g_callback; dvs_driver_handle g_driver_handle; struct AdapterInfo { DEVNODE devnode; int num_devices; }; AdapterInfo g_adapters[16]; int g_num_adapters; struct DeviceInfo { unsigned adapter, bus, target, lun; DCB* dcb; unsigned char drive_letter; unsigned char type; char model[27]; }; const int MAX_DEVICES = 64; DeviceInfo g_device_info[MAX_DEVICES]; int g_num_devices; HWND g_hlistview; inline scsi_result_t SendScsiCommand(DCB* dcb, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) { // note the final dummy argument specifying the size of the SenseData buffer return (scsi_result_t)g_callback->Driver_Call(g_driver_handle, "SendScsiCommand", "p>i>&i>i", dcb, cdb, cdblen, buffer, pbuflen, inout, sense, sizeof(SenseData)); } inline DEVNODE GetDevnodeParent(DEVNODE dn) { DEVNODE result = 0; if (CR_SUCCESS == (CONFIGRET)g_callback->Driver_Call(g_driver_handle, "CONFIGMG_Get_Parent", "&pi", &result, dn, 0)) { return result; } else { return 0; } } inline DCB* GetNextDCB(DCB* dcb) { return (DCB*)g_callback->Driver_Call(g_driver_handle, "GetNextDCB", "p", dcb); } bool GetRegString(HKEY base, const char* subkey, const char* value, char* buf, ULONG* pbuflen) { HKEY hkey; if (ERROR_SUCCESS != RegOpenKeyEx(base, subkey, 0, KEY_READ, &hkey)) { return false; } long query_error = RegQueryValueEx(hkey, value, NULL, NULL, (unsigned char*)buf, pbuflen); RegCloseKey(hkey); return (query_error == ERROR_SUCCESS); } bool GetDevNodeHardwareKey(DEVNODE dn, char* buf, ULONG buflen) { char dyn_data_key[32]; wsprintf(dyn_data_key, "Config Manager\\Enum\\%08X", dn); lstrcpy(buf, "Enum\\"); ULONG len = buflen-6; if (!GetRegString(HKEY_DYN_DATA, dyn_data_key, "HardWareKey", buf+5, &len)) { return false; } buf[5+len] = 0; return true; } bool GetDevNodeRegString(DEVNODE dn, const char* name, char* buf, ULONG buflen) { char hardware_key[256]; if (!GetDevNodeHardwareKey(dn, hardware_key, 256)) { return false; } ULONG len = buflen; if (!GetRegString(HKEY_LOCAL_MACHINE, hardware_key, name, buf, &len)) { return false; } buf[min(len,buflen-1)] = 0; return true; } char GetDevnodeDriveLetter(DEVNODE dn) { char buf[64]; if (GetDevNodeRegString(dn, "CurrentDriveLetterAssignment", buf, 64)) { return buf[0]; } else { return 0; } } void SetModelField(char* dst, const unsigned char* src) { // copy vendor ID field memcpy(dst, src, 8); char* p; // lop off trailing spaces p = dst+8; while (p > dst && p[-1] == ' ') --p; // copy product ID field, with delimiting space *p++ = ' '; memcpy(p, src+8, 16); // lop off trailing spaces p += 16; while (p > dst && p[-1] == ' ') --p; *p = 0; } void ScanForDevices() { DEVNODE dvdproxy_devnode = (DEVNODE)g_callback->GetDvdsynthDevicesID(); DCB* dcb = 0; while ((dcb = GetNextDCB(dcb)) != 0 && g_num_devices < MAX_DEVICES) { printf("DCB = %08X\n", dcb); // skip logical devices if (!(dcb->DCB_cmn.DCB_device_flags & DCB_DEV_PHYSICAL)) { continue; } // only consider SCSI/IDE devices (is this wise?) if (dcb->DCB_bus_type != DCB_BUS_SCSI && dcb->DCB_bus_type != DCB_BUS_ESDI) { printf("A\n"); continue; } // the parent devnode is the adapter controlling this device DEVNODE adapter_devnode = GetDevnodeParent(dcb->DCB_dev_node); printf("parent = %08X\n", adapter_devnode); if (adapter_devnode == 0 || adapter_devnode == dvdproxy_devnode) { printf("B\n"); continue; } int adapter_number; for (adapter_number=0; adapter_numberDCB_cmn.DCB_device_flags2 & DCB_DEV2_ATAPI_DEVICE) { // For ATAPI devices, count number of previous devices on this bus bus = 0; target = g_adapters[adapter_number].num_devices - 1; lun = 0; } else if (dcb->DCB_bus_type == DCB_BUS_SCSI) { // For SCSI devices, SCSI address is stored in the DCB. bus = dcb->DCB_bus_number; target = dcb->DCB_scsi_target_id; lun = dcb->DCB_scsi_lun; } else { // Don't consider IDE devices any further. printf("D\n"); continue; } /* static const unsigned char cdb_inquiry[6] = { 0x12, 0, 0, 0, 36, 0 }; unsigned char inquiry_buf[36]; memset(inquiry_buf, 0, sizeof(inquiry_buf)); unsigned long buflen = 36; SenseData sense; if (SCSIRESULT_SUCCESS != SendScsiCommand(dcb, cdb_inquiry, 6, inquiry_buf, &buflen, 1, &sense)) { printf("E\n"); continue; } */ DeviceInfo& di = g_device_info[g_num_devices++]; memset(&di, 0, sizeof(DeviceInfo)); di.adapter = adapter_number; di.bus = bus; di.target = target; di.lun = lun; di.dcb = dcb; di.drive_letter = GetDevnodeDriveLetter(dcb->DCB_dev_node); di.type = dcb->DCB_inquiry_flags[0]; SetModelField(di.model, dcb->DCB_vendor_id); // di.type = inquiry_buf[0]; // SetModelField(di.model, inquiry_buf+8); } } void AddListViewColumn(HWND hlv, int index, const char* text, int width) { LVCOLUMN lvc; lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; lvc.fmt = 0; lvc.cx = width; lvc.pszText = (LPTSTR)text; ListView_InsertColumn(hlv, index, &lvc); } BOOL NewInstanceDialog_InitDialog(HWND hdlg) { HWND hlist = GetDlgItem(hdlg, IDC_DEVICELIST); ListView_SetExtendedListViewStyle(hlist, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP); // get the width of the list view and size its columns proportionally RECT r; GetClientRect(hlist, &r); unsigned width = r.right; unsigned divider1 = width*14/32; unsigned divider2 = width*21/32; unsigned divider3 = width*27/32; AddListViewColumn(hlist, 0, "Make and model", divider1); AddListViewColumn(hlist, 1, "Type", divider2-divider1); AddListViewColumn(hlist, 2, "Letter", divider3-divider2); AddListViewColumn(hlist, 3, "Address", width-divider3); g_hlistview = hlist; g_num_devices = 0; g_num_adapters = 0; ScanForDevices(); // ListView_SetItemCount does not work!!! LVITEM new_item; memset(&new_item, 0, sizeof(new_item)); for (int i=0; imask & LVIF_TEXT)) return; int i = plvi->iItem, c = plvi->iSubItem; if (i < 0 || i >= g_num_devices || i >= MAX_DEVICES) return; char buf[32]; const char* text; const DeviceInfo& di = g_device_info[i]; switch (c) { case 0: text = di.model; break; case 1: if (di.type <= sizeof(scsi_device_type)/sizeof(scsi_device_type[0])) { text = scsi_device_type[di.type]; } else { wsprintf(buf, "unknown (0x%02X)", di.type); text = buf; } break; case 2: wsprintf(buf, "%c:", di.drive_letter); text = buf; break; case 3: if (di.bus) { wsprintf(buf, "%d.%d:%d:%d", di.adapter, di.bus, di.target, di.lun); } else { wsprintf(buf, "%d:%d:%d", di.adapter, di.target, di.lun); } text = buf; break; default: return; } lstrcpyn(plvi->pszText, text, plvi->cchTextMax); plvi->pszText[plvi->cchTextMax-1] = 0; } BOOL CALLBACK NewInstanceDialogProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) { // printf("NewInstanceDialogProc(%X, %d, %d, %d)\n", hdlg, msg, wparam, lparam); switch (msg) { case WM_INITDIALOG: return NewInstanceDialog_InitDialog(hdlg); case WM_COMMAND: switch (LOWORD(wparam)) { case IDOK: EndDialog(hdlg, GetLVSelection(GetDlgItem(hdlg, IDC_DEVICELIST))); return TRUE; case IDCANCEL: EndDialog(hdlg, -1); return TRUE; } break; case WM_NOTIFY: if (wparam == IDC_DEVICELIST) { LPNMHDR pnmh = (LPNMHDR)lparam; // printf("code = %d\n", pnmh->code); if (pnmh->code == LVN_GETDISPINFO) { GetDispInfo(&((LV_DISPINFO*)pnmh)->item); return TRUE; } else if (pnmh->code == LVN_ITEMCHANGED) { EnableWindow(GetDlgItem(hdlg, IDOK), ListView_GetSelectedCount(pnmh->hwndFrom)); return TRUE; } else if (pnmh->code == LVN_ITEMACTIVATE) { EndDialog(hdlg, GetLVSelection(GetDlgItem(hdlg, IDC_DEVICELIST))); return TRUE; } } break; } return FALSE; } DvsDeviceUser_vtable mirror_drive_user_vtable = { 0, // AddDeviceMenuItems 0, // QueryUnplug 0, // Delete }; DvsDeviceUser mirror_drive_user = { &mirror_drive_user_vtable }; void CreateNewInstance(void*, int) { DvsDockingBay* bay = g_callback->ReserveDockingBay(); if (!bay) return; int selection = DialogBox(g_hinstance, MAKEINTRESOURCE(IDD_CHOOSEDEVICE), NULL, NewInstanceDialogProc); if (selection >= 0 && selection < g_num_devices) { MirrorDriveKernel* k = (MirrorDriveKernel*)bay->vtable->SharedPool_Alloc(bay, sizeof(MirrorDriveKernel)); k->ScsiCommand = (dvs_scsi_func*)g_callback->Driver_Call(g_driver_handle, "GetDispatchFunc", ""); k->dcb = g_device_info[selection].dcb; bay->vtable->SetHandlers(bay, &mirror_drive_user, k); } } void AddNewMenuItems(DvsMenu* menu) { menu->vtable->AddItem(menu, "Mirror Drive", false, CreateNewInstance, 0, 0); } void AddAboutMenuItems(DvsMenu* menu) {} DvsDeviceGlobal global_funcs = { AddNewMenuItems, AddAboutMenuItems, 0,//AddMainMenuItems, 0 }; extern "C" DvsDeviceGlobal* DvdsynthDevicePluginEntry(DvsDockingBayGlobal* bays) { if (!bays->Is95()) return 0; g_callback = bays; if (g_driver_handle == 0) { g_driver_handle = g_callback->Driver_Load("MirrorDrive95.kll"); } return g_driver_handle ? &global_funcs : 0; } extern "C" BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID) { g_hinstance = hinst; // don't ever unload the driver because we registered with IOS /* if (dwReason == DLL_PROCESS_DETACH && g_driver_handle != 0 && g_callback != 0) { g_callback->Driver_Unload(g_driver_handle); g_driver_handle = 0; } */ return TRUE; }