diff --git a/system/i3c/CMakeLists.txt b/system/i3c/CMakeLists.txt new file mode 100644 index 00000000000..e6423f072f4 --- /dev/null +++ b/system/i3c/CMakeLists.txt @@ -0,0 +1,25 @@ +# ############################################################################## +# apps/system/i3c/CMakeLists.txt +# +# 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. +# +# ############################################################################## + +if(CONFIG_SYSTEM_I3CTOOL) + set(SRCS i3c_main.c) + nuttx_add_application(NAME i3c SRCS ${SRCS} STACKSIZE + ${CONFIG_DEFAULT_TASK_STACKSIZE}) +endif() diff --git a/system/i3c/Kconfig b/system/i3c/Kconfig new file mode 100644 index 00000000000..e631b873dcd --- /dev/null +++ b/system/i3c/Kconfig @@ -0,0 +1,28 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig SYSTEM_I3CTOOL + tristate "I3C tool" + default n + depends on I3C + select I3C_DRIVER + ---help--- + Enable support for the I3C tool. + +if SYSTEM_I3CTOOL + +config I3CTOOL_DEF_TARGETADDR + hex "Default I3C device target address" + default 0xffff + ---help--- + Default I3C device target address (default: 0xffff) + +config I3CTOOL_DEFBUS + int "Default I2C bus number" + default 0 + ---help--- + Default I3C bus number (default: 0) + +endif diff --git a/system/i3c/Make.defs b/system/i3c/Make.defs new file mode 100644 index 00000000000..0e5fbdb99f4 --- /dev/null +++ b/system/i3c/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/system/i3c/Make.defs +# +# 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. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_I3CTOOL),) +CONFIGURED_APPS += $(APPDIR)/system/i3c +endif diff --git a/system/i3c/Makefile b/system/i3c/Makefile new file mode 100644 index 00000000000..3bf8fece881 --- /dev/null +++ b/system/i3c/Makefile @@ -0,0 +1,30 @@ +############################################################################ +# apps/system/i3c/Makefile +# +# 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. +# +############################################################################ + +include $(APPDIR)/Make.defs + +MAINSRC = i3c_main.c + +PROGNAME = i3c +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) +MODULE = $(CONFIG_SYSTEM_I3CTOOL) + +include $(APPDIR)/Application.mk diff --git a/system/i3c/README.md b/system/i3c/README.md new file mode 100644 index 00000000000..60b5400b33e --- /dev/null +++ b/system/i3c/README.md @@ -0,0 +1,89 @@ +# System / `i3c` I3C Tool + +The I3C tool provides a way to debug I3C related problems. This README file will +provide usage information for the I3C tools. + +- System Requirements + - I3C Driver + - I2C Driver, If Has I2C Device + - Configuration Options + +# Configuration Options + + - `CONFIG_NSH_BUILTIN_APPS` – Build the tools as an NSH built-in command. + - `CONFIG_I3CTOOL_DEFBUS` – A default bus number (default `0`). + +# Usage: + +Write one data byte in single private transfer + +i3c -b bus -m manufid -p partid -w 0xde + +Write multiple data bytes in single private transfer + +i3c -b bus -m manufid -p partid -w "0xde,0xad,0xbe,0xef" + +Write multiple data bytes in multiple private transfers + +i3c -b bus -m manufid -p partid -w "0xde,0xad" -w "0xbe,0xef" + +Read multiple data bytes in single private transfer + +i3c -b bus -m manufid -p partid -r + +Read multiple data bytes in multiple private transfer + +i3c -b bus -m manufid -p partid -r -r + +Read and write multiple data bytes in multiple private transfer + +i3c -b bus -m manufid -p partid -w "0xde,0xad,0xbe,0xef" -r + +Get device information using PID + +i3c -b bus -m manufid -p partid -g + +Parameters: + + 1) bus: I3C bus number + 2) manufid: Manufacturer ID (upper 16 bits of device PID) + 3) partid: Part ID (middle 16 bits of device PID) + 4) : Data length to read on this message + +Note: The manufid and partid are extracted from the device's 48-bit Provisional +ID (PID). To find these values, first use the -g option to get device information, +then extract: + - manufid = (PID >> 32) & 0xFFFF + - partid = (PID >> 16) & 0xFFFF + +# Example + 1. i3c -b 0 -m 0x01E0 -p 0x0001 -w 0xde + 2. i3c -b 0 -m 0x01E0 -p 0x0001 -w "0xde,0xad,0xbe,0xef" + 3. i3c -b 0 -m 0x01E0 -p 0x0001 -w "0xde,0xad" -w "0xbe,0xef" + 4. i3c -b 0 -m 0x01E0 -p 0x0001 -r 0x10 + 5. i3c -b 0 -m 0x01E0 -p 0x0001 -r 0x10 -r 0x10 + 6. i3c -b 0 -m 0x01E0 -p 0x0001 -w "0xde,0xad,0xbe,0xef" -r 0x10 + 7. i3c -b 0 -m 0x01E0 -p 0x0001 -g + +# Migration from target_addr + +Previous versions used `-d targetaddr` to specify the device address. The new +version uses `-m manufid -p partid` to identify devices by their unique PID. + +To migrate: +1. Get device PID using the old command (if available): + ``` + i3c -b 0 -d -g + ``` + Output: `i3c_device_info - pid 0x01E000010000XXXX` + +2. Extract manufid and partid from PID: + ``` + manufid = 0x01E0 (bits 47-32) + partid = 0x0001 (bits 31-16) + ``` + +3. Use new command format: + ``` + i3c -b 0 -m 0x01E0 -p 0x0001 -r 0x10 + ``` diff --git a/system/i3c/i3c_main.c b/system/i3c/i3c_main.c new file mode 100644 index 00000000000..4e985bd7710 --- /dev/null +++ b/system/i3c/i3c_main.c @@ -0,0 +1,416 @@ +/**************************************************************************** + * apps/system/i3c/i3c_main.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 +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Device naming */ + +#define DEVNAME_FMT "/dev/i3c%d" +#define DEVNAME_FMTLEN (8 + 3 + 1) + +/**************************************************************************** + * Private Type + ****************************************************************************/ + +struct i3c_ioc_priv_xfer +{ + uint8_t rnw; /* encodes the transfer direction. true for a read, false for a write */ + uint16_t len; /* Length of data buffer buffers, in bytes */ + FAR uint8_t *data; /* Holds pointer to userspace buffer with transmit data */ + uint8_t pad[5]; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static FAR const char *g_sopts = "b:m:p:r:w:h:g"; +static const struct option g_lopts[] = +{ + {"bus", required_argument, NULL, 'b' }, + {"manufid", required_argument, NULL, 'm' }, + {"partid", required_argument, NULL, 'p' }, + {"read", required_argument, NULL, 'r' }, + {"write", required_argument, NULL, 'w' }, + {"get", required_argument, NULL, 'g' }, + {"command", required_argument, NULL, 'c' }, + {"help", no_argument, NULL, 'h' }, + {0, 0, 0, 0} +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: print_usage + ****************************************************************************/ + +static void print_usage(FAR const char *name) +{ + fprintf(stdout, "usage: %s options...\n", name); + fprintf(stdout, " options:\n"); + fprintf(stdout, " -b --bus bus to use.\n"); + fprintf(stdout, " -m --manufid manufacturer ID " + "(upper 16 bits of PID).\n"); + fprintf(stdout, " -p --partid part ID " + "(lower 32 bits of PID).\n"); + fprintf(stdout, " -r --read read data.\n"); + fprintf(stdout, " -w --write Write data block.\n"); + fprintf(stdout, " -g --get get a dev info.\n"); + fprintf(stdout, " -h --help Output usage message and exit.\n"); +} + +/**************************************************************************** + * Name: rx_args_to_xfer + ****************************************************************************/ + +static int rx_args_to_xfer(int length, int rnw, FAR uint8_t **data, + FAR char *arg) +{ + int32_t len = strtol(arg, NULL, 0); + FAR uint8_t *tmp; + + tmp = calloc(len, sizeof(uint8_t)); + if (!tmp) + { + return -ENOMEM; + } + + rnw = 1; + length = len; + *data = tmp; + + return 0; +} + +/**************************************************************************** + * Name: w_args_to_xfer + ****************************************************************************/ + +static int w_args_to_xfer(uint8_t length, FAR uint8_t **data, FAR char *arg) +{ + FAR char *data_ptrs[256]; + FAR uint8_t *tmp; + int i = 0; + int len; + + data_ptrs[i] = strtok(arg, ","); + while (data_ptrs[i] && i < 255) + { + data_ptrs[++i] = strtok(NULL, ","); + } + + tmp = calloc(i, sizeof(uint8_t)); + if (!tmp) + { + return -ENOMEM; + } + + for (len = 0; len < i; len++) + { + tmp[len] = (uint8_t)strtol(data_ptrs[len], NULL, 0); + } + + length = len; + *data = tmp; + + return 0; +} + +/**************************************************************************** + * Name: print_rx_data + ****************************************************************************/ + +static void print_rx_data(FAR struct i3c_ioc_priv_xfer *xfer) +{ + FAR uint8_t *tmp; + uint32_t i; + + tmp = calloc(xfer->len, sizeof(uint8_t)); + if (!tmp) + { + return; + } + + memcpy(tmp, (FAR void *)(uintptr_t)xfer->data, + xfer->len * sizeof(uint8_t)); + + fprintf(stdout, " received data:\n"); + for (i = 0; i < xfer->len; i++) + { + fprintf(stdout, " 0x%02x\n", tmp[i]); + } + + free(tmp); +} + +/**************************************************************************** + * Name: i3c_transfers + ****************************************************************************/ + +static int i3c_transfers(int argc, FAR char *argv[], const int fd, + uint16_t manufid, uint16_t partid, int nxfers) +{ + FAR struct i3c_ioc_priv_xfer *xfers; + struct i3c_transfer_s transfers; + int ret = 0; + int opt; + int i; + + if (nxfers <= 0) + { + return EXIT_FAILURE; + } + + transfers.manufid = manufid; + transfers.partid = partid; + + /* init receive or send works */ + + xfers = calloc(nxfers, sizeof(*xfers)); + if (!xfers) + { + return -ENOMEM; + } + + optind = 1; + while ((opt = getopt_long(argc, argv, g_sopts, g_lopts, NULL)) != EOF) + { + switch (opt) + { + case 'h': + case 'm': + case 'p': + case 'b': + break; + case 'r': + if (rx_args_to_xfer(xfers->len, xfers->rnw, &xfers->data, + optarg)) + { + ret = EXIT_FAILURE; + goto err_free; + } + + break; + case 'w': + if (w_args_to_xfer(xfers->len, &xfers->data, optarg)) + { + ret = EXIT_FAILURE; + goto err_free; + } + + break; + } + } + + transfers.nxfers = nxfers; + transfers.xfers = (FAR struct i3c_priv_xfer *)xfers; + + if (ioctl(fd, I3CIOC_PRIV_XFERS, transfers) < 0) + { + fprintf(stdout, "Error: transfer failed!\n"); + ret = EXIT_FAILURE; + goto err_free; + } + + /* printf the received data */ + + for (i = 0; i < nxfers; i++) + { + fprintf(stdout, "Success on message %d\n", i); + if (xfers[i].rnw) + { + print_rx_data(&xfers[i]); + } + } + + ret = EXIT_SUCCESS; + +err_free: + for (i = 0; i < nxfers; i++) + { + free(xfers[i].data); + } + + free(xfers); + return ret; +} + +/**************************************************************************** + * Name: i3c_get_devinfo + ****************************************************************************/ + +static int i3c_get_devinfo(int argc, FAR char *argv[], int fd, + uint16_t manufid, uint16_t partid) +{ + struct i3c_transfer_s transfers; + struct i3c_device_info dev_info; + + transfers.manufid = manufid; + transfers.partid = partid; + transfers.info = &dev_info; + + if (ioctl(fd, I3CIOC_GET_DEVINFO, transfers) < 0) + { + fprintf(stderr, "Error: transfer failed: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + + printf("i3c_device_info - pid % "PRIi64" \n", dev_info.pid); + printf("i3c_device_info - bcr %d\n", dev_info.bcr); + printf("i3c_device_info - dcr %d\n", dev_info.dcr); + printf("i3c_device_info - static_addr %d\n", dev_info.static_addr); + printf("i3c_device_info - dyn_addr %d\n", dev_info.dyn_addr); + printf("i3c_device_info - hdr_cap %d\n", dev_info.hdr_cap); + printf("i3c_device_info - max_read_ds %d\n", dev_info.max_read_ds); + printf("i3c_device_info - max_write_ds %d\n", dev_info.max_write_ds); + printf("i3c_device_info - max_ibi_len %d\n", dev_info.max_ibi_len); + printf("i3c_device_info - max_read_turnaround %d\n", + dev_info.max_read_turnaround); + printf("i3c_device_info - max_read_len %d\n", dev_info.max_read_len); + printf("i3c_device_info - max_write_len %d\n", dev_info.max_write_len); + + return EXIT_SUCCESS; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + uint16_t manufid = 0; + uint16_t partid = 0; + int bus_num = CONFIG_I3CTOOL_DEFBUS; + char devname[DEVNAME_FMTLEN]; + int ret = EXIT_FAILURE; + int nxfers = 0; + int action; + int opt; + int fd; + + if (argc < 2 || (argv[1][0] != '-') || (argv[1][0] == '-' && !argv[1][1])) + { + print_usage(argv[0]); + return ret; + } + + while ((opt = getopt_long(argc, argv, g_sopts, g_lopts, NULL)) != EOF) + { + switch (opt) + { + case 'b': + { + bus_num = strtol(optarg, NULL, 0); + break; + } + + case 'm': + { + manufid = (uint16_t)strtol(optarg, NULL, 0); + break; + } + + case 'p': + { + partid = (uint16_t)strtol(optarg, NULL, 0); + break; + } + + case 'r': + case 'w': + { + action = opt; + nxfers++; + break; + } + + case 'g': + { + action = opt; + break; + } + + default: + { + print_usage(argv[0]); + return ret; + } + } + } + + /* bus number and target device info */ + + fprintf(stdout, "bus_num is 0x%x, manufid is 0x%04x, partid is 0x%04x\n", + bus_num, manufid, partid); + + /* i3c driver node to open */ + + memset(devname, 0, DEVNAME_FMTLEN); + snprintf(devname, DEVNAME_FMTLEN, DEVNAME_FMT, bus_num); + fd = open(devname, O_RDONLY); + if (fd < 0) + { + fprintf(stdout, "open i3c driver %s failed\n", devname); + return fd; + } + + fprintf(stdout, "begin to parser test data.\n"); + + switch (action) + { + case 'r': + case 'w': + fprintf(stdout, "opt - w/r, nxfers %d\n", nxfers); + ret = i3c_transfers(argc, argv, fd, manufid, partid, nxfers); + break; + case 'g': + fprintf(stdout, "opt - g\n"); + ret = i3c_get_devinfo(argc, argv, fd, manufid, partid); + break; + default: + print_usage(argv[0]); + } + + close(fd); + return ret; +}