/** * Send an SCSI INQUIRY command to a device and return selected information. * @returns iprt status code * @returns VERR_TRY_AGAIN if the query failed but might succeed next time * @param pcszNode the full path to the device node * @param pu8Type where to store the SCSI device type on success (optional) * @param pchVendor where to store the vendor id string on success (optional) * @param cchVendor the size of the @a pchVendor buffer * @param pchModel where to store the product id string on success (optional) * @param cchModel the size of the @a pchModel buffer * @note check documentation on the SCSI INQUIRY command and the Linux kernel * SCSI headers included above if you want to understand what is going * on in this method. */ static int cdromDoInquiry(const char *pcszNode, uint8_t *pu8Type, char *pchVendor, size_t cchVendor, char *pchModel, size_t cchModel) { LogRelFlowFunc(("pcszNode=%s, pu8Type=%p, pchVendor=%p, cchVendor=%llu, pchModel=%p, cchModel=%llu\n", pcszNode, pu8Type, pchVendor, cchVendor, pchModel, cchModel)); AssertPtrReturn(pcszNode, VERR_INVALID_POINTER); AssertPtrNullReturn(pu8Type, VERR_INVALID_POINTER); AssertPtrNullReturn(pchVendor, VERR_INVALID_POINTER); AssertPtrNullReturn(pchModel, VERR_INVALID_POINTER); RTFILE hFile; int rc = RTFileOpen(&hFile, pcszNode, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK); if (RT_SUCCESS(rc)) { int rcIoCtl = 0; unsigned char u8Response[96] = { 0 }; struct cdrom_generic_command CdromCommandReq; RT_ZERO(CdromCommandReq); CdromCommandReq.cmd[0] = INQUIRY; CdromCommandReq.cmd[4] = sizeof(u8Response); CdromCommandReq.buffer = u8Response; CdromCommandReq.buflen = sizeof(u8Response); CdromCommandReq.data_direction = CGC_DATA_READ; CdromCommandReq.timeout = 5000; /* ms */ rc = RTFileIoCtl(hFile, CDROM_SEND_PACKET, &CdromCommandReq, 0, &rcIoCtl); if (RT_SUCCESS(rc) && rcIoCtl < 0) rc = RTErrConvertFromErrno(-CdromCommandReq.stat); RTFileClose(hFile); if (RT_SUCCESS(rc)) { if (pu8Type) *pu8Type = u8Response[0] & 0x1f; if (pchVendor) RTStrPrintf(pchVendor, cchVendor, "%.8s", &u8Response[8] /* vendor id string */); if (pchModel) RTStrPrintf(pchModel, cchModel, "%.16s", &u8Response[16] /* product id string */); LogRelFlowFunc(("returning success: type=%u, vendor=%.8s, product=%.16s\n", u8Response[0] & 0x1f, &u8Response[8], &u8Response[16])); return VINF_SUCCESS; } } LogRelFlowFunc(("returning %Rrc\n", rc)); return rc; }
/** * Get the name of a floppy drive according to the Linux floppy driver. * @returns true on success, false if the name was not available (i.e. the * device was not readable, or the file name wasn't a PC floppy * device) * @param pcszNode the path to the device node for the device * @param Number the Linux floppy driver number for the drive. Required. * @param pszName where to store the name retrieved */ static bool floppyGetName(const char *pcszNode, unsigned Number, floppy_drive_name pszName) { AssertPtrReturn(pcszNode, false); AssertPtrReturn(pszName, false); AssertReturn(Number <= 7, false); RTFILE File; int rc = RTFileOpen(&File, pcszNode, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK); if (RT_SUCCESS(rc)) { int rcIoCtl; rc = RTFileIoCtl(File, FDGETDRVTYP, pszName, 0, &rcIoCtl); RTFileClose(File); if (RT_SUCCESS(rc) && rcIoCtl >= 0) return true; } return false; }
/** * The client driver IOCtl Wrapper function. * * @returns VBox status code. * @param pDevSol The Solaris device instance. * @param Function The Function. * @param pvData Opaque pointer to the data. * @param cbData Size of the data pointed to by pvData. */ static int usbProxySolarisIOCtl(PUSBPROXYDEVSOL pDevSol, unsigned Function, void *pvData, size_t cbData) { if (RT_UNLIKELY(pDevSol->hFile == NIL_RTFILE)) { LogFlow((USBPROXY ":usbProxySolarisIOCtl connection to driver gone!\n")); return VERR_VUSB_DEVICE_NOT_ATTACHED; } VBOXUSBREQ Req; Req.u32Magic = VBOXUSB_MAGIC; Req.rc = -1; Req.cbData = cbData; Req.pvDataR3 = pvData; int Ret = -1; int rc = RTFileIoCtl(pDevSol->hFile, Function, &Req, sizeof(Req), &Ret); if (RT_SUCCESS(rc)) { if (RT_FAILURE(Req.rc)) { if (Req.rc == VERR_VUSB_DEVICE_NOT_ATTACHED) { pDevSol->pProxyDev->fDetached = true; usbProxySolarisCloseFile(pDevSol); LogRel((USBPROXY ":Command %#x failed, USB Device '%s' disconnected!\n", Function, pDevSol->pProxyDev->pUsbIns->pszName)); } else LogRel((USBPROXY ":Command %#x failed. Req.rc=%Rrc\n", Function, Req.rc)); } return Req.rc; } LogRel((USBPROXY ":Function %#x failed. rc=%Rrc\n", Function, rc)); return rc; }
/** * Internal wrapper around various OS specific ioctl implementations. * * @returns VBox status code as returned by VBoxGuestCommonIOCtl, or * an failure returned by the OS specific ioctl APIs. * * @param iFunction The requested function. * @param pvData The input and output data buffer. * @param cbData The size of the buffer. * * @remark Exactly how the VBoxGuestCommonIOCtl is ferried back * here is OS specific. On BSD and Darwin we can use errno, * while on OS/2 we use the 2nd buffer of the IOCtl. */ int vbglR3DoIOCtl(unsigned iFunction, void *pvData, size_t cbData) { #if defined(RT_OS_WINDOWS) DWORD cbReturned = 0; if (!DeviceIoControl(g_hFile, iFunction, pvData, (DWORD)cbData, pvData, (DWORD)cbData, &cbReturned, NULL)) { /** @todo The passing of error codes needs to be tested and fixed (as does *all* the other hosts except for * OS/2). The idea is that the VBox status codes in ring-0 should be transferred without loss down to * ring-3. However, it's not vitally important right now (obviously, since the other guys has been * ignoring it for 1+ years now). On Linux and Solaris the transfer is done, but it is currently not * lossless, so still needs fixing. */ DWORD LastErr = GetLastError(); return RTErrConvertFromWin32(LastErr); } return VINF_SUCCESS; #elif defined(RT_OS_OS2) ULONG cbOS2Parm = cbData; int32_t vrc = VERR_INTERNAL_ERROR; ULONG cbOS2Data = sizeof(vrc); APIRET rc = DosDevIOCtl((uintptr_t)g_File, VBOXGUEST_IOCTL_CATEGORY, iFunction, pvData, cbData, &cbOS2Parm, &vrc, sizeof(vrc), &cbOS2Data); if (RT_LIKELY(!rc)) return vrc; return RTErrConvertFromOS2(rc); #elif defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) VBGLBIGREQ Hdr; Hdr.u32Magic = VBGLBIGREQ_MAGIC; Hdr.cbData = cbData; Hdr.pvDataR3 = pvData; # if HC_ARCH_BITS == 32 Hdr.u32Padding = 0; # endif /** @todo test status code passing! Check that the kernel doesn't do any * error checks using specific errno values, and just pass an VBox * error instead of an errno.h one. Alternatively, extend/redefine the * header with an error code return field (much better alternative * actually). */ #ifdef VBOX_VBGLR3_XFREE86 int rc = xf86ioctl(g_File, iFunction, &Hdr); #else int rc = ioctl(RTFileToNative(g_File), iFunction, &Hdr); #endif if (rc == -1) { rc = errno; return RTErrConvertFromErrno(rc); } return VINF_SUCCESS; #elif defined(RT_OS_LINUX) # ifdef VBOX_VBGLR3_XFREE86 int rc = xf86ioctl((int)g_File, iFunction, pvData); # else if (g_File == NIL_RTFILE) return VERR_INVALID_HANDLE; int rc = ioctl(RTFileToNative(g_File), iFunction, pvData); # endif if (RT_LIKELY(rc == 0)) return VINF_SUCCESS; /* Positive values are negated VBox error status codes. */ if (rc > 0) rc = -rc; else # ifdef VBOX_VBGLR3_XFREE86 rc = VERR_FILE_IO_ERROR; # else rc = RTErrConvertFromErrno(errno); # endif NOREF(cbData); return rc; #elif defined(VBOX_VBGLR3_XFREE86) /* PORTME - This is preferred over the RTFileIOCtl variant below, just be careful with the (int). */ /** @todo test status code passing! */ int rc = xf86ioctl(g_File, iFunction, pvData); if (rc == -1) return VERR_FILE_IO_ERROR; /* This is purely legacy stuff, it has to work and no more. */ return VINF_SUCCESS; #else /* Default implementation - PORTME: Do not use this without testings that passing errors works! */ /** @todo test status code passing! */ int rc2 = VERR_INTERNAL_ERROR; int rc = RTFileIoCtl(g_File, (int)iFunction, pvData, cbData, &rc2); if (RT_SUCCESS(rc)) rc = rc2; return rc; #endif }
int main() { #ifdef RT_OS_L4 __environ = myenv; #endif int rcRet = 0; int ret, err; printf("tstIoCtl: TESTING\n"); RTFILE File; err = RTFileOpen(&File, "/dev/dsp", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK); if (RT_FAILURE(err)) { printf("Fatal error: failed to open /dev/dsp:\n" "VBox error code: %d\n", err); return 1; } int rate = 100; if (RT_FAILURE(err = RTFileIoCtl(File, SNDCTL_DSP_SPEED, &rate, sizeof(rate), &ret)) || ret) { printf("Failed to set playback speed on /dev/dsp\n" "VBox error code: %d, IOCTL return value: %d\n", err, ret); rcRet++; } else printf("Playback speed successfully set to 100, reported speed is %d\n", rate); rate = 48000; if (RT_FAILURE(err = RTFileIoCtl(File, SNDCTL_DSP_SPEED, &rate, sizeof(rate), &ret)) || ret) { printf("Failed to set playback speed on /dev/dsp\n" "VBox error code: %d, IOCTL return value: %d\n", err, ret); rcRet++; } else printf("Playback speed successfully set to 48000, reported speed is %d\n", rate); /* * Cleanup. */ ret = RTFileClose(File); if (RT_FAILURE(ret)) { printf("Failed to close /dev/dsp. ret=%d\n", ret); rcRet++; } /* Under Linux and L4, this involves ioctls internally */ RTUUID TestUuid; if (RT_FAILURE(RTUuidCreate(&TestUuid))) { printf("Failed to create a UUID. ret=%d\n", ret); rcRet++; } /* * Summary */ if (rcRet == 0) printf("tstIoCtl: SUCCESS\n"); else printf("tstIoCtl: FAILURE - %d errors\n", rcRet); return rcRet; }
/** * Search for available CD/DVD drives using the CAM layer. * * @returns iprt status code * @param pList the list to append the drives found to * @param pfSuccess this will be set to true if we found at least one drive * and to false otherwise. Optional. */ static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess) { int rc = VINF_SUCCESS; RTFILE FileXpt; rc = RTFileOpen(&FileXpt, "/dev/xpt0", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); if (RT_SUCCESS(rc)) { union ccb DeviceCCB; struct dev_match_pattern DeviceMatchPattern; struct dev_match_result *paMatches = NULL; RT_ZERO(DeviceCCB); RT_ZERO(DeviceMatchPattern); /* We want to get all devices. */ DeviceCCB.ccb_h.func_code = XPT_DEV_MATCH; DeviceCCB.ccb_h.path_id = CAM_XPT_PATH_ID; DeviceCCB.ccb_h.target_id = CAM_TARGET_WILDCARD; DeviceCCB.ccb_h.target_lun = CAM_LUN_WILDCARD; /* Setup the pattern */ DeviceMatchPattern.type = DEV_MATCH_DEVICE; DeviceMatchPattern.pattern.device_pattern.path_id = CAM_XPT_PATH_ID; DeviceMatchPattern.pattern.device_pattern.target_id = CAM_TARGET_WILDCARD; DeviceMatchPattern.pattern.device_pattern.target_lun = CAM_LUN_WILDCARD; DeviceMatchPattern.pattern.device_pattern.flags = DEV_MATCH_INQUIRY; #if __FreeBSD_version >= 900000 # define INQ_PAT data.inq_pat #else #define INQ_PAT inq_pat #endif DeviceMatchPattern.pattern.device_pattern.INQ_PAT.type = T_CDROM; DeviceMatchPattern.pattern.device_pattern.INQ_PAT.media_type = SIP_MEDIA_REMOVABLE | SIP_MEDIA_FIXED; DeviceMatchPattern.pattern.device_pattern.INQ_PAT.vendor[0] = '*'; /* Matches anything */ DeviceMatchPattern.pattern.device_pattern.INQ_PAT.product[0] = '*'; /* Matches anything */ DeviceMatchPattern.pattern.device_pattern.INQ_PAT.revision[0] = '*'; /* Matches anything */ #undef INQ_PAT DeviceCCB.cdm.num_patterns = 1; DeviceCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result); DeviceCCB.cdm.patterns = &DeviceMatchPattern; /* * Allocate the buffer holding the matches. * We will allocate for 10 results and call * CAM multiple times if we have more results. */ paMatches = (struct dev_match_result *)RTMemAllocZ(10 * sizeof(struct dev_match_result)); if (paMatches) { DeviceCCB.cdm.num_matches = 0; DeviceCCB.cdm.match_buf_len = 10 * sizeof(struct dev_match_result); DeviceCCB.cdm.matches = paMatches; do { rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &DeviceCCB, sizeof(union ccb), NULL); if (RT_FAILURE(rc)) { Log(("Error while querying available CD/DVD devices rc=%Rrc\n", rc)); break; } for (unsigned i = 0; i < DeviceCCB.cdm.num_matches; i++) { if (paMatches[i].type == DEV_MATCH_DEVICE) { /* We have the drive now but need the appropriate device node */ struct device_match_result *pDevResult = &paMatches[i].result.device_result; union ccb PeriphCCB; struct dev_match_pattern PeriphMatchPattern; struct dev_match_result aPeriphMatches[2]; struct periph_match_result *pPeriphResult = NULL; unsigned iPeriphMatch = 0; RT_ZERO(PeriphCCB); RT_ZERO(PeriphMatchPattern); RT_ZERO(aPeriphMatches); /* This time we only want the specific nodes for the device. */ PeriphCCB.ccb_h.func_code = XPT_DEV_MATCH; PeriphCCB.ccb_h.path_id = paMatches[i].result.device_result.path_id; PeriphCCB.ccb_h.target_id = paMatches[i].result.device_result.target_id; PeriphCCB.ccb_h.target_lun = paMatches[i].result.device_result.target_lun; /* Setup the pattern */ PeriphMatchPattern.type = DEV_MATCH_PERIPH; PeriphMatchPattern.pattern.periph_pattern.path_id = paMatches[i].result.device_result.path_id; PeriphMatchPattern.pattern.periph_pattern.target_id = paMatches[i].result.device_result.target_id; PeriphMatchPattern.pattern.periph_pattern.target_lun = paMatches[i].result.device_result.target_lun; PeriphMatchPattern.pattern.periph_pattern.flags = PERIPH_MATCH_PATH | PERIPH_MATCH_TARGET | PERIPH_MATCH_LUN; PeriphCCB.cdm.num_patterns = 1; PeriphCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result); PeriphCCB.cdm.patterns = &PeriphMatchPattern; PeriphCCB.cdm.num_matches = 0; PeriphCCB.cdm.match_buf_len = sizeof(aPeriphMatches); PeriphCCB.cdm.matches = aPeriphMatches; do { rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &PeriphCCB, sizeof(union ccb), NULL); if (RT_FAILURE(rc)) { Log(("Error while querying available periph devices rc=%Rrc\n", rc)); break; } for (iPeriphMatch = 0; iPeriphMatch < PeriphCCB.cdm.num_matches; iPeriphMatch++) { if ( (aPeriphMatches[iPeriphMatch].type == DEV_MATCH_PERIPH) && (!strcmp(aPeriphMatches[iPeriphMatch].result.periph_result.periph_name, "cd"))) { pPeriphResult = &aPeriphMatches[iPeriphMatch].result.periph_result; break; /* We found the periph device */ } } if (iPeriphMatch < PeriphCCB.cdm.num_matches) break; } while ( (DeviceCCB.ccb_h.status == CAM_REQ_CMP) && (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE)); if (pPeriphResult) { char szPath[RTPATH_MAX]; char szDesc[256]; RTStrPrintf(szPath, sizeof(szPath), "/dev/%s%d", pPeriphResult->periph_name, pPeriphResult->unit_number); /* Remove trailing white space. */ strLenRemoveTrailingWhiteSpace(pDevResult->inq_data.vendor, sizeof(pDevResult->inq_data.vendor)); strLenRemoveTrailingWhiteSpace(pDevResult->inq_data.product, sizeof(pDevResult->inq_data.product)); dvdCreateDeviceString(pDevResult->inq_data.vendor, pDevResult->inq_data.product, szDesc, sizeof(szDesc)); pList->push_back(DriveInfo(szPath, "", szDesc)); if (pfSuccess) *pfSuccess = true; } } } } while ( (DeviceCCB.ccb_h.status == CAM_REQ_CMP) && (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE)); RTMemFree(paMatches); } else rc = VERR_NO_MEMORY; RTFileClose(FileXpt); } return rc; }