VirtualBox

source: vbox/trunk/src/VBox/Main/freebsd/HostHardwareFreeBSD.cpp@ 25275

Last change on this file since 25275 was 23973, checked in by vboxsync, 15 years ago

*,RTFileOpen: Fixing RTFileOpen flag misdesign: The deny, access and action flags are mandatory now.

  • 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 23973 2009-10-22 12:34:22Z vboxsync $ */
2/** @file
3 * Classes for handling hardware detection under FreeBSD.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
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 * 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.
20 */
21
22#define LOG_GROUP LOG_GROUP_MAIN
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27
28#include <HostHardwareLinux.h>
29
30#include <VBox/log.h>
31
32#include <iprt/dir.h>
33#include <iprt/env.h>
34#include <iprt/file.h>
35#include <iprt/mem.h>
36#include <iprt/param.h>
37#include <iprt/path.h>
38#include <iprt/thread.h> /* for RTThreadSleep() */
39#include <iprt/string.h>
40
41#ifdef RT_OS_FREEBSD
42# include <sys/types.h>
43# include <sys/stat.h>
44# include <unistd.h>
45# include <sys/ioctl.h>
46# include <fcntl.h>
47# include <cam/cam.h>
48# include <cam/cam_ccb.h>
49# include <cam/scsi/scsi_pass.h>
50#endif /* RT_OS_FREEBSD */
51#include <vector>
52
53/******************************************************************************
54* Typedefs and Defines *
55******************************************************************************/
56
57static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
58 bool isDVD, bool *pfSuccess);
59static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess);
60
61/** Find the length of a string, ignoring trailing non-ascii or control
62 * characters */
63static size_t strLenStripped(const char *pcsz)
64{
65 size_t cch = 0;
66 for (size_t i = 0; pcsz[i] != '\0'; ++i)
67 if (pcsz[i] > 32 && pcsz[i] < 127)
68 cch = i;
69 return cch + 1;
70}
71
72static void strLenRemoveTrailingWhiteSpace(char *psz, size_t cchStr)
73{
74 while ( (cchStr > 0)
75 && (psz[cchStr -1] == ' '))
76 psz[--cchStr] = '\0';
77}
78
79/**
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
86 */
87/* static */
88void dvdCreateDeviceString(const char *pcszVendor, const char *pcszModel,
89 char *pszDesc, size_t cchDesc)
90{
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);
97
98 /* Construct the description string as "Vendor Product" */
99 if (pszDesc)
100 {
101 if (cchVendor > 0)
102 RTStrPrintf(pszDesc, cchDesc, "%.*s %s", cchVendor, pcszVendor,
103 cchModel > 0 ? pcszModel : "(unknown drive model)");
104 else
105 RTStrPrintf(pszDesc, cchDesc, "%s", pcszModel);
106 }
107}
108
109
110int VBoxMainDriveInfo::updateDVDs ()
111{
112 LogFlowThisFunc(("entered\n"));
113 int rc = VINF_SUCCESS;
114 bool fSuccess = false; /* Have we succeeded in finding anything yet? */
115
116 try
117 {
118 mDVDList.clear ();
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 */,
123 &fSuccess);
124 if (RT_SUCCESS(rc) && !fSuccess)
125 rc = getDVDInfoFromCAM(&mDVDList, &fSuccess);
126 }
127 catch(std::bad_alloc &e)
128 {
129 rc = VERR_NO_MEMORY;
130 }
131 LogFlowThisFunc(("rc=%Rrc\n", rc));
132 return rc;
133}
134
135int VBoxMainDriveInfo::updateFloppies ()
136{
137 LogFlowThisFunc(("entered\n"));
138 int rc = VINF_SUCCESS;
139 bool fSuccess = false; /* Have we succeeded in finding anything yet? */
140
141 try
142 {
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 */,
148 &fSuccess);
149 }
150 catch(std::bad_alloc &e)
151 {
152 rc = VERR_NO_MEMORY;
153 }
154 LogFlowThisFunc(("rc=%Rrc\n", rc));
155 return rc;
156}
157
158/**
159 * Search for available CD/DVD drives using the CAM layer.
160 *
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.
165 */
166static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess)
167{
168 int rc = VINF_SUCCESS;
169 RTFILE FileXpt;
170
171 rc = RTFileOpen(&FileXpt, "/dev/xpt0", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
172 if (RT_SUCCESS(rc))
173 {
174 union ccb DeviceCCB;
175 struct dev_match_pattern DeviceMatchPattern;
176 struct dev_match_result *paMatches = NULL;
177
178 memset(&DeviceCCB, 0, sizeof(union ccb));
179 memset(&DeviceMatchPattern, 0, sizeof(struct device_match_pattern));
180
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;
186
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;
201
202 /*
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.
206 */
207 paMatches = (struct dev_match_result *)RTMemAllocZ(10 * sizeof(struct dev_match_result));
208 if (paMatches)
209 {
210 DeviceCCB.cdm.num_matches = 0;
211 DeviceCCB.cdm.match_buf_len = 10 * sizeof(struct dev_match_result);
212 DeviceCCB.cdm.matches = paMatches;
213
214 do
215 {
216 rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &DeviceCCB, sizeof(union ccb), NULL);
217 if (RT_FAILURE(rc))
218 {
219 Log(("Error while querying available CD/DVD devices rc=%Rrc\n", rc));
220 break;
221 }
222
223 for (unsigned i = 0; i < DeviceCCB.cdm.num_matches; i++)
224 {
225 if (paMatches[i].type == DEV_MATCH_DEVICE)
226 {
227 /* We have the drive now but need the appropriate device node */
228 struct device_match_result *pDevResult = &paMatches[i].result.device_result;
229 union ccb PeriphCCB;
230 struct dev_match_pattern PeriphMatchPattern;
231 struct dev_match_result aPeriphMatches[2];
232 struct periph_match_result *pPeriphResult = NULL;
233 unsigned iPeriphMatch = 0;
234
235 memset(&PeriphCCB, 0, sizeof(union ccb));
236 memset(&PeriphMatchPattern, 0, sizeof(struct dev_match_pattern));
237 memset(aPeriphMatches, 0, sizeof(aPeriphMatches));
238
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;
244
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;
257
258 do
259 {
260 rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &PeriphCCB, sizeof(union ccb), NULL);
261 if (RT_FAILURE(rc))
262 {
263 Log(("Error while querying available periph devices rc=%Rrc\n", rc));
264 break;
265 }
266
267 for (iPeriphMatch = 0; iPeriphMatch < PeriphCCB.cdm.num_matches; iPeriphMatch++)
268 {
269 if ( (aPeriphMatches[iPeriphMatch].type == DEV_MATCH_PERIPH)
270 && (!strcmp(aPeriphMatches[iPeriphMatch].result.periph_result.periph_name, "cd")))
271 {
272 pPeriphResult = &aPeriphMatches[iPeriphMatch].result.periph_result;
273 break; /* We found the periph device */
274 }
275 }
276
277 if (iPeriphMatch < PeriphCCB.cdm.num_matches)
278 break;
279
280 } while ( (DeviceCCB.ccb_h.status == CAM_REQ_CMP)
281 && (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE));
282
283 if (pPeriphResult)
284 {
285 char szPath[RTPATH_MAX];
286 char szDesc[256];
287
288 RTStrPrintf(szPath, sizeof(szPath), "/dev/%s%d",
289 pPeriphResult->periph_name, pPeriphResult->unit_number);
290
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));
296
297 dvdCreateDeviceString(pDevResult->inq_data.vendor,
298 pDevResult->inq_data.product,
299 szDesc, sizeof(szDesc));
300
301 pList->push_back(DriveInfo(szPath, NULL, szDesc));
302 if (pfSuccess)
303 *pfSuccess = true;
304 }
305 }
306 }
307 } while ( (DeviceCCB.ccb_h.status == CAM_REQ_CMP)
308 && (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE));
309
310 RTMemFree(paMatches);
311 }
312 else
313 rc = VERR_NO_MEMORY;
314
315 RTFileClose(FileXpt);
316 }
317
318 return rc;
319}
320
321/**
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
327 * by ':' characters.
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.
332 */
333static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
334 bool isDVD, bool *pfSuccess)
335{
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;
343
344 try
345 {
346 const char *pcszCurrent = RTEnvGet (pcszVar);
347 while (pcszCurrent && *pcszCurrent != '\0')
348 {
349 const char *pcszNext = strchr(pcszCurrent, ':');
350 char szPath[RTPATH_MAX], szReal[RTPATH_MAX];
351 char szDesc[256], szUdi[256];
352 if (pcszNext)
353 RTStrPrintf(szPath, sizeof(szPath), "%.*s",
354 pcszNext - pcszCurrent - 1, pcszCurrent);
355 else
356 RTStrPrintf(szPath, sizeof(szPath), "%s", pcszCurrent);
357 if (RT_SUCCESS(RTPathReal(szPath, szReal, sizeof(szReal))))
358 {
359 pList->push_back(DriveInfo(szReal, szUdi, szDesc));
360 success = true;
361 }
362 pcszCurrent = pcszNext ? pcszNext + 1 : NULL;
363 }
364 if (pfSuccess != NULL)
365 *pfSuccess = success;
366 }
367 catch(std::bad_alloc &e)
368 {
369 rc = VERR_NO_MEMORY;
370 }
371 LogFlowFunc (("rc=%Rrc, success=%d\n", rc, success));
372 return rc;
373}
374
375#if 0
376int VBoxMainUSBDeviceInfo::UpdateDevices ()
377{
378 LogFlowThisFunc(("entered\n"));
379 int rc = VINF_SUCCESS;
380 bool success = false; /* Have we succeeded in finding anything yet? */
381 try
382 {
383 bool halSuccess = false;
384 mDeviceList.clear();
385#if defined(RT_OS_LINUX)
386#ifdef VBOX_WITH_DBUS
387 if ( RT_SUCCESS(rc)
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);
395 if (!success)
396 success = halSuccess;
397#endif /* VBOX_WITH_DBUS defined */
398#endif /* RT_OS_LINUX */
399 }
400 catch(std::bad_alloc &e)
401 {
402 rc = VERR_NO_MEMORY;
403 }
404 LogFlowThisFunc(("rc=%Rrc\n", rc));
405 return rc;
406}
407
408struct VBoxMainHotplugWaiter::Context
409{
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
414 * it is read. */
415 volatile bool mTriggered;
416 /** A flag to say that we wish to interrupt the current wait. */
417 volatile bool mInterrupt;
418 /** Constructor */
419 Context() : mTriggered(false), mInterrupt(false) {}
420#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
421};
422
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
426 * removed. */
427VBoxMainHotplugWaiter::VBoxMainHotplugWaiter ()
428{
429#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
430 int rc = VINF_SUCCESS;
431
432 mContext = new Context;
433 if (RT_SUCCESS(RTDBusLoadLib()))
434 {
435 for (unsigned i = 0; RT_SUCCESS(rc) && i < 5 && !mContext->mConnection; ++i)
436 {
437 rc = halInitPrivate (&mContext->mConnection);
438 }
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. */
445 if ( RT_SUCCESS(rc)
446 && !dbus_connection_add_filter (mContext->mConnection.get(),
447 dbusFilterFunction,
448 (void *) &mContext->mTriggered, NULL))
449 rc = VERR_NO_MEMORY;
450 if (RT_FAILURE(rc))
451 mContext->mConnection.reset();
452 }
453#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
454}
455
456/* Destructor */
457VBoxMainHotplugWaiter::~VBoxMainHotplugWaiter ()
458{
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);
463 delete mContext;
464#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
465}
466
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. */
470int VBoxMainHotplugWaiter::Wait(unsigned cMillies)
471{
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;
482 else
483 cRealMillies = DBUS_POLL_TIMEOUT;
484 while ( RT_SUCCESS(rc) && connected && !mContext->mTriggered
485 && !mContext->mInterrupt)
486 {
487 connected = dbus_connection_read_write_dispatch (mContext->mConnection.get(),
488 cRealMillies);
489 if (mContext->mInterrupt)
490 LogFlowFunc(("wait loop interrupted\n"));
491 if (cMillies != RT_INDEFINITE_WAIT)
492 mContext->mInterrupt = true;
493 }
494 if (!connected)
495 rc = VERR_TRY_AGAIN;
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) */
499 return rc;
500}
501
502/* Set a flag to tell the Wait not to resume next time it times out. */
503void VBoxMainHotplugWaiter::Interrupt()
504{
505#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
506 LogFlowFunc(("\n"));
507 mContext->mInterrupt = true;
508#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
509}
510#endif
511
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use