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