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