2 * Copyright (C) 2005-2011 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "Peripherals.h"
23 #include "bus/PeripheralBus.h"
24 #include "devices/PeripheralBluetooth.h"
25 #include "devices/PeripheralDisk.h"
26 #include "devices/PeripheralHID.h"
27 #include "devices/PeripheralNIC.h"
28 #include "devices/PeripheralNyxboard.h"
29 #include "devices/PeripheralTuner.h"
30 #if defined(HAVE_LIBCEC)
31 #include "devices/PeripheralCecAdapter.h"
33 #include "bus/PeripheralBusUSB.h"
34 #include "dialogs/GUIDialogPeripheralManager.h"
36 #include "threads/SingleLock.h"
37 #include "utils/log.h"
38 #include "utils/XMLUtils.h"
39 #include "settings/GUISettings.h"
40 #include "tinyXML/tinyxml.h"
41 #include "filesystem/Directory.h"
42 #include "guilib/GUIWindowManager.h"
43 #include "guilib/LocalizeStrings.h"
44 #include "dialogs/GUIDialogKaiToast.h"
45 #include "utils/StringUtils.h"
47 using namespace PERIPHERALS;
48 using namespace XFILE;
51 CPeripherals::CPeripherals(void)
53 CDirectory::Create("special://profile/peripheral_data");
58 CPeripherals::~CPeripherals(void)
63 CPeripherals &CPeripherals::Get(void)
65 static CPeripherals peripheralsInstance;
66 return peripheralsInstance;
69 void CPeripherals::Initialise(void)
71 CSingleLock lock(m_critSection);
76 /* load mappings from peripherals.xml */
79 #if defined(HAVE_PERIPHERAL_BUS_USB)
80 m_busses.push_back(new CPeripheralBusUSB(this));
83 /* initialise all known busses */
84 for (int iBusPtr = (int)m_busses.size() - 1; iBusPtr >= 0; iBusPtr--)
86 if (!m_busses.at(iBusPtr)->Initialise())
88 CLog::Log(LOGERROR, "%s - failed to initialise bus %s", __FUNCTION__, PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type()));
89 delete m_busses.at(iBusPtr);
90 m_busses.erase(m_busses.begin() + iBusPtr);
94 m_bInitialised = true;
98 void CPeripherals::Clear(void)
100 CSingleLock lock(m_critSection);
101 /* delete busses and devices */
102 for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
103 delete m_busses.at(iBusPtr);
106 /* delete mappings */
107 for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
108 m_mappings.at(iMappingPtr).m_settings.clear();
111 /* reset class state */
112 m_bIsStarted = false;
113 m_bInitialised = false;
116 void CPeripherals::TriggerDeviceScan(const PeripheralBusType type /* = PERIPHERAL_BUS_UNKNOWN */)
118 CSingleLock lock(m_critSection);
119 for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
121 if (type == PERIPHERAL_BUS_UNKNOWN || m_busses.at(iBusPtr)->Type() == type)
123 m_busses.at(iBusPtr)->TriggerDeviceScan();
124 if (type != PERIPHERAL_BUS_UNKNOWN)
130 CPeripheralBus *CPeripherals::GetBusByType(const PeripheralBusType type) const
132 CPeripheralBus *bus(NULL);
134 CSingleLock lock(m_critSection);
135 for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
137 if (m_busses.at(iBusPtr)->Type() == type)
139 bus = m_busses.at(iBusPtr);
147 CPeripheral *CPeripherals::GetPeripheralAtLocation(const CStdString &strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
149 CPeripheral *peripheral(NULL);
150 CSingleLock lock(m_critSection);
151 for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
153 /* check whether the bus matches if a bus type other than unknown was passed */
154 if (busType != PERIPHERAL_BUS_UNKNOWN && m_busses.at(iBusPtr)->Type() != busType)
157 /* return the first device that matches */
158 if ((peripheral = m_busses.at(iBusPtr)->GetPeripheral(strLocation)) != NULL)
165 bool CPeripherals::HasPeripheralAtLocation(const CStdString &strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
167 return (GetPeripheralAtLocation(strLocation, busType) != NULL);
170 CPeripheralBus *CPeripherals::GetBusWithDevice(const CStdString &strLocation) const
172 CSingleLock lock(m_critSection);
173 for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
175 /* return the first bus that matches */
176 if (m_busses.at(iBusPtr)->HasPeripheral(strLocation))
177 return m_busses.at(iBusPtr);
183 int CPeripherals::GetPeripheralsWithFeature(vector<CPeripheral *> &results, const PeripheralFeature feature, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
186 CSingleLock lock(m_critSection);
187 for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
189 /* check whether the bus matches if a bus type other than unknown was passed */
190 if (busType != PERIPHERAL_BUS_UNKNOWN && m_busses.at(iBusPtr)->Type() != busType)
193 iReturn += m_busses.at(iBusPtr)->GetPeripheralsWithFeature(results, feature);
199 bool CPeripherals::HasPeripheralWithFeature(const PeripheralFeature feature, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
201 vector<CPeripheral *> dummy;
202 return (GetPeripheralsWithFeature(dummy, feature, busType) > 0);
205 CPeripheral *CPeripherals::CreatePeripheral(CPeripheralBus &bus, const PeripheralType type, const CStdString &strLocation, int iVendorId /* = 0 */, int iProductId /* = 0 */)
207 CPeripheral *peripheral = NULL;
208 /* check whether there's something mapped in peripherals.xml */
209 PeripheralType mappedType = type;
210 CStdString strDeviceName;
211 int iMappingPtr = GetMappingForDevice(bus, type, iVendorId, iProductId);
212 bool bHasMapping(iMappingPtr >= 0);
215 mappedType = m_mappings[iMappingPtr].m_mappedTo;
216 strDeviceName = m_mappings[iMappingPtr].m_strDeviceName;
220 /* don't create instances for devices that aren't mapped in peripherals.xml */
227 peripheral = new CPeripheralHID(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
231 peripheral = new CPeripheralNIC(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
234 case PERIPHERAL_DISK:
235 peripheral = new CPeripheralDisk(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
238 case PERIPHERAL_NYXBOARD:
239 peripheral = new CPeripheralNyxboard(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
242 case PERIPHERAL_TUNER:
243 peripheral = new CPeripheralTuner(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
246 case PERIPHERAL_BLUETOOTH:
247 peripheral = new CPeripheralBluetooth(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
250 #if defined(HAVE_LIBCEC)
252 peripheral = new CPeripheralCecAdapter(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
262 /* try to initialise the new peripheral
263 * Initialise() will make sure that each device is only initialised once */
264 if (peripheral->Initialise())
267 peripheral->SetHidden(true);
268 bus.Register(peripheral);
272 CLog::Log(LOGDEBUG, "%s - failed to initialise peripheral on '%s'", __FUNCTION__, strLocation.c_str());
281 void CPeripherals::OnDeviceAdded(const CPeripheralBus &bus, const CStdString &strLocation)
283 CGUIDialogPeripheralManager *dialog = (CGUIDialogPeripheralManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PERIPHERAL_MANAGER);
284 if (dialog && dialog->IsActive())
287 CPeripheral *peripheral = GetByPath(strLocation);
289 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35005), peripheral->DeviceName());
292 void CPeripherals::OnDeviceDeleted(const CPeripheralBus &bus, const CStdString &strLocation)
294 CGUIDialogPeripheralManager *dialog = (CGUIDialogPeripheralManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PERIPHERAL_MANAGER);
295 if (dialog && dialog->IsActive())
298 CPeripheral *peripheral = GetByPath(strLocation);
300 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35006), peripheral->DeviceName());
303 int CPeripherals::GetMappingForDevice(const CPeripheralBus &bus, const PeripheralType classType, int iVendorId, int iProductId) const
305 /* check all mappings in the order in which they are defined in peripherals.xml */
306 for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
308 PeripheralDeviceMapping mapping = m_mappings.at(iMappingPtr);
309 bool bBusMatch = (mapping.m_busType == PERIPHERAL_BUS_UNKNOWN || mapping.m_busType == bus.Type());
310 bool bVendorMatch = (mapping.m_iVendorId == 0 || mapping.m_iVendorId == iVendorId);
311 bool bProductMatch = (mapping.m_iProductId == 0 || mapping.m_iProductId == iProductId);
312 bool bClassMatch = (mapping.m_class == PERIPHERAL_UNKNOWN || mapping.m_class == classType);
314 if (bBusMatch && bVendorMatch && bProductMatch && bClassMatch)
316 CLog::Log(LOGDEBUG, "%s - device (%s:%s) mapped to %s (type = %s)", __FUNCTION__, PeripheralTypeTranslator::IntToHexString(iVendorId), PeripheralTypeTranslator::IntToHexString(iProductId), mapping.m_strDeviceName.c_str(), PeripheralTypeTranslator::TypeToString(mapping.m_mappedTo));
324 void CPeripherals::GetSettingsFromMapping(CPeripheral &peripheral) const
326 /* check all mappings in the order in which they are defined in peripherals.xml */
327 for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
329 const PeripheralDeviceMapping *mapping = &m_mappings.at(iMappingPtr);
330 bool bBusMatch = (mapping->m_busType == PERIPHERAL_BUS_UNKNOWN || mapping->m_busType == peripheral.GetBusType());
331 bool bVendorMatch = (mapping->m_iVendorId == 0 || mapping->m_iVendorId == peripheral.VendorId());
332 bool bProductMatch = (mapping->m_iProductId == 0 || mapping->m_iProductId == peripheral.ProductId());
333 bool bClassMatch = (mapping->m_class == PERIPHERAL_UNKNOWN || mapping->m_class == peripheral.Type());
335 if (bBusMatch && bVendorMatch && bProductMatch && bClassMatch)
337 for (map<CStdString, CSetting *>::const_iterator itr = mapping->m_settings.begin(); itr != mapping->m_settings.end(); itr++)
338 peripheral.AddSetting((*itr).first, (*itr).second);
343 bool CPeripherals::LoadMappings(void)
345 TiXmlDocument xmlDoc;
346 if (!xmlDoc.LoadFile("special://xbmc/system/peripherals.xml"))
348 CLog::Log(LOGWARNING, "%s - peripherals.xml does not exist", __FUNCTION__);
352 TiXmlElement *pRootElement = xmlDoc.RootElement();
353 if (strcmpi(pRootElement->Value(), "peripherals") != 0)
355 CLog::Log(LOGERROR, "%s - peripherals.xml does not contain <peripherals>", __FUNCTION__);
359 TiXmlElement *currentNode = pRootElement->FirstChildElement("peripheral");
362 PeripheralDeviceMapping mapping;
364 mapping.m_iVendorId = currentNode->Attribute("vendor") ? PeripheralTypeTranslator::HexStringToInt(currentNode->Attribute("vendor")) : 0;
365 mapping.m_iProductId = currentNode->Attribute("product") ? PeripheralTypeTranslator::HexStringToInt(currentNode->Attribute("product")) : 0;
366 mapping.m_busType = PeripheralTypeTranslator::GetBusTypeFromString(currentNode->Attribute("bus"));
367 mapping.m_class = PeripheralTypeTranslator::GetTypeFromString(currentNode->Attribute("class"));
368 mapping.m_strDeviceName = currentNode->Attribute("name") ? CStdString(currentNode->Attribute("name")) : StringUtils::EmptyString;
369 mapping.m_mappedTo = PeripheralTypeTranslator::GetTypeFromString(currentNode->Attribute("mapTo"));
370 GetSettingsFromMappingsFile(currentNode, mapping.m_settings);
372 m_mappings.push_back(mapping);
373 currentNode = currentNode->NextSiblingElement("peripheral");
379 void CPeripherals::GetSettingsFromMappingsFile(TiXmlElement *xmlNode, map<CStdString, CSetting *> &m_settings)
381 TiXmlElement *currentNode = xmlNode->FirstChildElement("setting");
384 CSetting *setting = NULL;
385 CStdString strKey(currentNode->Attribute("key"));
386 if (strKey.IsEmpty())
389 CStdString strSettingsType(currentNode->Attribute("type"));
390 int iLabelId = currentNode->Attribute("label") ? atoi(currentNode->Attribute("label")) : -1;
391 bool bConfigurable = (!currentNode->Attribute("configurable") ||
392 strcmp(currentNode->Attribute("configurable"), "") == 0 ||
393 (strcmp(currentNode->Attribute("configurable"), "no") != 0 &&
394 strcmp(currentNode->Attribute("configurable"), "false") != 0 &&
395 strcmp(currentNode->Attribute("configurable"), "0") != 0));
396 if (strSettingsType.Equals("bool"))
398 bool bValue = (strcmp(currentNode->Attribute("value"), "no") != 0 &&
399 strcmp(currentNode->Attribute("value"), "false") != 0 &&
400 strcmp(currentNode->Attribute("value"), "0") != 0);
401 setting = new CSettingBool(0, strKey, iLabelId, bValue, CHECKMARK_CONTROL);
403 else if (strSettingsType.Equals("int"))
405 int iValue = currentNode->Attribute("value") ? atoi(currentNode->Attribute("value")) : 0;
406 int iMin = currentNode->Attribute("min") ? atoi(currentNode->Attribute("min")) : 0;
407 int iStep = currentNode->Attribute("step") ? atoi(currentNode->Attribute("step")) : 0;
408 int iMax = currentNode->Attribute("max") ? atoi(currentNode->Attribute("max")) : 0;
409 CStdString strFormat(currentNode->Attribute("format"));
410 setting = new CSettingInt(0, strKey, iLabelId, iValue, iMin, iStep, iMax, SPIN_CONTROL_INT, strFormat);
412 else if (strSettingsType.Equals("float"))
414 float fValue = currentNode->Attribute("value") ? atof(currentNode->Attribute("value")) : 0;
415 float fMin = currentNode->Attribute("min") ? atof(currentNode->Attribute("min")) : 0;
416 float fStep = currentNode->Attribute("step") ? atof(currentNode->Attribute("step")) : 0;
417 float fMax = currentNode->Attribute("max") ? atof(currentNode->Attribute("max")) : 0;
418 setting = new CSettingFloat(0, strKey, iLabelId, fValue, fMin, fStep, fMax, SPIN_CONTROL_FLOAT);
422 CStdString strValue(currentNode->Attribute("value"));
423 setting = new CSettingString(0, strKey, iLabelId, strValue, EDIT_CONTROL_INPUT, !bConfigurable, -1);
426 //TODO add more types if needed
427 setting->SetVisible(bConfigurable);
428 m_settings[strKey] = setting;
429 currentNode = currentNode->NextSiblingElement("setting");
433 void CPeripherals::GetDirectory(const CStdString &strPath, CFileItemList &items) const
435 if (!strPath.Left(14).Equals("peripherals://"))
438 CStdString strPathCut = strPath.Right(strPath.length() - 14);
439 CStdString strBus = strPathCut.Left(strPathCut.Find('/'));
441 CSingleLock lock(m_critSection);
442 for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
444 if (strBus.Equals("all") || strBus.Equals(PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type())))
445 m_busses.at(iBusPtr)->GetDirectory(strPath, items);
449 CPeripheral *CPeripherals::GetByPath(const CStdString &strPath) const
451 if (!strPath.Left(14).Equals("peripherals://"))
454 CStdString strPathCut = strPath.Right(strPath.length() - 14);
455 CStdString strBus = strPathCut.Left(strPathCut.Find('/'));
457 CSingleLock lock(m_critSection);
458 for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
460 if (strBus.Equals(PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type())))
461 return m_busses.at(iBusPtr)->GetByPath(strPath);