1 #pragma once
2 
3 #include "common/utils.hpp"
4 #include "libpldmresponder/pdr.hpp"
5 #include "pdr_utils.hpp"
6 #include "pldmd/handler.hpp"
7 
8 #include <libpldm/platform.h>
9 #include <libpldm/states.h>
10 #include <math.h>
11 #include <stdint.h>
12 
13 #include <phosphor-logging/lg2.hpp>
14 
15 #include <map>
16 #include <optional>
17 
18 PHOSPHOR_LOG2_USING;
19 
20 namespace pldm
21 {
22 namespace responder
23 {
24 namespace platform_numeric_effecter
25 {
26 /** @brief Function to get the effecter value by PDR factor coefficient, etc.
27  *  @param[in] pdr - The structure of pldm_numeric_effecter_value_pdr.
28  *  @param[in] effecterValue - effecter value.
29  *  @param[in] propertyType - type of the D-Bus property.
30  *
31  *  @return - std::pair<int, std::optional<PropertyValue>> - rc:Success or
32  *          failure, PropertyValue: The value to be set
33  */
34 template <typename T>
35 std::pair<int, std::optional<pldm::utils::PropertyValue>>
36     getEffecterRawValue(const pldm_numeric_effecter_value_pdr* pdr,
37                         T& effecterValue, std::string propertyType)
38 {
39     // X = Round [ (Y - B) / m ]
40     // refer to DSP0248_1.2.0 27.8
41     int rc = 0;
42     pldm::utils::PropertyValue value;
43     switch (pdr->effecter_data_size)
44     {
45         case PLDM_EFFECTER_DATA_SIZE_UINT8:
46         {
47             auto rawValue = static_cast<uint8_t>(
48                 round(effecterValue - pdr->offset) / pdr->resolution);
49             if (pdr->min_settable.value_u8 < pdr->max_settable.value_u8 &&
50                 (rawValue < pdr->min_settable.value_u8 ||
51                  rawValue > pdr->max_settable.value_u8))
52             {
53                 rc = PLDM_ERROR_INVALID_DATA;
54             }
55             value = rawValue;
56             if (propertyType == "uint64_t")
57             {
58                 auto tempValue = std::get<uint8_t>(value);
59                 value = static_cast<uint64_t>(tempValue);
60             }
61             else if (propertyType == "uint32_t")
62             {
63                 auto tempValue = std::get<uint8_t>(value);
64                 value = static_cast<uint32_t>(tempValue);
65             }
66             break;
67         }
68         case PLDM_EFFECTER_DATA_SIZE_SINT8:
69         {
70             auto rawValue = static_cast<int8_t>(
71                 round(effecterValue - pdr->offset) / pdr->resolution);
72             if (pdr->min_settable.value_s8 < pdr->max_settable.value_s8 &&
73                 (rawValue < pdr->min_settable.value_s8 ||
74                  rawValue > pdr->max_settable.value_s8))
75             {
76                 rc = PLDM_ERROR_INVALID_DATA;
77             }
78             value = rawValue;
79             break;
80         }
81         case PLDM_EFFECTER_DATA_SIZE_UINT16:
82         {
83             auto rawValue = static_cast<uint16_t>(
84                 round(effecterValue - pdr->offset) / pdr->resolution);
85             if (pdr->min_settable.value_u16 < pdr->max_settable.value_u16 &&
86                 (rawValue < pdr->min_settable.value_u16 ||
87                  rawValue > pdr->max_settable.value_u16))
88             {
89                 rc = PLDM_ERROR_INVALID_DATA;
90             }
91             value = rawValue;
92             if (propertyType == "uint64_t")
93             {
94                 auto tempValue = std::get<uint16_t>(value);
95                 value = static_cast<uint64_t>(tempValue);
96             }
97             else if (propertyType == "uint32_t")
98             {
99                 auto tempValue = std::get<uint16_t>(value);
100                 value = static_cast<uint32_t>(tempValue);
101             }
102             break;
103         }
104         case PLDM_EFFECTER_DATA_SIZE_SINT16:
105         {
106             auto rawValue = static_cast<int16_t>(
107                 round(effecterValue - pdr->offset) / pdr->resolution);
108             if (pdr->min_settable.value_s16 < pdr->max_settable.value_s16 &&
109                 (rawValue < pdr->min_settable.value_s16 ||
110                  rawValue > pdr->max_settable.value_s16))
111             {
112                 rc = PLDM_ERROR_INVALID_DATA;
113             }
114             value = rawValue;
115             if (propertyType == "uint64_t")
116             {
117                 auto tempValue = std::get<int16_t>(value);
118                 value = static_cast<uint64_t>(tempValue);
119             }
120             else if (propertyType == "uint32_t")
121             {
122                 auto tempValue = std::get<int16_t>(value);
123                 value = static_cast<uint32_t>(tempValue);
124             }
125             break;
126         }
127         case PLDM_EFFECTER_DATA_SIZE_UINT32:
128         {
129             auto rawValue = static_cast<uint32_t>(
130                 round(effecterValue - pdr->offset) / pdr->resolution);
131             if (pdr->min_settable.value_u32 < pdr->max_settable.value_u32 &&
132                 (rawValue < pdr->min_settable.value_u32 ||
133                  rawValue > pdr->max_settable.value_u32))
134             {
135                 rc = PLDM_ERROR_INVALID_DATA;
136             }
137             value = rawValue;
138             if (propertyType == "uint64_t")
139             {
140                 auto tempValue = std::get<uint32_t>(value);
141                 value = static_cast<uint64_t>(tempValue);
142             }
143             else if (propertyType == "uint32_t")
144             {
145                 auto tempValue = std::get<uint32_t>(value);
146                 value = static_cast<uint32_t>(tempValue);
147             }
148             break;
149         }
150         case PLDM_EFFECTER_DATA_SIZE_SINT32:
151         {
152             auto rawValue = static_cast<int32_t>(
153                 round(effecterValue - pdr->offset) / pdr->resolution);
154             if (pdr->min_settable.value_s32 < pdr->max_settable.value_s32 &&
155                 (rawValue < pdr->min_settable.value_s32 ||
156                  rawValue > pdr->max_settable.value_s32))
157             {
158                 rc = PLDM_ERROR_INVALID_DATA;
159             }
160             value = rawValue;
161             if (propertyType == "uint64_t")
162             {
163                 auto tempValue = std::get<int32_t>(value);
164                 value = static_cast<uint64_t>(tempValue);
165             }
166             else if (propertyType == "uint32_t")
167             {
168                 auto tempValue = std::get<int32_t>(value);
169                 value = static_cast<uint32_t>(tempValue);
170             }
171             break;
172         }
173     }
174 
175     return {rc, std::make_optional(std::move(value))};
176 }
177 
178 /** @brief Function to convert the D-Bus value by PDR factor and effecter value.
179  *  @param[in] pdr - The structure of pldm_numeric_effecter_value_pdr.
180  *  @param[in] effecterDataSize - effecter value size.
181  *  @param[in,out] effecterValue - effecter value.
182  *  @param[in] propertyType - type of the D-Bus property.
183  *
184  *  @return std::pair<int, std::optional<PropertyValue>> - rc:Success or
185  *          failure, PropertyValue: The value to be set
186  */
187 std::pair<int, std::optional<pldm::utils::PropertyValue>>
188     convertToDbusValue(const pldm_numeric_effecter_value_pdr* pdr,
189                        uint8_t effecterDataSize, uint8_t* effecterValue,
190                        std::string propertyType)
191 {
192     if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_UINT8)
193     {
194         uint8_t currentValue = *(reinterpret_cast<uint8_t*>(&effecterValue[0]));
195         return getEffecterRawValue<uint8_t>(pdr, currentValue, propertyType);
196     }
197     else if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_SINT8)
198     {
199         int8_t currentValue = *(reinterpret_cast<int8_t*>(&effecterValue[0]));
200         return getEffecterRawValue<int8_t>(pdr, currentValue, propertyType);
201     }
202     else if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_UINT16)
203     {
204         uint16_t currentValue =
205             *(reinterpret_cast<uint16_t*>(&effecterValue[0]));
206         return getEffecterRawValue<uint16_t>(pdr, currentValue, propertyType);
207     }
208     else if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_SINT16)
209     {
210         int16_t currentValue = *(reinterpret_cast<int16_t*>(&effecterValue[0]));
211         return getEffecterRawValue<int16_t>(pdr, currentValue, propertyType);
212     }
213     else if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_UINT32)
214     {
215         uint32_t currentValue =
216             *(reinterpret_cast<uint32_t*>(&effecterValue[0]));
217         return getEffecterRawValue<uint32_t>(pdr, currentValue, propertyType);
218     }
219     else if (effecterDataSize == PLDM_EFFECTER_DATA_SIZE_SINT32)
220     {
221         int32_t currentValue = *(reinterpret_cast<int32_t*>(&effecterValue[0]));
222         return getEffecterRawValue<int32_t>(pdr, currentValue, propertyType);
223     }
224     else
225     {
226         error("Unknown Effecter Size {SIZE}", "SIZE", effecterDataSize);
227         return {PLDM_ERROR, {}};
228     }
229 }
230 
231 /** @brief Function to set the effecter value requested by pldm requester
232  *  @tparam[in] DBusInterface - DBus interface type
233  *  @tparam[in] Handler - pldm::responder::platform::Handler
234  *  @param[in] dBusIntf - The interface object of DBusInterface
235  *  @param[in] handler - The interface object of
236  *             pldm::responder::platform::Handler
237  *  @param[in] effecterId - Effecter ID sent by the requester to act on
238  *  @param[in] effecterDataSize - The bit width and format of the setting
239  * 				value for the effecter
240  *  @param[in] effecter_value - The setting value of numeric effecter being
241  * 				requested.
242  *  @param[in] effecterValueLength - The setting value length of numeric
243  *              effecter being requested.
244  *  @return - Success or failure in setting the states. Returns failure in
245  * terms of PLDM completion codes if atleast one state fails to be set
246  */
247 template <class DBusInterface, class Handler>
248 int setNumericEffecterValueHandler(const DBusInterface& dBusIntf,
249                                    Handler& handler, uint16_t effecterId,
250                                    uint8_t effecterDataSize,
251                                    uint8_t* effecterValue,
252                                    size_t effecterValueLength)
253 {
254     constexpr auto effecterValueArrayLength = 4;
255     pldm_numeric_effecter_value_pdr* pdr = nullptr;
256 
257     std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)>
258         numericEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy);
259     if (!numericEffecterPdrRepo)
260     {
261         error("Failed to instantiate numeric effecter PDR repository");
262         return PLDM_ERROR;
263     }
264     pldm::responder::pdr_utils::Repo numericEffecterPDRs(
265         numericEffecterPdrRepo.get());
266     pldm::responder::pdr::getRepoByType(handler.getRepo(), numericEffecterPDRs,
267                                         PLDM_NUMERIC_EFFECTER_PDR);
268     if (numericEffecterPDRs.empty())
269     {
270         error("The Numeric Effecter PDR repo is empty.");
271         return PLDM_ERROR;
272     }
273 
274     // Get the pdr structure of pldm_numeric_effecter_value_pdr according
275     // to the effecterId
276     pldm::responder::pdr_utils::PdrEntry pdrEntry{};
277     auto pdrRecord = numericEffecterPDRs.getFirstRecord(pdrEntry);
278     while (pdrRecord)
279     {
280         pdr = reinterpret_cast<pldm_numeric_effecter_value_pdr*>(pdrEntry.data);
281         if (pdr->effecter_id != effecterId)
282         {
283             pdr = nullptr;
284             pdrRecord = numericEffecterPDRs.getNextRecord(pdrRecord, pdrEntry);
285             continue;
286         }
287 
288         break;
289     }
290 
291     if (!pdr)
292     {
293         return PLDM_PLATFORM_INVALID_EFFECTER_ID;
294     }
295 
296     if (effecterValueLength != effecterValueArrayLength)
297     {
298         error("Incorrect effecter data size {SIZE}", "SIZE",
299               effecterValueLength);
300         return PLDM_ERROR_INVALID_DATA;
301     }
302 
303     try
304     {
305         const auto& [dbusMappings,
306                      dbusValMaps] = handler.getDbusObjMaps(effecterId);
307         pldm::utils::DBusMapping dbusMapping{
308             dbusMappings[0].objectPath, dbusMappings[0].interface,
309             dbusMappings[0].propertyName, dbusMappings[0].propertyType};
310 
311         // convert to dbus effectervalue according to the factor
312         auto [rc, dbusValue] = convertToDbusValue(
313             pdr, effecterDataSize, effecterValue, dbusMappings[0].propertyType);
314         if (rc != PLDM_SUCCESS)
315         {
316             return rc;
317         }
318         try
319         {
320             dBusIntf.setDbusProperty(dbusMapping, dbusValue.value());
321         }
322         catch (const std::exception& e)
323         {
324             error(
325                 "Failed to set property '{PROPERTY}', interface '{INTERFACE}' and path '{PATH}', error - {ERROR}",
326                 "PROPERTY", dbusMapping.propertyName, "INTERFACE",
327                 dbusMapping.interface, "PATH", dbusMapping.objectPath, "ERROR",
328                 e);
329             return PLDM_ERROR;
330         }
331     }
332     catch (const std::out_of_range& e)
333     {
334         error("Unknown effecter ID '{EFFECTERID}', error - {ERROR}",
335               "EFFECTERID", effecterId, "ERROR", e);
336         return PLDM_ERROR;
337     }
338 
339     return PLDM_SUCCESS;
340 }
341 
342 /** @brief Function to convert the D-Bus value based on effecter data size
343  *         and create the response for getNumericEffecterValue request.
344  *  @param[in] PropertyValue - D-Bus Value
345  *  @param[in] effecterDataSize - effecter value size.
346  *  @param[in,out] responsePtr - Response of getNumericEffecterValue.
347  *  @param[in] responsePayloadLength - reponse length.
348  *  @param[in] instanceId - instance id for response
349  *
350  *  @return PLDM_SUCCESS/PLDM_ERROR
351  */
352 template <typename T>
353 int getEffecterValue(T propertyValue, uint8_t effecterDataSize,
354                      pldm_msg* responsePtr, size_t responsePayloadLength,
355                      uint8_t instanceId)
356 {
357     switch (effecterDataSize)
358     {
359         case PLDM_EFFECTER_DATA_SIZE_UINT8:
360         {
361             uint8_t value = static_cast<uint8_t>(propertyValue);
362             return (encode_get_numeric_effecter_value_resp(
363                 instanceId, PLDM_SUCCESS, effecterDataSize,
364                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING, &value, &value,
365                 responsePtr, responsePayloadLength));
366         }
367         case PLDM_EFFECTER_DATA_SIZE_SINT8:
368         {
369             int8_t value = static_cast<int8_t>(propertyValue);
370             return (encode_get_numeric_effecter_value_resp(
371                 instanceId, PLDM_SUCCESS, effecterDataSize,
372                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
373                 reinterpret_cast<uint8_t*>(&value),
374                 reinterpret_cast<uint8_t*>(&value), responsePtr,
375                 responsePayloadLength));
376         }
377         case PLDM_EFFECTER_DATA_SIZE_UINT16:
378         {
379             uint16_t value = static_cast<uint16_t>(propertyValue);
380             return (encode_get_numeric_effecter_value_resp(
381                 instanceId, PLDM_SUCCESS, effecterDataSize,
382                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
383                 reinterpret_cast<uint8_t*>(&value),
384                 reinterpret_cast<uint8_t*>(&value), responsePtr,
385                 responsePayloadLength));
386         }
387         case PLDM_EFFECTER_DATA_SIZE_SINT16:
388         {
389             int16_t value = static_cast<int16_t>(propertyValue);
390             return (encode_get_numeric_effecter_value_resp(
391                 instanceId, PLDM_SUCCESS, effecterDataSize,
392                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
393                 reinterpret_cast<uint8_t*>(&value),
394                 reinterpret_cast<uint8_t*>(&value), responsePtr,
395                 responsePayloadLength));
396         }
397         case PLDM_EFFECTER_DATA_SIZE_UINT32:
398         {
399             uint32_t value = static_cast<uint32_t>(propertyValue);
400             return (encode_get_numeric_effecter_value_resp(
401                 instanceId, PLDM_SUCCESS, effecterDataSize,
402                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
403                 reinterpret_cast<uint8_t*>(&value),
404                 reinterpret_cast<uint8_t*>(&value), responsePtr,
405                 responsePayloadLength));
406         }
407         case PLDM_EFFECTER_DATA_SIZE_SINT32:
408         {
409             int32_t value = static_cast<int32_t>(propertyValue);
410             return (encode_get_numeric_effecter_value_resp(
411                 instanceId, PLDM_SUCCESS, effecterDataSize,
412                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
413                 reinterpret_cast<uint8_t*>(&value),
414                 reinterpret_cast<uint8_t*>(&value), responsePtr,
415                 responsePayloadLength));
416         }
417         default:
418         {
419             error("Unknown Effecter Size {SIZE}", "SIZE", effecterDataSize);
420             return PLDM_ERROR;
421         }
422     }
423 }
424 
425 /** @brief Function to convert the D-Bus value to the effector data size value
426  *  @param[in] PropertyType - String contains the dataType of the Dbus value.
427  *  @param[in] PropertyValue - Variant contains the D-Bus Value
428  *  @param[in] effecterDataSize - effecter value size.
429  *  @param[in,out] responsePtr - Response of getNumericEffecterValue.
430  *  @param[in] responsePayloadLength - reponse length.
431  *  @param[in] instanceId - instance id for response
432  *
433  *  @return PLDM_SUCCESS/PLDM_ERROR
434  */
435 int getNumericEffecterValueHandler(const std::string& propertyType,
436                                    pldm::utils::PropertyValue propertyValue,
437                                    uint8_t effecterDataSize,
438                                    pldm_msg* responsePtr,
439                                    size_t responsePayloadLength,
440                                    uint8_t instanceId)
441 {
442     if (propertyType == "uint8_t")
443     {
444         uint8_t propVal = std::get<uint8_t>(propertyValue);
445         return getEffecterValue<uint8_t>(propVal, effecterDataSize, responsePtr,
446                                          responsePayloadLength, instanceId);
447     }
448     else if (propertyType == "uint16_t")
449     {
450         uint16_t propVal = std::get<uint16_t>(propertyValue);
451         return getEffecterValue<uint16_t>(propVal, effecterDataSize,
452                                           responsePtr, responsePayloadLength,
453                                           instanceId);
454     }
455     else if (propertyType == "uint32_t")
456     {
457         uint32_t propVal = std::get<uint32_t>(propertyValue);
458         return getEffecterValue<uint32_t>(propVal, effecterDataSize,
459                                           responsePtr, responsePayloadLength,
460                                           instanceId);
461     }
462     else if (propertyType == "uint64_t")
463     {
464         uint64_t propVal = std::get<uint64_t>(propertyValue);
465         return getEffecterValue<uint64_t>(propVal, effecterDataSize,
466                                           responsePtr, responsePayloadLength,
467                                           instanceId);
468     }
469     else
470     {
471         error("Property type '{TYPE}' not supported", "TYPE", propertyType);
472     }
473     return PLDM_ERROR;
474 }
475 
476 /** @brief Function to get the effecter details as data size, D-Bus property
477  *         type, D-Bus Value
478  *  @tparam[in] DBusInterface - DBus interface type
479  *  @tparam[in] Handler - pldm::responder::platform::Handler
480  *  @param[in] dBusIntf - The interface object of DBusInterface
481  *  @param[in] handler - The interface object of
482  *             pldm::responder::platform::Handler
483  *  @param[in] effecterId - Effecter ID sent by the requester to act on
484  *  @param[in] effecterDataSize - The bit width and format of the setting
485  *             value for the effecter
486  *  @param[in] propertyType - The data type of the D-Bus value
487  *  @param[in] propertyValue - The value of numeric effecter being
488  *                             requested.
489  *  @return - Success or failure in getting the D-Bus property or the
490  *  effecterId not found in the PDR repo
491  */
492 template <class DBusInterface, class Handler>
493 int getNumericEffecterData(const DBusInterface& dBusIntf, Handler& handler,
494                            uint16_t effecterId, uint8_t& effecterDataSize,
495                            std::string& propertyType,
496                            pldm::utils::PropertyValue& propertyValue)
497 {
498     pldm_numeric_effecter_value_pdr* pdr = nullptr;
499 
500     std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)>
501         numericEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy);
502     pldm::responder::pdr_utils::Repo numericEffecterPDRs(
503         numericEffecterPdrRepo.get());
504     pldm::responder::pdr::getRepoByType(handler.getRepo(), numericEffecterPDRs,
505                                         PLDM_NUMERIC_EFFECTER_PDR);
506     if (numericEffecterPDRs.empty())
507     {
508         error("The Numeric Effecter PDR repo is empty.");
509         return PLDM_ERROR;
510     }
511 
512     // Get the pdr structure of pldm_numeric_effecter_value_pdr according
513     // to the effecterId
514     pldm::responder::pdr_utils::PdrEntry pdrEntry{};
515     auto pdrRecord = numericEffecterPDRs.getFirstRecord(pdrEntry);
516 
517     while (pdrRecord)
518     {
519         pdr = reinterpret_cast<pldm_numeric_effecter_value_pdr*>(pdrEntry.data);
520         if (pdr->effecter_id != effecterId)
521         {
522             pdr = nullptr;
523             pdrRecord = numericEffecterPDRs.getNextRecord(pdrRecord, pdrEntry);
524             continue;
525         }
526         effecterDataSize = pdr->effecter_data_size;
527         break;
528     }
529 
530     if (!pdr)
531     {
532         error("Failed to find numeric effecter ID {EFFECTERID}", "EFFECTERID",
533               effecterId);
534         return PLDM_PLATFORM_INVALID_EFFECTER_ID;
535     }
536 
537     pldm::utils::DBusMapping dbusMapping{};
538     try
539     {
540         const auto& [dbusMappings,
541                      dbusValMaps] = handler.getDbusObjMaps(effecterId);
542         if (dbusMappings.size() > 0)
543         {
544             dbusMapping = {
545                 dbusMappings[0].objectPath, dbusMappings[0].interface,
546                 dbusMappings[0].propertyName, dbusMappings[0].propertyType};
547 
548             propertyValue = dBusIntf.getDbusPropertyVariant(
549                 dbusMapping.objectPath.c_str(),
550                 dbusMapping.propertyName.c_str(),
551                 dbusMapping.interface.c_str());
552             propertyType = dbusMappings[0].propertyType;
553         }
554     }
555     catch (const std::exception& e)
556     {
557         error(
558             "Failed to do dbus mapping or the dbus query for the effecter ID '{EFFECTERID}', error - {ERROR}",
559             "EFFECTERID", effecterId, "ERROR", e);
560         error(
561             "Dbus Details path [{PATH}], interface [{INTERFACE}] and  property [{PROPERTY}]",
562             "PATH", dbusMapping.objectPath, "INTERFACE", dbusMapping.interface,
563             "PROPERTY", dbusMapping.propertyName);
564         return PLDM_ERROR;
565     }
566 
567     return PLDM_SUCCESS;
568 }
569 
570 } // namespace platform_numeric_effecter
571 } // namespace responder
572 } // namespace pldm
573