I2C IOCTL Ошибка записи
Эй, я пытаюсь написать приложение из пользовательского пространства, чтобы переместить некоторые данные в I2C для встроенной системы, работающей под управлением PetaLinux, операционной системы для встроенного Linux, хотя я не думаю, что именно это влияет на проблему. Я получаю тайм-аут соединения и ошибку сегментации.
Функция имеет макросы, которые направляют ее на запись в первую шину I2C. Я указываю данные, которые хочу записать в main, и передаю их в i2c_write, которая затем передает их в i2c_ioctl_write.
Вот код:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
#define REG_ADDR 0x00
int i2c_ioctl_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
printf("i2c_ioctl_write\n");
int i, j = 0;
int ret;
uint8_t *buf;
buf = malloc(1 + 2 * (sizeof(data) / sizeof(data[0])));
if (buf == NULL) {
return -ENOMEM;
}
printf("\tBuffer Allocation Successful...\n");
buf[j ++] = regaddr;
for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {
buf[j ++] = (data[i] & 0xff00) >> 8;
buf[j ++] = data[i] & 0xff;
}
printf("\tBuffer Setup Successful...\n");
struct i2c_msg messages[] = {
{
.addr = dev,
.buf = buf,
.len = sizeof(buf) / sizeof(buf[0]),
},
};
printf("\tSetup I2C Messages...\n");
struct i2c_rdwr_ioctl_data payload = {
.msgs = messages,
.nmsgs = sizeof(messages) / sizeof(messages[0]),
};
printf("\tSetup I2C IOCTL Payload...\n");
ret = ioctl(fd, I2C_RDWR, &payload);
printf("\tWrote with IOCTL...\n");
if (ret < 0) {
ret = -errno;
}
free (buf);
return ret;
}
int i2c_ioctl_smbus_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
printf("i2c_ioctl_smbus_write\n");
int i, j = 0;
int ret;
uint8_t *buf;
buf = malloc(2 * (sizeof(data) / sizeof(data[0])));
if (buf == NULL) {
return -ENOMEM;
}
for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {
buf[j ++] = (data[i] & 0xff00) >> 8;
buf[j ++] = data[i] & 0xff;
}
struct i2c_smbus_ioctl_data payload = {
.read_write = I2C_SMBUS_WRITE,
.size = I2C_SMBUS_WORD_DATA,
.command = regaddr,
.data = (void *) buf,
};
ret = ioctl (fd, I2C_SLAVE_FORCE, dev);
if (ret < 0)
{
ret = -errno;
goto exit;
}
ret = ioctl (fd, I2C_SMBUS, &payload);
if (ret < 0)
{
ret = -errno;
goto exit;
}
exit:
free(buf);
return ret;
}
int i2c_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
printf("i2x_write\n");
uint64_t funcs;
if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
return -errno;
}
if (funcs & I2C_FUNC_I2C) {
return i2c_ioctl_write (fd, dev, regaddr, data);
} else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
return i2c_ioctl_smbus_write (fd, dev, regaddr, data);
} else {
return -ENOSYS;
}
}
int main (int argc, char *argv[])
{
printf("main\n");
uint8_t regaddr;
int fd;
int ret = 0;
uint16_t data[] = {1, 2, 4};
fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
ret = i2c_write(fd, I2C_DEVICE, REG_ADDR, data);
close(fd);
if (ret) {
fprintf (stderr, "%s.\n", strerror(-ret));
}
free(data);
return ret;
}
Когда я запускаю программу в QEMU, я получаю следующий вывод:
main i2x_write i2c_ioctl_write Распределение буфера выполнено успешно... Настройка буфера выполнена успешно... Настройка I2C-сообщений Настройка полезной нагрузки I2C IOCTL cdns-i2c e0004000.i2c: время ожидания истекло по завершении Писал с тайм-аутом соединения IOCTL. Ошибка сегментации
Я предполагаю, что это терпит неудачу на линии
ret = ioctl(fd, I2C_RDWR, &payload);
но я не уверен почему. Была ли полезная нагрузка построена неправильно?
Обновление: вот текущий код:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
int main (int argc, char *argv[])
{
int fd;
int ret = 0;
fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
uint64_t funcs;
int addr = 0X00;
if (ioctl(fd, I2C_SLAVE, addr) < 0) {
/* ERROR HANDLING; you can check errno to see what went wrong */
printf("Cannot setup as slave");
exit(1);
}
if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
printf("ioctl failed");
return -errno;
}
printf("funcs & I2C_FUNC_I2C: %llu\n", funcs & I2C_FUNC_I2C);
printf("funcs & I2C_FUNC_SMBUS_WORD_DATA: %llu\n", funcs & I2C_FUNC_SMBUS_WORD_DATA);
__u8 reg = 0x10;
__s32 res;
if (funcs & I2C_FUNC_I2C) {
char buf[10];
printf("Attempting to write to I2C bus via I2C protocol...\n");
buf[0] = reg;
buf[1] = 0x43;
buf[2] = 0x65;
int bytes_written = write(fd, buf, 3);
if(bytes_written != 3) {
printf("Wrote %d bytes", bytes_written);
printf("\tFailed to write to I2C Bus\n");
close(fd);
return -1;
}
else {
printf("\tSuccesful write to I2C Bus\n");
}
char buf2[10];
printf("Attempting to read from I2C bus via I2C protocol...\n");
if(read(fd, buf2, 1) != 1) {
printf("\tFailed to do I2C read from Bus\n");
close(fd);
return -1;
}
else {
printf("\tRead successful. Comparing read results from original write buffer...");
printf("\t\tWritten value: %c", buf[0]);
printf("\t\tRead value: %c", buf2[0]);
}
return 0;
} else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
printf("Attempting to write to I2C bus via SMBus protocol...\n");
//res = i2c_smbus_write_word_data(fd, REG_ADDR, 0x6543);
res = 1;
if(res < 0) {
printf("\tFailed to write to I2C Bus\n");
close(fd);
return -1;
}
else {
printf("\tSuccesful write to I2C Bus\n");
}
//res = i2c_smbus_read_word_data(fd, REG_ADDR);
if(res < 0) {
printf("\tFailed to read from I2C Bus\n");
close(fd);
return -1;
}
else {
printf("\tRead successful. Comparing read results from original write buffer...");
printf("\t\tWritten value: %c", 0x6543);
printf("\t\tRead value: %c", res);
}
} else {
printf("Cannot write to I2C");
return -ENOSYS;
}
close(fd);
if (ret) {
fprintf (stderr, "%s.\n", strerror(-ret));
}
return ret;
}
Я смог избавиться от ошибки seg, удалив free(), так что спасибо. Я точно определил проблему тайм-аута, которая возникает в драйвере Cadence I2C здесь:
который все еще происходит.
Как уже упоминалось, вероятно, есть некоторая проблема с тем, как я пишу на ведомое устройство, в результате чего ведомое устройство не отправляет ACK, что приводит к тайм-ауту. Я не уверен, какие регистры мне нужно написать, что для. У меня есть ощущение, что макрос I2C_DEVICE и переменные addr и reg нужно будет изменить.
1 ответ
cdns-i2c e0004000.i2c: время ожидания по завершении
Кажется, что драйвер i2c (cdns-i2s) не получает подтверждение от подчиненного устройства. Это может произойти, если вы используете адрес ведомого устройства I2C в качестве 0x00, который является общим адресом вызова. При использовании общего адреса вызова второй отправляемый байт имеет специальное назначение, упомянутое в спецификации i2c (раздел 3.1.13).
Если вы используете общий адрес вызова, вам нужно следовать спецификации, иначе попробуйте использовать точный подчиненный адрес i2c вместо общего адреса вызова (0x00).