VirtualBox

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

Last change on this file since 48967 was 48424, checked in by vboxsync, 12 years ago

Main: remove unnecessary includes

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

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