/*
PIP - Platform Independent Primitives
CAN
Andrey Bychkov work.a.b@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
*/
#include "pican.h"
#include "pipropertystorage.h"
#include "piwaitevent_p.h"
#if !defined(WINDOWS) && !defined(MAC_OS) && !defined(PIP_NO_SOCKET)
# define PIP_CAN
#endif
#ifdef PIP_CAN
# include
# include
# include
# include
# ifndef AF_CAN
# define AF_CAN 29
# endif
# ifndef PF_CAN
# define PF_CAN AF_CAN
# endif
#endif
REGISTER_DEVICE(PICAN)
#ifdef PIP_CAN
PRIVATE_DEFINITION_START(PICAN)
PIWaitEvent event;
PRIVATE_DEFINITION_END(PICAN)
#endif
PICAN::PICAN(const PIString & path, PIIODevice::DeviceMode mode): PIIODevice(path, mode) {
setThreadedReadBufferSize(256);
setPath(path);
#ifdef PIP_CAN
can_id = 0;
sock = 0;
PRIVATE->event.create();
#endif
}
PICAN::~PICAN() {
stopAndWait();
close();
#ifdef PIP_CAN
PRIVATE->event.destroy();
#endif
}
bool PICAN::openDevice() {
#ifdef PIP_CAN
piCout << "PICAN open device" << path();
sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (sock < 0) {
piCoutObj << "Error! while opening socket";
return false;
}
ifreq ifr;
strcpy(ifr.ifr_name, path().dataAscii());
piCout << "PICAN try to get interface index...";
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
piCoutObj << "Error! while determin the interface ioctl";
return false;
}
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv);
// bind socket to all CAN interface
sockaddr_can addr;
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
piCout << "PICAN try to bind socket to interface" << ifr.ifr_ifindex;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
piCoutObj << "Error! while binding socket";
return false;
}
piCout << "PICAN Open OK!";
return true;
#else
piCoutObj << "PICAN not implemented on windows";
return false;
#endif
}
bool PICAN::closeDevice() {
#ifdef PIP_CAN
interrupt();
if (sock > 0) ::close(sock);
#endif
return true;
}
ssize_t PICAN::readDevice(void * read_to, ssize_t max_size) {
#ifdef PIP_CAN
// piCout << "PICAN read";
can_frame frame;
ssize_t ret = 0;
if (PRIVATE->event.wait(sock)) ret = ::read(sock, &frame, sizeof(can_frame));
if (ret < 0) { /*piCoutObj << "Error while read CAN frame " << ret;*/
return -1;
}
// piCoutObj << "receive CAN frame Id =" << frame.can_id;
memcpy(read_to, frame.data, piMini(frame.can_dlc, max_size));
readed_id = frame.can_id;
return piMini(frame.can_dlc, max_size);
#endif
return 0;
}
ssize_t PICAN::writeDevice(const void * data, ssize_t max_size) {
#ifdef PIP_CAN
// piCout << "PICAN write" << can_id << max_size;
if (max_size > 8) {
piCoutObj << "Can't send CAN frame bigger than 8 bytes (requested " << max_size << ")!";
return -1;
}
can_frame frame;
frame.can_id = can_id;
frame.can_dlc = max_size;
memcpy(frame.data, data, max_size);
ssize_t ret = 0;
ret = ::write(sock, &frame, sizeof(can_frame));
if (ret < 0) {
piCoutObj << "Error while send CAN frame " << ret;
return -1;
}
return max_size;
#endif
return 0;
}
void PICAN::setCANID(int id) {
can_id = id;
}
int PICAN::CANID() const {
return can_id;
}
int PICAN::readedCANID() const {
return readed_id;
}
void PICAN::interrupt() {
#ifdef PIP_CAN
PRIVATE->event.interrupt();
#endif
}
PIString PICAN::constructFullPathDevice() const {
PIString ret;
ret += path() + ":" + PIString::fromNumber(CANID(), 16);
return ret;
}
void PICAN::configureFromFullPathDevice(const PIString & full_path) {
PIStringList pl = full_path.split(":");
for (int i = 0; i < pl.size_s(); ++i) {
PIString p(pl[i]);
switch (i) {
case 0: setPath(p); break;
case 1: setCANID(p.trimmed().toInt(16)); break;
default: break;
}
}
}
PIPropertyStorage PICAN::constructVariantDevice() const {
PIPropertyStorage ret;
ret.addProperty("path", path());
ret.addProperty("CAN ID", PIString::fromNumber(CANID(), 16));
return ret;
}
void PICAN::configureFromVariantDevice(const PIPropertyStorage & d) {
setPath(d.propertyValueByName("path").toString());
setCANID(d.propertyValueByName("CAN ID").toString().toInt(16));
}