dmm_display/DMM_HID.cpp

449 lines
7.1 KiB
C++

#include "DMM_HID.h"
#include <linux/hidraw.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <libudev.h>
#include <stdint.h>
#include <poll.h>
#include <limits>
#define VENDOR_ID 0x2571
#define PRODUCT_ID 0x4100
using std::vector;
using std::string;
constexpr int DMM_BUFSIZE = 8;
// Checks if a hidraw device matches the vendor id and product id
// If *_id is < 0 - it is not checked
bool check_hidraw_device(string dev, int vendor_id, int product_id)
{
int fd;
int res;
struct hidraw_devinfo info;
bool result = false;
bool vendor = true;
bool product = true;
fd = open(dev.c_str(), O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
return false;
}
memset(&info, 0, sizeof(info));
// Get Raw Info
res = ioctl(fd, HIDIOCGRAWINFO, &info);
if (res >= 0)
{
if ((vendor_id > 0) && (vendor_id != info.vendor))
{
vendor = false;
}
if ((product_id > 0) && (product_id != info.product))
{
product = false;
}
if (vendor && product)
{
result = true;
}
}
close(fd);
return result;
}
// Reads exact number of bytes from a fd for X milisec
bool read_exact(int fd, uint8_t *buf, int size, int timeout_ms)
{
struct pollfd poll_fd;
int bytes;
int bytes_read;
if ((fd < 0) || (nullptr == buf) || (size <= 0) || (timeout_ms <= 0))
{
return false;
}
poll_fd.fd = fd;
poll_fd.events = POLLIN;
bytes_read = 0;
while (bytes_read < size)
{
bytes = poll(&poll_fd, 1, timeout_ms);
if (bytes < 1)
{
return false;
}
bytes = read(fd, buf + bytes_read, size - bytes_read);
if (bytes < 1)
{
return false;
}
bytes_read += bytes;
}
return true;
}
vector<string> DMM_HID::get_dmm_devices()
{
vector<string> result;
struct udev *udev_handle = nullptr;
struct udev_enumerate *udev_enum_handle = nullptr;
struct udev_list_entry *udev_entry = nullptr;
struct udev_device *device = nullptr;
const char *syspath = nullptr;
string path;
int err;
// Create udev
udev_handle = udev_new();
if (nullptr == udev_handle)
{
return result;
}
// Create enumerator
udev_enum_handle = udev_enumerate_new(udev_handle);
if (nullptr == udev_enum_handle)
{
goto udev_out;
}
// Search only for "hidraw" devices
err = udev_enumerate_add_match_subsystem(udev_enum_handle, "hidraw");
if (err < 0)
{
goto udev_out;
}
// Scan devices
err = udev_enumerate_scan_devices(udev_enum_handle);
if (err < 0)
{
goto udev_out;
}
udev_entry = udev_enumerate_get_list_entry(udev_enum_handle);
if (nullptr == udev_entry)
{
goto udev_out;
}
// Iterate through devices
while (nullptr != udev_entry)
{
// Name is syspath
syspath = udev_list_entry_get_name(udev_entry);
if (nullptr != syspath)
{
device = udev_device_new_from_syspath(udev_handle, syspath);
if (nullptr != device)
{
// devnode is needed /dev/hidraw* path
path = udev_device_get_devnode(device);
// Check vendor id and product id pair
// There's no exit from the loop
// so it will return the last found device
if (check_hidraw_device(path, VENDOR_ID, PRODUCT_ID))
{
result.push_back(path);
}
udev_device_unref(device);
}
}
udev_entry = udev_list_entry_get_next(udev_entry);
}
udev_out:
// Cleanup
if (nullptr != udev_enum_handle)
{
udev_enumerate_unref(udev_enum_handle);
}
udev_unref(udev_handle);
return result;
}
DMM_HID::DMM_HID()
:m_fd(-1)
{
}
DMM_HID::~DMM_HID()
{
Close();
}
void DMM_HID::Open(const std::string& dev)
{
int fd;
if (is_open())
{
Close();
}
fd = open(dev.c_str(), O_RDONLY);
if (fd < 0)
{
return;
}
m_fd = fd;
}
void DMM_HID::Close()
{
if (is_open())
{
close(m_fd);
}
m_fd = -1;
}
bool DMM_HID::is_open()
{
if (m_fd < 0)
{
return false;
}
return true;
}
vector<Reading> DMM_HID::get_readings(int timeout_ms)
{
vector<Reading> result;
uint8_t buf[DMM_BUFSIZE];
Reading reading;
if (m_fd < 0)
{
return result;
}
while(read_exact(m_fd, buf, DMM_BUFSIZE, timeout_ms))
{
reading.value[0] = 0;
reading.value[1] = 0;
reading.value[2] = 0;
reading.value[3] = 0;
reading.modifier = 0;
reading.dot_pos = 0;
reading.is_inf = false;
reading.is_neg = false;
reading.is_DC = false;
reading.is_rel = false;
reading.is_hold = false;
reading.is_min = false;
reading.is_max = false;
reading.type = Reading::reading_type_max;
// Check for infinity
if ((0x4F == buf[1]) && (0x4C == buf[2]))
{
reading.is_inf = true;
}
// BCD 4-bit values
reading.value[0] = (buf[1] >> 4);
reading.value[1] = (buf[1] & 0x0F);
reading.value[2] = (buf[2] >> 4);
reading.value[3] = (buf[2] & 0x0F);
// Byte 0 Bit 6 - Negative
if (buf[0] & 0x40)
{
reading.is_neg = true;
}
// Byte 0 second half - Dot position
{
switch (buf[0] & 0x0F)
{
case 0:
// Do nothing
break;
case 1:
reading.dot_pos = 1;
break;
case 2:
reading.dot_pos = 2;
break;
case 3:
reading.dot_pos = 3;
break;
case 4:
reading.dot_pos = 3;
break;
case 7:
// Assuming 3&4 overlap
reading.dot_pos = 3;
break;
default:
// Do nothing
break;
}
}
// Unit Modifier
{
// Byte 4 Bit 1
if (buf[4] & 0x02)
{
// nano = 10^-9
reading.modifier = -9;
}
// Byte 5 Bit 7
else if (buf[5] & 0x80)
{
// micro = 10^-6
reading.modifier = -6;
}
// Byte 5 Bit 6
else if (buf[5] & 0x40)
{
// mili = 10^-3
reading.modifier = -3;
}
// Byte 5 Bit 5
else if (buf[5] & 0x20)
{
// kilo = 10^3
reading.modifier = 3;
}
// Byte 5 Bit 4
else if (buf[5] & 0x10)
{
// mega = 10^6
reading.modifier = 6;
}
}
// AC/DC
// Byte 3 Bit 4
if (buf[3] & 0x10)
{
reading.is_DC = true;
}
// Hold / min / max / rel
{
// Byte 3 Bit 1
if (buf[3] & 0x02)
{
reading.is_hold = true;
}
// Byte 3 Bit 2
if (buf[3] & 0x04)
{
reading.is_rel = true;
}
// Byte 4 Bit 5
if (buf[4] & 0x20)
{
reading.is_max = true;
}
// Byte 4 Bit 4
if (buf[4] & 0x10)
{
reading.is_min = true;
}
}
// Reading type
{
// if (buf[5] & 0x08)
// {
// reading.type = Reading::reading_continuity;
// }
// else if (buf[5] & 0x04)
// {
// reading.type = Reading::reading_diode;
// }
// else
if (buf[5] & 0x02)
{
reading.type = Reading::reading_percent;
}
else if (buf[6] & 0x80)
{
reading.type = Reading::reading_volts;
}
else if (buf[6] & 0x40)
{
reading.type = Reading::reading_amps;
}
else if (buf[6] & 0x20)
{
reading.type = Reading::reading_ohms;
}
else if (buf[6] & 0x10)
{
reading.type = Reading::reading_hFE;
}
else if (buf[6] & 0x08)
{
reading.type = Reading::reading_hertz;
}
else if (buf[6] & 0x04)
{
reading.type = Reading::reading_farads;
}
else if (buf[6] & 0x02)
{
reading.type = Reading::reading_celsius;
}
else if (buf[6] & 0x01)
{
reading.type = Reading::reading_fahrenheit;
}
}
result.push_back(reading);
}
return result;
}
void DMM_HID::lock()
{
m_mutex.lock();
}
void DMM_HID::unlock()
{
m_mutex.unlock();
}