Home | History | Annotate | Line # | Download | only in i2cscan
i2cscan.c revision 1.3.8.1
      1 /* $NetBSD: i2cscan.c,v 1.3.8.1 2014/08/20 00:05:08 tls 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.3.8.1 2014/08/20 00:05:08 tls 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 <stdio.h>
     42 #include <stdlib.h>
     43 #include <unistd.h>
     44 
     45 #include <dev/i2c/i2c_io.h>
     46 
     47 #define MODE_DEFAULT 0
     48 #define MODE_READ 1
     49 
     50 __dead static void
     51 usage(void)
     52 {
     53 	fprintf(stderr, "usage: %s [-r] <i2cdev>\n", getprogname());
     54 	exit(EXIT_FAILURE);
     55 }
     56 
     57 static int
     58 iic_smbus_quick_write(int fd, i2c_addr_t addr, int flags)
     59 {
     60 	i2c_ioctl_exec_t iie;
     61 
     62 	iie.iie_op = I2C_OP_WRITE_WITH_STOP;
     63 	iie.iie_addr = addr;
     64 	iie.iie_cmd = NULL;
     65 	iie.iie_cmdlen = 0;
     66 	iie.iie_buf = NULL;
     67 	iie.iie_buflen = 0;
     68 
     69 	if (ioctl(fd, I2C_IOCTL_EXEC, &iie) == -1)
     70 		return errno;
     71 	return 0;
     72 }
     73 
     74 static int
     75 iic_smbus_receive_byte(int fd, i2c_addr_t addr, uint8_t *valp, int flags)
     76 {
     77 	i2c_ioctl_exec_t iie;
     78 
     79 	iie.iie_op = I2C_OP_READ_WITH_STOP;
     80 	iie.iie_addr = addr;
     81 	iie.iie_cmd = NULL;
     82 	iie.iie_cmdlen = 0;
     83 	iie.iie_buf = valp;
     84 	iie.iie_buflen = 1;
     85 
     86 	if (ioctl(fd, I2C_IOCTL_EXEC, &iie) == -1)
     87 		return errno;
     88 	return 0;
     89 
     90 }
     91 
     92 static void
     93 do_i2c_scan(const char *dname, int fd, int mode)
     94 {
     95 	int error;
     96 	int found = 0;
     97 	i2c_addr_t addr;
     98 	uint8_t val;
     99 
    100 	for (addr = 0x09; addr < 0x78; addr++) {
    101 		/*
    102 		 * Skip certain i2c addresses:
    103 		 *	0x00		General Call / START
    104 		 *	0x01		CBUS Address
    105 		 *	0x02		Different Bus format
    106 		 *	0x03 - 0x07	Reserved
    107 		 *	0x08		Host Address
    108 		 *	0x0c		Alert Response Address
    109 		 *	0x28		ACCESS.Bus host
    110 		 *	0x37		ACCESS.Bus default address
    111 		 *	0x48 - 0x4b	Prototypes
    112 		 *	0x61		Device Default Address
    113 		 *	0x78 - 0x7b	10-bit addresses
    114 		 *	0x7c - 0x7f	Reserved
    115 		 *
    116 		 * Some of these are skipped by judicious selection
    117 		 * of the range of the above for (;;) statement.
    118 		 *
    119 		 * if (addr <= 0x08 || addr >= 0x78)
    120 		 *	continue;
    121 		 */
    122 		if (addr == 0x0c || addr == 0x28 || addr == 0x37 ||
    123 		    addr == 0x61 || (addr & 0x7c) == 0x48)
    124 			continue;
    125 
    126 		/*
    127 		 * Use SMBus quick_write command to detect most
    128 		 * addresses;  should avoid hanging the bus on
    129 		 * some write-only devices (like clocks that show
    130 		 * up at address 0x69)
    131 		 *
    132 		 * XXX The quick_write() is allegedly known to
    133 		 * XXX corrupt the Atmel AT24RF08 EEPROM found
    134 		 * XXX on some IBM Thinkpads!
    135 		 */
    136 		printf("\r%s: scanning 0x%02x", dname, addr);
    137 		fflush(stdout);
    138 		if ((addr & 0xf8) == 0x30 ||
    139 		    (addr & 0xf0) == 0x50 ||
    140 		    mode == MODE_READ)
    141 			error = iic_smbus_receive_byte(fd, addr, &val, 0);
    142 		else
    143 			error = iic_smbus_quick_write(fd, addr, 0);
    144 		if (error == 0) {
    145 			printf("\r%s: found device at 0x%02x\n",
    146 			    dname, addr);
    147 			++found;
    148 		}
    149 	}
    150 	if (found == 0)
    151 		printf("\r%s: no devices found\n", dname);
    152 	else
    153 		printf("\r%s: %d devices found\n", dname, found);
    154 }
    155 
    156 int
    157 main(int argc, char *argv[])
    158 {
    159 	int fd;
    160 	int ch, rflag;
    161 	int mode;
    162 
    163 	setprogname(*argv);
    164 
    165 	rflag = 0;
    166 
    167 	while ((ch = getopt(argc, argv, "r")) != -1)
    168 		switch (ch) {
    169 		case 'r':
    170 			rflag = 1;
    171 			break;
    172 		default:
    173 			break;
    174 		}
    175 	argv += optind;
    176 	argc -= optind;
    177 
    178 	if (rflag)
    179 		mode = MODE_READ;
    180 	else
    181 		mode = MODE_DEFAULT;
    182 
    183 	if (*argv == NULL)
    184 		usage();
    185 
    186 	fd = open(*argv, O_RDWR);
    187 	if (fd == -1)
    188 		err(EXIT_FAILURE, "couldn't open %s", *argv);
    189 
    190 	do_i2c_scan(*argv, fd, mode);
    191 
    192 	close(fd);
    193 
    194 	return EXIT_SUCCESS;
    195 }
    196