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
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);
|
|
}
|