/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see .
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeeuartadaptermonitor.h"
#include "loggingcategory.h"
#include
#include
ZigbeeUartAdapterMonitor::ZigbeeUartAdapterMonitor(QObject *parent) : QObject(parent)
{
qRegisterMetaType();
m_udev = udev_new();
if (!m_udev) {
qCWarning(dcZigbeeAdapterMonitor()) << "Could not initialize udev for the adapter monitor";
return;
}
// Read initially all tty devices
foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) {
addAdapterInternally(serialPortInfo.systemLocation());
}
// Create udev monitor
m_monitor = udev_monitor_new_from_netlink(m_udev, "udev");
if (!m_monitor) {
qCWarning(dcZigbeeAdapterMonitor()) << "Could not initialize udev monitor.";
udev_unref(m_udev);
m_udev = nullptr;
return;
}
// Set monitor filter to tty subsystem
if (udev_monitor_filter_add_match_subsystem_devtype(m_monitor, "tty", nullptr) < 0) {
qCWarning(dcZigbeeAdapterMonitor()) << "Could not set subsystem device type filter to tty.";
udev_monitor_unref(m_monitor);
m_monitor = nullptr;
udev_unref(m_udev);
m_udev = nullptr;
return;
}
// Enable the monitor
if (udev_monitor_enable_receiving(m_monitor) < 0) {
qCWarning(dcZigbeeAdapterMonitor()) << "Could not enable udev monitor.";
udev_monitor_unref(m_monitor);
m_monitor = nullptr;
udev_unref(m_udev);
m_udev = nullptr;
return;
}
// Create socket notifier for read
int socketDescriptor = udev_monitor_get_fd(m_monitor);
m_notifier = new QSocketNotifier(socketDescriptor, QSocketNotifier::Read, this);
connect(m_notifier, &QSocketNotifier::activated, this, [this, socketDescriptor](int socket){
// Make sure the socket matches
if (socketDescriptor != socket) {
qCWarning(dcZigbeeAdapterMonitor()) << "socket != socketdescriptor";
return;
}
// Create udev device
udev_device *device = udev_monitor_receive_device(m_monitor);
if (!device) {
qCWarning(dcZigbeeAdapterMonitor()) << "Got socket sotification but could not read device information.";
return;
}
QString actionString = QString::fromLatin1(udev_device_get_action(device));
QString systemPath = QString::fromLatin1(udev_device_get_property_value(device,"DEVNAME"));
QString manufacturerString = QString::fromLatin1(udev_device_get_property_value(device,"ID_VENDOR_ENC"));
QString descriptionString = QString::fromLatin1(udev_device_get_property_value(device,"ID_MODEL_ENC"));
QString serialNumberString = QString::fromLatin1(udev_device_get_property_value(device, "ID_SERIAL_SHORT"));
// Clean udev device
udev_device_unref(device);
// Make sure we know the action
if (actionString.isEmpty())
return;
if (actionString == "add") {
qCDebug(dcZigbeeAdapterMonitor()) << "[+]" << systemPath << serialNumberString;
if (!m_availableAdapters.contains(systemPath)) {
addAdapterInternally(systemPath);
}
}
if (actionString == "remove") {
qCDebug(dcZigbeeAdapterMonitor()) << "[-]" << systemPath << serialNumberString;
if (m_availableAdapters.contains(systemPath)) {
ZigbeeUartAdapter adapter = m_availableAdapters.take(systemPath);
qCDebug(dcZigbeeAdapterMonitor()) << "Removed" << adapter;
emit adapterRemoved(adapter);
}
}
});
m_notifier->setEnabled(true);
m_isValid = true;
}
ZigbeeUartAdapterMonitor::~ZigbeeUartAdapterMonitor()
{
if (m_notifier)
delete m_notifier;
if (m_monitor)
udev_monitor_unref(m_monitor);
if (m_udev)
udev_unref(m_udev);
}
QList ZigbeeUartAdapterMonitor::availableAdapters() const
{
return m_availableAdapters.values();
}
bool ZigbeeUartAdapterMonitor::hasAdapter(const QString &serialPort) const
{
return m_availableAdapters.contains(serialPort);
}
bool ZigbeeUartAdapterMonitor::isValid() const
{
return m_isValid;
}
void ZigbeeUartAdapterMonitor::addAdapterInternally(const QString &serialPort)
{
foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) {
if (serialPortInfo.systemLocation() != serialPort)
continue;
if (m_availableAdapters.keys().contains(serialPort)) {
qCWarning(dcZigbeeAdapterMonitor()) << "The adapter" << serialPort << "has already been added to the monitor.";
continue;
}
qCDebug(dcZigbeeAdapterMonitor()) << "Adapter candidate" << serialPortInfo.portName();
qCDebug(dcZigbeeAdapterMonitor()) << " Description:" << serialPortInfo.description();
qCDebug(dcZigbeeAdapterMonitor()) << " System location:" << serialPortInfo.systemLocation();
qCDebug(dcZigbeeAdapterMonitor()) << " Manufacturer:" << serialPortInfo.manufacturer();
qCDebug(dcZigbeeAdapterMonitor()) << " Serialnumber:" << serialPortInfo.serialNumber();
if (serialPortInfo.hasProductIdentifier()) {
qCDebug(dcZigbeeAdapterMonitor()) << " Product identifier:" << serialPortInfo.productIdentifier();
}
if (serialPortInfo.hasVendorIdentifier()) {
qCDebug(dcZigbeeAdapterMonitor()) << " Vendor identifier:" << serialPortInfo.vendorIdentifier();
}
// Check if we recognize this controller
ZigbeeUartAdapter adapter;
if (serialPortInfo.portName().isEmpty()) {
adapter.setName("Zigbee adapter");
} else {
adapter.setName(serialPortInfo.portName());
}
if (serialPortInfo.description().isEmpty()) {
adapter.setDescription("Unknown");
} else {
adapter.setDescription(serialPortInfo.description());
}
adapter.setSerialPort(serialPortInfo.systemLocation());
adapter.setSerialNumber(serialPortInfo.serialNumber());
// Check if we recognize this adapter from USB information
if (serialPortInfo.manufacturer().toLower().contains("dresden elektronik")) {
adapter.setHardwareRecognized(true);
adapter.setBackendType(Zigbee::ZigbeeBackendTypeDeconz);
adapter.setBaudRate(38400);
} else if (serialPortInfo.manufacturer().toLower().contains("nxp")) {
adapter.setHardwareRecognized(true);
adapter.setBackendType(Zigbee::ZigbeeBackendTypeNxp);
adapter.setBaudRate(115200);
}
qCDebug(dcZigbeeAdapterMonitor()) << "Added" << adapter;
m_availableAdapters.insert(adapter.serialPort(), adapter);
emit adapterAdded(adapter);
}
}