xref: /netbsd-src/usr.sbin/i2cscan/i2cscan.c (revision 9ad20c88f46addedc60002125536b0afec39dc24)
1 /* $NetBSD: i2cscan.c,v 1.5 2015/11/26 17:31:56 hubertf Exp $ */
2 
3 /*-
4  * Copyright (c) 2011, 2013 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Paul Goyette and Jared McNeill
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: i2cscan.c,v 1.5 2015/11/26 17:31:56 hubertf Exp $");
34 
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
37 
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <paths.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include <dev/i2c/i2c_io.h>
48 
49 #define MODE_DEFAULT 0
50 #define MODE_READ 1
51 
52 __dead static void
usage(void)53 usage(void)
54 {
55 	fprintf(stderr, "usage: %s [-r] i2cdev\n", getprogname());
56 	exit(EXIT_FAILURE);
57 }
58 
59 static int
iic_smbus_quick_write(int fd,i2c_addr_t addr,int flags)60 iic_smbus_quick_write(int fd, i2c_addr_t addr, int flags)
61 {
62 	i2c_ioctl_exec_t iie;
63 
64 	iie.iie_op = I2C_OP_WRITE_WITH_STOP;
65 	iie.iie_addr = addr;
66 	iie.iie_cmd = NULL;
67 	iie.iie_cmdlen = 0;
68 	iie.iie_buf = NULL;
69 	iie.iie_buflen = 0;
70 
71 	if (ioctl(fd, I2C_IOCTL_EXEC, &iie) == -1)
72 		return errno;
73 	return 0;
74 }
75 
76 static int
iic_smbus_receive_byte(int fd,i2c_addr_t addr,uint8_t * valp,int flags)77 iic_smbus_receive_byte(int fd, i2c_addr_t addr, uint8_t *valp, int flags)
78 {
79 	i2c_ioctl_exec_t iie;
80 
81 	iie.iie_op = I2C_OP_READ_WITH_STOP;
82 	iie.iie_addr = addr;
83 	iie.iie_cmd = NULL;
84 	iie.iie_cmdlen = 0;
85 	iie.iie_buf = valp;
86 	iie.iie_buflen = 1;
87 
88 	if (ioctl(fd, I2C_IOCTL_EXEC, &iie) == -1)
89 		return errno;
90 	return 0;
91 
92 }
93 
94 static void
do_i2c_scan(const char * dname,int fd,int mode)95 do_i2c_scan(const char *dname, int fd, int mode)
96 {
97 	int error;
98 	int found = 0;
99 	i2c_addr_t addr;
100 	uint8_t val;
101 
102 	for (addr = 0x09; addr < 0x78; addr++) {
103 		/*
104 		 * Skip certain i2c addresses:
105 		 *	0x00		General Call / START
106 		 *	0x01		CBUS Address
107 		 *	0x02		Different Bus format
108 		 *	0x03 - 0x07	Reserved
109 		 *	0x08		Host Address
110 		 *	0x0c		Alert Response Address
111 		 *	0x28		ACCESS.Bus host
112 		 *	0x37		ACCESS.Bus default address
113 		 *	0x48 - 0x4b	Prototypes
114 		 *	0x61		Device Default Address
115 		 *	0x78 - 0x7b	10-bit addresses
116 		 *	0x7c - 0x7f	Reserved
117 		 *
118 		 * Some of these are skipped by judicious selection
119 		 * of the range of the above for (;;) statement.
120 		 *
121 		 * if (addr <= 0x08 || addr >= 0x78)
122 		 *	continue;
123 		 */
124 		if (addr == 0x0c || addr == 0x28 || addr == 0x37 ||
125 		    addr == 0x61 || (addr & 0x7c) == 0x48)
126 			continue;
127 
128 		/*
129 		 * Use SMBus quick_write command to detect most
130 		 * addresses;  should avoid hanging the bus on
131 		 * some write-only devices (like clocks that show
132 		 * up at address 0x69)
133 		 *
134 		 * XXX The quick_write() is allegedly known to
135 		 * XXX corrupt the Atmel AT24RF08 EEPROM found
136 		 * XXX on some IBM Thinkpads!
137 		 */
138 		printf("\r%s: scanning 0x%02x", dname, addr);
139 		fflush(stdout);
140 		if ((addr & 0xf8) == 0x30 ||
141 		    (addr & 0xf0) == 0x50 ||
142 		    mode == MODE_READ)
143 			error = iic_smbus_receive_byte(fd, addr, &val, 0);
144 		else
145 			error = iic_smbus_quick_write(fd, addr, 0);
146 		if (error == 0) {
147 			printf("\r%s: found device at 0x%02x\n",
148 			    dname, addr);
149 			++found;
150 		}
151 	}
152 	if (found == 0)
153 		printf("\r%s: no devices found\n", dname);
154 	else
155 		printf("\r%s: %d devices found\n", dname, found);
156 }
157 
158 int
main(int argc,char * argv[])159 main(int argc, char *argv[])
160 {
161 	int fd;
162 	int ch, rflag;
163 	int mode;
164 	char *dev;
165 	char devn[32];
166 
167 	setprogname(*argv);
168 
169 	rflag = 0;
170 
171 	while ((ch = getopt(argc, argv, "r")) != -1)
172 		switch (ch) {
173 		case 'r':
174 			rflag = 1;
175 			break;
176 		default:
177 			break;
178 		}
179 	argv += optind;
180 	argc -= optind;
181 
182 	if (rflag)
183 		mode = MODE_READ;
184 	else
185 		mode = MODE_DEFAULT;
186 
187 	if (*argv == NULL)
188 		usage();
189 	dev = argv[0];
190 
191 	if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) {
192 		(void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev);
193 		dev = devn;
194 	}
195 
196 	fd = open(dev, O_RDWR);
197 	if (fd == -1)
198 		err(EXIT_FAILURE, "couldn't open %s", *argv);
199 
200 	do_i2c_scan(*argv, fd, mode);
201 
202 	close(fd);
203 
204 	return EXIT_SUCCESS;
205 }
206