root/i2c-tools/trunk/tools/i2cbusses.c @ 5884

Revision 5884, 9.8 KB (checked in by khali, 2 years ago)

tools: Drop arbitrary limit on I2C bus number

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    i2cbusses: Print the installed i2c busses for both 2.4 and 2.6 kernels.
3               Part of user-space programs to access for I2C
4               devices.
5    Copyright (c) 1999-2003  Frodo Looijaard <frodol@dds.nl> and
6                             Mark D. Studebaker <mdsxyz123@yahoo.com>
7    Copyright (C) 2008       Jean Delvare <khali@linux-fr.org>
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22    MA 02110-1301 USA.
23*/
24
25/* For strdup */
26#define _BSD_SOURCE 1
27
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <sys/param.h>  /* for NAME_MAX */
31#include <string.h>
32#include <strings.h>    /* for strcasecmp() */
33#include <stdio.h>
34#include <stdlib.h>
35#include <unistd.h>
36#include <limits.h>
37#include <dirent.h>
38#include <fcntl.h>
39#include <errno.h>
40#include "i2cbusses.h"
41#include <linux/i2c-dev.h>
42
43enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown };
44
45struct adap_type {
46        const char *funcs;
47        const char* algo;
48};
49
50static struct adap_type adap_types[5] = {
51        { .funcs        = "dummy",
52          .algo         = "Dummy bus", },
53        { .funcs        = "isa",
54          .algo         = "ISA bus", },
55        { .funcs        = "i2c",
56          .algo         = "I2C adapter", },
57        { .funcs        = "smbus",
58          .algo         = "SMBus adapter", },
59        { .funcs        = "unknown",
60          .algo         = "N/A", },
61};
62
63static enum adt i2c_get_funcs(int i2cbus)
64{
65        unsigned long funcs;
66        int file;
67        char filename[20];
68        enum adt ret;
69
70        file = open_i2c_dev(i2cbus, filename, 1);
71        if (file < 0)
72                return adt_unknown;
73
74        if (ioctl(file, I2C_FUNCS, &funcs) < 0)
75                ret = adt_unknown;
76        else if (funcs & I2C_FUNC_I2C)
77                ret = adt_i2c;
78        else if (funcs & (I2C_FUNC_SMBUS_BYTE |
79                          I2C_FUNC_SMBUS_BYTE_DATA |
80                          I2C_FUNC_SMBUS_WORD_DATA))
81                ret = adt_smbus;
82        else
83                ret = adt_dummy;
84
85        close(file);
86        return ret;
87}
88
89/* Remove trailing spaces from a string
90   Return the new string length including the trailing NUL */
91static int rtrim(char *s)
92{
93        int i;
94
95        for (i = strlen(s) - 1; i >= 0 && (s[i] == ' ' || s[i] == '\n'); i--)
96                s[i] = '\0';
97        return i + 2;
98}
99
100void free_adapters(struct i2c_adap *adapters)
101{
102        int i;
103
104        for (i = 0; adapters[i].name; i++)
105                free(adapters[i].name);
106        free(adapters);
107}
108
109/* We allocate space for the adapters in bunches. The last item is a
110   terminator, so here we start with room for 7 adapters, which should
111   be enough in most cases. If not, we allocate more later as needed. */
112#define BUNCH   8
113
114/* n must match the size of adapters at calling time */
115static struct i2c_adap *more_adapters(struct i2c_adap *adapters, int n)
116{
117        struct i2c_adap *new_adapters;
118
119        new_adapters = realloc(adapters, (n + BUNCH) * sizeof(struct i2c_adap));
120        if (!new_adapters) {
121                free_adapters(adapters);
122                return NULL;
123        }
124        memset(new_adapters + n, 0, BUNCH * sizeof(struct i2c_adap));
125
126        return new_adapters;
127}
128
129struct i2c_adap *gather_i2c_busses(void)
130{
131        char s[120];
132        struct dirent *de, *dde;
133        DIR *dir, *ddir;
134        FILE *f;
135        char fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX];
136        int foundsysfs = 0;
137        int count=0;
138        struct i2c_adap *adapters;
139
140        adapters = calloc(BUNCH, sizeof(struct i2c_adap));
141        if (!adapters)
142                return NULL;
143
144        /* look in /proc/bus/i2c */
145        if ((f = fopen("/proc/bus/i2c", "r"))) {
146                while (fgets(s, 120, f)) {
147                        char *algo, *name, *type, *all;
148                        int len_algo, len_name, len_type;
149                        int i2cbus;
150
151                        algo = strrchr(s, '\t');
152                        *(algo++) = '\0';
153                        len_algo = rtrim(algo);
154
155                        name = strrchr(s, '\t');
156                        *(name++) = '\0';
157                        len_name = rtrim(name);
158
159                        type = strrchr(s, '\t');
160                        *(type++) = '\0';
161                        len_type = rtrim(type);
162
163                        sscanf(s, "i2c-%d", &i2cbus);
164
165                        if ((count + 1) % BUNCH == 0) {
166                                /* We need more space */
167                                adapters = more_adapters(adapters, count + 1);
168                                if (!adapters)
169                                        return NULL;
170                        }
171
172                        all = malloc(len_name + len_type + len_algo);
173                        if (all == NULL) {
174                                free_adapters(adapters);
175                                return NULL;
176                        }
177                        adapters[count].nr = i2cbus;
178                        adapters[count].name = strcpy(all, name);
179                        adapters[count].funcs = strcpy(all + len_name, type);
180                        adapters[count].algo = strcpy(all + len_name + len_type,
181                                                      algo);
182                        count++;
183                }
184                fclose(f);
185                goto done;
186        }
187
188        /* look in sysfs */
189        /* First figure out where sysfs was mounted */
190        if ((f = fopen("/proc/mounts", "r")) == NULL) {
191                goto done;
192        }
193        while (fgets(n, NAME_MAX, f)) {
194                sscanf(n, "%*[^ ] %[^ ] %[^ ] %*s\n", sysfs, fstype);
195                if (strcasecmp(fstype, "sysfs") == 0) {
196                        foundsysfs++;
197                        break;
198                }
199        }
200        fclose(f);
201        if (! foundsysfs) {
202                goto done;
203        }
204
205        /* Bus numbers in i2c-adapter don't necessarily match those in
206           i2c-dev and what we really care about are the i2c-dev numbers.
207           Unfortunately the names are harder to get in i2c-dev */
208        strcat(sysfs, "/class/i2c-dev");
209        if(!(dir = opendir(sysfs)))
210                goto done;
211        /* go through the busses */
212        while ((de = readdir(dir)) != NULL) {
213                if (!strcmp(de->d_name, "."))
214                        continue;
215                if (!strcmp(de->d_name, ".."))
216                        continue;
217
218                /* this should work for kernels 2.6.5 or higher and */
219                /* is preferred because is unambiguous */
220                sprintf(n, "%s/%s/name", sysfs, de->d_name);
221                f = fopen(n, "r");
222                /* this seems to work for ISA */
223                if(f == NULL) {
224                        sprintf(n, "%s/%s/device/name", sysfs, de->d_name);
225                        f = fopen(n, "r");
226                }
227                /* non-ISA is much harder */
228                /* and this won't find the correct bus name if a driver
229                   has more than one bus */
230                if(f == NULL) {
231                        sprintf(n, "%s/%s/device", sysfs, de->d_name);
232                        if(!(ddir = opendir(n)))
233                                continue;
234                        while ((dde = readdir(ddir)) != NULL) {
235                                if (!strcmp(dde->d_name, "."))
236                                        continue;
237                                if (!strcmp(dde->d_name, ".."))
238                                        continue;
239                                if ((!strncmp(dde->d_name, "i2c-", 4))) {
240                                        sprintf(n, "%s/%s/device/%s/name",
241                                                sysfs, de->d_name, dde->d_name);
242                                        if((f = fopen(n, "r")))
243                                                goto found;
244                                }
245                        }
246                }
247
248found:
249                if (f != NULL) {
250                        int i2cbus;
251                        enum adt type;
252                        char *px;
253
254                        px = fgets(s, 120, f);
255                        fclose(f);
256                        if (!px) {
257                                fprintf(stderr, "%s: read error\n", n);
258                                continue;
259                        }
260                        if ((px = strchr(s, '\n')) != NULL)
261                                *px = 0;
262                        if (!sscanf(de->d_name, "i2c-%d", &i2cbus))
263                                continue;
264                        if (!strncmp(s, "ISA ", 4)) {
265                                type = adt_isa;
266                        } else {
267                                /* Attempt to probe for adapter capabilities */
268                                type = i2c_get_funcs(i2cbus);
269                        }
270
271                        if ((count + 1) % BUNCH == 0) {
272                                /* We need more space */
273                                adapters = more_adapters(adapters, count + 1);
274                                if (!adapters)
275                                        return NULL;
276                        }
277
278                        adapters[count].nr = i2cbus;
279                        adapters[count].name = strdup(s);
280                        if (adapters[count].name == NULL) {
281                                free_adapters(adapters);
282                                return NULL;
283                        }
284                        adapters[count].funcs = adap_types[type].funcs;
285                        adapters[count].algo = adap_types[type].algo;
286                        count++;
287                }
288        }
289        closedir(dir);
290
291done:
292        return adapters;
293}
294
295static int lookup_i2c_bus_by_name(const char *bus_name)
296{
297        struct i2c_adap *adapters;
298        int i, i2cbus = -1;
299
300        adapters = gather_i2c_busses();
301        if (adapters == NULL) {
302                fprintf(stderr, "Error: Out of memory!\n");
303                return -3;
304        }
305
306        /* Walk the list of i2c busses, looking for the one with the
307           right name */
308        for (i = 0; adapters[i].name; i++) {
309                if (strcmp(adapters[i].name, bus_name) == 0) {
310                        if (i2cbus >= 0) {
311                                fprintf(stderr,
312                                        "Error: I2C bus name is not unique!\n");
313                                i2cbus = -4;
314                                goto done;
315                        }
316                        i2cbus = adapters[i].nr;
317                }
318        }
319
320        if (i2cbus == -1)
321                fprintf(stderr, "Error: I2C bus name doesn't match any "
322                        "bus present!\n");
323
324done:
325        free_adapters(adapters);
326        return i2cbus;
327}
328
329/*
330 * Parse an I2CBUS command line argument and return the corresponding
331 * bus number, or a negative value if the bus is invalid.
332 */
333int lookup_i2c_bus(const char *i2cbus_arg)
334{
335        unsigned long i2cbus;
336        char *end;
337
338        i2cbus = strtoul(i2cbus_arg, &end, 0);
339        if (*end || !*i2cbus_arg) {
340                /* Not a number, maybe a name? */
341                return lookup_i2c_bus_by_name(i2cbus_arg);
342        }
343        if (i2cbus > INT_MAX) {
344                fprintf(stderr, "Error: I2C bus out of range!\n");
345                return -2;
346        }
347
348        return i2cbus;
349}
350
351/*
352 * Parse a CHIP-ADDRESS command line argument and return the corresponding
353 * chip address, or a negative value if the address is invalid.
354 */
355int parse_i2c_address(const char *address_arg)
356{
357        long address;
358        char *end;
359
360        address = strtol(address_arg, &end, 0);
361        if (*end || !*address_arg) {
362                fprintf(stderr, "Error: Chip address is not a number!\n");
363                return -1;
364        }
365        if (address < 0x03 || address > 0x77) {
366                fprintf(stderr, "Error: Chip address out of range "
367                        "(0x03-0x77)!\n");
368                return -2;
369        }
370
371        return address;
372}
373
374int open_i2c_dev(const int i2cbus, char *filename, const int quiet)
375{
376        int file;
377
378        sprintf(filename, "/dev/i2c/%d", i2cbus);
379        file = open(filename, O_RDWR);
380
381        if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
382                sprintf(filename, "/dev/i2c-%d", i2cbus);
383                file = open(filename, O_RDWR);
384        }
385
386        if (file < 0 && !quiet) {
387                if (errno == ENOENT) {
388                        fprintf(stderr, "Error: Could not open file "
389                                "`/dev/i2c-%d' or `/dev/i2c/%d': %s\n",
390                                i2cbus, i2cbus, strerror(ENOENT));
391                } else {
392                        fprintf(stderr, "Error: Could not open file "
393                                "`%s': %s\n", filename, strerror(errno));
394                        if (errno == EACCES)
395                                fprintf(stderr, "Run as root?\n");
396                }
397        }
398
399        return file;
400}
401
402int set_slave_addr(int file, int address, int force)
403{
404        /* With force, let the user read from/write to the registers
405           even when a driver is also running */
406        if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) {
407                fprintf(stderr,
408                        "Error: Could not set address to 0x%02x: %s\n",
409                        address, strerror(errno));
410                return -errno;
411        }
412
413        return 0;
414}
Note: See TracBrowser for help on using the browser.