1 /*
2 * Copyright (c) 2024 Astrolight
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <zephyr/drivers/spi.h>
12 #include <zephyr/shell/shell.h>
13 #include <zephyr/sys/util.h>
14
15 #define TXRX_ARGV_BYTES (1)
16 #define CONF_ARGV_DEV (1)
17 #define CONF_ARGV_FREQUENCY (2)
18 #define CONF_ARGV_SETTINGS (3)
19
20 /* Maximum bytes we can write and read at once */
21 #define MAX_SPI_BYTES MIN((CONFIG_SHELL_ARGC_MAX - TXRX_ARGV_BYTES), 32)
22
23 static struct device *spi_device;
24 static struct spi_config config = {.frequency = 1000000,
25 .operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8)};
26
device_name_get(size_t idx,struct shell_static_entry * entry)27 static void device_name_get(size_t idx, struct shell_static_entry *entry)
28 {
29 const struct device *dev = shell_device_lookup(idx, "spi");
30
31 entry->syntax = (dev != NULL) ? dev->name : NULL;
32 entry->handler = NULL;
33 entry->help = NULL;
34 entry->subcmd = NULL;
35 }
36
37 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
38
cmd_spi_transceive(const struct shell * ctx,size_t argc,char ** argv)39 static int cmd_spi_transceive(const struct shell *ctx, size_t argc, char **argv)
40 {
41 uint8_t rx_buffer[MAX_SPI_BYTES] = {0};
42 uint8_t tx_buffer[MAX_SPI_BYTES] = {0};
43
44 if (spi_device == NULL) {
45 shell_error(ctx, "SPI device isn't configured. Use `spi conf`");
46 return -ENODEV;
47 }
48
49 int bytes_to_send = argc - TXRX_ARGV_BYTES;
50
51 for (int i = 0; i < bytes_to_send; i++) {
52 tx_buffer[i] = strtol(argv[TXRX_ARGV_BYTES + i], NULL, 16);
53 }
54
55 const struct spi_buf tx_buffers = {.buf = tx_buffer, .len = bytes_to_send};
56 const struct spi_buf rx_buffers = {.buf = rx_buffer, .len = bytes_to_send};
57
58 const struct spi_buf_set tx_buf_set = {.buffers = &tx_buffers, .count = 1};
59 const struct spi_buf_set rx_buf_set = {.buffers = &rx_buffers, .count = 1};
60
61 int ret = spi_transceive(spi_device, &config, &tx_buf_set, &rx_buf_set);
62
63 if (ret < 0) {
64 shell_error(ctx, "spi_transceive returned %d", ret);
65 return ret;
66 }
67
68 shell_print(ctx, "TX:");
69 shell_hexdump(ctx, tx_buffer, bytes_to_send);
70
71 shell_print(ctx, "RX:");
72 shell_hexdump(ctx, rx_buffer, bytes_to_send);
73
74 return ret;
75 }
76
cmd_spi_conf(const struct shell * ctx,size_t argc,char ** argv)77 static int cmd_spi_conf(const struct shell *ctx, size_t argc, char **argv)
78 {
79 spi_operation_t operation = SPI_WORD_SET(8) | SPI_OP_MODE_MASTER;
80
81 /* warning: initialization discards 'const' qualifier from pointer */
82 /* target type */
83 struct device *dev = (struct device *)shell_device_get_binding(argv[CONF_ARGV_DEV]);
84
85 if (dev == NULL) {
86 shell_error(ctx, "device %s not found.", argv[CONF_ARGV_DEV]);
87 return -ENODEV;
88 }
89
90 uint32_t frequency = strtol(argv[CONF_ARGV_FREQUENCY], NULL, 10);
91
92 if (!IN_RANGE(frequency, 100 * 1000, 80 * 1000 * 1000)) {
93 shell_error(ctx, "frequency must be between 100000 and 80000000");
94 return -EINVAL;
95 }
96
97 /* no settings */
98 if (argc == (CONF_ARGV_FREQUENCY + 1)) {
99 goto out;
100 }
101
102 char *opts = argv[CONF_ARGV_SETTINGS];
103 bool all_opts_is_valid = true;
104
105 while (*opts != '\0') {
106 switch (*opts) {
107 case 'o':
108 operation |= SPI_MODE_CPOL;
109 break;
110 case 'h':
111 operation |= SPI_MODE_CPHA;
112 break;
113 case 'l':
114 operation |= SPI_TRANSFER_LSB;
115 break;
116 case 'T':
117 operation |= SPI_FRAME_FORMAT_TI;
118 break;
119 default:
120 all_opts_is_valid = false;
121 shell_error(ctx, "invalid setting %c", *opts);
122 }
123 opts++;
124 }
125
126 if (!all_opts_is_valid) {
127 return -EINVAL;
128 }
129
130 out:
131 config.frequency = frequency;
132 config.operation = operation;
133 spi_device = dev;
134
135 return 0;
136 }
137
138 SHELL_STATIC_SUBCMD_SET_CREATE(sub_spi_cmds,
139 SHELL_CMD_ARG(conf, &dsub_device_name,
140 "Configure SPI\n"
141 "Usage: spi conf <device> <frequency> [<settings>]\n"
142 "<settings> - any sequence of letters:\n"
143 "o - SPI_MODE_CPOL\n"
144 "h - SPI_MODE_CPHA\n"
145 "l - SPI_TRANSFER_LSB\n"
146 "T - SPI_FRAME_FORMAT_TI\n"
147 "example: spi conf spi1 1000000 ol",
148 cmd_spi_conf, 3, 1),
149 SHELL_CMD_ARG(transceive, NULL,
150 "Transceive data to and from an SPI device\n"
151 "Usage: spi transceive <TX byte 1> [<TX byte 2> ...]",
152 cmd_spi_transceive, 2, MAX_SPI_BYTES - 1),
153 SHELL_SUBCMD_SET_END);
154
155 SHELL_CMD_REGISTER(spi, &sub_spi_cmds, "SPI commands", NULL);
156