1145132Sanholt/*-
2145132Sanholt * Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka
3152909Sanholt * All rights reserved.
4152909Sanholt *
5145132Sanholt * Redistribution and use in source and binary forms, with or without
6145132Sanholt * modification, are permitted provided that the following conditions
7145132Sanholt * are met:
8145132Sanholt * 1. Redistributions of source code must retain the above copyright
9145132Sanholt *    notice, this list of conditions and the following disclaimer.
10145132Sanholt * 2. Redistributions in binary form must reproduce the above copyright
11145132Sanholt *    notice, this list of conditions and the following disclaimer in the
12145132Sanholt *    documentation and/or other materials provided with the distribution.
13145132Sanholt *
14145132Sanholt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15145132Sanholt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16145132Sanholt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17145132Sanholt * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18145132Sanholt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19145132Sanholt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20145132Sanholt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21145132Sanholt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22145132Sanholt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23145132Sanholt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24145132Sanholt * SUCH DAMAGE.
25145132Sanholt */
26145132Sanholt
27145132Sanholt#include <sys/cdefs.h>
28145132Sanholt__FBSDID("$FreeBSD: releng/10.3/usr.sbin/i2c/i2c.c 276876 2015-01-09 03:26:18Z loos $");
29145132Sanholt
30145132Sanholt#include <err.h>
31152909Sanholt#include <errno.h>
32152909Sanholt#include <sysexits.h>
33152909Sanholt#include <fcntl.h>
34145132Sanholt#include <stdio.h>
35145132Sanholt#include <stdlib.h>
36145132Sanholt#include <string.h>
37145132Sanholt#include <stdarg.h>
38145132Sanholt#include <unistd.h>
39145132Sanholt#include <sys/ioctl.h>
40145132Sanholt
41145132Sanholt#include <dev/iicbus/iic.h>
42145132Sanholt
43145132Sanholt#define	I2C_DEV			"/dev/iic0"
44145132Sanholt#define	I2C_MODE_NOTSET		0
45145132Sanholt#define	I2C_MODE_NONE		1
46145132Sanholt#define	I2C_MODE_STOP_START	2
47145132Sanholt#define	I2C_MODE_REPEATED_START	3
48145132Sanholt
49145132Sanholtstruct options {
50145132Sanholt	int	width;
51	int	count;
52	int	verbose;
53	int	addr_set;
54	int	binary;
55	int	scan;
56	int	skip;
57	int	reset;
58	int	mode;
59	char	dir;
60	uint32_t	addr;
61	uint32_t	off;
62};
63
64struct skip_range {
65	int	start;
66	int	end;
67};
68
69__dead2 static void
70usage(void)
71{
72
73	fprintf(stderr, "usage: %s -a addr [-f device] [-d [r|w]] [-o offset] "
74	    "[-w [0|8|16]] [-c count] [-m [ss|rs|no]] [-b] [-v]\n",
75	    getprogname());
76	fprintf(stderr, "       %s -s [-f device] [-n skip_addr] -v\n",
77	    getprogname());
78	fprintf(stderr, "       %s -r [-f device] -v\n", getprogname());
79	exit(EX_USAGE);
80}
81
82static struct skip_range
83skip_get_range(char *skip_addr)
84{
85	struct skip_range addr_range;
86	char *token;
87
88	addr_range.start = 0;
89	addr_range.end = 0;
90
91	token = strsep(&skip_addr, "..");
92	if (token) {
93		addr_range.start = strtoul(token, 0, 16);
94		token = strsep(&skip_addr, "..");
95		if ((token != NULL) && !atoi(token)) {
96			token = strsep(&skip_addr, "..");
97			if (token)
98				addr_range.end = strtoul(token, 0, 16);
99		}
100	}
101
102	return (addr_range);
103}
104
105/* Parse the string to get hex 7 bits addresses */
106static int
107skip_get_tokens(char *skip_addr, int *sk_addr, int max_index)
108{
109	char *token;
110	int i;
111
112	for (i = 0; i < max_index; i++) {
113		token = strsep(&skip_addr, ":");
114		if (token == NULL)
115			break;
116		sk_addr[i] = strtoul(token, 0, 16);
117	}
118	return (i);
119}
120
121static int
122scan_bus(struct iiccmd cmd, char *dev, int skip, char *skip_addr)
123{
124	struct skip_range addr_range = { 0, 0 };
125	int *tokens, fd, error, i, index, j;
126	int len = 0, do_skip = 0, no_range = 1;
127
128	fd = open(dev, O_RDWR);
129	if (fd == -1) {
130		fprintf(stderr, "Error opening I2C controller (%s) for "
131		    "scanning: %s\n", dev, strerror(errno));
132		return (EX_NOINPUT);
133	}
134
135	if (skip) {
136		len = strlen(skip_addr);
137		if (strstr(skip_addr, "..") != NULL) {
138			addr_range = skip_get_range(skip_addr);
139			no_range = 0;
140		} else {
141			tokens = (int *)malloc((len / 2 + 1) * sizeof(int));
142			if (tokens == NULL) {
143				fprintf(stderr, "Error allocating tokens "
144				    "buffer\n");
145				error = -1;
146				goto out;
147			}
148			index = skip_get_tokens(skip_addr, tokens,
149			    len / 2 + 1);
150		}
151
152		if (!no_range && (addr_range.start > addr_range.end)) {
153			fprintf(stderr, "Skip address out of range\n");
154			error = -1;
155			goto out;
156		}
157	}
158
159	printf("Scanning I2C devices on %s: ", dev);
160	for (i = 1; i < 127; i++) {
161
162		if (skip && ( addr_range.start < addr_range.end)) {
163			if (i >= addr_range.start && i <= addr_range.end)
164				continue;
165
166		} else if (skip && no_range)
167			for (j = 0; j < index; j++) {
168				if (tokens[j] == i) {
169					do_skip = 1;
170					break;
171				}
172			}
173
174		if (do_skip) {
175			do_skip = 0;
176			continue;
177		}
178
179		cmd.slave = i << 1;
180		cmd.last = 1;
181		cmd.count = 0;
182		error = ioctl(fd, I2CRSTCARD, &cmd);
183		if (error)
184			goto out;
185
186		cmd.slave = i << 1;
187		cmd.last = 1;
188		error = ioctl(fd, I2CSTART, &cmd);
189		if (!error)
190			printf("%x ", i);
191		cmd.slave = i << 1;
192		cmd.last = 1;
193		error = ioctl(fd, I2CSTOP, &cmd);
194	}
195	printf("\n");
196
197	error = ioctl(fd, I2CRSTCARD, &cmd);
198out:
199	close(fd);
200	if (skip && no_range)
201		free(tokens);
202
203	if (error) {
204		fprintf(stderr, "Error scanning I2C controller (%s): %s\n",
205		    dev, strerror(errno));
206		return (EX_NOINPUT);
207	} else
208		return (EX_OK);
209}
210
211static int
212reset_bus(struct iiccmd cmd, char *dev)
213{
214	int fd, error;
215
216	fd = open(dev, O_RDWR);
217	if (fd == -1) {
218		fprintf(stderr, "Error opening I2C controller (%s) for "
219		    "resetting: %s\n", dev, strerror(errno));
220		return (EX_NOINPUT);
221	}
222
223	printf("Resetting I2C controller on %s: ", dev);
224	error = ioctl(fd, I2CRSTCARD, &cmd);
225	close (fd);
226
227	if (error) {
228		printf("error: %s\n", strerror(errno));
229		return (EX_IOERR);
230	} else {
231		printf("OK\n");
232		return (EX_OK);
233	}
234}
235
236static char *
237prepare_buf(int size, uint32_t off)
238{
239	char *buf;
240
241	buf = malloc(size);
242	if (buf == NULL)
243		return (buf);
244
245	if (size == 1)
246		buf[0] = off & 0xff;
247	else if (size == 2) {
248		buf[0] = (off >> 8) & 0xff;
249		buf[1] = off & 0xff;
250	}
251
252	return (buf);
253}
254
255static int
256i2c_write(char *dev, struct options i2c_opt, char *i2c_buf)
257{
258	struct iiccmd cmd;
259	int ch, i, error, fd, bufsize;
260	char *err_msg, *buf;
261
262	/*
263	 * Read data to be written to the chip from stdin
264	 */
265	if (i2c_opt.verbose && !i2c_opt.binary)
266		fprintf(stderr, "Enter %u bytes of data: ", i2c_opt.count);
267
268	for (i = 0; i < i2c_opt.count; i++) {
269		ch = getchar();
270		if (ch == EOF) {
271			free(i2c_buf);
272			err(1, "not enough data, exiting\n");
273		}
274		i2c_buf[i] = ch;
275	}
276
277	fd = open(dev, O_RDWR);
278	if (fd == -1) {
279		free(i2c_buf);
280		err(1, "open failed");
281	}
282
283	/*
284	 * Write offset where the data will go
285	 */
286	cmd.slave = i2c_opt.addr;
287	error = ioctl(fd, I2CSTART, &cmd);
288	if (error == -1) {
289		err_msg = "ioctl: error sending start condition";
290		goto err1;
291	}
292
293	if (i2c_opt.width) {
294		bufsize = i2c_opt.width / 8;
295		buf = prepare_buf(bufsize, i2c_opt.off);
296		if (buf == NULL) {
297			err_msg = "error: offset malloc";
298			goto err1;
299		}
300
301		cmd.count = bufsize;
302		cmd.buf = buf;
303		error = ioctl(fd, I2CWRITE, &cmd);
304		free(buf);
305		if (error == -1) {
306			err_msg = "ioctl: error when write offset";
307			goto err1;
308		}
309	}
310
311	/* Mode - stop start */
312	if (i2c_opt.mode == I2C_MODE_STOP_START) {
313		cmd.slave = i2c_opt.addr;
314		error = ioctl(fd, I2CSTOP, &cmd);
315		if (error == -1) {
316			err_msg = "ioctl: error sending stop condition";
317			goto err2;
318		}
319		cmd.slave = i2c_opt.addr;
320		error = ioctl(fd, I2CSTART, &cmd);
321		if (error == -1) {
322			err_msg = "ioctl: error sending start condition";
323			goto err1;
324		}
325	}
326	/* Mode - repeated start */
327	if (i2c_opt.mode == I2C_MODE_REPEATED_START) {
328		cmd.slave = i2c_opt.addr;
329		error = ioctl(fd, I2CRPTSTART, &cmd);
330		if (error == -1) {
331			err_msg = "ioctl: error sending repeated start "
332			    "condition";
333			goto err1;
334		}
335	}
336
337	/*
338	 * Write the data
339	 */
340	cmd.count = i2c_opt.count;
341	cmd.buf = i2c_buf;
342	cmd.last = 0;
343	error = ioctl(fd, I2CWRITE, &cmd);
344	if (error == -1) {
345		err_msg = "ioctl: error when write";
346		goto err1;
347	}
348	cmd.slave = i2c_opt.addr;
349	error = ioctl(fd, I2CSTOP, &cmd);
350	if (error == -1) {
351		err_msg = "ioctl: error sending stop condition";
352		goto err2;
353	}
354
355	close(fd);
356	return (0);
357
358err1:
359	cmd.slave = i2c_opt.addr;
360	error = ioctl(fd, I2CSTOP, &cmd);
361	if (error == -1)
362		fprintf(stderr, "error sending stop condtion\n");
363err2:
364	if (err_msg)
365		fprintf(stderr, "%s", err_msg);
366
367	close(fd);
368	return (1);
369}
370
371static int
372i2c_read(char *dev, struct options i2c_opt, char *i2c_buf)
373{
374	struct iiccmd cmd;
375	int i, fd, error, bufsize;
376	char *err_msg, data = 0, *buf;
377
378	fd = open(dev, O_RDWR);
379	if (fd == -1)
380		err(1, "open failed");
381
382	bzero(&cmd, sizeof(cmd));
383
384	if (i2c_opt.width) {
385		cmd.slave = i2c_opt.addr;
386		cmd.count = 1;
387		cmd.last = 0;
388		cmd.buf = &data;
389		error = ioctl(fd, I2CSTART, &cmd);
390		if (error == -1) {
391			err_msg = "ioctl: error sending start condition";
392			goto err1;
393		}
394		bufsize = i2c_opt.width / 8;
395		buf = prepare_buf(bufsize, i2c_opt.off);
396		if (buf == NULL) {
397			err_msg = "error: offset malloc";
398			goto err1;
399		}
400
401		cmd.count = bufsize;
402		cmd.buf = buf;
403		cmd.last = 0;
404		error = ioctl(fd, I2CWRITE, &cmd);
405		free(buf);
406		if (error == -1) {
407			err_msg = "ioctl: error when write offset";
408			goto err1;
409		}
410
411		if (i2c_opt.mode == I2C_MODE_STOP_START) {
412			cmd.slave = i2c_opt.addr;
413			error = ioctl(fd, I2CSTOP, &cmd);
414			if (error == -1) {
415				err_msg = "error sending stop condtion\n";
416				goto err2;
417			}
418		}
419	}
420	cmd.slave = i2c_opt.addr;
421	cmd.count = 1;
422	cmd.last = 0;
423	cmd.buf = &data;
424	if (i2c_opt.mode == I2C_MODE_STOP_START) {
425		error = ioctl(fd, I2CSTART, &cmd);
426		if (error == -1) {
427			err_msg = "ioctl: error sending start condition";
428			goto err1;
429		}
430	} else if (i2c_opt.mode == I2C_MODE_REPEATED_START) {
431		error = ioctl(fd, I2CRPTSTART, &cmd);
432		if (error == -1) {
433			err_msg = "ioctl: error sending repeated start "
434			    "condition";
435			goto err1;
436		}
437	}
438	error = ioctl(fd, I2CSTOP, &cmd);
439	if (error == -1) {
440		err_msg = "error sending stop condtion\n";
441		goto err2;
442	}
443
444	for (i = 0; i < i2c_opt.count; i++) {
445		error = read(fd, &i2c_buf[i], 1);
446		if (error == -1) {
447			err_msg = "ioctl: error while reading";
448			goto err1;
449		}
450	}
451
452	close(fd);
453	return (0);
454
455err1:
456	cmd.slave = i2c_opt.addr;
457	error = ioctl(fd, I2CSTOP, &cmd);
458	if (error == -1)
459		fprintf(stderr, "error sending stop condtion\n");
460err2:
461	if (err_msg)
462		fprintf(stderr, "%s", err_msg);
463
464	close(fd);
465	return (1);
466}
467
468int
469main(int argc, char** argv)
470{
471	struct iiccmd cmd;
472	struct options i2c_opt;
473	char *dev, *skip_addr, *i2c_buf;
474	int error, chunk_size, i, j, ch;
475
476	errno = 0;
477	error = 0;
478
479	/* Line-break the output every chunk_size bytes */
480	chunk_size = 16;
481
482	dev = I2C_DEV;
483
484	/* Default values */
485	i2c_opt.addr_set = 0;
486	i2c_opt.off = 0;
487	i2c_opt.verbose = 0;
488	i2c_opt.dir = 'r';	/* direction = read */
489	i2c_opt.width = 8;
490	i2c_opt.count = 1;
491	i2c_opt.binary = 0;	/* ASCII text output */
492	i2c_opt.scan = 0;	/* no bus scan */
493	i2c_opt.skip = 0;	/* scan all addresses */
494	i2c_opt.reset = 0;	/* no bus reset */
495	i2c_opt.mode = I2C_MODE_NOTSET;
496
497	while ((ch = getopt(argc, argv, "a:f:d:o:w:c:m:n:sbvrh")) != -1) {
498		switch(ch) {
499		case 'a':
500			i2c_opt.addr = (strtoul(optarg, 0, 16) << 1);
501			if (i2c_opt.addr == 0 && errno == EINVAL)
502				i2c_opt.addr_set = 0;
503			else
504				i2c_opt.addr_set = 1;
505			break;
506		case 'f':
507			dev = optarg;
508			break;
509		case 'd':
510			i2c_opt.dir = optarg[0];
511			break;
512		case 'o':
513			i2c_opt.off = strtoul(optarg, 0, 16);
514			if (i2c_opt.off == 0 && errno == EINVAL)
515				error = 1;
516			break;
517		case 'w':
518			i2c_opt.width = atoi(optarg);
519			break;
520		case 'c':
521			i2c_opt.count = atoi(optarg);
522			break;
523		case 'm':
524			if (!strcmp(optarg, "no"))
525				i2c_opt.mode = I2C_MODE_NONE;
526			else if (!strcmp(optarg, "ss"))
527				i2c_opt.mode = I2C_MODE_STOP_START;
528			else if (!strcmp(optarg, "rs"))
529				i2c_opt.mode = I2C_MODE_REPEATED_START;
530			else
531				usage();
532			break;
533		case 'n':
534			i2c_opt.skip = 1;
535			skip_addr = optarg;
536			break;
537		case 's':
538			i2c_opt.scan = 1;
539			break;
540		case 'b':
541			i2c_opt.binary = 1;
542			break;
543		case 'v':
544			i2c_opt.verbose = 1;
545			break;
546		case 'r':
547			i2c_opt.reset = 1;
548			break;
549		case 'h':
550		default:
551			usage();
552		}
553	}
554	argc -= optind;
555	argv += optind;
556
557	/* Set default mode if option -m is not specified */
558	if (i2c_opt.mode == I2C_MODE_NOTSET) {
559		if (i2c_opt.dir == 'r')
560			i2c_opt.mode = I2C_MODE_STOP_START;
561		else if (i2c_opt.dir == 'w')
562			i2c_opt.mode = I2C_MODE_NONE;
563	}
564
565	/* Basic sanity check of command line arguments */
566	if (i2c_opt.scan) {
567		if (i2c_opt.addr_set)
568			usage();
569	} else if (i2c_opt.reset) {
570		if (i2c_opt.addr_set)
571			usage();
572	} else if (error) {
573		usage();
574	} else if ((i2c_opt.dir == 'r' || i2c_opt.dir == 'w')) {
575		if ((i2c_opt.addr_set == 0) ||
576		    !(i2c_opt.width == 0 || i2c_opt.width == 8 ||
577		    i2c_opt.width == 16))
578		usage();
579	}
580
581	if (i2c_opt.verbose)
582		fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, "
583		    "offset: 0x%02x, width: %u, count: %u\n", dev,
584		    i2c_opt.addr >> 1, i2c_opt.dir, i2c_opt.off,
585		    i2c_opt.width, i2c_opt.count);
586
587	if (i2c_opt.scan)
588		exit(scan_bus(cmd, dev, i2c_opt.skip, skip_addr));
589
590	if (i2c_opt.reset)
591		exit(reset_bus(cmd, dev));
592
593	i2c_buf = malloc(i2c_opt.count);
594	if (i2c_buf == NULL)
595		err(1, "data malloc");
596
597	if (i2c_opt.dir == 'w') {
598		error = i2c_write(dev, i2c_opt, i2c_buf);
599		if (error) {
600			free(i2c_buf);
601			return (1);
602		}
603	}
604	if (i2c_opt.dir == 'r') {
605		error = i2c_read(dev, i2c_opt, i2c_buf);
606		if (error) {
607			free(i2c_buf);
608			return (1);
609		}
610	}
611
612	if (i2c_opt.verbose)
613		fprintf(stderr, "\nData %s (hex):\n", i2c_opt.dir == 'r' ?
614		    "read" : "written");
615
616	i = 0;
617	j = 0;
618	while (i < i2c_opt.count) {
619		if (i2c_opt.verbose || (i2c_opt.dir == 'r' &&
620		    !i2c_opt.binary))
621			fprintf (stderr, "%02hhx ", i2c_buf[i++]);
622
623		if (i2c_opt.dir == 'r' && i2c_opt.binary) {
624			fprintf(stdout, "%c", i2c_buf[j++]);
625			if(!i2c_opt.verbose)
626				i++;
627		}
628		if (!i2c_opt.verbose && (i2c_opt.dir == 'w'))
629			break;
630		if ((i % chunk_size) == 0)
631			fprintf(stderr, "\n");
632	}
633	if ((i % chunk_size) != 0)
634		fprintf(stderr, "\n");
635
636	free(i2c_buf);
637	return (0);
638}
639