1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <errno.h> 6#include <fcntl.h> 7#include <zircon/syscalls.h> 8#include <zircon/types.h> 9#include <zircon/device/i2c.h> 10#include <lib/fdio/util.h> 11#include <stddef.h> 12#include <stdio.h> 13#include <stdlib.h> 14#include <string.h> 15#include <unistd.h> 16 17const char* prog_name; 18 19void print_usage(void) { 20 printf("Usage:\n"); 21 printf("\n"); 22 printf("%s DEVICE COMMAND [command arguments]\n", prog_name); 23 printf("DEVICE is either the i2c bus or i2c slave COMMAND applies to.\n"); 24 printf("COMMAND is one of the following commands, optionally followed \n"); 25 printf("arguments which are specific to each command.\n"); 26 printf("\n"); 27 printf("read LENGTH: Read data from the target slave device.\n"); 28 printf("LENGTH is the number of bytes to read in decimal.\n"); 29 printf("\n"); 30 printf("write [data]: Write data to the target slave device.\n"); 31 printf("data is a sequence of hex values which each represent one byte\n"); 32 printf("of data to write to the target device.\n"); 33 printf("\n"); 34 printf("transfer [segments]: Perform a tranfer to/from the i2c slave.\n"); 35 printf("segments is a series of segment descriptions which are a\n"); 36 printf("direction, a length, and then (for writes) a series of bytes\n"); 37 printf("in hexidecimal.\n"); 38 printf("\n"); 39 printf("The direction is specified as either \"w\" for writes, or\n"); 40 printf("\"r\" for reads.\n"); 41 printf("\n"); 42 printf("For example, to perform a write of one byte and then a read\n"); 43 printf("of one byte without giving up the bus:\n"); 44 printf("%s [dev] transfer w 1 00 r 1\n", prog_name); 45} 46 47int cmd_read(int fd, int argc, const char** argv) { 48 if (argc < 1) { 49 print_usage(); 50 return 1; 51 } 52 53 errno = 0; 54 long int length = strtol(argv[0], NULL, 10); 55 if (errno) { 56 print_usage(); 57 return errno; 58 } 59 60 uint8_t* buf = malloc(length); 61 if (!buf) { 62 printf("Failed to allocate buffer.\n"); 63 return 1; 64 } 65 66 int ret = read(fd, buf, length); 67 if (ret < 0) { 68 printf("Error reading from slave. (%d)\n", ret); 69 goto cmd_read_finish; 70 } 71 72 for (int i = 0; i < length; i++) { 73 printf(" %02x", buf[i]); 74 if (i % 32 == 31) printf("\n"); 75 } 76 printf("\n"); 77 78cmd_read_finish: 79 free(buf); 80 return ret; 81} 82 83int cmd_write(int fd, int argc, const char** argv) { 84 if (argc < 1) { 85 print_usage(); 86 return 1; 87 } 88 89 uint8_t* buf = malloc(argc); 90 if (!buf) { 91 printf("Failed to allocate buffer.\n"); 92 return 1; 93 } 94 95 int ret = 0; 96 97 errno = 0; 98 for (int i = 0; i < argc; i++) { 99 buf[i] = strtol(argv[i], NULL, 16); 100 if (errno) { 101 ret = errno; 102 print_usage(); 103 goto cmd_write_finish; 104 } 105 } 106 107 ret = write(fd, buf, argc); 108 if (ret < 0) 109 printf("Error writing to slave. (%d)\n", ret); 110 111cmd_write_finish: 112 free(buf); 113 return ret; 114} 115 116int cmd_transfer(int fd, int argc, const char** argv) { 117 const size_t base_size = sizeof(i2c_slave_ioctl_segment_t); 118 int ret = ZX_OK; 119 120 // Figure out how big our buffers need to be. 121 // Start the counters with enough space for the I2C_SEGMENT_TYPE_END 122 // segment. 123 size_t in_len = base_size; 124 size_t out_len = 0; 125 int segments = 1; 126 int count = argc; 127 const char** arg = argv; 128 errno = 0; 129 while (count) { 130 if (count < 2) { 131 print_usage(); 132 goto cmd_transfer_finish_2; 133 } 134 135 in_len += base_size; 136 137 int read; 138 if (!strcmp(arg[0], "r")) { 139 read = 1; 140 } else if (!strcmp(arg[0], "w")) { 141 read = 0; 142 } else { 143 print_usage(); 144 goto cmd_transfer_finish_2; 145 } 146 segments++; 147 148 long int length = strtol(arg[1], NULL, 10); 149 if (errno) { 150 print_usage(); 151 return errno; 152 } 153 arg += 2; 154 count -= 2; 155 if (read) { 156 out_len += length; 157 } else { 158 in_len += length; 159 if (length > count) { 160 print_usage(); 161 goto cmd_transfer_finish_2; 162 } 163 arg += length; 164 count -= length; 165 } 166 } 167 168 // Allocate the input and output buffers. 169 void* in_buf = malloc(in_len); 170 void* out_buf = malloc(out_len); 171 if (!in_buf || !out_buf) { 172 ret = 1; 173 goto cmd_transfer_finish_1; 174 } 175 uint8_t* data_addr = (uint8_t*)in_buf + segments * base_size; 176 uint8_t* data_buf = data_addr; 177 178 // Fill the "input" buffer which is sent to the ioctl. 179 uintptr_t in_addr = (uintptr_t)in_buf; 180 int i = 0; 181 i2c_slave_ioctl_segment_t* ioctl_segment = (i2c_slave_ioctl_segment_t*)in_addr; 182 while (i < argc) { 183 if (!strcmp(argv[i++], "r")) { 184 ioctl_segment->type = I2C_SEGMENT_TYPE_READ; 185 ioctl_segment->len = strtol(argv[i++], NULL, 10); 186 if (errno) { 187 print_usage(); 188 return errno; 189 } 190 } else { 191 ioctl_segment->type = I2C_SEGMENT_TYPE_WRITE; 192 ioctl_segment->len = strtol(argv[i++], NULL, 10); 193 if (errno) { 194 print_usage(); 195 return errno; 196 } 197 198 for (int seg = 0; seg < ioctl_segment->len; seg++) { 199 *data_buf++ = strtol(argv[i++], NULL, 16); 200 if (errno) { 201 print_usage(); 202 return errno; 203 } 204 } 205 } 206 ioctl_segment++; 207 } 208 ioctl_segment->type = I2C_SEGMENT_TYPE_END; 209 ioctl_segment->len = 0; 210 ioctl_segment++; 211 // We should be at the start of the data section now. 212 if ((uint8_t*)ioctl_segment != data_addr) { 213 ret = 1; 214 goto cmd_transfer_finish_1; 215 } 216 217 ret = ioctl_i2c_slave_transfer(fd, in_buf, in_len, out_buf, out_len); 218 if (ret < 0) 219 goto cmd_transfer_finish_1; 220 221 for (size_t i = 0; i < out_len; i++) { 222 printf(" %02x", ((uint8_t*)out_buf)[i]); 223 if (i % 32 == 31) printf("\n"); 224 } 225 printf("\n"); 226 227 ret = 0; 228 229cmd_transfer_finish_1: 230 free(in_buf); 231 free(out_buf); 232cmd_transfer_finish_2: 233 return ret; 234} 235 236int main(int argc, const char** argv) { 237 if (argc < 1) 238 return 1; 239 240 prog_name = argv[0]; 241 242 if (argc < 3) { 243 print_usage(); 244 return 1; 245 } 246 247 const char* dev = argv[1]; 248 const char* cmd = argv[2]; 249 250 argc -= 3; 251 argv += 3; 252 253 int fd = open(dev, O_RDWR); 254 if (fd < 0) { 255 printf("Error opening I2C device.\n"); 256 return 1; 257 } 258 259 if (!strcmp("read", cmd)) { 260 return cmd_read(fd, argc, argv); 261 } else if (!strcmp("write", cmd)) { 262 return cmd_write(fd, argc, argv); 263 } else if (!strcmp("transfer", cmd)) { 264 return cmd_transfer(fd, argc, argv); 265 } else { 266 printf("Unrecognized command %s.\n", cmd); 267 print_usage(); 268 return 1; 269 } 270 271 printf("We should never get here!.\n"); 272 return 1; 273} 274