1
/* $Id: HostHardwareFreeBSD.cpp $ */
3
* Classes for handling hardware detection under FreeBSD.
7
* Copyright (C) 2008 Sun Microsystems, Inc.
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.
17
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18
* Clara, CA 95054 USA or visit http://www.sun.com if you need
19
* additional information or have any questions.
22
#define LOG_GROUP LOG_GROUP_MAIN
24
/*******************************************************************************
26
*******************************************************************************/
28
#include <HostHardwareLinux.h>
34
#include <iprt/file.h>
36
#include <iprt/param.h>
37
#include <iprt/path.h>
38
#include <iprt/thread.h> /* for RTThreadSleep() */
39
#include <iprt/string.h>
42
# include <sys/types.h>
43
# include <sys/stat.h>
45
# include <sys/ioctl.h>
48
# include <cam/cam_ccb.h>
49
# include <cam/scsi/scsi_pass.h>
50
#endif /* RT_OS_FREEBSD */
53
/******************************************************************************
54
* Typedefs and Defines *
55
******************************************************************************/
57
static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
58
bool isDVD, bool *pfSuccess);
59
static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess);
61
/** Find the length of a string, ignoring trailing non-ascii or control
63
static size_t strLenStripped(const char *pcsz)
66
for (size_t i = 0; pcsz[i] != '\0'; ++i)
67
if (pcsz[i] > 32 && pcsz[i] < 127)
72
static void strLenRemoveTrailingWhiteSpace(char *psz, size_t cchStr)
75
&& (psz[cchStr -1] == ' '))
80
* Initialise the device description for a DVD drive based on
81
* vendor and model name strings.
82
* @param pcszVendor the vendor ID string
83
* @param pcszModel the product ID string
84
* @param pszDesc where to store the description string (optional)
85
* @param cchDesc the size of the buffer in @pszDesc
88
void dvdCreateDeviceString(const char *pcszVendor, const char *pcszModel,
89
char *pszDesc, size_t cchDesc)
91
AssertPtrReturnVoid(pcszVendor);
92
AssertPtrReturnVoid(pcszModel);
93
AssertPtrNullReturnVoid(pszDesc);
94
AssertReturnVoid(!pszDesc || cchDesc > 0);
95
size_t cchVendor = strLenStripped(pcszVendor);
96
size_t cchModel = strLenStripped(pcszModel);
98
/* Construct the description string as "Vendor Product" */
102
RTStrPrintf(pszDesc, cchDesc, "%.*s %s", cchVendor, pcszVendor,
103
cchModel > 0 ? pcszModel : "(unknown drive model)");
105
RTStrPrintf(pszDesc, cchDesc, "%s", pcszModel);
110
int VBoxMainDriveInfo::updateDVDs ()
112
LogFlowThisFunc(("entered\n"));
113
int rc = VINF_SUCCESS;
114
bool fSuccess = false; /* Have we succeeded in finding anything yet? */
119
/* Always allow the user to override our auto-detection using an
120
* environment variable. */
121
if (RT_SUCCESS(rc) && !fSuccess)
122
rc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */,
124
if (RT_SUCCESS(rc) && !fSuccess)
125
rc = getDVDInfoFromCAM(&mDVDList, &fSuccess);
127
catch(std::bad_alloc &e)
131
LogFlowThisFunc(("rc=%Rrc\n", rc));
135
int VBoxMainDriveInfo::updateFloppies ()
137
LogFlowThisFunc(("entered\n"));
138
int rc = VINF_SUCCESS;
139
bool fSuccess = false; /* Have we succeeded in finding anything yet? */
143
mFloppyList.clear ();
144
/* Always allow the user to override our auto-detection using an
145
* environment variable. */
146
if (RT_SUCCESS(rc) && !fSuccess)
147
rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */,
150
catch(std::bad_alloc &e)
154
LogFlowThisFunc(("rc=%Rrc\n", rc));
159
* Search for available CD/DVD drives using the CAM layer.
161
* @returns iprt status code
162
* @param pList the list to append the drives found to
163
* @param pfSuccess this will be set to true if we found at least one drive
164
* and to false otherwise. Optional.
166
static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess)
168
int rc = VINF_SUCCESS;
171
rc = RTFileOpen(&FileXpt, "/dev/xpt0", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
175
struct dev_match_pattern DeviceMatchPattern;
176
struct dev_match_result *paMatches = NULL;
178
memset(&DeviceCCB, 0, sizeof(union ccb));
179
memset(&DeviceMatchPattern, 0, sizeof(struct device_match_pattern));
181
/* We want to get all devices. */
182
DeviceCCB.ccb_h.func_code = XPT_DEV_MATCH;
183
DeviceCCB.ccb_h.path_id = CAM_XPT_PATH_ID;
184
DeviceCCB.ccb_h.target_id = CAM_TARGET_WILDCARD;
185
DeviceCCB.ccb_h.target_lun = CAM_LUN_WILDCARD;
187
/* Setup the pattern */
188
DeviceMatchPattern.type = DEV_MATCH_DEVICE;
189
DeviceMatchPattern.pattern.device_pattern.path_id = CAM_XPT_PATH_ID;
190
DeviceMatchPattern.pattern.device_pattern.target_id = CAM_TARGET_WILDCARD;
191
DeviceMatchPattern.pattern.device_pattern.target_lun = CAM_LUN_WILDCARD;
192
DeviceMatchPattern.pattern.device_pattern.flags = DEV_MATCH_INQUIRY;
193
DeviceMatchPattern.pattern.device_pattern.inq_pat.type = T_CDROM;
194
DeviceMatchPattern.pattern.device_pattern.inq_pat.media_type = SIP_MEDIA_REMOVABLE | SIP_MEDIA_FIXED;
195
DeviceMatchPattern.pattern.device_pattern.inq_pat.vendor[0] = '*'; /* Matches anything */
196
DeviceMatchPattern.pattern.device_pattern.inq_pat.product[0] = '*'; /* Matches anything */
197
DeviceMatchPattern.pattern.device_pattern.inq_pat.revision[0] = '*'; /* Matches anything */
198
DeviceCCB.cdm.num_patterns = 1;
199
DeviceCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result);
200
DeviceCCB.cdm.patterns = &DeviceMatchPattern;
203
* Allocate the buffer holding the matches.
204
* We will allocate for 10 results and call
205
* CAM multiple times if we have more results.
207
paMatches = (struct dev_match_result *)RTMemAllocZ(10 * sizeof(struct dev_match_result));
210
DeviceCCB.cdm.num_matches = 0;
211
DeviceCCB.cdm.match_buf_len = 10 * sizeof(struct dev_match_result);
212
DeviceCCB.cdm.matches = paMatches;
216
rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &DeviceCCB, sizeof(union ccb), NULL);
219
Log(("Error while querying available CD/DVD devices rc=%Rrc\n", rc));
223
for (unsigned i = 0; i < DeviceCCB.cdm.num_matches; i++)
225
if (paMatches[i].type == DEV_MATCH_DEVICE)
227
/* We have the drive now but need the appropriate device node */
228
struct device_match_result *pDevResult = &paMatches[i].result.device_result;
230
struct dev_match_pattern PeriphMatchPattern;
231
struct dev_match_result aPeriphMatches[2];
232
struct periph_match_result *pPeriphResult = NULL;
233
unsigned iPeriphMatch = 0;
235
memset(&PeriphCCB, 0, sizeof(union ccb));
236
memset(&PeriphMatchPattern, 0, sizeof(struct dev_match_pattern));
237
memset(aPeriphMatches, 0, sizeof(aPeriphMatches));
239
/* This time we only want the specific nodes for the device. */
240
PeriphCCB.ccb_h.func_code = XPT_DEV_MATCH;
241
PeriphCCB.ccb_h.path_id = paMatches[i].result.device_result.path_id;
242
PeriphCCB.ccb_h.target_id = paMatches[i].result.device_result.target_id;
243
PeriphCCB.ccb_h.target_lun = paMatches[i].result.device_result.target_lun;
245
/* Setup the pattern */
246
PeriphMatchPattern.type = DEV_MATCH_PERIPH;
247
PeriphMatchPattern.pattern.periph_pattern.path_id = paMatches[i].result.device_result.path_id;
248
PeriphMatchPattern.pattern.periph_pattern.target_id = paMatches[i].result.device_result.target_id;
249
PeriphMatchPattern.pattern.periph_pattern.target_lun = paMatches[i].result.device_result.target_lun;
250
PeriphMatchPattern.pattern.periph_pattern.flags = PERIPH_MATCH_PATH | PERIPH_MATCH_TARGET | PERIPH_MATCH_LUN;
251
PeriphCCB.cdm.num_patterns = 1;
252
PeriphCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result);
253
PeriphCCB.cdm.patterns = &PeriphMatchPattern;
254
PeriphCCB.cdm.num_matches = 0;
255
PeriphCCB.cdm.match_buf_len = sizeof(aPeriphMatches);
256
PeriphCCB.cdm.matches = aPeriphMatches;
260
rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &PeriphCCB, sizeof(union ccb), NULL);
263
Log(("Error while querying available periph devices rc=%Rrc\n", rc));
267
for (iPeriphMatch = 0; iPeriphMatch < PeriphCCB.cdm.num_matches; iPeriphMatch++)
269
if ( (aPeriphMatches[iPeriphMatch].type == DEV_MATCH_PERIPH)
270
&& (!strcmp(aPeriphMatches[iPeriphMatch].result.periph_result.periph_name, "cd")))
272
pPeriphResult = &aPeriphMatches[iPeriphMatch].result.periph_result;
273
break; /* We found the periph device */
277
if (iPeriphMatch < PeriphCCB.cdm.num_matches)
280
} while ( (DeviceCCB.ccb_h.status == CAM_REQ_CMP)
281
&& (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE));
285
char szPath[RTPATH_MAX];
288
RTStrPrintf(szPath, sizeof(szPath), "/dev/%s%d",
289
pPeriphResult->periph_name, pPeriphResult->unit_number);
291
/* Remove trailing white space. */
292
strLenRemoveTrailingWhiteSpace(pDevResult->inq_data.vendor,
293
sizeof(pDevResult->inq_data.vendor));
294
strLenRemoveTrailingWhiteSpace(pDevResult->inq_data.product,
295
sizeof(pDevResult->inq_data.product));
297
dvdCreateDeviceString(pDevResult->inq_data.vendor,
298
pDevResult->inq_data.product,
299
szDesc, sizeof(szDesc));
301
pList->push_back(DriveInfo(szPath, NULL, szDesc));
307
} while ( (DeviceCCB.ccb_h.status == CAM_REQ_CMP)
308
&& (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE));
310
RTMemFree(paMatches);
315
RTFileClose(FileXpt);
322
* Extract the names of drives from an environment variable and add them to a
323
* list if they are valid.
324
* @returns iprt status code
325
* @param pcszVar the name of the environment variable. The variable
326
* value should be a list of device node names, separated
328
* @param pList the list to append the drives found to
329
* @param isDVD are we looking for DVD drives or for floppies?
330
* @param pfSuccess this will be set to true if we found at least one drive
331
* and to false otherwise. Optional.
333
static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
334
bool isDVD, bool *pfSuccess)
336
AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
337
AssertPtrReturn(pList, VERR_INVALID_POINTER);
338
AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
339
LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar,
340
pList, isDVD, pfSuccess));
341
int rc = VINF_SUCCESS;
342
bool success = false;
346
const char *pcszCurrent = RTEnvGet (pcszVar);
347
while (pcszCurrent && *pcszCurrent != '\0')
349
const char *pcszNext = strchr(pcszCurrent, ':');
350
char szPath[RTPATH_MAX], szReal[RTPATH_MAX];
351
char szDesc[256], szUdi[256];
353
RTStrPrintf(szPath, sizeof(szPath), "%.*s",
354
pcszNext - pcszCurrent - 1, pcszCurrent);
356
RTStrPrintf(szPath, sizeof(szPath), "%s", pcszCurrent);
357
if (RT_SUCCESS(RTPathReal(szPath, szReal, sizeof(szReal))))
359
pList->push_back(DriveInfo(szReal, szUdi, szDesc));
362
pcszCurrent = pcszNext ? pcszNext + 1 : NULL;
364
if (pfSuccess != NULL)
365
*pfSuccess = success;
367
catch(std::bad_alloc &e)
371
LogFlowFunc (("rc=%Rrc, success=%d\n", rc, success));
376
int VBoxMainUSBDeviceInfo::UpdateDevices ()
378
LogFlowThisFunc(("entered\n"));
379
int rc = VINF_SUCCESS;
380
bool success = false; /* Have we succeeded in finding anything yet? */
383
bool halSuccess = false;
385
#if defined(RT_OS_LINUX)
386
#ifdef VBOX_WITH_DBUS
388
&& RT_SUCCESS(RTDBusLoadLib())
389
&& (!success || testing()))
390
rc = getUSBDeviceInfoFromHal(&mDeviceList, &halSuccess);
391
/* Try the old API if the new one *succeeded* as only one of them will
392
* pick up devices anyway. */
393
if (RT_SUCCESS(rc) && halSuccess && (!success || testing()))
394
rc = getOldUSBDeviceInfoFromHal(&mDeviceList, &halSuccess);
396
success = halSuccess;
397
#endif /* VBOX_WITH_DBUS defined */
398
#endif /* RT_OS_LINUX */
400
catch(std::bad_alloc &e)
404
LogFlowThisFunc(("rc=%Rrc\n", rc));
408
struct VBoxMainHotplugWaiter::Context
410
#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
411
/** The connection to DBus */
412
RTMemAutoPtr <DBusConnection, VBoxHalShutdownPrivate> mConnection;
413
/** Semaphore which is set when a device is hotplugged and reset when
415
volatile bool mTriggered;
416
/** A flag to say that we wish to interrupt the current wait. */
417
volatile bool mInterrupt;
419
Context() : mTriggered(false), mInterrupt(false) {}
420
#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
423
/* This constructor sets up a private connection to the DBus daemon, connects
424
* to the hal service and installs a filter which sets the mTriggered flag in
425
* the Context structure when a device (not necessarily USB) is added or
427
VBoxMainHotplugWaiter::VBoxMainHotplugWaiter ()
429
#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
430
int rc = VINF_SUCCESS;
432
mContext = new Context;
433
if (RT_SUCCESS(RTDBusLoadLib()))
435
for (unsigned i = 0; RT_SUCCESS(rc) && i < 5 && !mContext->mConnection; ++i)
437
rc = halInitPrivate (&mContext->mConnection);
439
if (!mContext->mConnection)
440
rc = VERR_NOT_SUPPORTED;
441
DBusMessage *pMessage;
442
while ( RT_SUCCESS(rc)
443
&& (pMessage = dbus_connection_pop_message (mContext->mConnection.get())) != NULL)
444
dbus_message_unref (pMessage); /* empty the message queue. */
446
&& !dbus_connection_add_filter (mContext->mConnection.get(),
448
(void *) &mContext->mTriggered, NULL))
451
mContext->mConnection.reset();
453
#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
457
VBoxMainHotplugWaiter::~VBoxMainHotplugWaiter ()
459
#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
460
if (!!mContext->mConnection)
461
dbus_connection_remove_filter (mContext->mConnection.get(), dbusFilterFunction,
462
(void *) &mContext->mTriggered);
464
#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
467
/* Currently this is implemented using a timed out wait on our private DBus
468
* connection. Because the connection is private we don't have to worry about
469
* blocking other users. */
470
int VBoxMainHotplugWaiter::Wait(unsigned cMillies)
472
int rc = VINF_SUCCESS;
473
#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
474
if (!mContext->mConnection)
475
rc = VERR_NOT_SUPPORTED;
476
bool connected = true;
477
mContext->mTriggered = false;
478
mContext->mInterrupt = false;
479
unsigned cRealMillies;
480
if (cMillies != RT_INDEFINITE_WAIT)
481
cRealMillies = cMillies;
483
cRealMillies = DBUS_POLL_TIMEOUT;
484
while ( RT_SUCCESS(rc) && connected && !mContext->mTriggered
485
&& !mContext->mInterrupt)
487
connected = dbus_connection_read_write_dispatch (mContext->mConnection.get(),
489
if (mContext->mInterrupt)
490
LogFlowFunc(("wait loop interrupted\n"));
491
if (cMillies != RT_INDEFINITE_WAIT)
492
mContext->mInterrupt = true;
496
#else /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
497
rc = VERR_NOT_IMPLEMENTED;
498
#endif /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
502
/* Set a flag to tell the Wait not to resume next time it times out. */
503
void VBoxMainHotplugWaiter::Interrupt()
505
#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
507
mContext->mInterrupt = true;
508
#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */