VirtualBox

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

Last change on this file since 55095 was 55095, checked in by vboxsync, 10 years ago

Assorted fixes for FreeBSD hosts, VBox compiles and runs again without further patches (tested on 10.1 amd64 )

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.8 KB
Line 
1/* $Id: HostHardwareFreeBSD.cpp 55095 2015-04-02 16:52:46Z vboxsync $ */
2/** @file
3 * Classes for handling hardware detection under FreeBSD.
4 */
5
6/*
7 * Copyright (C) 2008-2013 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* Header Files *
22*******************************************************************************/
23
24#include <HostHardwareLinux.h>
25
26#include <VBox/log.h>
27
28#include <iprt/dir.h>
29#include <iprt/env.h>
30#include <iprt/file.h>
31#include <iprt/mem.h>
32#include <iprt/param.h>
33#include <iprt/path.h>
34#include <iprt/string.h>
35
36#ifdef RT_OS_FREEBSD
37# include <sys/param.h>
38# include <sys/types.h>
39# include <sys/stat.h>
40# include <unistd.h>
41# include <stdio.h>
42# include <sys/ioctl.h>
43# include <fcntl.h>
44# include <cam/cam.h>
45# include <cam/cam_ccb.h>
46# include <cam/scsi/scsi_pass.h>
47#endif /* RT_OS_FREEBSD */
48#include <vector>
49
50/******************************************************************************
51* Typedefs and Defines *
52******************************************************************************/
53
54static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
55 bool isDVD, bool *pfSuccess);
56static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess);
57
58/** Find the length of a string, ignoring trailing non-ascii or control
59 * characters */
60static size_t strLenStripped(const char *pcsz)
61{
62 size_t cch = 0;
63 for (size_t i = 0; pcsz[i] != '\0'; ++i)
64 if (pcsz[i] > 32 && pcsz[i] < 127)
65 cch = i;
66 return cch + 1;
67}
68
69static void strLenRemoveTrailingWhiteSpace(char *psz, size_t cchStr)
70{
71 while ( (cchStr > 0)
72 && (psz[cchStr -1] == ' '))
73 psz[--cchStr] = '\0';
74}
75
76/**
77 * Initialise the device description for a DVD drive based on
78 * vendor and model name strings.
79 * @param pcszVendor the vendor ID string
80 * @param pcszModel the product ID string
81 * @param pszDesc where to store the description string (optional)
82 * @param cchDesc the size of the buffer in @pszDesc
83 */
84/* static */
85void dvdCreateDeviceString(const char *pcszVendor, const char *pcszModel,
86 char *pszDesc, size_t cchDesc)
87{
88 AssertPtrReturnVoid(pcszVendor);
89 AssertPtrReturnVoid(pcszModel);
90 AssertPtrNullReturnVoid(pszDesc);
91 AssertReturnVoid(!pszDesc || cchDesc > 0);
92 size_t cchVendor = strLenStripped(pcszVendor);
93 size_t cchModel = strLenStripped(pcszModel);
94
95 /* Construct the description string as "Vendor Product" */
96 if (pszDesc)
97 {
98 if (cchVendor > 0)
99 RTStrPrintf(pszDesc, cchDesc, "%.*s %s", cchVendor, pcszVendor,
100 cchModel > 0 ? pcszModel : "(unknown drive model)");
101 else
102 RTStrPrintf(pszDesc, cchDesc, "%s", pcszModel);
103 }
104}
105
106
107int VBoxMainDriveInfo::updateDVDs ()
108{
109 LogFlowThisFunc(("entered\n"));
110 int rc = VINF_SUCCESS;
111 bool fSuccess = false; /* Have we succeeded in finding anything yet? */
112
113 try
114 {
115 mDVDList.clear ();
116 /* Always allow the user to override our auto-detection using an
117 * environment variable. */
118 if (RT_SUCCESS(rc) && !fSuccess)
119 rc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */,
120 &fSuccess);
121 if (RT_SUCCESS(rc) && !fSuccess)
122 rc = getDVDInfoFromCAM(&mDVDList, &fSuccess);
123 }
124 catch(std::bad_alloc &e)
125 {
126 rc = VERR_NO_MEMORY;
127 }
128 LogFlowThisFunc(("rc=%Rrc\n", rc));
129 return rc;
130}
131
132int VBoxMainDriveInfo::updateFloppies ()
133{
134 LogFlowThisFunc(("entered\n"));
135 int rc = VINF_SUCCESS;
136 bool fSuccess = false; /* Have we succeeded in finding anything yet? */
137
138 try
139 {
140 mFloppyList.clear ();
141 /* Always allow the user to override our auto-detection using an
142 * environment variable. */
143 if (RT_SUCCESS(rc) && !fSuccess)
144 rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */,
145 &fSuccess);
146 }
147 catch(std::bad_alloc &e)
148 {
149 rc = VERR_NO_MEMORY;
150 }
151 LogFlowThisFunc(("rc=%Rrc\n", rc));
152 return rc;
153}
154
155/**
156 * Search for available CD/DVD drives using the CAM layer.
157 *
158 * @returns iprt status code
159 * @param pList the list to append the drives found to
160 * @param pfSuccess this will be set to true if we found at least one drive
161 * and to false otherwise. Optional.
162 */
163static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess)
164{
165 int rc = VINF_SUCCESS;
166 RTFILE FileXpt;
167
168 rc = RTFileOpen(&FileXpt, "/dev/xpt0", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
169 if (RT_SUCCESS(rc))
170 {
171 union ccb DeviceCCB;
172 struct dev_match_pattern DeviceMatchPattern;
173 struct dev_match_result *paMatches = NULL;
174
175 RT_ZERO(DeviceCCB);
176 RT_ZERO(DeviceMatchPattern);
177
178 /* We want to get all devices. */
179 DeviceCCB.ccb_h.func_code = XPT_DEV_MATCH;
180 DeviceCCB.ccb_h.path_id = CAM_XPT_PATH_ID;
181 DeviceCCB.ccb_h.target_id = CAM_TARGET_WILDCARD;
182 DeviceCCB.ccb_h.target_lun = CAM_LUN_WILDCARD;
183
184 /* Setup the pattern */
185 DeviceMatchPattern.type = DEV_MATCH_DEVICE;
186 DeviceMatchPattern.pattern.device_pattern.path_id = CAM_XPT_PATH_ID;
187 DeviceMatchPattern.pattern.device_pattern.target_id = CAM_TARGET_WILDCARD;
188 DeviceMatchPattern.pattern.device_pattern.target_lun = CAM_LUN_WILDCARD;
189 DeviceMatchPattern.pattern.device_pattern.flags = DEV_MATCH_INQUIRY;
190
191#if __FreeBSD_version >= 900000
192# define INQ_PAT data.inq_pat
193#else
194 #define INQ_PAT inq_pat
195#endif
196 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.type = T_CDROM;
197 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.media_type = SIP_MEDIA_REMOVABLE | SIP_MEDIA_FIXED;
198 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.vendor[0] = '*'; /* Matches anything */
199 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.product[0] = '*'; /* Matches anything */
200 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.revision[0] = '*'; /* Matches anything */
201#undef INQ_PAT
202 DeviceCCB.cdm.num_patterns = 1;
203 DeviceCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result);
204 DeviceCCB.cdm.patterns = &DeviceMatchPattern;
205
206 /*
207 * Allocate the buffer holding the matches.
208 * We will allocate for 10 results and call
209 * CAM multiple times if we have more results.
210 */
211 paMatches = (struct dev_match_result *)RTMemAllocZ(10 * sizeof(struct dev_match_result));
212 if (paMatches)
213 {
214 DeviceCCB.cdm.num_matches = 0;
215 DeviceCCB.cdm.match_buf_len = 10 * sizeof(struct dev_match_result);
216 DeviceCCB.cdm.matches = paMatches;
217
218 do
219 {
220 rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &DeviceCCB, sizeof(union ccb), NULL);
221 if (RT_FAILURE(rc))
222 {
223 Log(("Error while querying available CD/DVD devices rc=%Rrc\n", rc));
224 break;
225 }
226
227 for (unsigned i = 0; i < DeviceCCB.cdm.num_matches; i++)
228 {
229 if (paMatches[i].type == DEV_MATCH_DEVICE)
230 {
231 /* We have the drive now but need the appropriate device node */
232 struct device_match_result *pDevResult = &paMatches[i].result.device_result;
233 union ccb PeriphCCB;
234 struct dev_match_pattern PeriphMatchPattern;
235 struct dev_match_result aPeriphMatches[2];
236 struct periph_match_result *pPeriphResult = NULL;
237 unsigned iPeriphMatch = 0;
238
239 RT_ZERO(PeriphCCB);
240 RT_ZERO(PeriphMatchPattern);
241 RT_ZERO(aPeriphMatches);
242
243 /* This time we only want the specific nodes for the device. */
244 PeriphCCB.ccb_h.func_code = XPT_DEV_MATCH;
245 PeriphCCB.ccb_h.path_id = paMatches[i].result.device_result.path_id;
246 PeriphCCB.ccb_h.target_id = paMatches[i].result.device_result.target_id;
247 PeriphCCB.ccb_h.target_lun = paMatches[i].result.device_result.target_lun;
248
249 /* Setup the pattern */
250 PeriphMatchPattern.type = DEV_MATCH_PERIPH;
251 PeriphMatchPattern.pattern.periph_pattern.path_id = paMatches[i].result.device_result.path_id;
252 PeriphMatchPattern.pattern.periph_pattern.target_id = paMatches[i].result.device_result.target_id;
253 PeriphMatchPattern.pattern.periph_pattern.target_lun = paMatches[i].result.device_result.target_lun;
254 PeriphMatchPattern.pattern.periph_pattern.flags = PERIPH_MATCH_PATH | PERIPH_MATCH_TARGET |
255 PERIPH_MATCH_LUN;
256 PeriphCCB.cdm.num_patterns = 1;
257 PeriphCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result);
258 PeriphCCB.cdm.patterns = &PeriphMatchPattern;
259 PeriphCCB.cdm.num_matches = 0;
260 PeriphCCB.cdm.match_buf_len = sizeof(aPeriphMatches);
261 PeriphCCB.cdm.matches = aPeriphMatches;
262
263 do
264 {
265 rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &PeriphCCB, sizeof(union ccb), NULL);
266 if (RT_FAILURE(rc))
267 {
268 Log(("Error while querying available periph devices rc=%Rrc\n", rc));
269 break;
270 }
271
272 for (iPeriphMatch = 0; iPeriphMatch < PeriphCCB.cdm.num_matches; iPeriphMatch++)
273 {
274 if ( (aPeriphMatches[iPeriphMatch].type == DEV_MATCH_PERIPH)
275 && (!strcmp(aPeriphMatches[iPeriphMatch].result.periph_result.periph_name, "cd")))
276 {
277 pPeriphResult = &aPeriphMatches[iPeriphMatch].result.periph_result;
278 break; /* We found the periph device */
279 }
280 }
281
282 if (iPeriphMatch < PeriphCCB.cdm.num_matches)
283 break;
284
285 } while ( (DeviceCCB.ccb_h.status == CAM_REQ_CMP)
286 && (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE));
287
288 if (pPeriphResult)
289 {
290 char szPath[RTPATH_MAX];
291 char szDesc[256];
292
293 RTStrPrintf(szPath, sizeof(szPath), "/dev/%s%d",
294 pPeriphResult->periph_name, pPeriphResult->unit_number);
295
296 /* Remove trailing white space. */
297 strLenRemoveTrailingWhiteSpace(pDevResult->inq_data.vendor,
298 sizeof(pDevResult->inq_data.vendor));
299 strLenRemoveTrailingWhiteSpace(pDevResult->inq_data.product,
300 sizeof(pDevResult->inq_data.product));
301
302 dvdCreateDeviceString(pDevResult->inq_data.vendor,
303 pDevResult->inq_data.product,
304 szDesc, sizeof(szDesc));
305
306 pList->push_back(DriveInfo(szPath, "", szDesc));
307 if (pfSuccess)
308 *pfSuccess = true;
309 }
310 }
311 }
312 } while ( (DeviceCCB.ccb_h.status == CAM_REQ_CMP)
313 && (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE));
314
315 RTMemFree(paMatches);
316 }
317 else
318 rc = VERR_NO_MEMORY;
319
320 RTFileClose(FileXpt);
321 }
322
323 return rc;
324}
325
326/**
327 * Extract the names of drives from an environment variable and add them to a
328 * list if they are valid.
329 * @returns iprt status code
330 * @param pcszVar the name of the environment variable. The variable
331 * value should be a list of device node names, separated
332 * by ':' characters.
333 * @param pList the list to append the drives found to
334 * @param isDVD are we looking for DVD drives or for floppies?
335 * @param pfSuccess this will be set to true if we found at least one drive
336 * and to false otherwise. Optional.
337 */
338static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
339 bool isDVD, bool *pfSuccess)
340{
341 AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
342 AssertPtrReturn(pList, VERR_INVALID_POINTER);
343 AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
344 LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar,
345 pList, isDVD, pfSuccess));
346 int rc = VINF_SUCCESS;
347 bool success = false;
348 char *pszFreeMe = RTEnvDupEx(RTENV_DEFAULT, pcszVar);
349
350 try
351 {
352 const char *pcszCurrent = pszFreeMe;
353 while (pcszCurrent && *pcszCurrent != '\0')
354 {
355 const char *pcszNext = strchr(pcszCurrent, ':');
356 char szPath[RTPATH_MAX], szReal[RTPATH_MAX];
357 char szDesc[256], szUdi[256];
358 if (pcszNext)
359 RTStrPrintf(szPath, sizeof(szPath), "%.*s",
360 pcszNext - pcszCurrent - 1, pcszCurrent);
361 else
362 RTStrPrintf(szPath, sizeof(szPath), "%s", pcszCurrent);
363 if (RT_SUCCESS(RTPathReal(szPath, szReal, sizeof(szReal))))
364 {
365 szUdi[0] = '\0'; /** @todo r=bird: missing a call to devValidateDevice() here and szUdi wasn't
366 * initialized because of that. Need proper fixing. */
367 pList->push_back(DriveInfo(szReal, szUdi, szDesc));
368 success = true;
369 }
370 pcszCurrent = pcszNext ? pcszNext + 1 : NULL;
371 }
372 if (pfSuccess != NULL)
373 *pfSuccess = success;
374 }
375 catch(std::bad_alloc &e)
376 {
377 rc = VERR_NO_MEMORY;
378 }
379 RTStrFree(pszFreeMe);
380 LogFlowFunc(("rc=%Rrc, success=%d\n", rc, success));
381 return rc;
382}
383
384#if 0
385int VBoxMainUSBDeviceInfo::UpdateDevices ()
386{
387 LogFlowThisFunc(("entered\n"));
388 int rc = VINF_SUCCESS;
389 bool success = false; /* Have we succeeded in finding anything yet? */
390 try
391 {
392 bool halSuccess = false;
393 mDeviceList.clear();
394#if defined(RT_OS_LINUX)
395#ifdef VBOX_WITH_DBUS
396 if ( RT_SUCCESS(rc)
397 && RT_SUCCESS(RTDBusLoadLib())
398 && (!success || testing()))
399 rc = getUSBDeviceInfoFromHal(&mDeviceList, &halSuccess);
400 /* Try the old API if the new one *succeeded* as only one of them will
401 * pick up devices anyway. */
402 if (RT_SUCCESS(rc) && halSuccess && (!success || testing()))
403 rc = getOldUSBDeviceInfoFromHal(&mDeviceList, &halSuccess);
404 if (!success)
405 success = halSuccess;
406#endif /* VBOX_WITH_DBUS defined */
407#endif /* RT_OS_LINUX */
408 }
409 catch(std::bad_alloc &e)
410 {
411 rc = VERR_NO_MEMORY;
412 }
413 LogFlowThisFunc(("rc=%Rrc\n", rc));
414 return rc;
415}
416
417struct VBoxMainHotplugWaiter::Context
418{
419#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
420 /** The connection to DBus */
421 RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> mConnection;
422 /** Semaphore which is set when a device is hotplugged and reset when
423 * it is read. */
424 volatile bool mTriggered;
425 /** A flag to say that we wish to interrupt the current wait. */
426 volatile bool mInterrupt;
427 /** Constructor */
428 Context() : mTriggered(false), mInterrupt(false) {}
429#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
430};
431
432/* This constructor sets up a private connection to the DBus daemon, connects
433 * to the hal service and installs a filter which sets the mTriggered flag in
434 * the Context structure when a device (not necessarily USB) is added or
435 * removed. */
436VBoxMainHotplugWaiter::VBoxMainHotplugWaiter ()
437{
438#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
439 int rc = VINF_SUCCESS;
440
441 mContext = new Context;
442 if (RT_SUCCESS(RTDBusLoadLib()))
443 {
444 for (unsigned i = 0; RT_SUCCESS(rc) && i < 5 && !mContext->mConnection; ++i)
445 {
446 rc = halInitPrivate (&mContext->mConnection);
447 }
448 if (!mContext->mConnection)
449 rc = VERR_NOT_SUPPORTED;
450 DBusMessage *pMessage;
451 while ( RT_SUCCESS(rc)
452 && (pMessage = dbus_connection_pop_message (mContext->mConnection.get())) != NULL)
453 dbus_message_unref (pMessage); /* empty the message queue. */
454 if ( RT_SUCCESS(rc)
455 && !dbus_connection_add_filter (mContext->mConnection.get(),
456 dbusFilterFunction,
457 (void *) &mContext->mTriggered, NULL))
458 rc = VERR_NO_MEMORY;
459 if (RT_FAILURE(rc))
460 mContext->mConnection.reset();
461 }
462#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
463}
464
465/* Destructor */
466VBoxMainHotplugWaiter::~VBoxMainHotplugWaiter ()
467{
468#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
469 if (!!mContext->mConnection)
470 dbus_connection_remove_filter (mContext->mConnection.get(), dbusFilterFunction,
471 (void *) &mContext->mTriggered);
472 delete mContext;
473#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
474}
475
476/* Currently this is implemented using a timed out wait on our private DBus
477 * connection. Because the connection is private we don't have to worry about
478 * blocking other users. */
479int VBoxMainHotplugWaiter::Wait(RTMSINTERVAL cMillies)
480{
481 int rc = VINF_SUCCESS;
482#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
483 if (!mContext->mConnection)
484 rc = VERR_NOT_SUPPORTED;
485 bool connected = true;
486 mContext->mTriggered = false;
487 mContext->mInterrupt = false;
488 unsigned cRealMillies;
489 if (cMillies != RT_INDEFINITE_WAIT)
490 cRealMillies = cMillies;
491 else
492 cRealMillies = DBUS_POLL_TIMEOUT;
493 while ( RT_SUCCESS(rc) && connected && !mContext->mTriggered
494 && !mContext->mInterrupt)
495 {
496 connected = dbus_connection_read_write_dispatch (mContext->mConnection.get(),
497 cRealMillies);
498 if (mContext->mInterrupt)
499 LogFlowFunc(("wait loop interrupted\n"));
500 if (cMillies != RT_INDEFINITE_WAIT)
501 mContext->mInterrupt = true;
502 }
503 if (!connected)
504 rc = VERR_TRY_AGAIN;
505#else /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
506 rc = VERR_NOT_IMPLEMENTED;
507#endif /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
508 return rc;
509}
510
511/* Set a flag to tell the Wait not to resume next time it times out. */
512void VBoxMainHotplugWaiter::Interrupt()
513{
514#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
515 LogFlowFunc(("\n"));
516 mContext->mInterrupt = true;
517#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
518}
519#endif
520
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