/* Copyright (C) 2011-2022 Patrick H. E. Foubet - S.E.R.I.A.N.E. 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 *******************************************************************/ /* i2c.c */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #ifdef HAVE_LINUX_I2C_DEV_H #include #ifdef I2C_FUNC_I2C #define _WITH_I2C #endif #endif #include #include #include #include #ifdef _WITH_I2C /* This part is a merge of Linux i2c-tools code and Nife code. i2c-tools are user-space programs to detect, scan I2C devices, and read and/or write an I2C register. Copyright (C) 2005-2012 Jean Delvare Copyright (C) 2001-2003 Frodo Looijaard , and Mark D. Studebaker Copyright (C) 2004-2005 Jean Delvare 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 2 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 General Public License for more details. */ #include "i2cbusses.c" /* add code for I2C on Linux */ #define MODE_AUTO 0 #define MODE_QUICK 1 #define MODE_READ 2 #define MODE_FUNC 3 /* * Print the installed i2c busses. The format is those of Linux 2.4's * /proc/bus/i2c for historical compatibility reasons. */ static void print_i2c_busses(void) { struct i2c_adap *adapters; int count; adapters = gather_i2c_busses(); if (adapters == NULL) { fprintf(stderr, "Error: Out of memory!\n"); return; } printf("i2c-id\tfunction \tadapter name\t\t\t\talgorithm\n"); for (count = 0; adapters[count].name; count++) { printf("%d\t%-10s\t%-32s\t%s\n", adapters[count].nr, adapters[count].funcs, adapters[count].name, adapters[count].algo); } free_adapters(adapters); } static int scan_i2c_bus(int file, int mode, int first, int last) { int i, j; int res; printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); for (i = 0; i < 128; i += 16) { printf("%02x: ", i); for(j = 0; j < 16; j++) { fflush(stdout); /* Skip unwanted addresses */ if (i+j < first || i+j > last) { printf(" "); continue; } /* Set slave address */ if (ioctl(file, I2C_SLAVE, i+j) < 0) { if (errno == EBUSY) { printf("UU "); continue; } else { fprintf(stderr, "Error: Could not set " "address to 0x%02x: %s\n", i+j, strerror(errno)); return -1; } } /* Probe this address */ switch (mode) { case MODE_QUICK: /* This is known to corrupt the Atmel AT24RF08 EEPROM */ res = i2c_smbus_write_quick(file, I2C_SMBUS_WRITE); break; case MODE_READ: /* This is known to lock SMBus on various write-only chips (mainly clock chips) */ res = i2c_smbus_read_byte(file); break; default: if ((i+j >= 0x30 && i+j <= 0x37) || (i+j >= 0x50 && i+j <= 0x5F)) res = i2c_smbus_read_byte(file); else res = i2c_smbus_write_quick(file, I2C_SMBUS_WRITE); } if (res < 0) printf("-- "); else printf("%02x ", i+j); } printf("\n"); } return 0; } static int check_write_funcs(int file, int size, int pec) { unsigned long funcs; /* check adapter functionality */ if (ioctl(file, I2C_FUNCS, &funcs) < 0) { fprintf(stderr, "Error: Could not get the adapter " "functionality matrix: %s\n", strerror(errno)); return -1; } switch (size) { case I2C_SMBUS_BYTE: if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus send byte"); return -1; } break; case I2C_SMBUS_BYTE_DATA: if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus write byte"); return -1; } break; case I2C_SMBUS_WORD_DATA: if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus write word"); return -1; } break; case I2C_SMBUS_BLOCK_DATA: if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus block write"); return -1; } break; case I2C_SMBUS_I2C_BLOCK_DATA: if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { fprintf(stderr, MISSING_FUNC_FMT, "I2C block write"); return -1; } break; } if (pec && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C))) { fprintf(stderr, "Warning: Adapter does " "not seem to support PEC\n"); } return 0; } static int check_read_funcs(int file, int size, int daddress, int pec) { unsigned long funcs; /* check adapter functionality */ if (ioctl(file, I2C_FUNCS, &funcs) < 0) { fprintf(stderr, "Error: Could not get the adapter " "functionality matrix: %s\n", strerror(errno)); return -1; } switch (size) { case I2C_SMBUS_BYTE: if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus receive byte"); return -1; } if (daddress >= 0 && !(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus send byte"); return -1; } break; case I2C_SMBUS_BYTE_DATA: if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus read byte"); return -1; } break; case I2C_SMBUS_WORD_DATA: if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA)) { fprintf(stderr, MISSING_FUNC_FMT, "SMBus read word"); return -1; } break; } if (pec && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C))) { fprintf(stderr, "Warning: Adapter does " "not seem to support PEC\n"); } return 0; } #endif /* for _WITH_I2C */ /* *********************************************************************/ #include "stackN.h" #include "i2c.h" #include "err.h" #include "debug.h" void IF_listI2C (void) { #ifdef _WITH_I2C print_i2c_busses(); #else messErr(49); #endif } void IFD_listI2C (void) { _IFD_BEGIN_ IF_listI2C(); _IFD_END_ } void IF_showI2C (void) { long n; if (!getParLong(&n)) return; #ifdef _WITH_I2C int i2cbus, file; int mode=MODE_AUTO; int first = 0x03, last = 0x77; unsigned long funcs; char Bus[10], filename[20]; sprintf(Bus, "%ld",n); i2cbus = lookup_i2c_bus(Bus); if (i2cbus < 0) messErr(50); else { file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0); if (file < 0) messErr(51); else { if (ioctl(file, I2C_FUNCS, &funcs) < 0) messErr(52); else scan_i2c_bus(file, mode, first, last); close(file); } } #else messErr(49); #endif } void IFD_showI2C (void) { _IFD_BEGIN_ IF_showI2C(); _IFD_END_ } static void i2c_read(int id, int add, int off) { #ifdef _WITH_I2C int i2cbus, file, address, res; int size = I2C_SMBUS_BYTE_DATA; int pec = 0,force = 0; char Buf[10], filename[20]; sprintf(Buf, "%ld",id); i2cbus = lookup_i2c_bus(Buf); if (i2cbus < 0) messErr(50); else { sprintf(Buf, "%ld",add); address = parse_i2c_address(Buf); if (address < 0) messErr(53); else { if (off == -1) size = I2C_SMBUS_BYTE; if (off < -1 || off > 0xff) messErr(54); else { file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0); if (file < 0 || check_read_funcs(file, size, off, pec) || set_slave_addr(file, address, force)) messErr(51); else { if (size == I2C_SMBUS_BYTE) res = i2c_smbus_read_byte(file); else res = i2c_smbus_read_byte_data(file, off); close(file); if (res < 0) messErr(57); else putLong((long long)res); } } } } #else messErr(49); #endif } void IF_I2CRead (void) { long id, add, off; if (!getParLong(&id)) return; /* I2C ID */ if (!getParLong(&add)) return; /* CHIP ADDRESS */ if (!getParLong(&off)) return; /* DATA ADDRESS (OFFSET) */ i2c_read(id, add, off); } static void i2c_write(int id, int add, int off, int val) { #ifdef _WITH_I2C int i2cbus, file, address, res; int size = I2C_SMBUS_BYTE_DATA; int pec = 0,force = 0; char Buf[10], filename[20]; sprintf(Buf, "%ld",id); i2cbus = lookup_i2c_bus(Buf); if (i2cbus < 0) messErr(50); else { sprintf(Buf, "%ld",add); address = parse_i2c_address(Buf); if (address < 0) messErr(53); else { if (off < 0 || off > 0xff) messErr(54); else { if (val > 0xff) messErr(55); else { file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0); if (file < 0 || check_write_funcs(file, size, pec) || set_slave_addr(file, address, force)) messErr(51); else { res = i2c_smbus_write_byte_data(file, off, val); close(file); if (res < 0) messErr(56); } } } } } #else messErr(49); #endif } void IF_I2CWrite (void) { long id, add, off, val; if (!getParLong(&id)) return; /* I2C ID */ if (!getParLong(&add)) return; /* CHIP ADDRESS */ if (!getParLong(&off)) return; /* DATA ADDRESS (OFFSET) */ if (!getParLong(&val)) return; /* DATA BYTE */ i2c_write(id, add, off, val); return; }