/* * Copyright 2018 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Pim 2018-04-03: This is a Linux implementation of the mgos_i2c interface // which is useful to develop drivers directly on Linux. For Ubuntu/Debian, // make sure to install package 'libi2c-dev'. #include "mgos_i2c.h" #include #include #include #include #include #include #include #include struct mgos_i2c { int fd; uint16_t read_timeout_ms; // in msec char *filename; }; static struct mgos_i2c *s_global_i2c_bus = NULL; static size_t i2c_read_timeout(struct mgos_i2c *i2c, void *data, size_t len) { uint16_t tries; size_t ret=-1; if (!i2c) return -1; ret = read(i2c->fd, data, len); for(tries=i2c->read_timeout_ms; tries && ret!=len && len>0; tries--) { usleep(1000); ret = read(i2c->fd, data, len); } if (ret!=len) { // LOG(LL_ERROR, ("Read timeout on I2C")); return -1; } return len; } bool mgos_i2c_read(struct mgos_i2c *i2c, uint16_t addr, void *data, size_t len, bool stop) { size_t ret; if (!i2c) { LOG(LL_ERROR, ("No I2C bus")); return false; } if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus", addr)); return false; } ret = i2c_read_timeout(i2c, data, len); if (ret != len) { // LOG(LL_DEBUG, ("RECV %ld bytes (wanted %lu) from 0x%02x: %s", ret, len, addr, strerror(errno))); return false; } return true; (void)stop; } bool mgos_i2c_write(struct mgos_i2c *i2c, uint16_t addr, const void *data, size_t len, bool stop) { size_t ret; if (!i2c) { LOG(LL_ERROR, ("No I2C bus")); return false; } if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus", addr)); return false; } ret = write(i2c->fd, data, len); if (ret != len) { // LOG(LL_DEBUG, ("XMIT %ld bytes (wanted %lu) from 0x%02x: %s", ret, len, addr, strerror(errno))); return false; } return true; (void)stop; } void mgos_i2c_stop(struct mgos_i2c *i2c) { return; (void)i2c; } int mgos_i2c_get_freq(struct mgos_i2c *i2c) { return MGOS_I2C_FREQ_100KHZ; (void)i2c; } bool mgos_i2c_set_freq(struct mgos_i2c *i2c, int freq) { if (freq==MGOS_I2C_FREQ_100KHZ) return true; return false; (void)i2c; } int mgos_i2c_read_reg_b(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg) { uint8_t value; if (!mgos_i2c_read_reg_n(i2c, addr, reg, 1, &value)) return -1; return value; } int mgos_i2c_read_reg_w(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg) { uint16_t value; uint8_t data[2]; if (!mgos_i2c_read_reg_n(i2c, addr, reg, 2, data)) return -1; value=(data[0]<<8)+data[1]; return value; } bool mgos_i2c_read_reg_n(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg, size_t n, uint8_t *buf) { uint8_t outbuf; struct i2c_rdwr_ioctl_data packets; struct i2c_msg messages[2]; if (!i2c) { LOG(LL_ERROR, ("No I2C bus")); return -1; } if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus: %s", addr, strerror(errno))); return -1; } /* * In order to read a register, we first do a "dummy write" by writing * 0 bytes to the register we want to read from. This is similar to * the packet in set_i2c_register, except it's 1 byte rather than 2. */ outbuf = reg; messages[0].addr = addr; messages[0].flags = 0; messages[0].len = sizeof(outbuf); messages[0].buf = &outbuf; /* The data will get returned in this structure */ messages[1].addr = addr; messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/; messages[1].len = n; messages[1].buf = buf; /* Send the request to the kernel and get the result back */ packets.msgs = messages; packets.nmsgs = 2; if(ioctl(i2c->fd, I2C_RDWR, &packets) < 0) return false; return true; } bool mgos_i2c_write_reg_w(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg, uint16_t value) { uint8_t data[2]; data[0]=value>>8; data[1]=value&0xFF; return mgos_i2c_write_reg_n(i2c, addr, reg, 2, data); } bool mgos_i2c_write_reg_b(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg, uint8_t value) { return mgos_i2c_write_reg_n(i2c, addr, reg, 1, &value); } bool mgos_i2c_write_reg_n(struct mgos_i2c *i2c, uint16_t addr, uint8_t reg, size_t n, const uint8_t *buf) { unsigned char outbuf[n+1]; struct i2c_rdwr_ioctl_data packets; struct i2c_msg messages[1]; if (!i2c) { LOG(LL_ERROR, ("No I2C bus")); return false; } if (ioctl(i2c->fd,I2C_SLAVE,addr) < 0) { LOG(LL_ERROR, ("Cannot select slave 0x%04x on I2C bus: %s", addr, strerror(errno))); return false; } messages[0].addr=addr; messages[0].flags=0; messages[0].len=sizeof(outbuf); messages[0].buf=outbuf; outbuf[0]=reg; memcpy(outbuf+1, buf, n); packets.msgs = messages; packets.nmsgs = 1; if(ioctl(i2c->fd, I2C_RDWR, &packets) < 0) { return false; } return true; } void mgos_i2c_close(struct mgos_i2c *i2c) { return; (void)i2c; } struct mgos_i2c *mgos_i2c_get_global(void) { return s_global_i2c_bus; } // User provided function to interface with Linux I2C driver bool mgos_i2c_open(int busnr) { int fd; char filename[20]; struct mgos_i2c *i2c; sprintf(filename,"/dev/i2c-%d",busnr); if ((fd = open(filename,O_RDWR)) < 0) { LOG(LL_ERROR, ("Could not open %s", filename)); return false; } i2c = calloc(1, sizeof(struct mgos_i2c)); if (!i2c) { LOG(LL_ERROR, ("Could not allocate mgos_i2c handle for %s", filename)); return false; } i2c->fd=fd; i2c->filename=strdup(filename); i2c->read_timeout_ms=1000; s_global_i2c_bus=i2c; LOG(LL_INFO, ("Opened I2C bus on %s", filename)); return true; }