1d7be555eSGeorge Liu #include "NVMeBasicContext.hpp"
2d7be555eSGeorge Liu
3d7be555eSGeorge Liu #include "NVMeContext.hpp"
4d7be555eSGeorge Liu #include "NVMeSensor.hpp"
5d7be555eSGeorge Liu
6d7be555eSGeorge Liu #include <endian.h>
7d7be555eSGeorge Liu #include <sys/ioctl.h>
8d7be555eSGeorge Liu #include <unistd.h>
9d7be555eSGeorge Liu
10d7be555eSGeorge Liu #include <FileHandle.hpp>
11d7be555eSGeorge Liu #include <boost/asio/buffer.hpp>
12d7be555eSGeorge Liu #include <boost/asio/error.hpp>
13d7be555eSGeorge Liu #include <boost/asio/io_context.hpp>
14d7be555eSGeorge Liu #include <boost/asio/read.hpp>
15d7be555eSGeorge Liu #include <boost/asio/streambuf.hpp>
16d7be555eSGeorge Liu #include <boost/asio/write.hpp>
17*7201be43SGeorge Liu #include <phosphor-logging/lg2.hpp>
18*7201be43SGeorge Liu #include <phosphor-logging/lg2/flags.hpp>
19d7be555eSGeorge Liu
20d7be555eSGeorge Liu #include <array>
21d7be555eSGeorge Liu #include <cerrno>
22d7be555eSGeorge Liu #include <chrono>
23d7be555eSGeorge Liu #include <cmath>
24d7be555eSGeorge Liu #include <cstdint>
25d7be555eSGeorge Liu #include <cstdio>
26d7be555eSGeorge Liu #include <cstring>
27d7be555eSGeorge Liu #include <filesystem>
28d7be555eSGeorge Liu #include <iostream>
29d7be555eSGeorge Liu #include <iterator>
30d7be555eSGeorge Liu #include <limits>
31d7be555eSGeorge Liu #include <memory>
32d7be555eSGeorge Liu #include <stdexcept>
33d7be555eSGeorge Liu #include <string>
34d7be555eSGeorge Liu #include <system_error>
35d7be555eSGeorge Liu #include <thread>
36d7be555eSGeorge Liu #include <utility>
37d7be555eSGeorge Liu #include <vector>
38d7be555eSGeorge Liu
39d7be555eSGeorge Liu extern "C"
40d7be555eSGeorge Liu {
41d7be555eSGeorge Liu #include <i2c/smbus.h>
42d7be555eSGeorge Liu #include <linux/i2c-dev.h>
43d7be555eSGeorge Liu }
44d7be555eSGeorge Liu
45d7be555eSGeorge Liu /*
46d7be555eSGeorge Liu * NVMe-MI Basic Management Command
47d7be555eSGeorge Liu *
48d7be555eSGeorge Liu * https://nvmexpress.org/wp-content/uploads/NVMe_Management_-_Technical_Note_on_Basic_Management_Command.pdf
49d7be555eSGeorge Liu */
50d7be555eSGeorge Liu
encodeBasicQuery(int bus,uint8_t device,uint8_t offset)51556e04b8SPatrick Williams static std::shared_ptr<std::array<uint8_t, 6>> encodeBasicQuery(
52556e04b8SPatrick Williams int bus, uint8_t device, uint8_t offset)
53d7be555eSGeorge Liu {
54d7be555eSGeorge Liu if (bus < 0)
55d7be555eSGeorge Liu {
56d7be555eSGeorge Liu throw std::domain_error("Invalid bus argument");
57d7be555eSGeorge Liu }
58d7be555eSGeorge Liu
59d7be555eSGeorge Liu /* bus + address + command */
60d7be555eSGeorge Liu uint32_t busle = htole32(static_cast<uint32_t>(bus));
61d7be555eSGeorge Liu auto command =
62d7be555eSGeorge Liu std::make_shared<std::array<uint8_t, sizeof(busle) + 1 + 1>>();
63d7be555eSGeorge Liu memcpy(command->data(), &busle, sizeof(busle));
64d7be555eSGeorge Liu (*command)[sizeof(busle) + 0] = device;
65d7be555eSGeorge Liu (*command)[sizeof(busle) + 1] = offset;
66d7be555eSGeorge Liu
67d7be555eSGeorge Liu return command;
68d7be555eSGeorge Liu }
69d7be555eSGeorge Liu
decodeBasicQuery(const std::array<uint8_t,6> & req,int & bus,uint8_t & device,uint8_t & offset)70d7be555eSGeorge Liu static void decodeBasicQuery(const std::array<uint8_t, 6>& req, int& bus,
71d7be555eSGeorge Liu uint8_t& device, uint8_t& offset)
72d7be555eSGeorge Liu {
73d7be555eSGeorge Liu uint32_t busle = 0;
74d7be555eSGeorge Liu
75d7be555eSGeorge Liu memcpy(&busle, req.data(), sizeof(busle));
76d7be555eSGeorge Liu bus = le32toh(busle);
77d7be555eSGeorge Liu device = req[sizeof(busle) + 0];
78d7be555eSGeorge Liu offset = req[sizeof(busle) + 1];
79d7be555eSGeorge Liu }
80d7be555eSGeorge Liu
execBasicQuery(int bus,uint8_t addr,uint8_t cmd,std::vector<uint8_t> & resp)81d7be555eSGeorge Liu static void execBasicQuery(int bus, uint8_t addr, uint8_t cmd,
82d7be555eSGeorge Liu std::vector<uint8_t>& resp)
83d7be555eSGeorge Liu {
84d7be555eSGeorge Liu int32_t size = 0;
85d7be555eSGeorge Liu std::filesystem::path devpath = "/dev/i2c-" + std::to_string(bus);
86d7be555eSGeorge Liu
87d7be555eSGeorge Liu try
88d7be555eSGeorge Liu {
89d7be555eSGeorge Liu FileHandle fileHandle(devpath);
90d7be555eSGeorge Liu
91d7be555eSGeorge Liu /* Select the target device */
92d7be555eSGeorge Liu // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
93d7be555eSGeorge Liu if (::ioctl(fileHandle.handle(), I2C_SLAVE, addr) == -1)
94d7be555eSGeorge Liu {
95*7201be43SGeorge Liu lg2::error(
96*7201be43SGeorge Liu "Failed to configure device address '{ADDR}' for bus '{BUS}': ERRNO",
97*7201be43SGeorge Liu "ADDR", lg2::hex, addr, "BUS", bus, "ERRNO", lg2::hex, errno);
98d7be555eSGeorge Liu resp.resize(0);
99d7be555eSGeorge Liu return;
100d7be555eSGeorge Liu }
101d7be555eSGeorge Liu
102d7be555eSGeorge Liu resp.resize(UINT8_MAX + 1);
103d7be555eSGeorge Liu
104d7be555eSGeorge Liu /* Issue the NVMe MI basic command */
105d7be555eSGeorge Liu size = i2c_smbus_read_block_data(fileHandle.handle(), cmd, resp.data());
106d7be555eSGeorge Liu if (size < 0)
107d7be555eSGeorge Liu {
108*7201be43SGeorge Liu lg2::error(
109*7201be43SGeorge Liu "Failed to read block data from device '{ADDR}' on bus '{BUS}': ERRNO",
110*7201be43SGeorge Liu "ADDR", lg2::hex, addr, "BUS", bus, "ERRNO", lg2::hex, errno);
111d7be555eSGeorge Liu resp.resize(0);
112d7be555eSGeorge Liu }
113d7be555eSGeorge Liu else if (size > UINT8_MAX + 1)
114d7be555eSGeorge Liu {
115*7201be43SGeorge Liu lg2::error(
116*7201be43SGeorge Liu "Unexpected message length from device '{ADDR}' on bus '{BUS}': '{SIZE}' ({MAX})",
117*7201be43SGeorge Liu "ADDR", lg2::hex, addr, "BUS", bus, "SIZE", size, "MAX",
118*7201be43SGeorge Liu UINT8_MAX);
119d7be555eSGeorge Liu resp.resize(0);
120d7be555eSGeorge Liu }
121d7be555eSGeorge Liu else
122d7be555eSGeorge Liu {
123d7be555eSGeorge Liu resp.resize(size);
124d7be555eSGeorge Liu }
125d7be555eSGeorge Liu }
126d7be555eSGeorge Liu catch (const std::out_of_range& e)
127d7be555eSGeorge Liu {
128*7201be43SGeorge Liu lg2::error("Failed to create file handle for bus '{BUS}': '{ERR}'",
129*7201be43SGeorge Liu "BUS", bus, "ERR", e);
130d7be555eSGeorge Liu resp.resize(0);
131d7be555eSGeorge Liu }
132d7be555eSGeorge Liu }
133d7be555eSGeorge Liu
processBasicQueryStream(FileHandle & in,FileHandle & out)134d7be555eSGeorge Liu static ssize_t processBasicQueryStream(FileHandle& in, FileHandle& out)
135d7be555eSGeorge Liu {
136d7be555eSGeorge Liu std::vector<uint8_t> resp{};
137d7be555eSGeorge Liu ssize_t rc = 0;
138d7be555eSGeorge Liu
139d7be555eSGeorge Liu while (true)
140d7be555eSGeorge Liu {
141d7be555eSGeorge Liu uint8_t device = 0;
142d7be555eSGeorge Liu uint8_t offset = 0;
143d7be555eSGeorge Liu uint8_t len = 0;
144d7be555eSGeorge Liu int bus = 0;
145d7be555eSGeorge Liu
146d7be555eSGeorge Liu /* bus + address + command */
147d7be555eSGeorge Liu std::array<uint8_t, sizeof(uint32_t) + 1 + 1> req{};
148d7be555eSGeorge Liu
149d7be555eSGeorge Liu /* Read the command parameters */
150d7be555eSGeorge Liu ssize_t rc = ::read(in.handle(), req.data(), req.size());
151d7be555eSGeorge Liu if (rc != static_cast<ssize_t>(req.size()))
152d7be555eSGeorge Liu {
153*7201be43SGeorge Liu lg2::error(
154*7201be43SGeorge Liu "Failed to read request from in descriptor '{ERROR_MESSAGE}'",
155*7201be43SGeorge Liu "ERROR_MESSAGE", strerror(errno));
156d7be555eSGeorge Liu if (rc != 0)
157d7be555eSGeorge Liu {
158d7be555eSGeorge Liu return -errno;
159d7be555eSGeorge Liu }
160d7be555eSGeorge Liu return -EIO;
161d7be555eSGeorge Liu }
162d7be555eSGeorge Liu
163d7be555eSGeorge Liu decodeBasicQuery(req, bus, device, offset);
164d7be555eSGeorge Liu
165d7be555eSGeorge Liu /* Execute the query */
166d7be555eSGeorge Liu execBasicQuery(bus, device, offset, resp);
167d7be555eSGeorge Liu
168d7be555eSGeorge Liu /* Write out the response length */
169d7be555eSGeorge Liu len = resp.size();
170d7be555eSGeorge Liu rc = ::write(out.handle(), &len, sizeof(len));
171d7be555eSGeorge Liu if (rc != sizeof(len))
172d7be555eSGeorge Liu {
173*7201be43SGeorge Liu lg2::error(
174*7201be43SGeorge Liu "Failed to write block ({LEN}) length to out descriptor: '{ERRNO}'",
175*7201be43SGeorge Liu "LEN", len, "ERRNO", strerror(static_cast<int>(-rc)));
176d7be555eSGeorge Liu if (rc != 0)
177d7be555eSGeorge Liu {
178d7be555eSGeorge Liu return -errno;
179d7be555eSGeorge Liu }
180d7be555eSGeorge Liu return -EIO;
181d7be555eSGeorge Liu }
182d7be555eSGeorge Liu
183d7be555eSGeorge Liu /* Write out the response data */
184d7be555eSGeorge Liu std::vector<uint8_t>::iterator cursor = resp.begin();
185d7be555eSGeorge Liu while (cursor != resp.end())
186d7be555eSGeorge Liu {
187d7be555eSGeorge Liu size_t lenRemaining = std::distance(cursor, resp.end());
188d7be555eSGeorge Liu ssize_t egress = ::write(out.handle(), &(*cursor), lenRemaining);
189d7be555eSGeorge Liu if (egress == -1)
190d7be555eSGeorge Liu {
191*7201be43SGeorge Liu lg2::error(
192*7201be43SGeorge Liu "Failed to write block data of length '{LEN}' to out pipe: '{ERROR_MESSAGE}'",
193*7201be43SGeorge Liu "LEN", lenRemaining, "ERROR_MESSAGE", strerror(errno));
194d7be555eSGeorge Liu if (rc != 0)
195d7be555eSGeorge Liu {
196d7be555eSGeorge Liu return -errno;
197d7be555eSGeorge Liu }
198d7be555eSGeorge Liu return -EIO;
199d7be555eSGeorge Liu }
200d7be555eSGeorge Liu
201d7be555eSGeorge Liu cursor += egress;
202d7be555eSGeorge Liu }
203d7be555eSGeorge Liu }
204d7be555eSGeorge Liu
205d7be555eSGeorge Liu return rc;
206d7be555eSGeorge Liu }
207d7be555eSGeorge Liu
208d7be555eSGeorge Liu /* Throws std::error_code on failure */
209d7be555eSGeorge Liu /* FIXME: Probably shouldn't do fallible stuff in a constructor */
NVMeBasicContext(boost::asio::io_context & io,int rootBus)210d7be555eSGeorge Liu NVMeBasicContext::NVMeBasicContext(boost::asio::io_context& io, int rootBus) :
211d7be555eSGeorge Liu NVMeContext::NVMeContext(io, rootBus), io(io), reqStream(io), respStream(io)
212d7be555eSGeorge Liu {
213d7be555eSGeorge Liu std::array<int, 2> responsePipe{};
214d7be555eSGeorge Liu std::array<int, 2> requestPipe{};
215d7be555eSGeorge Liu
216d7be555eSGeorge Liu /* Set up inter-thread communication */
217d7be555eSGeorge Liu if (::pipe(requestPipe.data()) == -1)
218d7be555eSGeorge Liu {
219*7201be43SGeorge Liu lg2::error("Failed to create request pipe: '{ERROR}'", "ERROR",
220*7201be43SGeorge Liu strerror(errno));
221d7be555eSGeorge Liu throw std::error_code(errno, std::system_category());
222d7be555eSGeorge Liu }
223d7be555eSGeorge Liu
224d7be555eSGeorge Liu if (::pipe(responsePipe.data()) == -1)
225d7be555eSGeorge Liu {
226*7201be43SGeorge Liu lg2::error("Failed to create response pipe: '{ERROR}'", "ERROR",
227*7201be43SGeorge Liu strerror(errno));
228d7be555eSGeorge Liu
229d7be555eSGeorge Liu if (::close(requestPipe[0]) == -1)
230d7be555eSGeorge Liu {
231*7201be43SGeorge Liu lg2::error("Failed to close write fd of request pipe '{ERROR}'",
232*7201be43SGeorge Liu "ERROR", strerror(errno));
233d7be555eSGeorge Liu }
234d7be555eSGeorge Liu
235d7be555eSGeorge Liu if (::close(requestPipe[1]) == -1)
236d7be555eSGeorge Liu {
237*7201be43SGeorge Liu lg2::error("Failed to close read fd of request pipe '{ERROR}'",
238*7201be43SGeorge Liu "ERROR", strerror(errno));
239d7be555eSGeorge Liu }
240d7be555eSGeorge Liu
241d7be555eSGeorge Liu throw std::error_code(errno, std::system_category());
242d7be555eSGeorge Liu }
243d7be555eSGeorge Liu
244d7be555eSGeorge Liu reqStream.assign(requestPipe[1]);
245d7be555eSGeorge Liu FileHandle streamIn(requestPipe[0]);
246d7be555eSGeorge Liu FileHandle streamOut(responsePipe[1]);
247d7be555eSGeorge Liu respStream.assign(responsePipe[0]);
248d7be555eSGeorge Liu
249d7be555eSGeorge Liu thread = std::jthread([streamIn{std::move(streamIn)},
250d7be555eSGeorge Liu streamOut{std::move(streamOut)}]() mutable {
251d7be555eSGeorge Liu ssize_t rc = processBasicQueryStream(streamIn, streamOut);
252d7be555eSGeorge Liu
253d7be555eSGeorge Liu if (rc < 0)
254d7be555eSGeorge Liu {
255*7201be43SGeorge Liu lg2::error("Failure while processing query stream: '{ERROR}'",
256*7201be43SGeorge Liu "ERROR", strerror(static_cast<int>(-rc)));
257d7be555eSGeorge Liu }
258d7be555eSGeorge Liu
259*7201be43SGeorge Liu lg2::error("Terminating basic query thread");
260d7be555eSGeorge Liu });
261d7be555eSGeorge Liu }
262d7be555eSGeorge Liu
readAndProcessNVMeSensor()263d7be555eSGeorge Liu void NVMeBasicContext::readAndProcessNVMeSensor()
264d7be555eSGeorge Liu {
265d7be555eSGeorge Liu if (pollCursor == sensors.end())
266d7be555eSGeorge Liu {
267d7be555eSGeorge Liu this->pollNVMeDevices();
268d7be555eSGeorge Liu return;
269d7be555eSGeorge Liu }
270d7be555eSGeorge Liu
271d7be555eSGeorge Liu std::shared_ptr<NVMeSensor> sensor = *pollCursor++;
272d7be555eSGeorge Liu
273d7be555eSGeorge Liu if (!sensor->readingStateGood())
274d7be555eSGeorge Liu {
275d7be555eSGeorge Liu sensor->markAvailable(false);
276d7be555eSGeorge Liu sensor->updateValue(std::numeric_limits<double>::quiet_NaN());
277d7be555eSGeorge Liu readAndProcessNVMeSensor();
278d7be555eSGeorge Liu return;
279d7be555eSGeorge Liu }
280d7be555eSGeorge Liu
281d7be555eSGeorge Liu /* Potentially defer sampling the sensor sensor if it is in error */
282d7be555eSGeorge Liu if (!sensor->sample())
283d7be555eSGeorge Liu {
284d7be555eSGeorge Liu readAndProcessNVMeSensor();
285d7be555eSGeorge Liu return;
286d7be555eSGeorge Liu }
287d7be555eSGeorge Liu
288d7be555eSGeorge Liu auto command = encodeBasicQuery(sensor->bus, sensor->address, 0x00);
289d7be555eSGeorge Liu
290d7be555eSGeorge Liu /* Issue the request */
291d7be555eSGeorge Liu boost::asio::async_write(
292d7be555eSGeorge Liu reqStream, boost::asio::buffer(command->data(), command->size()),
293d7be555eSGeorge Liu [command](boost::system::error_code ec, std::size_t) {
294d7be555eSGeorge Liu if (ec)
295d7be555eSGeorge Liu {
296*7201be43SGeorge Liu lg2::error("Got error writing basic query: '{ERROR_MESSAGE}'",
297*7201be43SGeorge Liu "ERROR_MESSAGE", ec.message());
298d7be555eSGeorge Liu }
299d7be555eSGeorge Liu });
300d7be555eSGeorge Liu
301d7be555eSGeorge Liu auto response = std::make_shared<boost::asio::streambuf>();
302d7be555eSGeorge Liu response->prepare(1);
303d7be555eSGeorge Liu
304d7be555eSGeorge Liu /* Gather the response and dispatch for parsing */
305d7be555eSGeorge Liu boost::asio::async_read(
306d7be555eSGeorge Liu respStream, *response,
307d7be555eSGeorge Liu [response](const boost::system::error_code& ec, std::size_t n) {
308d7be555eSGeorge Liu if (ec)
309d7be555eSGeorge Liu {
310*7201be43SGeorge Liu lg2::error(
311*7201be43SGeorge Liu "Got error completing basic query: '{ERROR_MESSAGE}'",
312*7201be43SGeorge Liu "ERROR_MESSAGE", ec.message());
313d7be555eSGeorge Liu return static_cast<std::size_t>(0);
314d7be555eSGeorge Liu }
315d7be555eSGeorge Liu
316d7be555eSGeorge Liu if (n == 0)
317d7be555eSGeorge Liu {
318d7be555eSGeorge Liu return static_cast<std::size_t>(1);
319d7be555eSGeorge Liu }
320d7be555eSGeorge Liu
321d7be555eSGeorge Liu std::istream is(response.get());
322d7be555eSGeorge Liu size_t len = static_cast<std::size_t>(is.peek());
323d7be555eSGeorge Liu
324d7be555eSGeorge Liu if (n > len + 1)
325d7be555eSGeorge Liu {
326*7201be43SGeorge Liu lg2::error(
327*7201be43SGeorge Liu "Query stream has become unsynchronised: n: {N}, len: {LEN}",
328*7201be43SGeorge Liu "N", n, "LEN", len);
329d7be555eSGeorge Liu return static_cast<std::size_t>(0);
330d7be555eSGeorge Liu }
331d7be555eSGeorge Liu
332d7be555eSGeorge Liu if (n == len + 1)
333d7be555eSGeorge Liu {
334d7be555eSGeorge Liu return static_cast<std::size_t>(0);
335d7be555eSGeorge Liu }
336d7be555eSGeorge Liu
337d7be555eSGeorge Liu if (n > 1)
338d7be555eSGeorge Liu {
339d7be555eSGeorge Liu return len + 1 - n;
340d7be555eSGeorge Liu }
341d7be555eSGeorge Liu
342d7be555eSGeorge Liu response->prepare(len);
343d7be555eSGeorge Liu return len;
344d7be555eSGeorge Liu },
345d7be555eSGeorge Liu [weakSelf{weak_from_this()}, sensor, response](
346d7be555eSGeorge Liu const boost::system::error_code& ec, std::size_t length) mutable {
347d7be555eSGeorge Liu if (ec)
348d7be555eSGeorge Liu {
349*7201be43SGeorge Liu lg2::error("Got error reading basic query: '{ERROR_MESSAGE}'",
350*7201be43SGeorge Liu "ERROR_MESSAGE", ec.message());
351d7be555eSGeorge Liu return;
352d7be555eSGeorge Liu }
353d7be555eSGeorge Liu
354d7be555eSGeorge Liu if (length == 0)
355d7be555eSGeorge Liu {
356*7201be43SGeorge Liu lg2::error("Invalid message length: '{LEN}'", "LEN", length);
357d7be555eSGeorge Liu return;
358d7be555eSGeorge Liu }
359d7be555eSGeorge Liu
360d7be555eSGeorge Liu if (auto self = weakSelf.lock())
361d7be555eSGeorge Liu {
362d7be555eSGeorge Liu /* Deserialise the response */
363d7be555eSGeorge Liu response->consume(1); /* Drop the length byte */
364d7be555eSGeorge Liu std::istream is(response.get());
365d7be555eSGeorge Liu std::vector<char> data(response->size());
366d7be555eSGeorge Liu is.read(data.data(), response->size());
367d7be555eSGeorge Liu
368d7be555eSGeorge Liu /* Update the sensor */
369d7be555eSGeorge Liu self->processResponse(sensor, data.data(), data.size());
370d7be555eSGeorge Liu
371d7be555eSGeorge Liu /* Enqueue processing of the next sensor */
372d7be555eSGeorge Liu self->readAndProcessNVMeSensor();
373d7be555eSGeorge Liu }
374d7be555eSGeorge Liu });
375d7be555eSGeorge Liu }
376d7be555eSGeorge Liu
pollNVMeDevices()377d7be555eSGeorge Liu void NVMeBasicContext::pollNVMeDevices()
378d7be555eSGeorge Liu {
379d7be555eSGeorge Liu pollCursor = sensors.begin();
380d7be555eSGeorge Liu
381d7be555eSGeorge Liu scanTimer.expires_after(std::chrono::seconds(1));
382d7be555eSGeorge Liu scanTimer.async_wait([weakSelf{weak_from_this()}](
383d7be555eSGeorge Liu const boost::system::error_code errorCode) {
384d7be555eSGeorge Liu if (errorCode == boost::asio::error::operation_aborted)
385d7be555eSGeorge Liu {
386d7be555eSGeorge Liu return;
387d7be555eSGeorge Liu }
388d7be555eSGeorge Liu
389d7be555eSGeorge Liu if (errorCode)
390d7be555eSGeorge Liu {
391*7201be43SGeorge Liu lg2::error("error code: '{ERROR_MESSAGE}'", "ERROR_MESSAGE",
392*7201be43SGeorge Liu errorCode.message());
393d7be555eSGeorge Liu return;
394d7be555eSGeorge Liu }
395d7be555eSGeorge Liu
396d7be555eSGeorge Liu if (auto self = weakSelf.lock())
397d7be555eSGeorge Liu {
398d7be555eSGeorge Liu self->readAndProcessNVMeSensor();
399d7be555eSGeorge Liu }
400d7be555eSGeorge Liu });
401d7be555eSGeorge Liu }
402d7be555eSGeorge Liu
getTemperatureReading(int8_t reading)403d7be555eSGeorge Liu static double getTemperatureReading(int8_t reading)
404d7be555eSGeorge Liu {
405d7be555eSGeorge Liu if (reading == static_cast<int8_t>(0x80) ||
406d7be555eSGeorge Liu reading == static_cast<int8_t>(0x81))
407d7be555eSGeorge Liu {
408d7be555eSGeorge Liu // 0x80 = No temperature data or temperature data is more the 5 s
409d7be555eSGeorge Liu // old 0x81 = Temperature sensor failure
410d7be555eSGeorge Liu return std::numeric_limits<double>::quiet_NaN();
411d7be555eSGeorge Liu }
412d7be555eSGeorge Liu
413d7be555eSGeorge Liu return reading;
414d7be555eSGeorge Liu }
415d7be555eSGeorge Liu
processResponse(std::shared_ptr<NVMeSensor> & sensor,void * msg,size_t len)416d7be555eSGeorge Liu void NVMeBasicContext::processResponse(std::shared_ptr<NVMeSensor>& sensor,
417d7be555eSGeorge Liu void* msg, size_t len)
418d7be555eSGeorge Liu {
419d7be555eSGeorge Liu if (msg == nullptr || len < 6)
420d7be555eSGeorge Liu {
421d7be555eSGeorge Liu sensor->incrementError();
422d7be555eSGeorge Liu return;
423d7be555eSGeorge Liu }
424d7be555eSGeorge Liu
425d7be555eSGeorge Liu uint8_t* messageData = static_cast<uint8_t*>(msg);
426d7be555eSGeorge Liu
427d7be555eSGeorge Liu uint8_t status = messageData[0];
428d7be555eSGeorge Liu if (((status & NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY) != 0) ||
429d7be555eSGeorge Liu ((status & NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL) == 0))
430d7be555eSGeorge Liu {
431d7be555eSGeorge Liu sensor->markFunctional(false);
432d7be555eSGeorge Liu return;
433d7be555eSGeorge Liu }
434d7be555eSGeorge Liu
435d7be555eSGeorge Liu double value = getTemperatureReading(messageData[2]);
436d7be555eSGeorge Liu if (!std::isfinite(value))
437d7be555eSGeorge Liu {
438d7be555eSGeorge Liu sensor->incrementError();
439d7be555eSGeorge Liu return;
440d7be555eSGeorge Liu }
441d7be555eSGeorge Liu
442d7be555eSGeorge Liu sensor->updateValue(value);
443d7be555eSGeorge Liu }
444