summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp384
1 files changed, 384 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp b/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp
new file mode 100644
index 00000000..219aa69c
--- /dev/null
+++ b/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp
@@ -0,0 +1,384 @@
+/* $Id: HostHardwareFreeBSD.cpp $ */
+/** @file
+ * Classes for handling hardware detection under FreeBSD.
+ */
+
+/*
+ * Copyright (C) 2008-2020 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+
+#include <HostHardwareLinux.h>
+
+#include <VBox/log.h>
+
+#include <iprt/dir.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+#ifdef RT_OS_FREEBSD
+# include <sys/param.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+# include <stdio.h>
+# include <sys/ioctl.h>
+# include <fcntl.h>
+# include <cam/cam.h>
+# include <cam/cam_ccb.h>
+# include <cam/scsi/scsi_pass.h>
+#endif /* RT_OS_FREEBSD */
+#include <vector>
+
+
+/*********************************************************************************************************************************
+* Typedefs and Defines *
+*********************************************************************************************************************************/
+
+static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
+ bool isDVD, bool *pfSuccess);
+static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess);
+
+/** Find the length of a string, ignoring trailing non-ascii or control
+ * characters */
+static size_t strLenStripped(const char *pcsz)
+{
+ size_t cch = 0;
+ for (size_t i = 0; pcsz[i] != '\0'; ++i)
+ if (pcsz[i] > 32 && pcsz[i] < 127)
+ cch = i;
+ return cch + 1;
+}
+
+static void strLenRemoveTrailingWhiteSpace(char *psz, size_t cchStr)
+{
+ while ( (cchStr > 0)
+ && (psz[cchStr -1] == ' '))
+ psz[--cchStr] = '\0';
+}
+
+/**
+ * Initialise the device description for a DVD drive based on
+ * vendor and model name strings.
+ * @param pcszVendor the vendor ID string
+ * @param pcszModel the product ID string
+ * @param pszDesc where to store the description string (optional)
+ * @param cchDesc the size of the buffer in @pszDesc
+ */
+/* static */
+void dvdCreateDeviceString(const char *pcszVendor, const char *pcszModel,
+ char *pszDesc, size_t cchDesc)
+{
+ AssertPtrReturnVoid(pcszVendor);
+ AssertPtrReturnVoid(pcszModel);
+ AssertPtrNullReturnVoid(pszDesc);
+ AssertReturnVoid(!pszDesc || cchDesc > 0);
+ size_t cchVendor = strLenStripped(pcszVendor);
+ size_t cchModel = strLenStripped(pcszModel);
+
+ /* Construct the description string as "Vendor Product" */
+ if (pszDesc)
+ {
+ if (cchVendor > 0)
+ RTStrPrintf(pszDesc, cchDesc, "%.*s %s", cchVendor, pcszVendor,
+ cchModel > 0 ? pcszModel : "(unknown drive model)");
+ else
+ RTStrPrintf(pszDesc, cchDesc, "%s", pcszModel);
+ }
+}
+
+
+int VBoxMainDriveInfo::updateDVDs ()
+{
+ LogFlowThisFunc(("entered\n"));
+ int rc = VINF_SUCCESS;
+ bool fSuccess = false; /* Have we succeeded in finding anything yet? */
+
+ try
+ {
+ mDVDList.clear ();
+ /* Always allow the user to override our auto-detection using an
+ * environment variable. */
+ if (RT_SUCCESS(rc) && !fSuccess)
+ rc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */,
+ &fSuccess);
+ if (RT_SUCCESS(rc) && !fSuccess)
+ rc = getDVDInfoFromCAM(&mDVDList, &fSuccess);
+ }
+ catch(std::bad_alloc &e)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ LogFlowThisFunc(("rc=%Rrc\n", rc));
+ return rc;
+}
+
+int VBoxMainDriveInfo::updateFloppies ()
+{
+ LogFlowThisFunc(("entered\n"));
+ int rc = VINF_SUCCESS;
+ bool fSuccess = false; /* Have we succeeded in finding anything yet? */
+
+ try
+ {
+ mFloppyList.clear ();
+ /* Always allow the user to override our auto-detection using an
+ * environment variable. */
+ if (RT_SUCCESS(rc) && !fSuccess)
+ rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */,
+ &fSuccess);
+ }
+ catch(std::bad_alloc &e)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ LogFlowThisFunc(("rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * 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_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;
+}
+
+/**
+ * Extract the names of drives from an environment variable and add them to a
+ * list if they are valid.
+ * @returns iprt status code
+ * @param pcszVar the name of the environment variable. The variable
+ * value should be a list of device node names, separated
+ * by ':' characters.
+ * @param pList the list to append the drives found to
+ * @param isDVD are we looking for DVD drives or for floppies?
+ * @param pfSuccess this will be set to true if we found at least one drive
+ * and to false otherwise. Optional.
+ */
+static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
+ bool isDVD, bool *pfSuccess)
+{
+ AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
+ AssertPtrReturn(pList, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
+ LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar,
+ pList, isDVD, pfSuccess));
+ int rc = VINF_SUCCESS;
+ bool success = false;
+ char *pszFreeMe = RTEnvDupEx(RTENV_DEFAULT, pcszVar);
+
+ try
+ {
+ const char *pcszCurrent = pszFreeMe;
+ while (pcszCurrent && *pcszCurrent != '\0')
+ {
+ const char *pcszNext = strchr(pcszCurrent, ':');
+ char szPath[RTPATH_MAX], szReal[RTPATH_MAX];
+ char szDesc[256], szUdi[256];
+ if (pcszNext)
+ RTStrPrintf(szPath, sizeof(szPath), "%.*s",
+ pcszNext - pcszCurrent - 1, pcszCurrent);
+ else
+ RTStrPrintf(szPath, sizeof(szPath), "%s", pcszCurrent);
+ if (RT_SUCCESS(RTPathReal(szPath, szReal, sizeof(szReal))))
+ {
+ szUdi[0] = '\0'; /** @todo r=bird: missing a call to devValidateDevice() here and szUdi wasn't
+ * initialized because of that. Need proper fixing. */
+ pList->push_back(DriveInfo(szReal, szUdi, szDesc));
+ success = true;
+ }
+ pcszCurrent = pcszNext ? pcszNext + 1 : NULL;
+ }
+ if (pfSuccess != NULL)
+ *pfSuccess = success;
+ }
+ catch(std::bad_alloc &e)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ RTStrFree(pszFreeMe);
+ LogFlowFunc(("rc=%Rrc, success=%d\n", rc, success));
+ return rc;
+}