/*********************************************************************** 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 #include #include #include "resource.h" #include "../include/dvdsynth-device.h" #include "../dvdsynth2k/scsipt.h" #include #include HINSTANCE g_hinstance; DvsDockingBayGlobal* g_callback; struct DeviceInfo { unsigned char port, path, target, lun; char devname[8]; unsigned char type; char model[27]; }; const int MAX_DEVICES = 64; DeviceInfo g_device_info[MAX_DEVICES]; int g_num_devices; HWND g_hlistview; scsi_result_t SendScsiCommand(HANDLE hdevice, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) { SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb; memset(&swb, 0, sizeof(swb)); swb.spt.Length = sizeof(SCSI_PASS_THROUGH); swb.spt.CdbLength = cdblen; swb.spt.DataIn = inout==1 ? SCSI_IOCTL_DATA_IN : inout==2 ? SCSI_IOCTL_DATA_OUT : SCSI_IOCTL_DATA_UNSPECIFIED; swb.spt.DataTransferLength = *pbuflen; swb.spt.TimeOutValue = 60; swb.spt.DataBuffer = swb.spt.DataTransferLength ? buffer : NULL; swb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); swb.spt.SenseInfoLength = 18; memcpy(swb.ucSenseBuf, sense, swb.spt.SenseInfoLength); memcpy(swb.spt.Cdb, cdb, cdblen); ULONG length = sizeof(swb); ULONG returned; if (DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &swb, length, &swb, length, &returned, NULL)) { *pbuflen = swb.spt.DataTransferLength; memcpy(sense, swb.ucSenseBuf, swb.spt.SenseInfoLength); if (swb.spt.ScsiStatus) { return MAKE_SCSIRESULT(SRB_STATUS_ERROR, swb.spt.ScsiStatus, sense->sense_key, sense->asc, sense->ascq); } else { return SCSIRESULT_SUCCESS; } } else { DWORD error = GetLastError(); UCHAR srb_status; // Why can't the damned ioctl return this??? // It's impossible to recover the original value because many // different codes are collapsed into the same Win32 error. Is // this even worth it? switch (error) { case 37: srb_status = SRB_STATUS_INVALID_TARGET_ID; break; case 79: srb_status = SRB_STATUS_TIMEOUT; break; case 1167: srb_status = SRB_STATUS_SELECTION_TIMEOUT; break; default: printf("DeviceIoControl: got error %d\n", error); srb_status = SRB_STATUS_BUS_RESET; // no clue here... break; } return MAKE_SCSIRESULT(srb_status, 0, 0, 0, 0); } } class MirrorInstance : public DvsDeviceUser, public DvsDeviceKernel { DvsDockingBay* bay; HANDLE hdevice; bool need_not_ready_to_ready; public: MirrorInstance(DvsDockingBay* _bay, HANDLE _hdevice) { static DvsDeviceUser_vtable vt = { 0, 0, StaticDelete, }; vtable = &vt; ScsiCommand = StaticScsiCommand; bay = _bay; hdevice = _hdevice; need_not_ready_to_ready = false; } ~MirrorInstance() { CloseHandle(hdevice); } scsi_result_t _ScsiCommand(const BYTE* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) { scsi_result_t result = SendScsiCommand(hdevice, cdb, cdblen, buffer, pbuflen, inout, sense); if (result == SCSIRESULT_SUCCESS) { if (need_not_ready_to_ready) { result = MAKE_SCSIRESULT_ERROR(0x62800); // NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED need_not_ready_to_ready = false; } } else if (result == MAKE_SCSIRESULT_ERROR(0x62800)) { need_not_ready_to_ready = false; } else if ((result & 0xFFFFFF00) == MAKE_SCSIRESULT_ERROR(0x23A00)) { need_not_ready_to_ready = true; } return result; } // scsi_result_t ScsiCommand(const BYTE* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) { // return g_callback->SendScsiCommand(ha, targ, lun, cdb, cdblen, buffer, pbuflen, inout, sense); // } static void StaticDelete(DvsDeviceUser* self) { delete (MirrorInstance*)self; } static scsi_result_t StaticScsiCommand(DvsDeviceKernel* self, const BYTE* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) { return ((MirrorInstance*)self)->_ScsiCommand(cdb, cdblen, buffer, pbuflen, inout, sense); } }; 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; } HANDLE OpenScsiDevice(const char* name) { char buf[32]; wsprintf(buf, "\\\\.\\%s", name); return CreateFile(buf, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); } void ScanDevice(const char* name) { if (g_num_devices >= MAX_DEVICES) return; HANDLE hdevice = OpenScsiDevice(name); if (hdevice == INVALID_HANDLE_VALUE) return; SCSI_ADDRESS sa; ULONG returned; memset(&sa, 0, sizeof(sa)); if (!DeviceIoControl(hdevice, IOCTL_SCSI_GET_ADDRESS, NULL, 0, &sa, sizeof(sa), &returned, NULL)) { CloseHandle(hdevice); return; } for (int n=0; nmask & 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: text = di.devname; break; case 3: if (di.path) { wsprintf(buf, "%d.%d:%d:%d", di.port, di.path, di.target, di.lun); } else { wsprintf(buf, "%d:%d:%d", di.port, 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) { 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; 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; } 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) { HANDLE hdevice = OpenScsiDevice(g_device_info[selection].devname); if (hdevice != INVALID_HANDLE_VALUE) { MirrorInstance* instance = new MirrorInstance(bay, hdevice); bay->vtable->SetHandlers(bay, instance, instance); } else { MessageBox(NULL, "Unable to connect to the drive!", "Mirror Drive", MB_OK); } } } 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; return &global_funcs; } extern "C" BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID) { g_hinstance = hinst; return TRUE; }