| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
#define _BSD_SOURCE 1 |
|---|
| 27 |
|
|---|
| 28 |
#include <sys/types.h> |
|---|
| 29 |
#include <sys/stat.h> |
|---|
| 30 |
#include <sys/param.h> |
|---|
| 31 |
#include <string.h> |
|---|
| 32 |
#include <strings.h> |
|---|
| 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 |
|
|---|
| 43 |
enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown }; |
|---|
| 44 |
|
|---|
| 45 |
struct adap_type { |
|---|
| 46 |
const char *funcs; |
|---|
| 47 |
const char* algo; |
|---|
| 48 |
}; |
|---|
| 49 |
|
|---|
| 50 |
static 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 |
|
|---|
| 63 |
static 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 |
|
|---|
| 90 |
|
|---|
| 91 |
static 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 |
|
|---|
| 100 |
void 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 |
|
|---|
| 110 |
|
|---|
| 111 |
|
|---|
| 112 |
#define BUNCH 8 |
|---|
| 113 |
|
|---|
| 114 |
|
|---|
| 115 |
static 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 |
|
|---|
| 129 |
struct 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 |
|
|---|
| 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 |
|
|---|
| 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 |
|
|---|
| 189 |
|
|---|
| 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 |
|
|---|
| 206 |
|
|---|
| 207 |
|
|---|
| 208 |
strcat(sysfs, "/class/i2c-dev"); |
|---|
| 209 |
if(!(dir = opendir(sysfs))) |
|---|
| 210 |
goto done; |
|---|
| 211 |
|
|---|
| 212 |
while ((de = readdir(dir)) != NULL) { |
|---|
| 213 |
if (!strcmp(de->d_name, ".")) |
|---|
| 214 |
continue; |
|---|
| 215 |
if (!strcmp(de->d_name, "..")) |
|---|
| 216 |
continue; |
|---|
| 217 |
|
|---|
| 218 |
|
|---|
| 219 |
|
|---|
| 220 |
sprintf(n, "%s/%s/name", sysfs, de->d_name); |
|---|
| 221 |
f = fopen(n, "r"); |
|---|
| 222 |
|
|---|
| 223 |
if(f == NULL) { |
|---|
| 224 |
sprintf(n, "%s/%s/device/name", sysfs, de->d_name); |
|---|
| 225 |
f = fopen(n, "r"); |
|---|
| 226 |
} |
|---|
| 227 |
|
|---|
| 228 |
|
|---|
| 229 |
|
|---|
| 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 |
|
|---|
| 248 |
found: |
|---|
| 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 |
|
|---|
| 268 |
type = i2c_get_funcs(i2cbus); |
|---|
| 269 |
} |
|---|
| 270 |
|
|---|
| 271 |
if ((count + 1) % BUNCH == 0) { |
|---|
| 272 |
|
|---|
| 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 |
|
|---|
| 291 |
done: |
|---|
| 292 |
return adapters; |
|---|
| 293 |
} |
|---|
| 294 |
|
|---|
| 295 |
static 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 |
|
|---|
| 307 |
|
|---|
| 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 |
|
|---|
| 324 |
done: |
|---|
| 325 |
free_adapters(adapters); |
|---|
| 326 |
return i2cbus; |
|---|
| 327 |
} |
|---|
| 328 |
|
|---|
| 329 |
|
|---|
| 330 |
|
|---|
| 331 |
|
|---|
| 332 |
|
|---|
| 333 |
int lookup_i2c_bus(const char *i2cbus_arg) |
|---|
| 334 |
{ |
|---|
| 335 |
long i2cbus; |
|---|
| 336 |
char *end; |
|---|
| 337 |
|
|---|
| 338 |
i2cbus = strtol(i2cbus_arg, &end, 0); |
|---|
| 339 |
if (*end || !*i2cbus_arg) { |
|---|
| 340 |
|
|---|
| 341 |
return lookup_i2c_bus_by_name(i2cbus_arg); |
|---|
| 342 |
} |
|---|
| 343 |
if (i2cbus < 0 || i2cbus > 0xff) { |
|---|
| 344 |
fprintf(stderr, "Error: I2C bus out of range (0-255)!\n"); |
|---|
| 345 |
return -2; |
|---|
| 346 |
} |
|---|
| 347 |
|
|---|
| 348 |
return i2cbus; |
|---|
| 349 |
} |
|---|
| 350 |
|
|---|
| 351 |
|
|---|
| 352 |
|
|---|
| 353 |
|
|---|
| 354 |
|
|---|
| 355 |
int 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 |
|
|---|
| 374 |
int 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) { |
|---|
| 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 |
|
|---|
| 402 |
int set_slave_addr(int file, int address, int force) |
|---|
| 403 |
{ |
|---|
| 404 |
|
|---|
| 405 |
|
|---|
| 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 |
} |
|---|