From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- .../src-server/freebsd/HostHardwareFreeBSD.cpp | 560 +++++++++++++++++++++ src/VBox/Main/src-server/freebsd/Makefile.kup | 0 src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp | 458 +++++++++++++++++ .../Main/src-server/freebsd/PerformanceFreeBSD.cpp | 128 +++++ .../src-server/freebsd/USBProxyBackendFreeBSD.cpp | 353 +++++++++++++ 5 files changed, 1499 insertions(+) create mode 100644 src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp create mode 100644 src/VBox/Main/src-server/freebsd/Makefile.kup create mode 100644 src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp create mode 100644 src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp create mode 100644 src/VBox/Main/src-server/freebsd/USBProxyBackendFreeBSD.cpp (limited to 'src/VBox/Main/src-server/freebsd') 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..546810b8 --- /dev/null +++ b/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp @@ -0,0 +1,560 @@ +/* $Id: HostHardwareFreeBSD.cpp $ */ +/** @file + * VirtualBox Main - Code for handling hardware detection under FreeBSD, VBoxSVC. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * 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, in version 3 of the + * License. + * + * 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, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN +#include "HostHardwareLinux.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/********************************************************************************************************************************* +* Typedefs and Defines * +*********************************************************************************************************************************/ +typedef enum DriveType_T +{ + Fixed, + DVD, + Any +} DriveType_T; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_DEF; +static int getDriveInfoFromCAM(DriveInfoList *pList, DriveType_T enmDriveType, bool *pfSuccess) RT_NOTHROW_DEF; + + +/** Find the length of a string, ignoring trailing non-ascii or control + * characters + * @note Code duplicated in HostHardwareLinux.cpp */ +static size_t strLenStripped(const char *pcsz) RT_NOTHROW_DEF +{ + size_t cch = 0; + for (size_t i = 0; pcsz[i] != '\0'; ++i) + if (pcsz[i] > 32 /*space*/ && pcsz[i] < 127 /*delete*/) + cch = i; + return cch + 1; +} + + +/** + * Initialize the device description for a drive based on vendor and model name + * strings. + * + * @param pcszVendor The raw vendor ID string. + * @param pcszModel The raw product ID string. + * @param pszDesc Where to store the description string (optional) + * @param cbDesc The size of the buffer in @pszDesc + * + * @note Used for disks as well as DVDs. + */ +/* static */ +void dvdCreateDeviceString(const char *pcszVendor, const char *pcszModel, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF +{ + AssertPtrReturnVoid(pcszVendor); + AssertPtrReturnVoid(pcszModel); + AssertPtrNullReturnVoid(pszDesc); + AssertReturnVoid(!pszDesc || cbDesc > 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, cbDesc, "%.*s %s", cchVendor, pcszVendor, + cchModel > 0 ? pcszModel : "(unknown drive model)"); + else + RTStrPrintf(pszDesc, cbDesc, "%s", pcszModel); + RTStrPurgeEncoding(pszDesc); + } +} + + +int VBoxMainDriveInfo::updateDVDs() RT_NOEXCEPT +{ + LogFlowThisFunc(("entered\n")); + int vrc; + try + { + mDVDList.clear(); + /* Always allow the user to override our auto-detection using an + * environment variable. */ + bool fSuccess = false; /* Have we succeeded in finding anything yet? */ + vrc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */, &fSuccess); + if (RT_SUCCESS(vrc) && !fSuccess) + vrc = getDriveInfoFromCAM(&mDVDList, DVD, &fSuccess); + } + catch (std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; + } + LogFlowThisFunc(("vrc=%Rrc\n", vrc)); + return vrc; +} + +int VBoxMainDriveInfo::updateFloppies() RT_NOEXCEPT +{ + LogFlowThisFunc(("entered\n")); + int vrc; + try + { + /* Only got the enviornment variable here... */ + mFloppyList.clear(); + bool fSuccess = false; /* ignored */ + vrc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */, &fSuccess); + } + catch (std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; + } + LogFlowThisFunc(("vrc=%Rrc\n", vrc)); + return vrc; +} + +int VBoxMainDriveInfo::updateFixedDrives() RT_NOEXCEPT +{ + LogFlowThisFunc(("entered\n")); + int vrc; + try + { + mFixedDriveList.clear(); + bool fSuccess = false; /* ignored */ + vrc = getDriveInfoFromCAM(&mFixedDriveList, Fixed, &fSuccess); + } + catch (std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; + } + LogFlowThisFunc(("vrc=%Rrc\n", vrc)); + return vrc; +} + +static void strDeviceStringSCSI(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF +{ + char szVendor[128]; + cam_strvis((uint8_t *)szVendor, (const uint8_t *)pDevResult->inq_data.vendor, + sizeof(pDevResult->inq_data.vendor), sizeof(szVendor)); + char szProduct[128]; + cam_strvis((uint8_t *)szProduct, (const uint8_t *)pDevResult->inq_data.product, + sizeof(pDevResult->inq_data.product), sizeof(szProduct)); + dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc); +} + +static void strDeviceStringATA(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF +{ + char szProduct[256]; + cam_strvis((uint8_t *)szProduct, (const uint8_t *)pDevResult->ident_data.model, + sizeof(pDevResult->ident_data.model), sizeof(szProduct)); + dvdCreateDeviceString("", szProduct, pszDesc, cbDesc); +} + +static void strDeviceStringSEMB(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF +{ + sep_identify_data *pSid = (sep_identify_data *)&pDevResult->ident_data; + + char szVendor[128]; + cam_strvis((uint8_t *)szVendor, (const uint8_t *)pSid->vendor_id, + sizeof(pSid->vendor_id), sizeof(szVendor)); + char szProduct[128]; + cam_strvis((uint8_t *)szProduct, (const uint8_t *)pSid->product_id, + sizeof(pSid->product_id), sizeof(szProduct)); + dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc); +} + +static void strDeviceStringMMCSD(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF +{ + struct cam_device *pDev = cam_open_btl(pDevResult->path_id, pDevResult->target_id, + pDevResult->target_lun, O_RDWR, NULL); + if (pDev == NULL) + { + Log(("Error while opening drive device. Error: %s\n", cam_errbuf)); + return; + } + + union ccb *pCcb = cam_getccb(pDev); + if (pCcb != NULL) + { + struct mmc_params mmcIdentData; + RT_ZERO(mmcIdentData); + + struct ccb_dev_advinfo *pAdvi = &pCcb->cdai; + pAdvi->ccb_h.flags = CAM_DIR_IN; + pAdvi->ccb_h.func_code = XPT_DEV_ADVINFO; + pAdvi->flags = CDAI_FLAG_NONE; + pAdvi->buftype = CDAI_TYPE_MMC_PARAMS; + pAdvi->bufsiz = sizeof(mmcIdentData); + pAdvi->buf = (uint8_t *)&mmcIdentData; + + if (cam_send_ccb(pDev, pCcb) >= 0) + { + if (strlen((char *)mmcIdentData.model) > 0) + dvdCreateDeviceString("", (const char *)mmcIdentData.model, pszDesc, cbDesc); + else + dvdCreateDeviceString("", mmcIdentData.card_features & CARD_FEATURE_SDIO ? "SDIO card" : "Unknown card", + pszDesc, cbDesc); + } + else + Log(("error sending XPT_DEV_ADVINFO CCB\n")); + + cam_freeccb(pCcb); + } + else + Log(("Could not allocate CCB\n")); + cam_close_device(pDev); +} + +/** @returns boolean success indicator (true/false). */ +static int nvmeGetCData(struct cam_device *pDev, struct nvme_controller_data *pCData) RT_NOTHROW_DEF +{ + bool fSuccess = false; + union ccb *pCcb = cam_getccb(pDev); + if (pCcb != NULL) + { + struct ccb_dev_advinfo *pAdvi = &pCcb->cdai; + pAdvi->ccb_h.flags = CAM_DIR_IN; + pAdvi->ccb_h.func_code = XPT_DEV_ADVINFO; + pAdvi->flags = CDAI_FLAG_NONE; + pAdvi->buftype = CDAI_TYPE_NVME_CNTRL; + pAdvi->bufsiz = sizeof(struct nvme_controller_data); + pAdvi->buf = (uint8_t *)pCData; + RT_BZERO(pAdvi->buf, pAdvi->bufsiz); + + if (cam_send_ccb(pDev, pCcb) >= 0) + { + if (pAdvi->ccb_h.status == CAM_REQ_CMP) + fSuccess = true; + else + Log(("Got CAM error %#x\n", pAdvi->ccb_h.status)); + } + else + Log(("Error sending XPT_DEV_ADVINFO CC\n")); + cam_freeccb(pCcb); + } + else + Log(("Could not allocate CCB\n")); + return fSuccess; +} + +static void strDeviceStringNVME(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF +{ + struct cam_device *pDev = cam_open_btl(pDevResult->path_id, pDevResult->target_id, + pDevResult->target_lun, O_RDWR, NULL); + if (pDev) + { + struct nvme_controller_data CData; + if (nvmeGetCData(pDev, &CData)) + { + char szVendor[128]; + cam_strvis((uint8_t *)szVendor, CData.mn, sizeof(CData.mn), sizeof(szVendor)); + char szProduct[128]; + cam_strvis((uint8_t *)szProduct, CData.fr, sizeof(CData.fr), sizeof(szProduct)); + dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc); + } + else + Log(("Error while getting NVME drive info\n")); + cam_close_device(pDev); + } + else + Log(("Error while opening drive device. Error: %s\n", cam_errbuf)); +} + + +/** + * Search for available drives using the CAM layer. + * + * @returns iprt status code + * @param pList the list to append the drives found to + * @param enmDriveType search drives of specified type + * @param pfSuccess this will be set to true if we found at least one drive + * and to false otherwise. Optional. + */ +static int getDriveInfoFromCAM(DriveInfoList *pList, DriveType_T enmDriveType, bool *pfSuccess) RT_NOTHROW_DEF +{ + RTFILE hFileXpt = NIL_RTFILE; + int vrc = RTFileOpen(&hFileXpt, "/dev/xpt0", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); + if (RT_SUCCESS(vrc)) + { + 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 = enmDriveType == Fixed ? T_DIRECT + : enmDriveType == DVD ? T_CDROM : T_ANY; + 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 + { + vrc = RTFileIoCtl(hFileXpt, CAMIOCOMMAND, &DeviceCCB, sizeof(union ccb), NULL); + if (RT_FAILURE(vrc)) + { + Log(("Error while querying available CD/DVD devices vrc=%Rrc\n", vrc)); + break; + } + + for (unsigned i = 0; i < DeviceCCB.cdm.num_matches; i++) + { + if (paMatches[i].type == DEV_MATCH_DEVICE) + { + /* + * The result list can contain some empty entries with DEV_RESULT_UNCONFIGURED + * flag set, e.g. in case of T_DIRECT. Ignore them. + */ + if ( (paMatches[i].result.device_result.flags & DEV_RESULT_UNCONFIGURED) + == DEV_RESULT_UNCONFIGURED) + continue; + + /* 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 + { + vrc = RTFileIoCtl(hFileXpt, CAMIOCOMMAND, &PeriphCCB, sizeof(union ccb), NULL); + if (RT_FAILURE(vrc)) + { + Log(("Error while querying available periph devices vrc=%Rrc\n", vrc)); + break; + } + + for (iPeriphMatch = 0; iPeriphMatch < PeriphCCB.cdm.num_matches; iPeriphMatch++) + { + /* Ignore "passthrough mode" paths */ + if ( aPeriphMatches[iPeriphMatch].type == DEV_MATCH_PERIPH + && strcmp(aPeriphMatches[iPeriphMatch].result.periph_result.periph_name, "pass")) + { + 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]; + RTStrPrintf(szPath, sizeof(szPath), "/dev/%s%d", + pPeriphResult->periph_name, pPeriphResult->unit_number); + + char szDesc[256] = { 0 }; + switch (pDevResult->protocol) + { + case PROTO_SCSI: strDeviceStringSCSI( pDevResult, szDesc, sizeof(szDesc)); break; + case PROTO_ATA: strDeviceStringATA( pDevResult, szDesc, sizeof(szDesc)); break; + case PROTO_MMCSD: strDeviceStringMMCSD(pDevResult, szDesc, sizeof(szDesc)); break; + case PROTO_SEMB: strDeviceStringSEMB( pDevResult, szDesc, sizeof(szDesc)); break; + case PROTO_NVME: strDeviceStringNVME( pDevResult, szDesc, sizeof(szDesc)); break; + default: break; + } + + try + { + pList->push_back(DriveInfo(szPath, "", szDesc)); + } + catch (std::bad_alloc &) + { + pList->clear(); + vrc = VERR_NO_MEMORY; + break; + } + if (pfSuccess) + *pfSuccess = true; + } + } + } + } while ( DeviceCCB.ccb_h.status == CAM_REQ_CMP + && DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE + && RT_SUCCESS(vrc)); + + RTMemFree(paMatches); + } + else + vrc = VERR_NO_MEMORY; + + RTFileClose(hFileXpt); + } + + return vrc; +} + + +/** + * 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. + * + * @note This is duplicated in HostHardwareLinux.cpp. + */ +static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_DEF +{ + 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 vrc = VINF_SUCCESS; + bool success = false; + char *pszFreeMe = RTEnvDupEx(RTENV_DEFAULT, pcszVar); + + try + { + char *pszCurrent = pszFreeMe; + while (pszCurrent && *pszCurrent != '\0') + { + char *pszNext = strchr(pszCurrent, ':'); + if (pszNext) + *pszNext++ = '\0'; + + char szReal[RTPATH_MAX]; + char szDesc[1] = "", szUdi[1] = ""; /* differs on freebsd because no devValidateDevice */ + if ( RT_SUCCESS(RTPathReal(pszCurrent, szReal, sizeof(szReal))) + /*&& devValidateDevice(szReal, isDVD, NULL, szDesc, sizeof(szDesc), szUdi, sizeof(szUdi)) - linux only */) + { + pList->push_back(DriveInfo(szReal, szUdi, szDesc)); + success = true; + } + pszCurrent = pszNext; + } + if (pfSuccess != NULL) + *pfSuccess = success; + } + catch (std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; + } + RTStrFree(pszFreeMe); + LogFlowFunc(("vrc=%Rrc, success=%d\n", vrc, success)); + return vrc; +} + diff --git a/src/VBox/Main/src-server/freebsd/Makefile.kup b/src/VBox/Main/src-server/freebsd/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp b/src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp new file mode 100644 index 00000000..68fe2485 --- /dev/null +++ b/src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp @@ -0,0 +1,458 @@ +/* $Id: NetIf-freebsd.cpp $ */ +/** @file + * Main - NetIfList, FreeBSD implementation. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * 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, in version 3 of the + * License. + * + * 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, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +/* + * Original (C) 2009 Fredrik Lindberg . Contributed + * to VirtualBox under the MIT license by the author. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN_HOST +#include + +#include +#include +#include +#include +#include +#include + +#include +/* + * route.h includes net/radix.h which for some reason defines Free as a wrapper + * around free. This collides with Free defined in xpcom/include/nsIMemory.h + * Undefine it and hope for the best + */ +#undef Free + +#include +#include + +#include +#include +#include +#include + +#include + +#include "HostNetworkInterfaceImpl.h" +#include "netif.h" +#include "LoggingNew.h" + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +void extractAddresses(int iAddrMask, caddr_t cp, caddr_t cplim, struct sockaddr **pAddresses) +{ + struct sockaddr *sa; + + for (int i = 0; i < RTAX_MAX && cp < cplim; i++) { + if (!(iAddrMask & (1 << i))) + continue; + + sa = (struct sockaddr *)cp; + + pAddresses[i] = sa; + + ADVANCE(cp, sa); + } +} + +static int getDefaultIfaceIndex(unsigned short *pu16Index, int family) +{ + size_t cbNeeded; + char *pBuf, *pNext; + int aiMib[6]; + struct sockaddr *addresses[RTAX_MAX]; + aiMib[0] = CTL_NET; + aiMib[1] = PF_ROUTE; + aiMib[2] = 0; + aiMib[3] = family; /* address family */ + aiMib[4] = NET_RT_DUMP; + aiMib[5] = 0; + + if (sysctl(aiMib, 6, NULL, &cbNeeded, NULL, 0) < 0) + { + Log(("getDefaultIfaceIndex: Failed to get estimate for list size (errno=%d).\n", errno)); + return RTErrConvertFromErrno(errno); + } + if ((pBuf = (char*)malloc(cbNeeded)) == NULL) + return VERR_NO_MEMORY; + if (sysctl(aiMib, 6, pBuf, &cbNeeded, NULL, 0) < 0) + { + free(pBuf); + Log(("getDefaultIfaceIndex: Failed to retrieve interface table (errno=%d).\n", errno)); + return RTErrConvertFromErrno(errno); + } + + char *pEnd = pBuf + cbNeeded; + struct rt_msghdr *pRtMsg; + for (pNext = pBuf; pNext < pEnd; pNext += pRtMsg->rtm_msglen) + { + pRtMsg = (struct rt_msghdr *)pNext; + + if (pRtMsg->rtm_type != RTM_GET) + { + Log(("getDefaultIfaceIndex: Got message %u while expecting %u.\n", + pRtMsg->rtm_type, RTM_GET)); + //vrc = VERR_INTERNAL_ERROR; + continue; + } + if ((char*)(pRtMsg + 1) < pEnd) + { + /* Extract addresses from the message. */ + extractAddresses(pRtMsg->rtm_addrs, (char *)(pRtMsg + 1), + pRtMsg->rtm_msglen + (char *)pRtMsg, addresses); + if ((pRtMsg->rtm_addrs & RTA_DST)) + { + if (addresses[RTAX_DST]->sa_family != AF_INET) + continue; + struct sockaddr_in *addr = (struct sockaddr_in *)addresses[RTAX_DST]; + struct sockaddr_in *mask = (struct sockaddr_in *)addresses[RTAX_NETMASK]; + if ((addr->sin_addr.s_addr == INADDR_ANY) && + mask && + (ntohl(mask->sin_addr.s_addr) == 0L || + mask->sin_len == 0)) + { + *pu16Index = pRtMsg->rtm_index; + free(pBuf); + return VINF_SUCCESS; + } + } + } + } + free(pBuf); + return VERR_INTERNAL_ERROR; + +} + +void extractAddressesToNetInfo(int iAddrMask, caddr_t cp, caddr_t cplim, PNETIFINFO pInfo) +{ + struct sockaddr *addresses[RTAX_MAX]; + + extractAddresses(iAddrMask, cp, cplim, addresses); + switch (addresses[RTAX_IFA]->sa_family) + { + case AF_INET: + if (!pInfo->IPAddress.u) + { + pInfo->IPAddress.u = ((struct sockaddr_in *)addresses[RTAX_IFA])->sin_addr.s_addr; + pInfo->IPNetMask.u = ((struct sockaddr_in *)addresses[RTAX_NETMASK])->sin_addr.s_addr; + } + break; + case AF_INET6: + if (!pInfo->IPv6Address.s.Lo && !pInfo->IPv6Address.s.Hi) + { + memcpy(pInfo->IPv6Address.au8, + ((struct sockaddr_in6 *)addresses[RTAX_IFA])->sin6_addr.__u6_addr.__u6_addr8, + sizeof(pInfo->IPv6Address)); + memcpy(pInfo->IPv6NetMask.au8, + ((struct sockaddr_in6 *)addresses[RTAX_NETMASK])->sin6_addr.__u6_addr.__u6_addr8, + sizeof(pInfo->IPv6NetMask)); + } + break; + default: + Log(("NetIfList: Unsupported address family: %u\n", addresses[RTAX_IFA]->sa_family)); + break; + } +} + + +static bool isWireless(const char *pszName) +{ + bool fWireless = false; + int iSock = socket(AF_INET, SOCK_DGRAM, 0); + if (iSock >= 0) + { + struct ieee80211req WReq; + uint8_t abData[32]; + + RT_ZERO(WReq); + strncpy(WReq.i_name, pszName, sizeof(WReq.i_name)); + WReq.i_type = IEEE80211_IOC_SSID; + WReq.i_val = -1; + WReq.i_data = abData; + WReq.i_len = sizeof(abData); + + fWireless = ioctl(iSock, SIOCG80211, &WReq) >= 0; + close(iSock); + } + + return fWireless; +} + +int NetIfList(std::list > &list) +{ + int vrc = VINF_SUCCESS; + size_t cbNeeded; + char *pBuf, *pNext; + int aiMib[6]; + unsigned short u16DefaultIface = 0; /* shut up gcc. */ + bool fDefaultIfaceExistent = true; + + /* Get the index of the interface associated with default route. */ + vrc = getDefaultIfaceIndex(&u16DefaultIface, PF_INET); + if (RT_FAILURE(vrc)) + { + fDefaultIfaceExistent = false; + vrc = VINF_SUCCESS; + } + + aiMib[0] = CTL_NET; + aiMib[1] = PF_ROUTE; + aiMib[2] = 0; + aiMib[3] = 0; /* address family */ + aiMib[4] = NET_RT_IFLIST; + aiMib[5] = 0; + + if (sysctl(aiMib, 6, NULL, &cbNeeded, NULL, 0) < 0) + { + Log(("NetIfList: Failed to get estimate for list size (errno=%d).\n", errno)); + return RTErrConvertFromErrno(errno); + } + if ((pBuf = (char*)malloc(cbNeeded)) == NULL) + return VERR_NO_MEMORY; + if (sysctl(aiMib, 6, pBuf, &cbNeeded, NULL, 0) < 0) + { + free(pBuf); + Log(("NetIfList: Failed to retrieve interface table (errno=%d).\n", errno)); + return RTErrConvertFromErrno(errno); + } + + int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock < 0) + { + free(pBuf); + Log(("NetIfList: socket() -> %d\n", errno)); + return RTErrConvertFromErrno(errno); + } + + char *pEnd = pBuf + cbNeeded; + for (pNext = pBuf; pNext < pEnd;) + { + struct if_msghdr *pIfMsg = (struct if_msghdr *)pNext; + + if (pIfMsg->ifm_type != RTM_IFINFO) + { + Log(("NetIfList: Got message %u while expecting %u.\n", + pIfMsg->ifm_type, RTM_IFINFO)); + vrc = VERR_INTERNAL_ERROR; + break; + } + struct sockaddr_dl *pSdl = (struct sockaddr_dl *)(pIfMsg + 1); + + size_t cbNameLen = pSdl->sdl_nlen + 1; + PNETIFINFO pNew = (PNETIFINFO)RTMemAllocZ(RT_UOFFSETOF_DYN(NETIFINFO, szName[cbNameLen])); + if (!pNew) + { + vrc = VERR_NO_MEMORY; + break; + } + memcpy(pNew->MACAddress.au8, LLADDR(pSdl), sizeof(pNew->MACAddress.au8)); + pNew->enmMediumType = NETIF_T_ETHERNET; + Assert(sizeof(pNew->szShortName) >= cbNameLen); + strlcpy(pNew->szShortName, pSdl->sdl_data, cbNameLen); + strlcpy(pNew->szName, pSdl->sdl_data, cbNameLen); + /* Generate UUID from name and MAC address. */ + RTUUID uuid; + RTUuidClear(&uuid); + memcpy(&uuid, pNew->szShortName, RT_MIN(cbNameLen, sizeof(uuid))); + uuid.Gen.u8ClockSeqHiAndReserved = (uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80; + uuid.Gen.u16TimeHiAndVersion = (uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000; + memcpy(uuid.Gen.au8Node, pNew->MACAddress.au8, sizeof(uuid.Gen.au8Node)); + pNew->Uuid = uuid; + + pNext += pIfMsg->ifm_msglen; + while (pNext < pEnd) + { + struct ifa_msghdr *pIfAddrMsg = (struct ifa_msghdr *)pNext; + + if (pIfAddrMsg->ifam_type != RTM_NEWADDR) + break; + extractAddressesToNetInfo(pIfAddrMsg->ifam_addrs, + (char *)(pIfAddrMsg + 1), + pIfAddrMsg->ifam_msglen + (char *)pIfAddrMsg, + pNew); + pNext += pIfAddrMsg->ifam_msglen; + } + + if (pSdl->sdl_type == IFT_ETHER || pSdl->sdl_type == IFT_L2VLAN) + { + struct ifreq IfReq; + RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pNew->szShortName); + if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0) + { + Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno)); + pNew->enmStatus = NETIF_S_UNKNOWN; + } + else + pNew->enmStatus = (IfReq.ifr_flags & IFF_UP) ? NETIF_S_UP : NETIF_S_DOWN; + + HostNetworkInterfaceType_T enmType; + if (strncmp(pNew->szName, RT_STR_TUPLE("vboxnet"))) + enmType = HostNetworkInterfaceType_Bridged; + else + enmType = HostNetworkInterfaceType_HostOnly; + + pNew->fWireless = isWireless(pNew->szName); + + ComObjPtr IfObj; + IfObj.createObject(); + if (SUCCEEDED(IfObj->init(Bstr(pNew->szName), enmType, pNew))) + { + /* Make sure the default interface gets to the beginning. */ + if ( fDefaultIfaceExistent + && pIfMsg->ifm_index == u16DefaultIface) + list.push_front(IfObj); + else + list.push_back(IfObj); + } + } + RTMemFree(pNew); + } + + close(sock); + free(pBuf); + return vrc; +} + +int NetIfGetConfigByName(PNETIFINFO pInfo) +{ + int vrc = VINF_SUCCESS; + size_t cbNeeded; + char *pBuf, *pNext; + int aiMib[6]; + + aiMib[0] = CTL_NET; + aiMib[1] = PF_ROUTE; + aiMib[2] = 0; + aiMib[3] = 0; /* address family */ + aiMib[4] = NET_RT_IFLIST; + aiMib[5] = 0; + + if (sysctl(aiMib, 6, NULL, &cbNeeded, NULL, 0) < 0) + { + Log(("NetIfList: Failed to get estimate for list size (errno=%d).\n", errno)); + return RTErrConvertFromErrno(errno); + } + if ((pBuf = (char*)malloc(cbNeeded)) == NULL) + return VERR_NO_MEMORY; + if (sysctl(aiMib, 6, pBuf, &cbNeeded, NULL, 0) < 0) + { + free(pBuf); + Log(("NetIfList: Failed to retrieve interface table (errno=%d).\n", errno)); + return RTErrConvertFromErrno(errno); + } + + int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock < 0) + { + free(pBuf); + Log(("NetIfList: socket() -> %d\n", errno)); + return RTErrConvertFromErrno(errno); + } + + char *pEnd = pBuf + cbNeeded; + for (pNext = pBuf; pNext < pEnd;) + { + struct if_msghdr *pIfMsg = (struct if_msghdr *)pNext; + + if (pIfMsg->ifm_type != RTM_IFINFO) + { + Log(("NetIfList: Got message %u while expecting %u.\n", + pIfMsg->ifm_type, RTM_IFINFO)); + vrc = VERR_INTERNAL_ERROR; + break; + } + struct sockaddr_dl *pSdl = (struct sockaddr_dl *)(pIfMsg + 1); + + bool fSkip = !!strcmp(pInfo->szShortName, pSdl->sdl_data); + + pNext += pIfMsg->ifm_msglen; + while (pNext < pEnd) + { + struct ifa_msghdr *pIfAddrMsg = (struct ifa_msghdr *)pNext; + + if (pIfAddrMsg->ifam_type != RTM_NEWADDR) + break; + if (!fSkip) + extractAddressesToNetInfo(pIfAddrMsg->ifam_addrs, + (char *)(pIfAddrMsg + 1), + pIfAddrMsg->ifam_msglen + (char *)pIfAddrMsg, + pInfo); + pNext += pIfAddrMsg->ifam_msglen; + } + + if (!fSkip && (pSdl->sdl_type == IFT_ETHER || pSdl->sdl_type == IFT_L2VLAN)) + { + size_t cbNameLen = pSdl->sdl_nlen + 1; + memcpy(pInfo->MACAddress.au8, LLADDR(pSdl), sizeof(pInfo->MACAddress.au8)); + pInfo->enmMediumType = NETIF_T_ETHERNET; + /* Generate UUID from name and MAC address. */ + RTUUID uuid; + RTUuidClear(&uuid); + memcpy(&uuid, pInfo->szShortName, RT_MIN(cbNameLen, sizeof(uuid))); + uuid.Gen.u8ClockSeqHiAndReserved = (uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80; + uuid.Gen.u16TimeHiAndVersion = (uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000; + memcpy(uuid.Gen.au8Node, pInfo->MACAddress.au8, sizeof(uuid.Gen.au8Node)); + pInfo->Uuid = uuid; + + struct ifreq IfReq; + RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pInfo->szShortName); + if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0) + { + Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno)); + pInfo->enmStatus = NETIF_S_UNKNOWN; + } + else + pInfo->enmStatus = (IfReq.ifr_flags & IFF_UP) ? NETIF_S_UP : NETIF_S_DOWN; + + return VINF_SUCCESS; + } + } + close(sock); + free(pBuf); + return vrc; +} + +/** + * Retrieve the physical link speed in megabits per second. If the interface is + * not up or otherwise unavailable the zero speed is returned. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param puMbits Where to store the link speed. + */ +int NetIfGetLinkSpeed(const char * /*pcszIfName*/, uint32_t * /*puMbits*/) +{ + return VERR_NOT_IMPLEMENTED; +} diff --git a/src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp b/src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp new file mode 100644 index 00000000..fc55de17 --- /dev/null +++ b/src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp @@ -0,0 +1,128 @@ +/* $Id: PerformanceFreeBSD.cpp $ */ +/** @file + * VirtualBox Performance Collector, FreeBSD Specialization. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * 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, in version 3 of the + * License. + * + * 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, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include +#include +#include "Performance.h" + +namespace pm { + +class CollectorFreeBSD : public CollectorHAL +{ +public: + virtual int getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle); + virtual int getHostCpuMHz(ULONG *mhz); + virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available); + virtual int getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel); + virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used); +}; + + +CollectorHAL *createHAL() +{ + return new CollectorFreeBSD(); +} + +int CollectorFreeBSD::getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle) +{ + return VERR_NOT_IMPLEMENTED; +} + +int CollectorFreeBSD::getHostCpuMHz(ULONG *mhz) +{ + int CpuMHz = 0; + size_t cbParameter = sizeof(CpuMHz); + + /** @todo Howto support more than one CPU? */ + if (sysctlbyname("dev.cpu.0.freq", &CpuMHz, &cbParameter, NULL, 0)) + return VERR_NOT_SUPPORTED; + + *mhz = CpuMHz; + + return VINF_SUCCESS; +} + +int CollectorFreeBSD::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available) +{ + int vrc = VINF_SUCCESS; + u_long cbMemPhys = 0; + u_int cPagesMemFree = 0; + u_int cPagesMemInactive = 0; + u_int cPagesMemCached = 0; + u_int cPagesMemUsed = 0; + int cbPage = 0; + size_t cbParameter = sizeof(cbMemPhys); + int cProcessed = 0; + + if (!sysctlbyname("hw.physmem", &cbMemPhys, &cbParameter, NULL, 0)) + cProcessed++; + + cbParameter = sizeof(cPagesMemFree); + if (!sysctlbyname("vm.stats.vm.v_free_count", &cPagesMemFree, &cbParameter, NULL, 0)) + cProcessed++; + cbParameter = sizeof(cPagesMemUsed); + if (!sysctlbyname("vm.stats.vm.v_active_count", &cPagesMemUsed, &cbParameter, NULL, 0)) + cProcessed++; + cbParameter = sizeof(cPagesMemInactive); + if (!sysctlbyname("vm.stats.vm.v_inactive_count", &cPagesMemInactive, &cbParameter, NULL, 0)) + cProcessed++; + cbParameter = sizeof(cPagesMemCached); + if (!sysctlbyname("vm.stats.vm.v_cache_count", &cPagesMemCached, &cbParameter, NULL, 0)) + cProcessed++; + cbParameter = sizeof(cbPage); + if (!sysctlbyname("hw.pagesize", &cbPage, &cbParameter, NULL, 0)) + cProcessed++; + + if (cProcessed == 6) + { + *total = cbMemPhys / _1K; + *used = cPagesMemUsed * (cbPage / _1K); + *available = (cPagesMemFree + cPagesMemInactive + cPagesMemCached ) * (cbPage / _1K); + } + else + vrc = VERR_NOT_SUPPORTED; + + return vrc; +} + +int CollectorFreeBSD::getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel) +{ + return VERR_NOT_IMPLEMENTED; +} + +int CollectorFreeBSD::getProcessMemoryUsage(RTPROCESS process, ULONG *used) +{ + return VERR_NOT_IMPLEMENTED; +} + +int getDiskListByFs(const char *name, DiskList& list) +{ + return VERR_NOT_IMPLEMENTED; +} + +} /* namespace pm */ + diff --git a/src/VBox/Main/src-server/freebsd/USBProxyBackendFreeBSD.cpp b/src/VBox/Main/src-server/freebsd/USBProxyBackendFreeBSD.cpp new file mode 100644 index 00000000..79543717 --- /dev/null +++ b/src/VBox/Main/src-server/freebsd/USBProxyBackendFreeBSD.cpp @@ -0,0 +1,353 @@ +/* $Id: USBProxyBackendFreeBSD.cpp $ */ +/** @file + * VirtualBox USB Proxy Service, FreeBSD Specialization. + */ + +/* + * Copyright (C) 2005-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * 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, in version 3 of the + * License. + * + * 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, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN_USBPROXYBACKEND +#include "USBProxyBackend.h" +#include "LoggingNew.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * Initialize data members. + */ +USBProxyBackendFreeBSD::USBProxyBackendFreeBSD() + : USBProxyBackend(), mNotifyEventSem(NIL_RTSEMEVENT) +{ + LogFlowThisFunc(("\n")); +} + +USBProxyBackendFreeBSD::~USBProxyBackendFreeBSD() +{ + LogFlowThisFunc(("\n")); +} + +/** + * Initializes the object (called right after construction). + * + * @returns S_OK on success and non-fatal failures, some COM error otherwise. + */ +int USBProxyBackendFreeBSD::init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId, + const com::Utf8Str &strAddress, bool fLoadingSettings) +{ + USBProxyBackend::init(pUsbProxyService, strId, strAddress, fLoadingSettings); + + unconst(m_strBackend) = Utf8Str("host"); + + /* + * Create semaphore. + */ + int vrc = RTSemEventCreate(&mNotifyEventSem); + if (RT_FAILURE(vrc)) + return vrc; + + /* + * Start the poller thread. + */ + start(); + return VINF_SUCCESS; +} + + +/** + * Stop all service threads and free the device chain. + */ +void USBProxyBackendFreeBSD::uninit() +{ + LogFlowThisFunc(("\n")); + + /* + * Stop the service. + */ + if (isActive()) + stop(); + + RTSemEventDestroy(mNotifyEventSem); + mNotifyEventSem = NULL; + USBProxyBackend::uninit(); +} + + +int USBProxyBackendFreeBSD::captureDevice(HostUSBDevice *aDevice) +{ + AssertReturn(aDevice, VERR_GENERAL_FAILURE); + AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE); + + AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS); + LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str())); + + /* + * Don't think we need to do anything when the device is held... fake it. + */ + Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing); + devLock.release(); + interruptWait(); + + return VINF_SUCCESS; +} + + +int USBProxyBackendFreeBSD::releaseDevice(HostUSBDevice *aDevice) +{ + AssertReturn(aDevice, VERR_GENERAL_FAILURE); + AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE); + + AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS); + LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str())); + + /* + * We're not really holding it atm., just fake it. + */ + Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost); + devLock.release(); + interruptWait(); + + return VINF_SUCCESS; +} + + +bool USBProxyBackendFreeBSD::isFakeUpdateRequired() +{ + return true; +} + + +int USBProxyBackendFreeBSD::wait(RTMSINTERVAL aMillies) +{ + return RTSemEventWait(mNotifyEventSem, aMillies < 1000 ? 1000 : 5000); +} + + +int USBProxyBackendFreeBSD::interruptWait(void) +{ + return RTSemEventSignal(mNotifyEventSem); +} + + +/** + * Dumps a USBDEVICE structure to the log using LogLevel 3. + * @param pDev The structure to log. + * @todo This is really common code. + */ +DECLINLINE(void) usbLogDevice(PUSBDEVICE pDev) +{ + NOREF(pDev); + + Log3(("USB device:\n")); + Log3(("Product: %s (%x)\n", pDev->pszProduct, pDev->idProduct)); + Log3(("Manufacturer: %s (Vendor ID %x)\n", pDev->pszManufacturer, pDev->idVendor)); + Log3(("Serial number: %s (%llx)\n", pDev->pszSerialNumber, pDev->u64SerialHash)); + Log3(("Device revision: %d\n", pDev->bcdDevice)); + Log3(("Device class: %x\n", pDev->bDeviceClass)); + Log3(("Device subclass: %x\n", pDev->bDeviceSubClass)); + Log3(("Device protocol: %x\n", pDev->bDeviceProtocol)); + Log3(("USB version number: %d\n", pDev->bcdUSB)); + Log3(("Device speed: %s\n", + pDev->enmSpeed == USBDEVICESPEED_UNKNOWN ? "unknown" + : pDev->enmSpeed == USBDEVICESPEED_LOW ? "1.5 MBit/s" + : pDev->enmSpeed == USBDEVICESPEED_FULL ? "12 MBit/s" + : pDev->enmSpeed == USBDEVICESPEED_HIGH ? "480 MBit/s" + : pDev->enmSpeed == USBDEVICESPEED_VARIABLE ? "variable" + : "invalid")); + Log3(("Number of configurations: %d\n", pDev->bNumConfigurations)); + Log3(("Bus number: %d\n", pDev->bBus)); + Log3(("Port number: %d\n", pDev->bPort)); + Log3(("Device number: %d\n", pDev->bDevNum)); + Log3(("Device state: %s\n", + pDev->enmState == USBDEVICESTATE_UNSUPPORTED ? "unsupported" + : pDev->enmState == USBDEVICESTATE_USED_BY_HOST ? "in use by host" + : pDev->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE ? "in use by host, possibly capturable" + : pDev->enmState == USBDEVICESTATE_UNUSED ? "not in use" + : pDev->enmState == USBDEVICESTATE_HELD_BY_PROXY ? "held by proxy" + : pDev->enmState == USBDEVICESTATE_USED_BY_GUEST ? "used by guest" + : "invalid")); + Log3(("OS device address: %s\n", pDev->pszAddress)); +} + + +PUSBDEVICE USBProxyBackendFreeBSD::getDevices(void) +{ + PUSBDEVICE pDevices = NULL; + int FileUsb = 0; + int iBus = 0; + int iAddr = 1; + int vrc = VINF_SUCCESS; + char *pszDevicePath = NULL; + uint32_t PlugTime = 0; + + for (;;) + { + vrc = RTStrAPrintf(&pszDevicePath, "/dev/%s%d.%d", USB_GENERIC_NAME, iBus, iAddr); + if (RT_FAILURE(vrc)) + break; + + LogFlowFunc((": Opening %s\n", pszDevicePath)); + + FileUsb = open(pszDevicePath, O_RDONLY); + if (FileUsb < 0) + { + RTStrFree(pszDevicePath); + + if ((errno == ENOENT) && (iAddr > 1)) + { + iAddr = 1; + iBus++; + continue; + } + else if (errno == EACCES) + { + /* Skip devices without the right permission. */ + iAddr++; + continue; + } + else + break; + } + + LogFlowFunc((": %s opened successfully\n", pszDevicePath)); + + struct usb_device_info UsbDevInfo; + RT_ZERO(UsbDevInfo); + + vrc = ioctl(FileUsb, USB_GET_DEVICEINFO, &UsbDevInfo); + if (vrc < 0) + { + LogFlowFunc((": Error querying device info vrc=%Rrc\n", RTErrConvertFromErrno(errno))); + close(FileUsb); + RTStrFree(pszDevicePath); + break; + } + + /* Filter out hubs */ + if (UsbDevInfo.udi_class != 0x09) + { + PUSBDEVICE pDevice = (PUSBDEVICE)RTMemAllocZ(sizeof(USBDEVICE)); + if (!pDevice) + { + close(FileUsb); + RTStrFree(pszDevicePath); + break; + } + + pDevice->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; + pDevice->bBus = UsbDevInfo.udi_bus; + pDevice->bPort = UsbDevInfo.udi_hubport; + pDevice->bDeviceClass = UsbDevInfo.udi_class; + pDevice->bDeviceSubClass = UsbDevInfo.udi_subclass; + pDevice->bDeviceProtocol = UsbDevInfo.udi_protocol; + pDevice->bNumConfigurations = UsbDevInfo.udi_config_no; + pDevice->idVendor = UsbDevInfo.udi_vendorNo; + pDevice->idProduct = UsbDevInfo.udi_productNo; + pDevice->bDevNum = UsbDevInfo.udi_index; + + switch (UsbDevInfo.udi_speed) + { + case USB_SPEED_LOW: + pDevice->enmSpeed = USBDEVICESPEED_LOW; + break; + case USB_SPEED_FULL: + pDevice->enmSpeed = USBDEVICESPEED_FULL; + break; + case USB_SPEED_HIGH: + pDevice->enmSpeed = USBDEVICESPEED_HIGH; + break; + case USB_SPEED_SUPER: + pDevice->enmSpeed = USBDEVICESPEED_SUPER; + break; + case USB_SPEED_VARIABLE: + pDevice->enmSpeed = USBDEVICESPEED_VARIABLE; + break; + default: + pDevice->enmSpeed = USBDEVICESPEED_UNKNOWN; + break; + } + + if (UsbDevInfo.udi_vendor[0] != '\0') + { + USBLibPurgeEncoding(UsbDevInfo.udi_vendor); + pDevice->pszManufacturer = RTStrDupN(UsbDevInfo.udi_vendor, sizeof(UsbDevInfo.udi_vendor)); + } + + if (UsbDevInfo.udi_product[0] != '\0') + { + USBLibPurgeEncoding(UsbDevInfo.udi_product); + pDevice->pszProduct = RTStrDupN(UsbDevInfo.udi_product, sizeof(UsbDevInfo.udi_product)); + } + + if (UsbDevInfo.udi_serial[0] != '\0') + { + USBLibPurgeEncoding(UsbDevInfo.udi_serial); + pDevice->pszSerialNumber = RTStrDupN(UsbDevInfo.udi_serial, sizeof(UsbDevInfo.udi_serial)); + pDevice->u64SerialHash = USBLibHashSerial(UsbDevInfo.udi_serial); + } + vrc = ioctl(FileUsb, USB_GET_PLUGTIME, &PlugTime); + if (vrc == 0) + pDevice->u64SerialHash += PlugTime; + + pDevice->pszAddress = RTStrDup(pszDevicePath); + pDevice->pszBackend = RTStrDup("host"); + + usbLogDevice(pDevice); + + pDevice->pNext = pDevices; + if (pDevices) + pDevices->pPrev = pDevice; + pDevices = pDevice; + } + close(FileUsb); + RTStrFree(pszDevicePath); + iAddr++; + } + + return pDevices; +} -- cgit v1.2.3