1e3e3c97aSAndrew Jeffery #include "NVMeBasicContext.hpp"
2e3e3c97aSAndrew Jeffery 
3*eacbfdd1SEd Tanous #include "NVMeContext.hpp"
4*eacbfdd1SEd Tanous #include "NVMeSensor.hpp"
5*eacbfdd1SEd Tanous 
6e3e3c97aSAndrew Jeffery #include <endian.h>
7e3e3c97aSAndrew Jeffery #include <sys/ioctl.h>
8e3e3c97aSAndrew Jeffery #include <unistd.h>
9e3e3c97aSAndrew Jeffery 
1073030639SEd Tanous #include <FileHandle.hpp>
11*eacbfdd1SEd Tanous #include <boost/asio/buffer.hpp>
12*eacbfdd1SEd Tanous #include <boost/asio/error.hpp>
13*eacbfdd1SEd Tanous #include <boost/asio/io_context.hpp>
14e3e3c97aSAndrew Jeffery #include <boost/asio/read.hpp>
15e3e3c97aSAndrew Jeffery #include <boost/asio/streambuf.hpp>
16e3e3c97aSAndrew Jeffery #include <boost/asio/write.hpp>
17e3e3c97aSAndrew Jeffery 
18*eacbfdd1SEd Tanous #include <array>
19e3e3c97aSAndrew Jeffery #include <cerrno>
20*eacbfdd1SEd Tanous #include <chrono>
21*eacbfdd1SEd Tanous #include <cmath>
22*eacbfdd1SEd Tanous #include <cstdint>
23e3e3c97aSAndrew Jeffery #include <cstdio>
24e3e3c97aSAndrew Jeffery #include <cstring>
25*eacbfdd1SEd Tanous #include <filesystem>
26*eacbfdd1SEd Tanous #include <ios>
27*eacbfdd1SEd Tanous #include <iostream>
28*eacbfdd1SEd Tanous #include <iterator>
29*eacbfdd1SEd Tanous #include <limits>
30*eacbfdd1SEd Tanous #include <memory>
31*eacbfdd1SEd Tanous #include <stdexcept>
32*eacbfdd1SEd Tanous #include <string>
33e3e3c97aSAndrew Jeffery #include <system_error>
34*eacbfdd1SEd Tanous #include <thread>
35*eacbfdd1SEd Tanous #include <utility>
36*eacbfdd1SEd Tanous #include <vector>
37e3e3c97aSAndrew Jeffery 
38e3e3c97aSAndrew Jeffery extern "C"
39e3e3c97aSAndrew Jeffery {
40e3e3c97aSAndrew Jeffery #include <i2c/smbus.h>
41e3e3c97aSAndrew Jeffery #include <linux/i2c-dev.h>
42e3e3c97aSAndrew Jeffery }
43e3e3c97aSAndrew Jeffery 
44e3e3c97aSAndrew Jeffery /*
45e3e3c97aSAndrew Jeffery  * NVMe-MI Basic Management Command
46e3e3c97aSAndrew Jeffery  *
47e3e3c97aSAndrew Jeffery  * https://nvmexpress.org/wp-content/uploads/NVMe_Management_-_Technical_Note_on_Basic_Management_Command.pdf
48e3e3c97aSAndrew Jeffery  */
49e3e3c97aSAndrew Jeffery 
50e3e3c97aSAndrew Jeffery static std::shared_ptr<std::array<uint8_t, 6>>
encodeBasicQuery(int bus,uint8_t device,uint8_t offset)51e3e3c97aSAndrew Jeffery     encodeBasicQuery(int bus, uint8_t device, uint8_t offset)
52e3e3c97aSAndrew Jeffery {
53e3e3c97aSAndrew Jeffery     if (bus < 0)
54e3e3c97aSAndrew Jeffery     {
55e3e3c97aSAndrew Jeffery         throw std::domain_error("Invalid bus argument");
56e3e3c97aSAndrew Jeffery     }
57e3e3c97aSAndrew Jeffery 
58e3e3c97aSAndrew Jeffery     /* bus + address + command */
59e3e3c97aSAndrew Jeffery     uint32_t busle = htole32(static_cast<uint32_t>(bus));
60e3e3c97aSAndrew Jeffery     auto command =
61e3e3c97aSAndrew Jeffery         std::make_shared<std::array<uint8_t, sizeof(busle) + 1 + 1>>();
62e3e3c97aSAndrew Jeffery     memcpy(command->data(), &busle, sizeof(busle));
63e3e3c97aSAndrew Jeffery     (*command)[sizeof(busle) + 0] = device;
64e3e3c97aSAndrew Jeffery     (*command)[sizeof(busle) + 1] = offset;
65e3e3c97aSAndrew Jeffery 
66e3e3c97aSAndrew Jeffery     return command;
67e3e3c97aSAndrew Jeffery }
68e3e3c97aSAndrew Jeffery 
decodeBasicQuery(const std::array<uint8_t,6> & req,int & bus,uint8_t & device,uint8_t & offset)69e3e3c97aSAndrew Jeffery static void decodeBasicQuery(const std::array<uint8_t, 6>& req, int& bus,
70e3e3c97aSAndrew Jeffery                              uint8_t& device, uint8_t& offset)
71e3e3c97aSAndrew Jeffery {
72a771f6a7SEd Tanous     uint32_t busle = 0;
73e3e3c97aSAndrew Jeffery 
74e3e3c97aSAndrew Jeffery     memcpy(&busle, req.data(), sizeof(busle));
75e3e3c97aSAndrew Jeffery     bus = le32toh(busle);
76e3e3c97aSAndrew Jeffery     device = req[sizeof(busle) + 0];
77e3e3c97aSAndrew Jeffery     offset = req[sizeof(busle) + 1];
78e3e3c97aSAndrew Jeffery }
79e3e3c97aSAndrew Jeffery 
execBasicQuery(int bus,uint8_t addr,uint8_t cmd,std::vector<uint8_t> & resp)801a143028SAndrew Jeffery static void execBasicQuery(int bus, uint8_t addr, uint8_t cmd,
81e3e3c97aSAndrew Jeffery                            std::vector<uint8_t>& resp)
82e3e3c97aSAndrew Jeffery {
83a771f6a7SEd Tanous     int32_t size = 0;
8473030639SEd Tanous     std::filesystem::path devpath = "/dev/i2c-" + std::to_string(bus);
850fe02294Schaul.ampere 
860fe02294Schaul.ampere     try
870fe02294Schaul.ampere     {
8873030639SEd Tanous         FileHandle fileHandle(devpath);
89e3e3c97aSAndrew Jeffery 
90e3e3c97aSAndrew Jeffery         /* Select the target device */
9199c4409aSEd Tanous         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
9273030639SEd Tanous         if (::ioctl(fileHandle.handle(), I2C_SLAVE, addr) == -1)
93e3e3c97aSAndrew Jeffery         {
949ca98ec9SAndrew Jeffery             std::cerr << "Failed to configure device address 0x" << std::hex
959ca98ec9SAndrew Jeffery                       << (int)addr << " for bus " << std::dec << bus << ": "
969ca98ec9SAndrew Jeffery                       << strerror(errno) << "\n";
970fe02294Schaul.ampere             resp.resize(0);
981a143028SAndrew Jeffery             return;
99e3e3c97aSAndrew Jeffery         }
100e3e3c97aSAndrew Jeffery 
101a7afacc4SAndrew Jeffery         resp.resize(UINT8_MAX + 1);
102e3e3c97aSAndrew Jeffery 
103e3e3c97aSAndrew Jeffery         /* Issue the NVMe MI basic command */
10473030639SEd Tanous         size = i2c_smbus_read_block_data(fileHandle.handle(), cmd, resp.data());
105e3e3c97aSAndrew Jeffery         if (size < 0)
106e3e3c97aSAndrew Jeffery         {
1079ca98ec9SAndrew Jeffery             std::cerr << "Failed to read block data from device 0x" << std::hex
1089ca98ec9SAndrew Jeffery                       << (int)addr << " on bus " << std::dec << bus << ": "
1099ca98ec9SAndrew Jeffery                       << strerror(errno) << "\n";
1101a143028SAndrew Jeffery             resp.resize(0);
111e3e3c97aSAndrew Jeffery         }
1121a143028SAndrew Jeffery         else if (size > UINT8_MAX + 1)
113e3e3c97aSAndrew Jeffery         {
1149ca98ec9SAndrew Jeffery             std::cerr << "Unexpected message length from device 0x" << std::hex
1150fe02294Schaul.ampere                       << (int)addr << " on bus " << std::dec << bus << ": "
1160fe02294Schaul.ampere                       << size << " (" << UINT8_MAX << ")\n";
1171a143028SAndrew Jeffery             resp.resize(0);
118e3e3c97aSAndrew Jeffery         }
1191a143028SAndrew Jeffery         else
1201a143028SAndrew Jeffery         {
121a7afacc4SAndrew Jeffery             resp.resize(size);
1221a143028SAndrew Jeffery         }
123e3e3c97aSAndrew Jeffery     }
1240fe02294Schaul.ampere     catch (const std::out_of_range& e)
1250fe02294Schaul.ampere     {
1260fe02294Schaul.ampere         std::cerr << "Failed to create file handle for bus " << std::dec << bus
1270fe02294Schaul.ampere                   << ": " << e.what() << "\n";
1280fe02294Schaul.ampere         resp.resize(0);
1290fe02294Schaul.ampere     }
1300fe02294Schaul.ampere }
131e3e3c97aSAndrew Jeffery 
processBasicQueryStream(FileHandle & in,FileHandle & out)13273030639SEd Tanous static ssize_t processBasicQueryStream(FileHandle& in, FileHandle& out)
133e3e3c97aSAndrew Jeffery {
134e3e3c97aSAndrew Jeffery     std::vector<uint8_t> resp{};
135a771f6a7SEd Tanous     ssize_t rc = 0;
136e3e3c97aSAndrew Jeffery 
137e3e3c97aSAndrew Jeffery     while (true)
138e3e3c97aSAndrew Jeffery     {
139a771f6a7SEd Tanous         uint8_t device = 0;
140a771f6a7SEd Tanous         uint8_t offset = 0;
141a771f6a7SEd Tanous         uint8_t len = 0;
142a771f6a7SEd Tanous         int bus = 0;
143e3e3c97aSAndrew Jeffery 
144e3e3c97aSAndrew Jeffery         /* bus + address + command */
145e3e3c97aSAndrew Jeffery         std::array<uint8_t, sizeof(uint32_t) + 1 + 1> req{};
146e3e3c97aSAndrew Jeffery 
147e3e3c97aSAndrew Jeffery         /* Read the command parameters */
14873030639SEd Tanous         ssize_t rc = ::read(in.handle(), req.data(), req.size());
14973030639SEd Tanous         if (rc != static_cast<ssize_t>(req.size()))
150e3e3c97aSAndrew Jeffery         {
15173030639SEd Tanous             std::cerr << "Failed to read request from in descriptor "
15273030639SEd Tanous                       << strerror(errno) << "\n";
1532049bd26SEd Tanous             if (rc != 0)
154e3e3c97aSAndrew Jeffery             {
15573030639SEd Tanous                 return -errno;
156e3e3c97aSAndrew Jeffery             }
15773030639SEd Tanous             return -EIO;
158e3e3c97aSAndrew Jeffery         }
159e3e3c97aSAndrew Jeffery 
160e3e3c97aSAndrew Jeffery         decodeBasicQuery(req, bus, device, offset);
161e3e3c97aSAndrew Jeffery 
162e3e3c97aSAndrew Jeffery         /* Execute the query */
1631a143028SAndrew Jeffery         execBasicQuery(bus, device, offset, resp);
164e3e3c97aSAndrew Jeffery 
165e3e3c97aSAndrew Jeffery         /* Write out the response length */
1661a143028SAndrew Jeffery         len = resp.size();
16773030639SEd Tanous         rc = ::write(out.handle(), &len, sizeof(len));
16873030639SEd Tanous         if (rc != sizeof(len))
169e3e3c97aSAndrew Jeffery         {
1709ca98ec9SAndrew Jeffery             std::cerr << "Failed to write block (" << std::dec << len
17173030639SEd Tanous                       << ") length to out descriptor: "
17273030639SEd Tanous                       << strerror(static_cast<int>(-rc)) << "\n";
1732049bd26SEd Tanous             if (rc != 0)
17473030639SEd Tanous             {
17573030639SEd Tanous                 return -errno;
17673030639SEd Tanous             }
17773030639SEd Tanous             return -EIO;
178e3e3c97aSAndrew Jeffery         }
179e3e3c97aSAndrew Jeffery 
180e3e3c97aSAndrew Jeffery         /* Write out the response data */
18173030639SEd Tanous         std::vector<uint8_t>::iterator cursor = resp.begin();
18273030639SEd Tanous         while (cursor != resp.end())
183e3e3c97aSAndrew Jeffery         {
18473030639SEd Tanous             size_t lenRemaining = std::distance(cursor, resp.end());
18573030639SEd Tanous             ssize_t egress = ::write(out.handle(), &(*cursor), lenRemaining);
186a771f6a7SEd Tanous             if (egress == -1)
187e3e3c97aSAndrew Jeffery             {
1889ca98ec9SAndrew Jeffery                 std::cerr << "Failed to write block data of length " << std::dec
18973030639SEd Tanous                           << lenRemaining << " to out pipe: " << strerror(errno)
19073030639SEd Tanous                           << "\n";
1912049bd26SEd Tanous                 if (rc != 0)
19273030639SEd Tanous                 {
19373030639SEd Tanous                     return -errno;
19473030639SEd Tanous                 }
19573030639SEd Tanous                 return -EIO;
196e3e3c97aSAndrew Jeffery             }
197e3e3c97aSAndrew Jeffery 
198e3e3c97aSAndrew Jeffery             cursor += egress;
199e3e3c97aSAndrew Jeffery         }
200e3e3c97aSAndrew Jeffery     }
201e3e3c97aSAndrew Jeffery 
202e3e3c97aSAndrew Jeffery     return rc;
203e3e3c97aSAndrew Jeffery }
204e3e3c97aSAndrew Jeffery 
205e3e3c97aSAndrew Jeffery /* Throws std::error_code on failure */
206e3e3c97aSAndrew Jeffery /* FIXME: Probably shouldn't do fallible stuff in a constructor */
NVMeBasicContext(boost::asio::io_context & io,int rootBus)2071f978631SEd Tanous NVMeBasicContext::NVMeBasicContext(boost::asio::io_context& io, int rootBus) :
208e3e3c97aSAndrew Jeffery     NVMeContext::NVMeContext(io, rootBus), io(io), reqStream(io), respStream(io)
209e3e3c97aSAndrew Jeffery {
210a771f6a7SEd Tanous     std::array<int, 2> responsePipe{};
211a771f6a7SEd Tanous     std::array<int, 2> requestPipe{};
212e3e3c97aSAndrew Jeffery 
213e3e3c97aSAndrew Jeffery     /* Set up inter-thread communication */
214e3e3c97aSAndrew Jeffery     if (::pipe(requestPipe.data()) == -1)
215e3e3c97aSAndrew Jeffery     {
216e3e3c97aSAndrew Jeffery         std::cerr << "Failed to create request pipe: " << strerror(errno)
217e3e3c97aSAndrew Jeffery                   << "\n";
218e3e3c97aSAndrew Jeffery         throw std::error_code(errno, std::system_category());
219e3e3c97aSAndrew Jeffery     }
220e3e3c97aSAndrew Jeffery 
221e3e3c97aSAndrew Jeffery     if (::pipe(responsePipe.data()) == -1)
222e3e3c97aSAndrew Jeffery     {
223e3e3c97aSAndrew Jeffery         std::cerr << "Failed to create response pipe: " << strerror(errno)
224e3e3c97aSAndrew Jeffery                   << "\n";
225e3e3c97aSAndrew Jeffery 
226e3e3c97aSAndrew Jeffery         if (::close(requestPipe[0]) == -1)
227e3e3c97aSAndrew Jeffery         {
228e3e3c97aSAndrew Jeffery             std::cerr << "Failed to close write fd of request pipe: "
229e3e3c97aSAndrew Jeffery                       << strerror(errno) << "\n";
230e3e3c97aSAndrew Jeffery         }
231e3e3c97aSAndrew Jeffery 
232e3e3c97aSAndrew Jeffery         if (::close(requestPipe[1]) == -1)
233e3e3c97aSAndrew Jeffery         {
234e3e3c97aSAndrew Jeffery             std::cerr << "Failed to close read fd of request pipe: "
235e3e3c97aSAndrew Jeffery                       << strerror(errno) << "\n";
236e3e3c97aSAndrew Jeffery         }
237e3e3c97aSAndrew Jeffery 
238e3e3c97aSAndrew Jeffery         throw std::error_code(errno, std::system_category());
239e3e3c97aSAndrew Jeffery     }
240e3e3c97aSAndrew Jeffery 
241e3e3c97aSAndrew Jeffery     reqStream.assign(requestPipe[1]);
24273030639SEd Tanous     FileHandle streamIn(requestPipe[0]);
24373030639SEd Tanous     FileHandle streamOut(responsePipe[1]);
244e3e3c97aSAndrew Jeffery     respStream.assign(responsePipe[0]);
245e3e3c97aSAndrew Jeffery 
2463cbd5a14SAndrew Jeffery     thread = std::jthread([streamIn{std::move(streamIn)},
24773030639SEd Tanous                            streamOut{std::move(streamOut)}]() mutable {
24872b39116SEd Tanous         ssize_t rc = processBasicQueryStream(streamIn, streamOut);
249e3e3c97aSAndrew Jeffery 
25072b39116SEd Tanous         if (rc < 0)
251e3e3c97aSAndrew Jeffery         {
252e3e3c97aSAndrew Jeffery             std::cerr << "Failure while processing query stream: "
253a771f6a7SEd Tanous                       << strerror(static_cast<int>(-rc)) << "\n";
254e3e3c97aSAndrew Jeffery         }
255e3e3c97aSAndrew Jeffery 
256e3e3c97aSAndrew Jeffery         std::cerr << "Terminating basic query thread\n";
257e3e3c97aSAndrew Jeffery     });
258e3e3c97aSAndrew Jeffery }
259e3e3c97aSAndrew Jeffery 
readAndProcessNVMeSensor()260b5d7a7fbSAndrew Jeffery void NVMeBasicContext::readAndProcessNVMeSensor()
261e3e3c97aSAndrew Jeffery {
262b5d7a7fbSAndrew Jeffery     if (pollCursor == sensors.end())
263e3e3c97aSAndrew Jeffery     {
2648c7074e7SAndrew Jeffery         this->pollNVMeDevices();
265e3e3c97aSAndrew Jeffery         return;
266e3e3c97aSAndrew Jeffery     }
267e3e3c97aSAndrew Jeffery 
268b5d7a7fbSAndrew Jeffery     std::shared_ptr<NVMeSensor> sensor = *pollCursor++;
269e3e3c97aSAndrew Jeffery 
2703859c7f7SAndrew Jeffery     if (!sensor->readingStateGood())
2713859c7f7SAndrew Jeffery     {
2723859c7f7SAndrew Jeffery         sensor->markAvailable(false);
2733859c7f7SAndrew Jeffery         sensor->updateValue(std::numeric_limits<double>::quiet_NaN());
274b5d7a7fbSAndrew Jeffery         readAndProcessNVMeSensor();
2753859c7f7SAndrew Jeffery         return;
2763859c7f7SAndrew Jeffery     }
2773859c7f7SAndrew Jeffery 
27814108bb5SAndrew Jeffery     /* Potentially defer sampling the sensor sensor if it is in error */
27914108bb5SAndrew Jeffery     if (!sensor->sample())
28014108bb5SAndrew Jeffery     {
281b5d7a7fbSAndrew Jeffery         readAndProcessNVMeSensor();
28214108bb5SAndrew Jeffery         return;
28314108bb5SAndrew Jeffery     }
28414108bb5SAndrew Jeffery 
28506cd9885SNnamdi Ajah     auto command = encodeBasicQuery(sensor->bus, sensor->address, 0x00);
286e3e3c97aSAndrew Jeffery 
287e3e3c97aSAndrew Jeffery     /* Issue the request */
288e3e3c97aSAndrew Jeffery     boost::asio::async_write(
289e3e3c97aSAndrew Jeffery         reqStream, boost::asio::buffer(command->data(), command->size()),
29076b2bc7dSEd Tanous         [command](boost::system::error_code ec, std::size_t) {
291e3e3c97aSAndrew Jeffery             if (ec)
292e3e3c97aSAndrew Jeffery             {
293e3e3c97aSAndrew Jeffery                 std::cerr << "Got error writing basic query: " << ec << "\n";
294e3e3c97aSAndrew Jeffery             }
295e3e3c97aSAndrew Jeffery         });
296e3e3c97aSAndrew Jeffery 
29784c16876SAndrew Jeffery     auto response = std::make_shared<boost::asio::streambuf>();
298e3e3c97aSAndrew Jeffery     response->prepare(1);
299e3e3c97aSAndrew Jeffery 
300e3e3c97aSAndrew Jeffery     /* Gather the response and dispatch for parsing */
301e3e3c97aSAndrew Jeffery     boost::asio::async_read(
302e3e3c97aSAndrew Jeffery         respStream, *response,
303e3e3c97aSAndrew Jeffery         [response](const boost::system::error_code& ec, std::size_t n) {
304e3e3c97aSAndrew Jeffery             if (ec)
305e3e3c97aSAndrew Jeffery             {
306e3e3c97aSAndrew Jeffery                 std::cerr << "Got error completing basic query: " << ec << "\n";
307e3e3c97aSAndrew Jeffery                 return static_cast<std::size_t>(0);
308e3e3c97aSAndrew Jeffery             }
309e3e3c97aSAndrew Jeffery 
310e3e3c97aSAndrew Jeffery             if (n == 0)
311e3e3c97aSAndrew Jeffery             {
312e3e3c97aSAndrew Jeffery                 return static_cast<std::size_t>(1);
313e3e3c97aSAndrew Jeffery             }
314e3e3c97aSAndrew Jeffery 
315e3e3c97aSAndrew Jeffery             std::istream is(response.get());
316e3e3c97aSAndrew Jeffery             size_t len = static_cast<std::size_t>(is.peek());
317e3e3c97aSAndrew Jeffery 
318e3e3c97aSAndrew Jeffery             if (n > len + 1)
319e3e3c97aSAndrew Jeffery             {
320e3e3c97aSAndrew Jeffery                 std::cerr << "Query stream has become unsynchronised: "
321e3e3c97aSAndrew Jeffery                           << "n: " << n << ", "
322e3e3c97aSAndrew Jeffery                           << "len: " << len << "\n";
323e3e3c97aSAndrew Jeffery                 return static_cast<std::size_t>(0);
324e3e3c97aSAndrew Jeffery             }
325e3e3c97aSAndrew Jeffery 
326e3e3c97aSAndrew Jeffery             if (n == len + 1)
327e3e3c97aSAndrew Jeffery             {
328e3e3c97aSAndrew Jeffery                 return static_cast<std::size_t>(0);
329e3e3c97aSAndrew Jeffery             }
330e3e3c97aSAndrew Jeffery 
331e3e3c97aSAndrew Jeffery             if (n > 1)
332e3e3c97aSAndrew Jeffery             {
333e3e3c97aSAndrew Jeffery                 return len + 1 - n;
334e3e3c97aSAndrew Jeffery             }
335e3e3c97aSAndrew Jeffery 
336e3e3c97aSAndrew Jeffery             response->prepare(len);
337e3e3c97aSAndrew Jeffery             return len;
338e3e3c97aSAndrew Jeffery         },
3393cbd5a14SAndrew Jeffery         [weakSelf{weak_from_this()}, sensor, response](
3408c7074e7SAndrew Jeffery             const boost::system::error_code& ec, std::size_t length) mutable {
341e3e3c97aSAndrew Jeffery             if (ec)
342e3e3c97aSAndrew Jeffery             {
343e3e3c97aSAndrew Jeffery                 std::cerr << "Got error reading basic query: " << ec << "\n";
344e3e3c97aSAndrew Jeffery                 return;
345e3e3c97aSAndrew Jeffery             }
346e3e3c97aSAndrew Jeffery 
347e3e3c97aSAndrew Jeffery             if (length == 0)
348e3e3c97aSAndrew Jeffery             {
349e3e3c97aSAndrew Jeffery                 std::cerr << "Invalid message length: " << length << "\n";
350e3e3c97aSAndrew Jeffery                 return;
351e3e3c97aSAndrew Jeffery             }
352e3e3c97aSAndrew Jeffery 
3533cbd5a14SAndrew Jeffery             if (auto self = weakSelf.lock())
3543cbd5a14SAndrew Jeffery             {
355e3e3c97aSAndrew Jeffery                 /* Deserialise the response */
356e3e3c97aSAndrew Jeffery                 response->consume(1); /* Drop the length byte */
357e3e3c97aSAndrew Jeffery                 std::istream is(response.get());
358e3e3c97aSAndrew Jeffery                 std::vector<char> data(response->size());
359e3e3c97aSAndrew Jeffery                 is.read(data.data(), response->size());
360e3e3c97aSAndrew Jeffery 
3618c7074e7SAndrew Jeffery                 /* Update the sensor */
3628c7074e7SAndrew Jeffery                 self->processResponse(sensor, data.data(), data.size());
3638c7074e7SAndrew Jeffery 
3648c7074e7SAndrew Jeffery                 /* Enqueue processing of the next sensor */
365b5d7a7fbSAndrew Jeffery                 self->readAndProcessNVMeSensor();
3663cbd5a14SAndrew Jeffery             }
367e3e3c97aSAndrew Jeffery         });
368e3e3c97aSAndrew Jeffery }
369e3e3c97aSAndrew Jeffery 
pollNVMeDevices()370e3e3c97aSAndrew Jeffery void NVMeBasicContext::pollNVMeDevices()
371e3e3c97aSAndrew Jeffery {
372b5d7a7fbSAndrew Jeffery     pollCursor = sensors.begin();
3738c7074e7SAndrew Jeffery 
37483db50caSEd Tanous     scanTimer.expires_after(std::chrono::seconds(1));
3753cbd5a14SAndrew Jeffery     scanTimer.async_wait([weakSelf{weak_from_this()}](
3763cbd5a14SAndrew Jeffery                              const boost::system::error_code errorCode) {
377e3e3c97aSAndrew Jeffery         if (errorCode == boost::asio::error::operation_aborted)
378e3e3c97aSAndrew Jeffery         {
379e3e3c97aSAndrew Jeffery             return;
380e3e3c97aSAndrew Jeffery         }
381e3e3c97aSAndrew Jeffery 
382e3e3c97aSAndrew Jeffery         if (errorCode)
383e3e3c97aSAndrew Jeffery         {
384e3e3c97aSAndrew Jeffery             std::cerr << errorCode.message() << "\n";
385e3e3c97aSAndrew Jeffery             return;
386e3e3c97aSAndrew Jeffery         }
387e3e3c97aSAndrew Jeffery 
3883cbd5a14SAndrew Jeffery         if (auto self = weakSelf.lock())
3893cbd5a14SAndrew Jeffery         {
390b5d7a7fbSAndrew Jeffery             self->readAndProcessNVMeSensor();
3913cbd5a14SAndrew Jeffery         }
392e3e3c97aSAndrew Jeffery     });
393e3e3c97aSAndrew Jeffery }
394e3e3c97aSAndrew Jeffery 
getTemperatureReading(int8_t reading)395e3e3c97aSAndrew Jeffery static double getTemperatureReading(int8_t reading)
396e3e3c97aSAndrew Jeffery {
397e3e3c97aSAndrew Jeffery     if (reading == static_cast<int8_t>(0x80) ||
398e3e3c97aSAndrew Jeffery         reading == static_cast<int8_t>(0x81))
399e3e3c97aSAndrew Jeffery     {
400e3e3c97aSAndrew Jeffery         // 0x80 = No temperature data or temperature data is more the 5 s
401e3e3c97aSAndrew Jeffery         // old 0x81 = Temperature sensor failure
402e3e3c97aSAndrew Jeffery         return std::numeric_limits<double>::quiet_NaN();
403e3e3c97aSAndrew Jeffery     }
404e3e3c97aSAndrew Jeffery 
405e3e3c97aSAndrew Jeffery     return reading;
406e3e3c97aSAndrew Jeffery }
407e3e3c97aSAndrew Jeffery 
processResponse(std::shared_ptr<NVMeSensor> & sensor,void * msg,size_t len)4088c7074e7SAndrew Jeffery void NVMeBasicContext::processResponse(std::shared_ptr<NVMeSensor>& sensor,
4098c7074e7SAndrew Jeffery                                        void* msg, size_t len)
410e3e3c97aSAndrew Jeffery {
41114108bb5SAndrew Jeffery     if (msg == nullptr || len < 6)
412e3e3c97aSAndrew Jeffery     {
41314108bb5SAndrew Jeffery         sensor->incrementError();
414e3e3c97aSAndrew Jeffery         return;
415e3e3c97aSAndrew Jeffery     }
416e3e3c97aSAndrew Jeffery 
417e3e3c97aSAndrew Jeffery     uint8_t* messageData = static_cast<uint8_t*>(msg);
4187aeb1a5eSAndrew Jeffery 
4197aeb1a5eSAndrew Jeffery     uint8_t status = messageData[0];
4202049bd26SEd Tanous     if (((status & NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY) != 0) ||
4212049bd26SEd Tanous         ((status & NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL) == 0))
4227aeb1a5eSAndrew Jeffery     {
4237aeb1a5eSAndrew Jeffery         sensor->markFunctional(false);
4247aeb1a5eSAndrew Jeffery         return;
4257aeb1a5eSAndrew Jeffery     }
4267aeb1a5eSAndrew Jeffery 
427e3e3c97aSAndrew Jeffery     double value = getTemperatureReading(messageData[2]);
42814108bb5SAndrew Jeffery     if (!std::isfinite(value))
429e3e3c97aSAndrew Jeffery     {
430e3e3c97aSAndrew Jeffery         sensor->incrementError();
43114108bb5SAndrew Jeffery         return;
432e3e3c97aSAndrew Jeffery     }
43314108bb5SAndrew Jeffery 
43414108bb5SAndrew Jeffery     sensor->updateValue(value);
435e3e3c97aSAndrew Jeffery }
436