// // driver_handler.cpp // // Copyright (c) 2019 2020 Andrea Bondavalli. All rights reserved. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include #include #include "log.hpp" #include "driver_handler.hpp" /* void dump(const void* mem, unsigned int n) { const char* p = reinterpret_cast(mem); for (unsigned int i = 0; i < n; i++) { std::cout << std::hex << int(p[i]) << " "; } std::cout << std::endl; } */ bool DriverHandler::init(const Config& /* config */) { if (running_) { return true; } try { client_u2k_.init(nl_endpoint(0), nl_protocol(NETLINK_U2K_ID)); client_k2u_.init(nl_endpoint(0), nl_protocol(NETLINK_K2U_ID)); running_ = true; res_ = std::async(std::launch::async, &DriverHandler::event_receiver, this); return true; } catch (const boost::system::system_error& se) { BOOST_LOG_TRIVIAL(fatal) << "driver_handler:: init " << se.what(); BOOST_LOG_TRIVIAL(fatal) << "Kernel module not loaded ?"; return false; } } void DriverHandler::send(enum MT_ALSA_msg_id id, NetlinkClient& client, uint8_t* buffer, size_t data_size, const uint8_t* data) { struct MT_ALSA_msg alsa_msg; memset(&alsa_msg, 0, sizeof(alsa_msg)); alsa_msg.id = id; alsa_msg.errCode = 0; alsa_msg.dataSize = data_size; struct nlmsghdr* nlh = (struct nlmsghdr*)buffer; nlh->nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct MT_ALSA_msg) + data_size; nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = 0; nlh->nlmsg_type = NLMSG_DONE; memcpy(NLMSG_DATA(nlh), &alsa_msg, sizeof(struct MT_ALSA_msg)); if (data != nullptr && data_size > 0) { memcpy(reinterpret_cast(NLMSG_DATA(nlh)) + sizeof(struct MT_ALSA_msg), data, data_size); } nl_endpoint kernel_endpoint(0, 0); /* For Linux Kernel */ client.get_socket().send_to(boost::asio::buffer(nlh, nlh->nlmsg_len), kernel_endpoint); } bool DriverHandler::event_receiver() { while (running_) { boost::system::error_code ec; auto bytes = client_k2u_.receive(boost::asio::buffer(event_buffer_, max_payload), boost::posix_time::seconds(reply_timeout_secs), ec); if (ec) { if (ec != boost::asio::error::operation_aborted) { BOOST_LOG_TRIVIAL(fatal) << "driver_handler::k2u_receive " << ec.message(); return false; } } for (struct nlmsghdr* nlh = (nlmsghdr*)event_buffer_; NLMSG_OK(nlh, (size_t)bytes); nlh = NLMSG_NEXT(nlh, bytes)) { if (nlh->nlmsg_type == NLMSG_DONE) { struct MT_ALSA_msg* palsa_msg = reinterpret_cast NLMSG_DATA(nlh); BOOST_LOG_TRIVIAL(debug) << "driver_handler:: received event code " << palsa_msg->id << " error " << palsa_msg->errCode << " data len " << palsa_msg->dataSize; if (palsa_msg->errCode == 0) { size_t res_size = sizeof(int32_t); uint8_t res[sizeof(int32_t)]; memset(res, 0, res_size); on_event(palsa_msg->id, res_size, res, palsa_msg->dataSize, reinterpret_cast(palsa_msg) + data_offset); BOOST_LOG_TRIVIAL(debug) << "driver_handler::sending event response " << palsa_msg->id << " data len " << res_size; memset(response_buffer_, 0, sizeof(response_buffer_)); try { send(palsa_msg->id, client_k2u_, response_buffer_, res_size, res); } catch (boost::system::error_code& ec) { BOOST_LOG_TRIVIAL(error) << "driver_handler::k2u_send_to " << ec.message(); on_event_error(palsa_msg->id, DaemonErrc::send_u2k_failed); } } else { on_event_error(palsa_msg->id, get_driver_error(palsa_msg->errCode)); } } } } return true; } bool DriverHandler::terminate() { if (running_) { running_ = false; client_u2k_.terminate(); client_k2u_.terminate(); return res_.get(); } return true; } void DriverHandler::send_command(enum MT_ALSA_msg_id id, size_t data_size, const uint8_t* data) { std::lock_guard lock(mutex_); if (data_size > max_payload) { on_command_error(id, DaemonErrc::send_invalid_size); return; } BOOST_LOG_TRIVIAL(debug) << "driver_handler:: sending command code " << id << " data len " << data_size; memset(command_buffer_, 0, sizeof(command_buffer_)); try { send(id, client_u2k_, command_buffer_, data_size, data); } catch (boost::system::error_code& ec) { BOOST_LOG_TRIVIAL(error) << "driver_handler:: u2k_send_to " << ec.message(); on_command_error(id, DaemonErrc::send_u2k_failed); return; } BOOST_LOG_TRIVIAL(debug) << "driver_handler:: command code " << id << " data len " << data_size << " sent"; boost::system::error_code ec; auto bytes = client_u2k_.receive(boost::asio::buffer(command_buffer_, max_payload), boost::posix_time::seconds(reply_timeout_secs), ec); if (ec) { BOOST_LOG_TRIVIAL(error) << "driver_handler:: u2k_receive " << ec.message(); on_command_error(id, DaemonErrc::receive_u2k_failed); return; } for (struct nlmsghdr* nlh = (nlmsghdr*)command_buffer_; NLMSG_OK(nlh, (size_t)bytes); nlh = NLMSG_NEXT(nlh, bytes)) { if (nlh->nlmsg_type == NLMSG_DONE) { struct MT_ALSA_msg* palsa_msg = reinterpret_cast NLMSG_DATA(nlh); BOOST_LOG_TRIVIAL(debug) << "driver_handler:: received cmd code " << palsa_msg->id << " error " << palsa_msg->errCode << " data len " << palsa_msg->dataSize; if (id != palsa_msg->id) { BOOST_LOG_TRIVIAL(warning) << "driver_handler:: unexpected cmd response:" << "sent " << id << " received " << palsa_msg->id; on_command_error(palsa_msg->id, DaemonErrc::invalid_driver_response); } else { if (palsa_msg->errCode == 0) { // dump((uint8_t*)palsa_msg + data_offset, palsa_msg->dataSize); on_command_done( palsa_msg->id, palsa_msg->dataSize, reinterpret_cast(palsa_msg) + data_offset); } else { on_command_error(palsa_msg->id, get_driver_error(palsa_msg->errCode)); } } } } }