xref: /openbmc/dbus-sensors/src/ipmb/IpmbSensor.cpp (revision 73f6cdcfb63720f47822bbf626bb9ae72b791263)
1 /*
2 // Copyright (c) 2019 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "IpmbSensor.hpp"
18 
19 #include "IpmbSDRSensor.hpp"
20 #include "SensorPaths.hpp"
21 #include "Thresholds.hpp"
22 #include "Utils.hpp"
23 #include "VariantVisitors.hpp"
24 #include "sensor.hpp"
25 
26 #include <boost/asio/error.hpp>
27 #include <boost/asio/io_context.hpp>
28 #include <boost/asio/steady_timer.hpp>
29 #include <boost/container/flat_map.hpp>
30 #include <phosphor-logging/lg2.hpp>
31 #include <sdbusplus/asio/connection.hpp>
32 #include <sdbusplus/asio/object_server.hpp>
33 #include <sdbusplus/message.hpp>
34 #include <sdbusplus/message/native_types.hpp>
35 
36 #include <algorithm>
37 #include <array>
38 #include <chrono>
39 #include <cstddef>
40 #include <cstdint>
41 #include <iomanip>
42 #include <iostream>
43 #include <limits>
44 #include <memory>
45 #include <sstream>
46 #include <stdexcept>
47 #include <string>
48 #include <tuple>
49 #include <utility>
50 #include <variant>
51 #include <vector>
52 
53 constexpr const bool debug = false;
54 
55 static constexpr double ipmbMaxReading = 0xFF;
56 static constexpr double ipmbMinReading = 0;
57 
58 static constexpr uint8_t meAddress = 1;
59 static constexpr uint8_t lun = 0;
60 static constexpr uint8_t hostSMbusIndexDefault = 0x03;
61 static constexpr uint8_t ipmbBusIndexDefault = 0;
62 static constexpr float pollRateDefault = 1; // in seconds
63 
64 static constexpr const char* sensorPathPrefix = "/xyz/openbmc_project/sensors/";
65 
IpmbSensor(std::shared_ptr<sdbusplus::asio::connection> & conn,boost::asio::io_context & io,const std::string & sensorName,const std::string & sensorConfiguration,sdbusplus::asio::object_server & objectServer,std::vector<thresholds::Threshold> && thresholdData,uint8_t deviceAddress,uint8_t hostSMbusIndex,const float pollRate,std::string & sensorTypeName)66 IpmbSensor::IpmbSensor(
67     std::shared_ptr<sdbusplus::asio::connection>& conn,
68     boost::asio::io_context& io, const std::string& sensorName,
69     const std::string& sensorConfiguration,
70     sdbusplus::asio::object_server& objectServer,
71     std::vector<thresholds::Threshold>&& thresholdData, uint8_t deviceAddress,
72     uint8_t hostSMbusIndex, const float pollRate, std::string& sensorTypeName) :
73     Sensor(escapeName(sensorName), std::move(thresholdData),
74            sensorConfiguration, "IpmbSensor", false, false, ipmbMaxReading,
75            ipmbMinReading, conn, PowerState::on),
76     deviceAddress(deviceAddress), hostSMbusIndex(hostSMbusIndex),
77     sensorPollMs(static_cast<int>(pollRate * 1000)), objectServer(objectServer),
78     waitTimer(io)
79 {
80     std::string dbusPath = sensorPathPrefix + sensorTypeName + "/" + name;
81 
82     sensorInterface = objectServer.add_interface(
83         dbusPath, "xyz.openbmc_project.Sensor.Value");
84 
85     for (const auto& threshold : thresholds)
86     {
87         std::string interface = thresholds::getInterface(threshold.level);
88         thresholdInterfaces[static_cast<size_t>(threshold.level)] =
89             objectServer.add_interface(dbusPath, interface);
90     }
91     association = objectServer.add_interface(dbusPath, association::interface);
92 }
93 
~IpmbSensor()94 IpmbSensor::~IpmbSensor()
95 {
96     waitTimer.cancel();
97     for (const auto& iface : thresholdInterfaces)
98     {
99         objectServer.remove_interface(iface);
100     }
101     objectServer.remove_interface(sensorInterface);
102     objectServer.remove_interface(association);
103 }
104 
getSubTypeUnits() const105 std::string IpmbSensor::getSubTypeUnits() const
106 {
107     switch (subType)
108     {
109         case IpmbSubType::temp:
110             return sensor_paths::unitDegreesC;
111         case IpmbSubType::curr:
112             return sensor_paths::unitAmperes;
113         case IpmbSubType::power:
114             return sensor_paths::unitWatts;
115         case IpmbSubType::volt:
116             return sensor_paths::unitVolts;
117         case IpmbSubType::util:
118             return sensor_paths::unitPercent;
119         default:
120             throw std::runtime_error("Invalid sensor type");
121     }
122 }
123 
init()124 void IpmbSensor::init()
125 {
126     loadDefaults();
127     setInitialProperties(getSubTypeUnits());
128     if (initCommand)
129     {
130         runInitCmd();
131     }
132     read();
133 }
134 
initCmdCb(const std::weak_ptr<IpmbSensor> & weakRef,const boost::system::error_code & ec,const IpmbMethodType & response)135 static void initCmdCb(const std::weak_ptr<IpmbSensor>& weakRef,
136                       const boost::system::error_code& ec,
137                       const IpmbMethodType& response)
138 {
139     std::shared_ptr<IpmbSensor> self = weakRef.lock();
140     if (!self)
141     {
142         return;
143     }
144     const int& status = std::get<0>(response);
145     if (ec || (status != 0))
146     {
147         lg2::error("Error setting init command for device: '{NAME}'", "NAME",
148                    self->name);
149     }
150 }
151 
runInitCmd()152 void IpmbSensor::runInitCmd()
153 {
154     if (!initCommand)
155     {
156         return;
157     }
158     dbusConnection->async_method_call(
159         [weakRef{weak_from_this()}](const boost::system::error_code& ec,
160                                     const IpmbMethodType& response) {
161             initCmdCb(weakRef, ec, response);
162         },
163         "xyz.openbmc_project.Ipmi.Channel.Ipmb",
164         "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
165         "sendRequest", commandAddress, netfn, lun, *initCommand, initData);
166 }
167 
loadDefaults()168 void IpmbSensor::loadDefaults()
169 {
170     if (type == IpmbType::meSensor)
171     {
172         commandAddress = meAddress;
173         netfn = ipmi::sensor::netFn;
174         command = ipmi::sensor::getSensorReading;
175         commandData = {deviceAddress};
176         readingFormat = ReadingFormat::byte0;
177     }
178     else if (type == IpmbType::PXE1410CVR)
179     {
180         commandAddress = meAddress;
181         netfn = ipmi::me_bridge::netFn;
182         command = ipmi::me_bridge::sendRawPmbus;
183         initCommand = ipmi::me_bridge::sendRawPmbus;
184         // pmbus read temp
185         commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
186                        deviceAddress, 0x00, 0x00, 0x00, 0x00,
187                        0x01,          0x02, 0x8d};
188         // goto page 0
189         initData = {0x57,          0x01, 0x00, 0x14, hostSMbusIndex,
190                     deviceAddress, 0x00, 0x00, 0x00, 0x00,
191                     0x02,          0x00, 0x00, 0x00};
192         readingFormat = ReadingFormat::linearElevenBit;
193     }
194     else if (type == IpmbType::IR38363VR)
195     {
196         commandAddress = meAddress;
197         netfn = ipmi::me_bridge::netFn;
198         command = ipmi::me_bridge::sendRawPmbus;
199         // pmbus read temp
200         commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
201                        deviceAddress, 00,   0x00, 0x00, 0x00,
202                        0x01,          0x02, 0x8D};
203         readingFormat = ReadingFormat::elevenBitShift;
204     }
205     else if (type == IpmbType::ADM1278HSC)
206     {
207         commandAddress = meAddress;
208         uint8_t snsNum = 0;
209         switch (subType)
210         {
211             case IpmbSubType::temp:
212             case IpmbSubType::curr:
213                 if (subType == IpmbSubType::temp)
214                 {
215                     snsNum = 0x8d;
216                 }
217                 else
218                 {
219                     snsNum = 0x8c;
220                 }
221                 netfn = ipmi::me_bridge::netFn;
222                 command = ipmi::me_bridge::sendRawPmbus;
223                 commandData = {0x57, 0x01, 0x00, 0x86, deviceAddress,
224                                0x00, 0x00, 0x01, 0x02, snsNum};
225                 readingFormat = ReadingFormat::elevenBit;
226                 break;
227             case IpmbSubType::power:
228             case IpmbSubType::volt:
229                 netfn = ipmi::sensor::netFn;
230                 command = ipmi::sensor::getSensorReading;
231                 commandData = {deviceAddress};
232                 readingFormat = ReadingFormat::byte0;
233                 break;
234             default:
235                 throw std::runtime_error("Invalid sensor type");
236         }
237     }
238     else if (type == IpmbType::mpsVR)
239     {
240         commandAddress = meAddress;
241         netfn = ipmi::me_bridge::netFn;
242         command = ipmi::me_bridge::sendRawPmbus;
243         initCommand = ipmi::me_bridge::sendRawPmbus;
244         // pmbus read temp
245         commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
246                        deviceAddress, 0x00, 0x00, 0x00, 0x00,
247                        0x01,          0x02, 0x8d};
248         // goto page 0
249         initData = {0x57,          0x01, 0x00, 0x14, hostSMbusIndex,
250                     deviceAddress, 0x00, 0x00, 0x00, 0x00,
251                     0x02,          0x00, 0x00, 0x00};
252         readingFormat = ReadingFormat::byte3;
253     }
254     else if (type == IpmbType::SMPro)
255     {
256         // This is an Ampere SMPro reachable via a BMC.  For example,
257         // this architecture is used on ADLINK Ampere Altra systems.
258         // See the Ampere Family SoC BMC Interface Specification at
259         // https://amperecomputing.com/customer-connect/products/altra-family-software---firmware
260         // for details of the sensors.
261         commandAddress = 0;
262         netfn = 0x30;
263         command = 0x31;
264         commandData = {0x9e, deviceAddress};
265         switch (subType)
266         {
267             case IpmbSubType::temp:
268                 readingFormat = ReadingFormat::nineBit;
269                 break;
270             case IpmbSubType::power:
271                 readingFormat = ReadingFormat::tenBit;
272                 break;
273             case IpmbSubType::curr:
274             case IpmbSubType::volt:
275                 readingFormat = ReadingFormat::fifteenBit;
276                 break;
277             default:
278                 throw std::runtime_error("Invalid sensor type");
279         }
280     }
281     else
282     {
283         throw std::runtime_error("Invalid sensor type");
284     }
285 
286     if (subType == IpmbSubType::util)
287     {
288         // Utilization need to be scaled to percent
289         maxValue = 100;
290         minValue = 0;
291     }
292 }
293 
checkThresholds()294 void IpmbSensor::checkThresholds()
295 {
296     thresholds::checkThresholds(this);
297 }
298 
processReading(ReadingFormat readingFormat,uint8_t command,const std::vector<uint8_t> & data,double & resp,size_t errCount)299 bool IpmbSensor::processReading(ReadingFormat readingFormat, uint8_t command,
300                                 const std::vector<uint8_t>& data, double& resp,
301                                 size_t errCount)
302 {
303     switch (readingFormat)
304     {
305         case (ReadingFormat::byte0):
306         {
307             if (command == ipmi::sensor::getSensorReading &&
308                 !ipmi::sensor::isValid(data))
309             {
310                 return false;
311             }
312             resp = data[0];
313             return true;
314         }
315         case (ReadingFormat::byte3):
316         {
317             if (data.size() < 4)
318             {
319                 if (errCount == 0U)
320                 {
321                     lg2::error("Invalid data length returned");
322                 }
323                 return false;
324             }
325             resp = data[3];
326             return true;
327         }
328         case (ReadingFormat::nineBit):
329         case (ReadingFormat::tenBit):
330         case (ReadingFormat::fifteenBit):
331         {
332             if (data.size() != 2)
333             {
334                 if (errCount == 0U)
335                 {
336                     lg2::error("Invalid data length returned");
337                 }
338                 return false;
339             }
340 
341             // From the Altra Family SoC BMC Interface Specification:
342             // 0xFFFF – This sensor data is either missing or is not supported
343             // by the device.
344             if ((data[0] == 0xff) && (data[1] == 0xff))
345             {
346                 return false;
347             }
348 
349             if (readingFormat == ReadingFormat::nineBit)
350             {
351                 int16_t value = data[0];
352                 if ((data[1] & 0x1) != 0)
353                 {
354                     // Sign extend to 16 bits
355                     value |= 0xFF00;
356                 }
357                 resp = value;
358             }
359             else if (readingFormat == ReadingFormat::tenBit)
360             {
361                 uint16_t value = ((data[1] & 0x3) << 8) + data[0];
362                 resp = value;
363             }
364             else if (readingFormat == ReadingFormat::fifteenBit)
365             {
366                 uint16_t value = ((data[1] & 0x7F) << 8) + data[0];
367                 // Convert mV to V
368                 resp = value / 1000.0;
369             }
370 
371             return true;
372         }
373         case (ReadingFormat::elevenBit):
374         {
375             if (data.size() < 5)
376             {
377                 if (errCount == 0U)
378                 {
379                     lg2::error("Invalid data length returned");
380                 }
381                 return false;
382             }
383 
384             int16_t value = ((data[4] << 8) | data[3]);
385             resp = value;
386             return true;
387         }
388         case (ReadingFormat::elevenBitShift):
389         {
390             if (data.size() < 5)
391             {
392                 if (errCount == 0U)
393                 {
394                     lg2::error("Invalid data length returned");
395                 }
396                 return false;
397             }
398 
399             resp = ((data[4] << 8) | data[3]) >> 3;
400             return true;
401         }
402         case (ReadingFormat::linearElevenBit):
403         {
404             if (data.size() < 5)
405             {
406                 if (errCount == 0U)
407                 {
408                     lg2::error("Invalid data length returned");
409                 }
410                 return false;
411             }
412 
413             int16_t value = ((data[4] << 8) | data[3]);
414             constexpr const size_t shift = 16 - 11; // 11bit into 16bit
415             value <<= shift;
416             value >>= shift;
417             resp = value;
418             return true;
419         }
420         default:
421             throw std::runtime_error("Invalid reading type");
422     }
423 }
424 
ipmbRequestCompletionCb(const boost::system::error_code & ec,const IpmbMethodType & response)425 void IpmbSensor::ipmbRequestCompletionCb(const boost::system::error_code& ec,
426                                          const IpmbMethodType& response)
427 {
428     const int& status = std::get<0>(response);
429     if (ec || (status != 0))
430     {
431         incrementError();
432         read();
433         return;
434     }
435     const std::vector<uint8_t>& data = std::get<5>(response);
436     if constexpr (debug)
437     {
438         std::ostringstream tempStream;
439         for (int d : data)
440         {
441             tempStream << std::setfill('0') << std::setw(2) << std::hex << d
442                        << " ";
443         }
444         lg2::info("'{NAME}': '{DATA}'", "NAME", name, "DATA", tempStream.str());
445     }
446     if (data.empty())
447     {
448         incrementError();
449         read();
450         return;
451     }
452 
453     double value = 0;
454 
455     if (!processReading(readingFormat, command, data, value, errCount))
456     {
457         incrementError();
458         read();
459         return;
460     }
461 
462     // rawValue only used in debug logging
463     // up to 5th byte in data are used to derive value
464     size_t end = std::min(sizeof(uint64_t), data.size());
465     uint64_t rawData = 0;
466     for (size_t i = 0; i < end; i++)
467     {
468         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
469         reinterpret_cast<uint8_t*>(&rawData)[i] = data[i];
470     }
471     rawValue = static_cast<double>(rawData);
472 
473     /* Adjust value as per scale and offset */
474     value = (value * scaleVal) + offsetVal;
475     updateValue(value);
476     read();
477 }
478 
read()479 void IpmbSensor::read()
480 {
481     waitTimer.expires_after(std::chrono::milliseconds(sensorPollMs));
482     waitTimer.async_wait(
483         [weakRef{weak_from_this()}](const boost::system::error_code& ec) {
484             if (ec == boost::asio::error::operation_aborted)
485             {
486                 return; // we're being canceled
487             }
488             std::shared_ptr<IpmbSensor> self = weakRef.lock();
489             if (!self)
490             {
491                 return;
492             }
493             self->sendIpmbRequest();
494         });
495 }
496 
sendIpmbRequest()497 void IpmbSensor::sendIpmbRequest()
498 {
499     if (!readingStateGood())
500     {
501         updateValue(std::numeric_limits<double>::quiet_NaN());
502         read();
503         return;
504     }
505     dbusConnection->async_method_call(
506         [weakRef{weak_from_this()}](boost::system::error_code ec,
507                                     const IpmbMethodType& response) {
508             std::shared_ptr<IpmbSensor> self = weakRef.lock();
509             if (!self)
510             {
511                 return;
512             }
513             self->ipmbRequestCompletionCb(ec, response);
514         },
515         "xyz.openbmc_project.Ipmi.Channel.Ipmb",
516         "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
517         "sendRequest", commandAddress, netfn, lun, command, commandData);
518 }
519 
sensorClassType(const std::string & sensorClass)520 bool IpmbSensor::sensorClassType(const std::string& sensorClass)
521 {
522     if (sensorClass == "PxeBridgeTemp")
523     {
524         type = IpmbType::PXE1410CVR;
525     }
526     else if (sensorClass == "IRBridgeTemp")
527     {
528         type = IpmbType::IR38363VR;
529     }
530     else if (sensorClass == "HSCBridge")
531     {
532         type = IpmbType::ADM1278HSC;
533     }
534     else if (sensorClass == "MpsBridgeTemp")
535     {
536         type = IpmbType::mpsVR;
537     }
538     else if (sensorClass == "METemp" || sensorClass == "MESensor")
539     {
540         type = IpmbType::meSensor;
541     }
542     else if (sensorClass == "SMPro")
543     {
544         type = IpmbType::SMPro;
545     }
546     else
547     {
548         lg2::error("Invalid class '{SENSOR}'", "SENSOR", sensorClass);
549         return false;
550     }
551     return true;
552 }
553 
sensorSubType(const std::string & sensorTypeName)554 void IpmbSensor::sensorSubType(const std::string& sensorTypeName)
555 {
556     if (sensorTypeName == "voltage")
557     {
558         subType = IpmbSubType::volt;
559     }
560     else if (sensorTypeName == "power")
561     {
562         subType = IpmbSubType::power;
563     }
564     else if (sensorTypeName == "current")
565     {
566         subType = IpmbSubType::curr;
567     }
568     else if (sensorTypeName == "utilization")
569     {
570         subType = IpmbSubType::util;
571     }
572     else
573     {
574         subType = IpmbSubType::temp;
575     }
576 }
577 
parseConfigValues(const SensorBaseConfigMap & entry)578 void IpmbSensor::parseConfigValues(const SensorBaseConfigMap& entry)
579 {
580     auto findScaleVal = entry.find("ScaleValue");
581     if (findScaleVal != entry.end())
582     {
583         scaleVal = std::visit(VariantToDoubleVisitor(), findScaleVal->second);
584     }
585 
586     auto findOffsetVal = entry.find("OffsetValue");
587     if (findOffsetVal != entry.end())
588     {
589         offsetVal = std::visit(VariantToDoubleVisitor(), findOffsetVal->second);
590     }
591 
592     readState = getPowerState(entry);
593 }
594 
createSensors(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,boost::container::flat_map<std::string,std::shared_ptr<IpmbSensor>> & sensors,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection)595 void createSensors(
596     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
597     boost::container::flat_map<std::string, std::shared_ptr<IpmbSensor>>&
598         sensors,
599     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
600 {
601     if (!dbusConnection)
602     {
603         lg2::error("Connection not created");
604         return;
605     }
606     dbusConnection->async_method_call(
607         [&](boost::system::error_code ec, const ManagedObjectType& resp) {
608             if (ec)
609             {
610                 lg2::error("Error contacting entity manager");
611                 return;
612             }
613             for (const auto& [path, interfaces] : resp)
614             {
615                 for (const auto& [intf, cfg] : interfaces)
616                 {
617                     if (intf != configInterfaceName(sensorType))
618                     {
619                         continue;
620                     }
621                     std::string name = loadVariant<std::string>(cfg, "Name");
622 
623                     std::vector<thresholds::Threshold> sensorThresholds;
624                     if (!parseThresholdsFromConfig(interfaces,
625                                                    sensorThresholds))
626                     {
627                         lg2::error("error populating thresholds '{NAME}'",
628                                    "NAME", name);
629                     }
630                     uint8_t deviceAddress =
631                         loadVariant<uint8_t>(cfg, "Address");
632 
633                     std::string sensorClass =
634                         loadVariant<std::string>(cfg, "Class");
635 
636                     uint8_t hostSMbusIndex = hostSMbusIndexDefault;
637                     auto findSmType = cfg.find("HostSMbusIndex");
638                     if (findSmType != cfg.end())
639                     {
640                         hostSMbusIndex = std::visit(
641                             VariantToUnsignedIntVisitor(), findSmType->second);
642                     }
643 
644                     float pollRate = getPollRate(cfg, pollRateDefault);
645 
646                     uint8_t ipmbBusIndex = ipmbBusIndexDefault;
647                     auto findBusType = cfg.find("Bus");
648                     if (findBusType != cfg.end())
649                     {
650                         ipmbBusIndex = std::visit(VariantToUnsignedIntVisitor(),
651                                                   findBusType->second);
652                         lg2::error("Ipmb Bus Index for '{NAME}' is '{INDEX}'",
653                                    "NAME", name, "INDEX", ipmbBusIndex);
654                     }
655 
656                     /* Default sensor type is "temperature" */
657                     std::string sensorTypeName = "temperature";
658                     auto findType = cfg.find("SensorType");
659                     if (findType != cfg.end())
660                     {
661                         sensorTypeName = std::visit(VariantToStringVisitor(),
662                                                     findType->second);
663                     }
664 
665                     auto& sensor = sensors[name];
666                     sensor = nullptr;
667                     sensor = std::make_shared<IpmbSensor>(
668                         dbusConnection, io, name, path, objectServer,
669                         std::move(sensorThresholds), deviceAddress,
670                         hostSMbusIndex, pollRate, sensorTypeName);
671 
672                     sensor->parseConfigValues(cfg);
673                     if (!(sensor->sensorClassType(sensorClass)))
674                     {
675                         continue;
676                     }
677                     sensor->sensorSubType(sensorTypeName);
678                     sensor->init();
679                 }
680             }
681         },
682         entityManagerName, "/xyz/openbmc_project/inventory",
683         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
684 }
685 
interfaceRemoved(sdbusplus::message_t & message,boost::container::flat_map<std::string,std::shared_ptr<IpmbSensor>> & sensors)686 void interfaceRemoved(
687     sdbusplus::message_t& message,
688     boost::container::flat_map<std::string, std::shared_ptr<IpmbSensor>>&
689         sensors)
690 {
691     if (message.is_method_error())
692     {
693         lg2::error("interfacesRemoved callback method error");
694         return;
695     }
696 
697     sdbusplus::message::object_path removedPath;
698     std::vector<std::string> interfaces;
699 
700     message.read(removedPath, interfaces);
701 
702     // If the xyz.openbmc_project.Confguration.X interface was removed
703     // for one or more sensors, delete those sensor objects.
704     auto sensorIt = sensors.begin();
705     while (sensorIt != sensors.end())
706     {
707         if ((sensorIt->second->configurationPath == removedPath) &&
708             (std::find(interfaces.begin(), interfaces.end(),
709                        configInterfaceName(sdrInterface)) != interfaces.end()))
710         {
711             sensorIt = sensors.erase(sensorIt);
712         }
713         else
714         {
715             sensorIt++;
716         }
717     }
718 }
719