Home | History | Annotate | Line # | Download | only in i2cscan
      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
     53 usage(void)
     54 {
     55 	fprintf(stderr, "usage: %s [-r] i2cdev\n", getprogname());
     56 	exit(EXIT_FAILURE);
     57 }
     58 
     59 static int
     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
     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
     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
    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