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