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