diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig index 9498a68cf2..64f034e94f 100644 --- a/arch/sim/Kconfig +++ b/arch/sim/Kconfig @@ -534,6 +534,36 @@ config SIM_HCISOCKET control of the device, but is abstracted from the physical interface which is still handled by Linux. +config SIM_I2CBUS + bool "Simulated I2C Bus" + default n + select I2C + ---help--- + Build in support for simulated i2c bus + +if SIM_I2CBUS + +choice + prompt "Simulated I2C Bus Type" + default SIM_I2CBUS_LINUX + +config SIM_I2CBUS_LINUX + bool "Linux I2C Bus Character Dev" + depends on HOST_LINUX + ---help--- + Attach a Linux I2C bus via the character device + interface. This should be used with caution as it + could interfere with devices internal to the system. + It is recommended to use this with a USB<>I2C device + like the MCP2221 and set udev rules so that only + the bus provided by this device can be controlled + by the user running the simulator. + https://www.kernel.org/doc/html/latest/i2c/dev-interface.html + +endchoice + +endif + config SIM_UART_NUMBER int "The number of tty ports on sim platform, range is 0~4" default 0 diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 52982375bd..af0f228982 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -201,6 +201,14 @@ ifeq ($(CONFIG_SIM_HCISOCKET),y) CSRCS += up_hcisocket.c endif +ifeq ($(CONFIG_I2C_RESET),y) + HOSTCFLAGS += -DCONFIG_I2C_RESET=1 +endif + +ifeq ($(CONFIG_SIM_I2CBUS_LINUX),y) + HOSTSRCS += up_i2cbuslinux.c +endif + ifeq ($(CONFIG_RPTUN),y) CSRCS += up_rptun.c endif diff --git a/arch/sim/src/nuttx-names.in b/arch/sim/src/nuttx-names.in index e235c87e00..42cee0bf7d 100644 --- a/arch/sim/src/nuttx-names.in +++ b/arch/sim/src/nuttx-names.in @@ -32,6 +32,7 @@ NXSYMBOLS(__cxa_atexit) NXSYMBOLS(bind) +NXSYMBOLS(calloc) NXSYMBOLS(clock_gettime) NXSYMBOLS(close) NXSYMBOLS(closedir) @@ -46,6 +47,7 @@ NXSYMBOLS(if_nametoindex) NXSYMBOLS(ioctl) NXSYMBOLS(lseek) NXSYMBOLS(malloc) +NXSYMBOLS(memcpy) NXSYMBOLS(mkdir) NXSYMBOLS(mmap) NXSYMBOLS(munmap) diff --git a/arch/sim/src/sim/up_i2cbus.h b/arch/sim/src/sim/up_i2cbus.h new file mode 100644 index 0000000000..f899b75473 --- /dev/null +++ b/arch/sim/src/sim/up_i2cbus.h @@ -0,0 +1,76 @@ +/**************************************************************************** + * arch/sim/src/sim/up_i2cbus.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +#ifndef _ARCH_SIM_SRC_SIM_I2CBUS_H_ +#define _ARCH_SIM_SRC_SIM_I2CBUS_H_ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* NuttX msg flags (see i2c_master.h) */ + +#define NUTTX_I2C_M_READ 0x0001 /* Read data, from slave to master */ +#define NUTTX_I2C_M_TEN 0x0002 /* Ten bit address */ +#define NUTTX_I2C_M_NOSTOP 0x0040 /* Message should not end with a STOP */ +#define NUTTX_I2C_M_NOSTART 0x0080 /* Message should not begin with a START */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* NuttX i2c msg struct (see i2c_master.h) */ + +struct i2c_msg_s +{ + uint32_t frequency; /* I2C frequency */ + uint16_t addr; /* Slave address (7- or 10-bit) */ + uint16_t flags; /* See I2C_M_* definitions */ + uint8_t *buffer; /* Buffer to be transferred */ + ssize_t length; /* Length of the buffer in bytes */ +}; + +/* Structs needed for interacting with the NuttX i2c_master */ + +struct i2c_master_s +{ + const struct i2c_ops_s *ops; +}; + +struct i2c_ops_s +{ + int (*transfer)(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count); +#ifdef CONFIG_I2C_RESET + int (*reset)(struct i2c_master_s *dev); +#endif +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#endif /* _ARCH_SIM_SRC_SIM_I2CBUS_H_ */ diff --git a/arch/sim/src/sim/up_i2cbuslinux.c b/arch/sim/src/sim/up_i2cbuslinux.c new file mode 100644 index 0000000000..09512b4171 --- /dev/null +++ b/arch/sim/src/sim/up_i2cbuslinux.c @@ -0,0 +1,310 @@ +/**************************************************************************** + * arch/sim/src/sim/up_i2cbuslinux.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "up_i2cbus.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ERROR(fmt, ...) \ + syslog(LOG_ERR, "up_i2cbuslinux: " fmt "\n", ##__VA_ARGS__) +#define INFO(fmt, ...) \ + syslog(LOG_ERR, "up_i2cbuslinux: " fmt "\n", ##__VA_ARGS__) +#define DEBUG(fmt, ...) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct linux_i2cbus_master_s +{ + const struct i2c_ops_s *ops; /* I2C vtable */ + int file; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int linux_i2cbus_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count); +#ifdef CONFIG_I2C_RESET +static int linux_i2cbus_reset(struct i2c_master_s *dev); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct i2c_ops_s i2c_linux_ops = +{ + .transfer = linux_i2cbus_transfer, +#ifdef CONFIG_I2C_RESET + .reset = linux_i2cbus_reset, +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: linux_i2cbus_reset + * + * Description: + * Provide i2c reset + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_RESET +static int linux_i2cbus_reset(struct i2c_master_s *dev) +{ + return -1; /* Not implemented */ +} +#endif + +/**************************************************************************** + * Name: linux_i2cbus_transfer + * + * Description: + * Provide i2c transfer + * + ****************************************************************************/ + +static int linux_i2cbus_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count) +{ + int ret = 0; + struct linux_i2cbus_master_s *priv = (struct linux_i2cbus_master_s *)dev; + int file = priv->file; + uint8_t *pack_buf = NULL; + struct i2c_rdwr_ioctl_data ioctl_data; + struct i2c_msg l_msgs[2]; /* We only support up to 2 messages */ + ioctl_data.msgs = l_msgs; + + /* Many i2c bus do not play well with combined messages via the Linux + * interface, this makes stitching things together a little harder + * because NuttX provides the ability to hold the bus without ending + * with a STOP which is not ideal in general, and not possible with + * Linux. + */ + + if ((msgs[0].flags & NUTTX_I2C_M_TEN) || (msgs[1].flags & NUTTX_I2C_M_TEN)) + { + /* Linux also has somewhat poor support for 10bit addresses and they + * are quite rare so we just don't support them for now here + */ + + return -1; + } + + ioctl_data.msgs = l_msgs; + + if (count == 1) + { + if (msgs[0].flags & NUTTX_I2C_M_NOSTOP || \ + msgs[0].flags & NUTTX_I2C_M_NOSTART) + { + /* Do not support leaving the bus hanging or try to send + * without first starting. + */ + + return -1; + } + + l_msgs[0].addr = msgs[0].addr; + l_msgs[0].buf = msgs[0].buffer; + l_msgs[0].len = msgs[0].length; + l_msgs[0].flags = 0; + if (msgs[0].flags & NUTTX_I2C_M_READ) + { + l_msgs[0].flags |= I2C_M_RD; + } + + ioctl_data.nmsgs = 1; + } + else if(count == 2) + { + /* Addresses should be the same */ + + if (msgs[0].addr != msgs[1].addr) + { + return -1; + } + + /* Check if we are about to do a read of a register + * NuttX interface represents this as WRITE(NOSTOP) + READ + * Linux interface represents this as WRITE + READ + */ + + if (msgs[0].flags & NUTTX_I2C_M_NOSTOP && \ + msgs[1].flags & NUTTX_I2C_M_READ) + { + l_msgs[0].addr = msgs[0].addr; + l_msgs[0].flags = 0; + l_msgs[0].buf = msgs[0].buffer; + l_msgs[0].len = msgs[0].length; + + l_msgs[1].addr = msgs[1].addr; + l_msgs[1].flags = I2C_M_RD; + l_msgs[1].buf = msgs[1].buffer; + l_msgs[1].len = msgs[1].length; + ioctl_data.nmsgs = 2; + } + else if (!(msgs[0].flags & NUTTX_I2C_M_READ) && \ + !(msgs[1].flags & NUTTX_I2C_M_READ) && \ + (msgs[0].flags & NUTTX_I2C_M_NOSTOP) && \ + (msgs[1].flags & NUTTX_I2C_M_NOSTART)) + { + /* These writes are actually just a single write in Linux + * so we pack the data in a single buffer and the unpack + * it at the end. This could support for for more than just 2 + * messages, but in most cases it is just two because it is + * connivent to write the register address and the data into two + * different buffers. + */ + + pack_buf = malloc(msgs[0].length + msgs[1].length); + if (pack_buf == NULL) + { + return -1; + } + + memcpy(pack_buf, msgs[0].buffer, msgs[0].length); + memcpy(pack_buf + msgs[0].length, msgs[1].buffer, msgs[1].length); + l_msgs[0].len = msgs[0].length + msgs[1].length; + l_msgs[0].flags = 0; + l_msgs[0].addr = msgs[0].addr; + ioctl_data.msgs[0].buf = pack_buf; + ioctl_data.nmsgs = 1; + } + else + { + /* Many busses cannot handle more than 2 messages */ + + return -1; + } + } + else + { + return -1; + } + + if (ioctl(file, I2C_RDWR, &ioctl_data) < 1) + { + ret = -1; + } + + /* Unpack from buffer back to msg buffers if needed */ + + if (pack_buf != NULL) + { + if (ret == 0) + { + int idx; + uint8_t *msg_p = pack_buf; + for (idx = 0; idx < count; idx++) + { + memcpy(msgs[idx].buffer, msg_p, msgs[idx].length); + msg_p += msgs[idx].length; + } + } + + free(pack_buf); + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sim_i2cbus_initialize + * + * Description: + * Initialize one I2C bus + * + ****************************************************************************/ + +struct i2c_master_s *sim_i2cbus_initialize(int bus) +{ + struct linux_i2cbus_master_s *priv; + char filename[20]; + + priv = (struct linux_i2cbus_master_s *)malloc(sizeof(priv)); + if (priv == NULL) + { + ERROR("Failed to allocate private i2c master driver"); + return NULL; + } + + snprintf(filename, 19, "/dev/i2c-%d", bus); + priv->file = open(filename, O_RDWR); + if (priv->file < 0) + { + ERROR("Failed to open %s: %d", filename, priv->file); + free(priv); + return NULL; + } + + priv->ops = &i2c_linux_ops; + return (struct i2c_master_s *)priv; +} + +/**************************************************************************** + * Name: sim_i2cbus_uninitialize + * + * Description: + * Uninitialize an I2C bus + * + ****************************************************************************/ + +int sim_i2cbus_uninitialize(struct i2c_master_s *dev) +{ + struct linux_i2cbus_master_s *priv = (struct linux_i2cbus_master_s *)dev; + if (priv->file >= 0) + { + close(priv->file); + } + + free(priv); + return 0; +} diff --git a/arch/sim/src/sim/up_internal.h b/arch/sim/src/sim/up_internal.h index 23ebd2fd19..8c1f77b44a 100644 --- a/arch/sim/src/sim/up_internal.h +++ b/arch/sim/src/sim/up_internal.h @@ -160,6 +160,7 @@ struct tcb_s; struct spi_dev_s; struct qspi_dev_s; struct ioexpander_dev_s; +struct i2c_master_s; /**************************************************************************** * Public Data @@ -404,6 +405,13 @@ struct audio_lowerhalf_s *sim_audio_initialize(bool playback); void sim_audio_loop(void); #endif +/* up_i2cbus*.c *************************************************************/ + +#ifdef CONFIG_SIM_I2CBUS +struct i2c_master_s *sim_i2cbus_initialize(int bus); +int sim_i2cbus_uninitialize(struct i2c_master_s *dev); +#endif + /* Debug ********************************************************************/ #ifdef CONFIG_STACK_COLORATION diff --git a/boards/sim/sim/sim/Kconfig b/boards/sim/sim/sim/Kconfig index 8063ef5058..ff72269636 100644 --- a/boards/sim/sim/sim/Kconfig +++ b/boards/sim/sim/sim/Kconfig @@ -54,3 +54,11 @@ config SIM_WTGAHRS2_UARTN We can select the number accoding to which SIM_UARTX_NAME is uesd to sensor. This range is 0-4. endif + +config SIM_I2CBUS_ID + int "I2C host bus ID to attach to simulator" + default 0 + depends on SIM_I2CBUS + ---help--- + This is the bus identifier that should be used by the host implementation to + attach to the simulator driver. diff --git a/boards/sim/sim/sim/configs/linuxi2c/defconfig b/boards/sim/sim/sim/configs/linuxi2c/defconfig new file mode 100644 index 0000000000..43a1c8233e --- /dev/null +++ b/boards/sim/sim/sim/configs/linuxi2c/defconfig @@ -0,0 +1,43 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="sim" +CONFIG_ARCH_BOARD="sim" +CONFIG_ARCH_BOARD_SIM=y +CONFIG_ARCH_CHIP="sim" +CONFIG_ARCH_SIM=y +CONFIG_BOARDCTL_APP_SYMTAB=y +CONFIG_BOARDCTL_POWEROFF=y +CONFIG_BOARDCTL_ROMDISK=y +CONFIG_BOARD_LOOPSPERMSEC=0 +CONFIG_BOOT_RUNFROMEXTSRAM=y +CONFIG_BUILTIN=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEV_ZERO=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_FS_PROCFS=y +CONFIG_IDLETHREAD_STACKSIZE=4096 +CONFIG_LIBC_EXECFUNCS=y +CONFIG_MAX_TASKS=64 +CONFIG_NFILE_DESCRIPTORS=32 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_READLINE=y +CONFIG_POSIX_SPAWN_PROXY_STACKSIZE=2048 +CONFIG_READLINE_TABCOMPLETION=y +CONFIG_SCHED_HAVE_PARENT=y +CONFIG_SCHED_ONEXIT=y +CONFIG_SCHED_WAITPID=y +CONFIG_SDCLONE_DISABLE=y +CONFIG_SIM_I2CBUS=y +CONFIG_START_MONTH=6 +CONFIG_START_YEAR=2008 +CONFIG_SYSTEM_I2CTOOL=y +CONFIG_SYSTEM_NSH=y +CONFIG_USERMAIN_STACKSIZE=4096 +CONFIG_USER_ENTRYPOINT="nsh_main" diff --git a/boards/sim/sim/sim/src/sim_bringup.c b/boards/sim/sim/sim/src/sim_bringup.c index cf7fce27bb..852cf0b13c 100644 --- a/boards/sim/sim/sim/src/sim_bringup.c +++ b/boards/sim/sim/sim/src/sim_bringup.c @@ -1,5 +1,5 @@ /**************************************************************************** - * boards/sim/sim/sim/src/sam_bringup.c + * boards/sim/sim/sim/src/sim_bringup.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -40,6 +40,8 @@ #include #include #include +#include +#include #ifdef CONFIG_LCD_DEV #include @@ -68,6 +70,13 @@ int sim_bringup(void) #ifdef CONFIG_RAMMTD FAR uint8_t *ramstart; #endif +#ifdef CONFIG_SIM_I2CBUS + FAR struct i2c_master_s *i2cbus; +#endif +#ifdef CONFIG_MPU60X0_I2C + FAR struct mpu_config_s *mpu_config; +#endif + int ret = OK; #ifdef CONFIG_FS_BINFS @@ -335,5 +344,41 @@ int sim_bringup(void) } #endif +#ifdef CONFIG_SIM_I2CBUS + /* Initialize the i2c master bus device */ + + i2cbus = sim_i2cbus_initialize(CONFIG_SIM_I2CBUS_ID); + if (i2cbus == NULL) + { + syslog(LOG_ERR, "ERROR: sim_i2cbus_initialize failed.\n"); + } +#if defined(CONFIG_SYSTEM_I2CTOOL) || defined(CONFIG_MPU60X0_I2C) + else + { + ret = i2c_register(i2cbus, 0); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to register I2C%d driver: %d\n", + 0, ret); + sim_i2cbus_uninitialize(i2cbus); + } + +#ifdef CONFIG_MPU60X0_I2C + mpu_config = kmm_zalloc(sizeof(struct mpu_config_s)); + if (mpu_config == NULL) + { + syslog(LOG_ERR, "ERROR: Failed to allocate mpu60x0 driver\n"); + } + else + { + mpu_config->i2c = i2cbus; + mpu_config->addr = 0x68; + mpu60x0_register("/dev/imu0", mpu_config); + } +#endif + } +#endif +#endif + return ret; } diff --git a/drivers/sensors/mpu60x0.c b/drivers/sensors/mpu60x0.c index e23bf3abc8..72ade271c4 100644 --- a/drivers/sensors/mpu60x0.c +++ b/drivers/sensors/mpu60x0.c @@ -453,13 +453,11 @@ static int __mpu_write_reg_i2c(FAR struct mpu_dev_s *dev, msg[0].flags = I2C_M_NOSTOP; msg[0].buffer = ®_addr; msg[0].length = 1; - msg[1].frequency = CONFIG_MPU60X0_I2C_FREQ; msg[1].addr = dev->config.addr; msg[1].flags = I2C_M_NOSTART; msg[1].buffer = (FAR uint8_t *)buf; msg[1].length = len; - ret = I2C_TRANSFER(dev->config.i2c, msg, 2); if (ret < 0) { @@ -718,19 +716,19 @@ static int mpu_reset(FAR struct mpu_dev_s *dev) do { - up_mdelay(50); /* msecs (arbitrary) */ + nxsig_usleep(50000); /* usecs (arbitrary) */ } while (__mpu_read_pwr_mgmt_1(dev) & PWR_MGMT_1__DEVICE_RESET); /* Reset signal paths */ __mpu_write_signal_path_reset(dev, SIGNAL_PATH_RESET__ALL_RESET); - up_mdelay(2); + nxsig_usleep(2000); /* Disable SLEEP, use PLL with z-axis clock source */ __mpu_write_pwr_mgmt_1(dev, 3); - up_mdelay(2); + nxsig_usleep(2000); /* Disable i2c if we're on spi. */