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("Wrong field 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("effecter data size is incorrect.");
299         return PLDM_ERROR_INVALID_DATA;
300     }
301 
302     try
303     {
304         const auto& [dbusMappings,
305                      dbusValMaps] = handler.getDbusObjMaps(effecterId);
306         pldm::utils::DBusMapping dbusMapping{
307             dbusMappings[0].objectPath, dbusMappings[0].interface,
308             dbusMappings[0].propertyName, dbusMappings[0].propertyType};
309 
310         // convert to dbus effectervalue according to the factor
311         auto [rc, dbusValue] = convertToDbusValue(
312             pdr, effecterDataSize, effecterValue, dbusMappings[0].propertyType);
313         if (rc != PLDM_SUCCESS)
314         {
315             return rc;
316         }
317         try
318         {
319             dBusIntf.setDbusProperty(dbusMapping, dbusValue.value());
320         }
321         catch (const std::exception& e)
322         {
323             error(
324                 "Error setting property, ERROR={ERR_EXCEP} PROPERTY={DBUS_PROP} INTERFACE={DBUS_INTF} PATH={DBUS_OBJ_PATH}",
325                 "ERR_EXCEP", e.what(), "DBUS_PROP", dbusMapping.propertyName,
326                 "DBUS_INTF", dbusMapping.interface, "DBUS_OBJ_PATH",
327                 dbusMapping.objectPath.c_str());
328             return PLDM_ERROR;
329         }
330     }
331     catch (const std::out_of_range& e)
332     {
333         error("Unknown effecter ID : {EFFECTER_ID} {ERR_EXCEP}", "EFFECTER_ID",
334               effecterId, "ERR_EXCEP", e.what());
335         return PLDM_ERROR;
336     }
337 
338     return PLDM_SUCCESS;
339 }
340 
341 /** @brief Function to convert the D-Bus value based on effecter data size
342  *         and create the response for getNumericEffecterValue request.
343  *  @param[in] PropertyValue - D-Bus Value
344  *  @param[in] effecterDataSize - effecter value size.
345  *  @param[in,out] responsePtr - Response of getNumericEffecterValue.
346  *  @param[in] responsePayloadLength - reponse length.
347  *  @param[in] instanceId - instance id for response
348  *
349  *  @return PLDM_SUCCESS/PLDM_ERROR
350  */
351 template <typename T>
352 int getEffecterValue(T propertyValue, uint8_t effecterDataSize,
353                      pldm_msg* responsePtr, size_t responsePayloadLength,
354                      uint8_t instanceId)
355 {
356     switch (effecterDataSize)
357     {
358         case PLDM_EFFECTER_DATA_SIZE_UINT8:
359         {
360             uint8_t value = static_cast<uint8_t>(propertyValue);
361             return (encode_get_numeric_effecter_value_resp(
362                 instanceId, PLDM_SUCCESS, effecterDataSize,
363                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING, &value, &value,
364                 responsePtr, responsePayloadLength));
365         }
366         case PLDM_EFFECTER_DATA_SIZE_SINT8:
367         {
368             int8_t value = static_cast<int8_t>(propertyValue);
369             return (encode_get_numeric_effecter_value_resp(
370                 instanceId, PLDM_SUCCESS, effecterDataSize,
371                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
372                 reinterpret_cast<uint8_t*>(&value),
373                 reinterpret_cast<uint8_t*>(&value), responsePtr,
374                 responsePayloadLength));
375         }
376         case PLDM_EFFECTER_DATA_SIZE_UINT16:
377         {
378             uint16_t value = static_cast<uint16_t>(propertyValue);
379             return (encode_get_numeric_effecter_value_resp(
380                 instanceId, PLDM_SUCCESS, effecterDataSize,
381                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
382                 reinterpret_cast<uint8_t*>(&value),
383                 reinterpret_cast<uint8_t*>(&value), responsePtr,
384                 responsePayloadLength));
385         }
386         case PLDM_EFFECTER_DATA_SIZE_SINT16:
387         {
388             int16_t value = static_cast<int16_t>(propertyValue);
389             return (encode_get_numeric_effecter_value_resp(
390                 instanceId, PLDM_SUCCESS, effecterDataSize,
391                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
392                 reinterpret_cast<uint8_t*>(&value),
393                 reinterpret_cast<uint8_t*>(&value), responsePtr,
394                 responsePayloadLength));
395         }
396         case PLDM_EFFECTER_DATA_SIZE_UINT32:
397         {
398             uint32_t value = static_cast<uint32_t>(propertyValue);
399             return (encode_get_numeric_effecter_value_resp(
400                 instanceId, PLDM_SUCCESS, effecterDataSize,
401                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
402                 reinterpret_cast<uint8_t*>(&value),
403                 reinterpret_cast<uint8_t*>(&value), responsePtr,
404                 responsePayloadLength));
405         }
406         case PLDM_EFFECTER_DATA_SIZE_SINT32:
407         {
408             int32_t value = static_cast<int32_t>(propertyValue);
409             return (encode_get_numeric_effecter_value_resp(
410                 instanceId, PLDM_SUCCESS, effecterDataSize,
411                 EFFECTER_OPER_STATE_ENABLED_NOUPDATEPENDING,
412                 reinterpret_cast<uint8_t*>(&value),
413                 reinterpret_cast<uint8_t*>(&value), responsePtr,
414                 responsePayloadLength));
415         }
416         default:
417         {
418             error("Unknown Effecter Size");
419             return PLDM_ERROR;
420         }
421     }
422 }
423 
424 /** @brief Function to convert the D-Bus value to the effector data size value
425  *  @param[in] PropertyType - String contains the dataType of the Dbus value.
426  *  @param[in] PropertyValue - Variant contains the D-Bus Value
427  *  @param[in] effecterDataSize - effecter value size.
428  *  @param[in,out] responsePtr - Response of getNumericEffecterValue.
429  *  @param[in] responsePayloadLength - reponse length.
430  *  @param[in] instanceId - instance id for response
431  *
432  *  @return PLDM_SUCCESS/PLDM_ERROR
433  */
434 int getNumericEffecterValueHandler(const std::string& propertyType,
435                                    pldm::utils::PropertyValue propertyValue,
436                                    uint8_t effecterDataSize,
437                                    pldm_msg* responsePtr,
438                                    size_t responsePayloadLength,
439                                    uint8_t instanceId)
440 {
441     if (propertyType == "uint8_t")
442     {
443         uint8_t propVal = std::get<uint8_t>(propertyValue);
444         return getEffecterValue<uint8_t>(propVal, effecterDataSize, responsePtr,
445                                          responsePayloadLength, instanceId);
446     }
447     else if (propertyType == "uint16_t")
448     {
449         uint16_t propVal = std::get<uint16_t>(propertyValue);
450         return getEffecterValue<uint16_t>(propVal, effecterDataSize,
451                                           responsePtr, responsePayloadLength,
452                                           instanceId);
453     }
454     else if (propertyType == "uint32_t")
455     {
456         uint32_t propVal = std::get<uint32_t>(propertyValue);
457         return getEffecterValue<uint32_t>(propVal, effecterDataSize,
458                                           responsePtr, responsePayloadLength,
459                                           instanceId);
460     }
461     else if (propertyType == "uint64_t")
462     {
463         uint64_t propVal = std::get<uint64_t>(propertyValue);
464         return getEffecterValue<uint64_t>(propVal, effecterDataSize,
465                                           responsePtr, responsePayloadLength,
466                                           instanceId);
467     }
468     else
469     {
470         error("Property Type [{PROPERTYTYPE}] not supported", "PROPERTYTYPE",
471               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("The Numeric Effecter not found EFFECTERID={EFFECTERID}",
533               "EFFECTERID", 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             "Dbus Mapping or the Dbus query for the Effecter failed for effecter id: {EFFECTER_ID}, {ERR_EXCEP}",
559             "EFFECTER_ID", effecterId, "ERR_EXCEP", e.what());
560         error(
561             "Dbus Details objPath : [{OBJ_PATH}] interface : [{INTF}], property : [{PROPERTY}]",
562             "OBJ_PATH", dbusMapping.objectPath.c_str(), "INTF",
563             dbusMapping.interface.c_str(), "PROPERTY",
564             dbusMapping.propertyName.c_str());
565         return PLDM_ERROR;
566     }
567 
568     return PLDM_SUCCESS;
569 }
570 
571 } // namespace platform_numeric_effecter
572 } // namespace responder
573 } // namespace pldm
574