VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp@ 72919

Last change on this file since 72919 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.4 KB
Line 
1/* $Id: HostHardwareFreeBSD.cpp 69500 2017-10-28 15:14:05Z vboxsync $ */
2/** @file
3 * Classes for handling hardware detection under FreeBSD.
4 */
5
6/*
7 * Copyright (C) 2008-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24
25#include <HostHardwareLinux.h>
26
27#include <VBox/log.h>
28
29#include <iprt/dir.h>
30#include <iprt/env.h>
31#include <iprt/file.h>
32#include <iprt/mem.h>
33#include <iprt/param.h>
34#include <iprt/path.h>
35#include <iprt/string.h>
36
37#ifdef RT_OS_FREEBSD
38# include <sys/param.h>
39# include <sys/types.h>
40# include <sys/stat.h>
41# include <unistd.h>
42# include <stdio.h>
43# include <sys/ioctl.h>
44# include <fcntl.h>
45# include <cam/cam.h>
46# include <cam/cam_ccb.h>
47# include <cam/scsi/scsi_pass.h>
48#endif /* RT_OS_FREEBSD */
49#include <vector>
50
51
52/*********************************************************************************************************************************
53* Typedefs and Defines *
54*********************************************************************************************************************************/
55
56static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
57 bool isDVD, bool *pfSuccess);
58static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess);
59
60/** Find the length of a string, ignoring trailing non-ascii or control
61 * characters */
62static size_t strLenStripped(const char *pcsz)
63{
64 size_t cch = 0;
65 for (size_t i = 0; pcsz[i] != '\0'; ++i)
66 if (pcsz[i] > 32 && pcsz[i] < 127)
67 cch = i;
68 return cch + 1;
69}
70
71static void strLenRemoveTrailingWhiteSpace(char *psz, size_t cchStr)
72{
73 while ( (cchStr > 0)
74 && (psz[cchStr -1] == ' '))
75 psz[--cchStr] = '\0';
76}
77
78/**
79 * Initialise the device description for a DVD drive based on
80 * vendor and model name strings.
81 * @param pcszVendor the vendor ID string
82 * @param pcszModel the product ID string
83 * @param pszDesc where to store the description string (optional)
84 * @param cchDesc the size of the buffer in @pszDesc
85 */
86/* static */
87void dvdCreateDeviceString(const char *pcszVendor, const char *pcszModel,
88 char *pszDesc, size_t cchDesc)
89{
90 AssertPtrReturnVoid(pcszVendor);
91 AssertPtrReturnVoid(pcszModel);
92 AssertPtrNullReturnVoid(pszDesc);
93 AssertReturnVoid(!pszDesc || cchDesc > 0);
94 size_t cchVendor = strLenStripped(pcszVendor);
95 size_t cchModel = strLenStripped(pcszModel);
96
97 /* Construct the description string as "Vendor Product" */
98 if (pszDesc)
99 {
100 if (cchVendor > 0)
101 RTStrPrintf(pszDesc, cchDesc, "%.*s %s", cchVendor, pcszVendor,
102 cchModel > 0 ? pcszModel : "(unknown drive model)");
103 else
104 RTStrPrintf(pszDesc, cchDesc, "%s", pcszModel);
105 }
106}
107
108
109int VBoxMainDriveInfo::updateDVDs ()
110{
111 LogFlowThisFunc(("entered\n"));
112 int rc = VINF_SUCCESS;
113 bool fSuccess = false; /* Have we succeeded in finding anything yet? */
114
115 try
116 {
117 mDVDList.clear ();
118 /* Always allow the user to override our auto-detection using an
119 * environment variable. */
120 if (RT_SUCCESS(rc) && !fSuccess)
121 rc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */,
122 &fSuccess);
123 if (RT_SUCCESS(rc) && !fSuccess)
124 rc = getDVDInfoFromCAM(&mDVDList, &fSuccess);
125 }
126 catch(std::bad_alloc &e)
127 {
128 rc = VERR_NO_MEMORY;
129 }
130 LogFlowThisFunc(("rc=%Rrc\n", rc));
131 return rc;
132}
133
134int VBoxMainDriveInfo::updateFloppies ()
135{
136 LogFlowThisFunc(("entered\n"));
137 int rc = VINF_SUCCESS;
138 bool fSuccess = false; /* Have we succeeded in finding anything yet? */
139
140 try
141 {
142 mFloppyList.clear ();
143 /* Always allow the user to override our auto-detection using an
144 * environment variable. */
145 if (RT_SUCCESS(rc) && !fSuccess)
146 rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */,
147 &fSuccess);
148 }
149 catch(std::bad_alloc &e)
150 {
151 rc = VERR_NO_MEMORY;
152 }
153 LogFlowThisFunc(("rc=%Rrc\n", rc));
154 return rc;
155}
156
157/**
158 * Search for available CD/DVD drives using the CAM layer.
159 *
160 * @returns iprt status code
161 * @param pList the list to append the drives found to
162 * @param pfSuccess this will be set to true if we found at least one drive
163 * and to false otherwise. Optional.
164 */
165static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess)
166{
167 int rc = VINF_SUCCESS;
168 RTFILE FileXpt;
169
170 rc = RTFileOpen(&FileXpt, "/dev/xpt0", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
171 if (RT_SUCCESS(rc))
172 {
173 union ccb DeviceCCB;
174 struct dev_match_pattern DeviceMatchPattern;
175 struct dev_match_result *paMatches = NULL;
176
177 RT_ZERO(DeviceCCB);
178 RT_ZERO(DeviceMatchPattern);
179
180 /* We want to get all devices. */
181 DeviceCCB.ccb_h.func_code = XPT_DEV_MATCH;
182 DeviceCCB.ccb_h.path_id = CAM_XPT_PATH_ID;
183 DeviceCCB.ccb_h.target_id = CAM_TARGET_WILDCARD;
184 DeviceCCB.ccb_h.target_lun = CAM_LUN_WILDCARD;
185
186 /* Setup the pattern */
187 DeviceMatchPattern.type = DEV_MATCH_DEVICE;
188 DeviceMatchPattern.pattern.device_pattern.path_id = CAM_XPT_PATH_ID;
189 DeviceMatchPattern.pattern.device_pattern.target_id = CAM_TARGET_WILDCARD;
190 DeviceMatchPattern.pattern.device_pattern.target_lun = CAM_LUN_WILDCARD;
191 DeviceMatchPattern.pattern.device_pattern.flags = DEV_MATCH_INQUIRY;
192
193#if __FreeBSD_version >= 900000
194# define INQ_PAT data.inq_pat
195#else
196 #define INQ_PAT inq_pat
197#endif
198 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.type = T_CDROM;
199 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.media_type = SIP_MEDIA_REMOVABLE | SIP_MEDIA_FIXED;
200 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.vendor[0] = '*'; /* Matches anything */
201 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.product[0] = '*'; /* Matches anything */
202 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.revision[0] = '*'; /* Matches anything */
203#undef INQ_PAT
204 DeviceCCB.cdm.num_patterns = 1;
205 DeviceCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result);
206 DeviceCCB.cdm.patterns = &DeviceMatchPattern;
207
208 /*
209 * Allocate the buffer holding the matches.
210 * We will allocate for 10 results and call
211 * CAM multiple times if we have more results.
212 */
213 paMatches = (struct dev_match_result *)RTMemAllocZ(10 * sizeof(struct dev_match_result));
214 if (paMatches)
215 {
216 DeviceCCB.cdm.num_matches = 0;
217 DeviceCCB.cdm.match_buf_len = 10 * sizeof(struct dev_match_result);
218 DeviceCCB.cdm.matches = paMatches;
219
220 do
221 {
222 rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &DeviceCCB, sizeof(union ccb), NULL);
223 if (RT_FAILURE(rc))
224 {
225 Log(("Error while querying available CD/DVD devices rc=%Rrc\n", rc));
226 break;
227 }
228
229 for (unsigned i = 0; i < DeviceCCB.cdm.num_matches; i++)
230 {
231 if (paMatches[i].type == DEV_MATCH_DEVICE)
232 {
233 /* We have the drive now but need the appropriate device node */
234 struct device_match_result *pDevResult = &paMatches[i].result.device_result;
235 union ccb PeriphCCB;
236 struct dev_match_pattern PeriphMatchPattern;
237 struct dev_match_result aPeriphMatches[2];
238 struct periph_match_result *pPeriphResult = NULL;
239 unsigned iPeriphMatch = 0;
240
241 RT_ZERO(PeriphCCB);
242 RT_ZERO(PeriphMatchPattern);
243 RT_ZERO(aPeriphMatches);
244
245 /* This time we only want the specific nodes for the device. */
246 PeriphCCB.ccb_h.func_code = XPT_DEV_MATCH;
247 PeriphCCB.ccb_h.path_id = paMatches[i].result.device_result.path_id;
248 PeriphCCB.ccb_h.target_id = paMatches[i].result.device_result.target_id;
249 PeriphCCB.ccb_h.target_lun = paMatches[i].result.device_result.target_lun;
250
251 /* Setup the pattern */
252 PeriphMatchPattern.type = DEV_MATCH_PERIPH;
253 PeriphMatchPattern.pattern.periph_pattern.path_id = paMatches[i].result.device_result.path_id;
254 PeriphMatchPattern.pattern.periph_pattern.target_id = paMatches[i].result.device_result.target_id;
255 PeriphMatchPattern.pattern.periph_pattern.target_lun = paMatches[i].result.device_result.target_lun;
256 PeriphMatchPattern.pattern.periph_pattern.flags = PERIPH_MATCH_PATH | PERIPH_MATCH_TARGET |
257 PERIPH_MATCH_LUN;
258 PeriphCCB.cdm.num_patterns = 1;
259 PeriphCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result);
260 PeriphCCB.cdm.patterns = &PeriphMatchPattern;
261 PeriphCCB.cdm.num_matches = 0;
262 PeriphCCB.cdm.match_buf_len = sizeof(aPeriphMatches);
263 PeriphCCB.cdm.matches = aPeriphMatches;
264
265 do
266 {
267 rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &PeriphCCB, sizeof(union ccb), NULL);
268 if (RT_FAILURE(rc))
269 {
270 Log(("Error while querying available periph devices rc=%Rrc\n", rc));
271 break;
272 }
273
274 for (iPeriphMatch = 0; iPeriphMatch < PeriphCCB.cdm.num_matches; iPeriphMatch++)
275 {
276 if ( (aPeriphMatches[iPeriphMatch].type == DEV_MATCH_PERIPH)
277 && (!strcmp(aPeriphMatches[iPeriphMatch].result.periph_result.periph_name, "cd")))
278 {
279 pPeriphResult = &aPeriphMatches[iPeriphMatch].result.periph_result;
280 break; /* We found the periph device */
281 }
282 }
283
284 if (iPeriphMatch < PeriphCCB.cdm.num_matches)
285 break;
286
287 } while ( (DeviceCCB.ccb_h.status == CAM_REQ_CMP)
288 && (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE));
289
290 if (pPeriphResult)
291 {
292 char szPath[RTPATH_MAX];
293 char szDesc[256];
294
295 RTStrPrintf(szPath, sizeof(szPath), "/dev/%s%d",
296 pPeriphResult->periph_name, pPeriphResult->unit_number);
297
298 /* Remove trailing white space. */
299 strLenRemoveTrailingWhiteSpace(pDevResult->inq_data.vendor,
300 sizeof(pDevResult->inq_data.vendor));
301 strLenRemoveTrailingWhiteSpace(pDevResult->inq_data.product,
302 sizeof(pDevResult->inq_data.product));
303
304 dvdCreateDeviceString(pDevResult->inq_data.vendor,
305 pDevResult->inq_data.product,
306 szDesc, sizeof(szDesc));
307
308 pList->push_back(DriveInfo(szPath, "", szDesc));
309 if (pfSuccess)
310 *pfSuccess = true;
311 }
312 }
313 }
314 } while ( (DeviceCCB.ccb_h.status == CAM_REQ_CMP)
315 && (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE));
316
317 RTMemFree(paMatches);
318 }
319 else
320 rc = VERR_NO_MEMORY;
321
322 RTFileClose(FileXpt);
323 }
324
325 return rc;
326}
327
328/**
329 * Extract the names of drives from an environment variable and add them to a
330 * list if they are valid.
331 * @returns iprt status code
332 * @param pcszVar the name of the environment variable. The variable
333 * value should be a list of device node names, separated
334 * by ':' characters.
335 * @param pList the list to append the drives found to
336 * @param isDVD are we looking for DVD drives or for floppies?
337 * @param pfSuccess this will be set to true if we found at least one drive
338 * and to false otherwise. Optional.
339 */
340static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
341 bool isDVD, bool *pfSuccess)
342{
343 AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
344 AssertPtrReturn(pList, VERR_INVALID_POINTER);
345 AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
346 LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar,
347 pList, isDVD, pfSuccess));
348 int rc = VINF_SUCCESS;
349 bool success = false;
350 char *pszFreeMe = RTEnvDupEx(RTENV_DEFAULT, pcszVar);
351
352 try
353 {
354 const char *pcszCurrent = pszFreeMe;
355 while (pcszCurrent && *pcszCurrent != '\0')
356 {
357 const char *pcszNext = strchr(pcszCurrent, ':');
358 char szPath[RTPATH_MAX], szReal[RTPATH_MAX];
359 char szDesc[256], szUdi[256];
360 if (pcszNext)
361 RTStrPrintf(szPath, sizeof(szPath), "%.*s",
362 pcszNext - pcszCurrent - 1, pcszCurrent);
363 else
364 RTStrPrintf(szPath, sizeof(szPath), "%s", pcszCurrent);
365 if (RT_SUCCESS(RTPathReal(szPath, szReal, sizeof(szReal))))
366 {
367 szUdi[0] = '\0'; /** @todo r=bird: missing a call to devValidateDevice() here and szUdi wasn't
368 * initialized because of that. Need proper fixing. */
369 pList->push_back(DriveInfo(szReal, szUdi, szDesc));
370 success = true;
371 }
372 pcszCurrent = pcszNext ? pcszNext + 1 : NULL;
373 }
374 if (pfSuccess != NULL)
375 *pfSuccess = success;
376 }
377 catch(std::bad_alloc &e)
378 {
379 rc = VERR_NO_MEMORY;
380 }
381 RTStrFree(pszFreeMe);
382 LogFlowFunc(("rc=%Rrc, success=%d\n", rc, success));
383 return rc;
384}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette