You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

601 lines
14 KiB

#include "i2cif.h"
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <QCoreApplication>
#include <QSettings>
#include <QProcess>
#include <QThread>
#include <sailfishapp.h>
#include "conv.h"
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <iostream>
#include <iomanip>
#include <netinet/in.h>
#include <algorithm>
#define DEFAULT_I2C_DEV QString("/dev/i2c-1/")
I2cif::I2cif(QObject *parent) :
QObject(parent)
{
m_probingResult = QStringList();
m_readResult = "Nothing yet";
tohVddSet("on");
emit tohVddStatusChanged();
}
I2cif::~I2cif()
{
tohVddSet("off");
}
QStringList I2cif::i2cProbingStatus()
{
return m_probingResult;
}
QString I2cif::i2cReadResult()
{
return m_readResult;
}
/*
* Function to set TOH VDD On and Off
*
*
*/
void I2cif::tohVddSet(QString onOff)
{
int fd;
fd = open("/sys/devices/platform/reg-userspace-consumer.0/state", O_WRONLY);
if (!(fd < 0))
{
if (write (fd, QString::localeAwareCompare( onOff, "on") ? "0" : "1", 1) != 1)
fprintf(stderr, "Vdd set failed\n");
else
fprintf(stderr, "Vdd set OK\n");
close(fd);
}
else
fprintf(stderr, "Vdd failed to open. Did you start i2ctool as root?\n");
emit tohVddStatusChanged();
}
void I2cif::requestTohVddState()
{
emit tohVddStatusChanged();
}
/*
* Function to check which is current state of Vdd
*
*/
bool I2cif::tohVddGet()
{
int fd;
int retval = 0;
char buf[1] = { 0 };
fd = open("/sys/devices/platform/reg-userspace-consumer.0/state", O_RDONLY);
if (!(fd < 0))
{
retval += read(fd, buf ,1);
close(fd);
}
return (buf[0] == 'e'); // values are "enabled" or "disabled"
}
/*
* I2C Write Function
*
*
*/
Q_INVOKABLE int I2cif::i2cWrite(const uint8_t &slaveAddr, const uint16_t &writeAddress, const uint16_t &data){
int file;
__u8 buf[4];
const char* devNameChar = "/dev/i2c-1";
//fprintf(stderr, "writing to address %02x: ", slaveAddr);
if ((file = open (devNameChar, O_RDWR)) < 0)
{
fprintf(stderr,"open error\n");
emit i2cError();
return -1;
}
if (ioctl(file, I2C_SLAVE, slaveAddr) < 0)
{
close(file);
fprintf(stderr,"ioctl error\n");
emit i2cError();
return -1;
}
buf[0] = (writeAddress >> 8) & 0xFF; // High byte
buf[1] = writeAddress & 0xFF; // Low byte
buf[2] = (data >> 8) & 0xFF; // High byte
buf[3] = data & 0xFF; // Low byte
/*fprintf(stderr, "Data to be written: ");
for (int i = 0; i < 4; i++) {
fprintf(stderr, "%02x ", buf[i]);
}
fprintf(stderr, "\n");
*/
/* write the data */
if (write(file, buf, 4) != 4)
{
close(file);
fprintf(stderr,"write error\n");
emit i2cError();
return -1;
}
close(file);
//fprintf(stderr,"write ok\n");
emit i2cWriteOk();
return 0;
}
int I2cif::i2cRead(const uint8_t &slaveAddr, const uint16_t &startAddress, const uint16_t &nMemAddressRead, uint16_t *data)
{
int file;
Conv conv;
m_readResult = QString();
uint16_t addr = startAddress;
__u8 *buf = new __u8[2 * nMemAddressRead];
const char* devNameChar = "/dev/i2c-1";
if ((file = open (devNameChar, O_RDWR)) < 0)
{
fprintf(stderr,"open error\n");
emit i2cError();
delete[] buf;
return -1;
}
struct i2c_rdwr_ioctl_data i2c_data;
struct i2c_msg msg[2];
__u8 regAddrBytes[2] = {0,0};
regAddrBytes[0] = (startAddress >> 8) & 0xFF; // High byte
regAddrBytes[1] = startAddress & 0xFF; // Low byte
i2c_data.msgs = msg;
i2c_data.nmsgs = 2;
i2c_data.msgs[0].addr = slaveAddr;
i2c_data.msgs[0].flags = 0;
i2c_data.msgs[0].len = 2;
i2c_data.msgs[0].buf = regAddrBytes;
i2c_data.msgs[1].addr = slaveAddr;
i2c_data.msgs[1].flags = I2C_M_RD;
constexpr uint16_t ChunkSize = 32; // 192 bytes (96 uint16_t)
uint16_t chunks = nMemAddressRead / ChunkSize;
uint16_t remainder = nMemAddressRead % ChunkSize;
for (uint16_t chunk = 0; chunk <= chunks; chunk++) {
uint16_t length = (chunk == chunks ? remainder : ChunkSize);
if (length == 0)
break;
regAddrBytes[0] = (addr >> 8) & 0xFF; // High byte
regAddrBytes[1] = addr & 0xFF; // Low byte
i2c_data.msgs[0].buf = regAddrBytes;
i2c_data.msgs[1].len = length * 2;
i2c_data.msgs[1].buf = &buf[chunk * ChunkSize * 2];
int ret = ioctl(file, I2C_RDWR, &i2c_data);
if (ret < 0) {
fprintf(stderr, "read data fail %d\n", ret);
emit i2cError();
delete[] buf;
return -1;
}
//fprintf(stderr, "read ");
for (uint16_t i = 0; i < length; ++i) {
uint16_t index = chunk * ChunkSize + i;
data[index] = ((buf[2 * index] << 8) == 0xFF ? 0x00 : (buf[2 * index] << 8)) | buf[2 * index + 1];
m_readResult = m_readResult + conv.toHex(data[index], 4) + " ";
//fprintf(stderr, "%04x ", data[index]);
}
//fprintf(stderr, "\n");
// Wait 0.4ms before next chunk
usleep(400);
addr += ChunkSize;
}
//fprintf(stderr, "\n");
delete[] buf;
emit i2cReadResultChanged();
close(file);
return 0;
}
/*
* I2C Read function
*
*/
void I2cif::i2cRead(QString devName, unsigned char address, int count)
{
int file;
char buf[200];
Conv conv;
m_readResult = "";
//emit i2cReadResultChanged();
QByteArray tmpBa = devName.toUtf8();
const char* devNameChar = tmpBa.constData();
fprintf(stderr, "reading from address %02x count %d\n", address, count);
if ((file = open (devNameChar, O_RDWR)) < 0)
{
fprintf(stderr,"open error\n");
emit i2cError();
return;
}
if (ioctl(file, I2C_SLAVE, address) < 0)
{
close(file);
fprintf(stderr,"ioctl error\n");
emit i2cError();
return;
}
/* Read data */
if (read( file, buf, count ) != count)
{
close(file);
fprintf(stderr,"read error\n");
emit i2cError();
return;
}
close(file);
/* copy buf to m_readResult */
int i;
fprintf(stderr, "read ");
for (i=0; i<count ; i++)
{
m_readResult = m_readResult + conv.toHex(buf[i],2) + " ";
fprintf(stderr, "%02x ", buf[i]);
}
fprintf(stderr, "\n");
emit i2cReadResultChanged();
}
//--------------------------------------------------------------------------------
void I2cif::i2cWrite(QString devName, unsigned char address, QString data)
{
int file;
char buf[200];
bool parseOk;
QByteArray tmpBa = devName.toUtf8();
const char* devNameChar = tmpBa.constData();
fprintf(stderr, "writing to address %02x: ", address);
/* parse QString data to buf */
QStringList bytes = data.split(" ");
int i;
for (i=0 ; i<bytes.length(); i++)
{
QString tmp = bytes.value(i);
buf[i] = tmp.toInt(&parseOk, 16);
if (!parseOk)
{
fprintf(stderr, "parsing error %d\n", i);
emit i2cError();
return;
}
fprintf(stderr, "%02x ", buf[i]);
}
fprintf(stderr, "\n");
if ((file = open (devNameChar, O_RDWR)) < 0)
{
fprintf(stderr,"open error\n");
emit i2cError();
return;
}
if (ioctl(file, I2C_SLAVE, address) < 0)
{
close(file);
fprintf(stderr,"ioctl error\n");
emit i2cError();
return;
}
/* write the data */
if (write( file, buf, bytes.length() ) != bytes.length())
{
close(file);
fprintf(stderr,"write error\n");
emit i2cError();
return;
}
close(file);
fprintf(stderr,"write ok\n");
emit i2cWriteOk();
}
/*
* I2C write then read with repeated stop
*/
void I2cif::i2cWriteThenRead(QString devName, unsigned char address, QString data, int count)
{
int file;
Conv conv;
m_readResult = QString();
QByteArray tmpBa = devName.toUtf8();
const char* devNameChar = tmpBa.constData();
if ((file = open (devNameChar, O_RDWR)) < 0)
{
fprintf(stderr,"open error\n");
emit i2cError();
return;
}
struct i2c_rdwr_ioctl_data i2c_data;
struct i2c_msg msg[2];
unsigned char *buftx;
unsigned char *bufrx;
bufrx = (unsigned char *)malloc(count +1);
i2c_data.msgs = msg;
i2c_data.nmsgs = 2;
QStringList bytes = data.split(" ");
int i;
buftx = (unsigned char *)malloc(bytes.length()+1);
for (i=0 ; i<bytes.length(); i++)
{
bool parseOk;
QString tmp = bytes.value(i);
buftx[i] = tmp.toInt(&parseOk, 16);
if (!parseOk)
{
fprintf(stderr, "parsing error %d\n", i);
emit i2cError();
return;
}
fprintf(stderr, "%02x ", buftx[i]);
}
fprintf(stderr, "\n");
i2c_data.msgs[0].addr = address;
i2c_data.msgs[0].flags = 0;
i2c_data.msgs[0].len = bytes.length();
i2c_data.msgs[0].buf = (unsigned char *)buftx;
i2c_data.msgs[1].addr = address;
i2c_data.msgs[1].flags = I2C_M_RD;
i2c_data.msgs[1].len = count;
i2c_data.msgs[1].buf = (unsigned char *)bufrx;
int ret = ioctl(file, I2C_RDWR, &i2c_data);
if (ret < 0)
{
fprintf(stderr, "read data fail %d\n", ret);
emit i2cError();
return;
}
close(file);
fprintf(stderr, "read ");
for (i=0; i<count ; i++)
{
m_readResult = m_readResult + conv.toHex(bufrx[i],2) + " ";
fprintf(stderr, "%02x ", bufrx[i]);
}
fprintf(stderr, "\n");
emit i2cReadResultChanged();
}
/*
* Simple probing function to check its presence in I2C bus
*
* Returns a stringlist through i2cProbingStatus()
* emits a i2cProbingChanged() signal when complete
*
* "ok" - reading 2 bytes was succesful
* "openFail - open() command faild to open device
* "ioctlFail" - ioctl() to slave address failed
* "readFail" - read() 2 bytes from slave failed (basically NACK)
* "skipped" - addresses 4...7 are skipped to speed up operation
*
*/
void I2cif::i2cProbe(QString devName)
{
int file;
char buf[2];
unsigned char address;
m_probingResult.clear();
for (address = 0; address < 128; address++)
{
/* These few addresses takes long time to probe, so skip them brutely */
if (address >= 4 && address <= 7)
{
m_probingResult.append("skipped");
continue;
}
QByteArray tmpBa = devName.toUtf8();
const char* devNameChar = tmpBa.constData();
fprintf(stderr, "probing %s address %02x: ", devNameChar, address);
if ((file = open (devNameChar, O_RDWR)) < 0)
{
m_probingResult.append("openFail");
fprintf(stderr, "open failed\n");
close(file);
continue;
}
if (ioctl(file, I2C_SLAVE, address) < 0)
{
close(file);
m_probingResult.append("ioctlFail");
fprintf(stderr, "ioctl failed\n");
close(file);
continue;
}
/* Try to read 2 bytes. This is also safe for LM75 */
if (read( file, buf, 2 ) != 2)
{
close(file);
m_probingResult.append("readFail");
fprintf(stderr, "read failed\n");
close(file);
continue;
}
close(file);
m_probingResult.append("ok");
fprintf(stderr, "device found at address %02x\n", address);
}
emit i2cProbingChanged();
}
/*
* If microswitch is pressed tohd takes control over i2c-1-0050 (eeprom)
* this is used to release it.
* but duh, needs to be done as root
* echo toh-core.0 > /sys/bus/platform/drivers/toh-core/unbind
*/
void I2cif::unbindTohCore()
{
int fd;
fd = open("/sys/bus/platform/drivers/toh-core/unbind", O_WRONLY);
if (!(fd < 0))
{
if (write (fd, "toh-core.0", 10) != 10)
fprintf(stderr, "unbind failed\n");
else
fprintf(stderr, "unbind OK\n");
close(fd);
}
else
fprintf(stderr, "unbind failed. Did you start i2ctool as root?\n");
}
/*
* Default values commands
*
*/
void I2cif::setAsDefault(QString index, QString value)
{
QSettings s("harbour-i2ctool", "harbour-i2ctool");
s.beginGroup("Defaults");
s.setValue(index, value);
s.endGroup();
}
QString I2cif::getDefault(QString index)
{
QString value;
QSettings s("harbour-i2ctool", "harbour-i2ctool");
s.beginGroup("Defaults");
value = s.value(index, firstTimeDefault(index)).toString();
s.endGroup();
return value;
}
/* helper :) */
QString I2cif::firstTimeDefault(QString index)
{
if (index == "0") return "4B4C";
if (index == "1") return "0001";
if (index == "2") return "01";
if (index == "3") return "0100";
if (index == "4") return "0040";
if (index == "5") return "0040";
if (index == "6") return "0080";
if (index == "7") return "0000";
return "0000";
}
/* use xdg-open to start pdf-viewer for showing usersguide
*/
void I2cif::openUsersGuide()
{
QProcess proc;
QString ugpath = SailfishApp::pathTo("i2ctool-ug.pdf").toString();
fprintf(stderr, "loading user's guide %s\n", qPrintable(ugpath));
proc.startDetached("/usr/bin/xdg-open" , QStringList() << ugpath);
QThread::msleep(100);
}