]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/i2c/i2c.c
MFV r304060:
[FreeBSD/FreeBSD.git] / usr.sbin / i2c / i2c.c
1 /*-
2  * Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <err.h>
31 #include <errno.h>
32 #include <sysexits.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdarg.h>
38 #include <unistd.h>
39 #include <sys/ioctl.h>
40
41 #include <dev/iicbus/iic.h>
42
43 #define I2C_DEV                 "/dev/iic0"
44 #define I2C_MODE_NOTSET         0
45 #define I2C_MODE_NONE           1
46 #define I2C_MODE_STOP_START     2
47 #define I2C_MODE_REPEATED_START 3
48
49 struct options {
50         int     width;
51         int     count;
52         int     verbose;
53         int     addr_set;
54         int     binary;
55         int     scan;
56         int     skip;
57         int     reset;
58         int     mode;
59         char    dir;
60         uint32_t        addr;
61         uint32_t        off;
62 };
63
64 struct skip_range {
65         int     start;
66         int     end;
67 };
68
69 __dead2 static void
70 usage(void)
71 {
72
73         fprintf(stderr, "usage: %s -a addr [-f device] [-d [r|w]] [-o offset] "
74             "[-w [0|8|16]] [-c count] [-m [ss|rs|no]] [-b] [-v]\n",
75             getprogname());
76         fprintf(stderr, "       %s -s [-f device] [-n skip_addr] -v\n",
77             getprogname());
78         fprintf(stderr, "       %s -r [-f device] -v\n", getprogname());
79         exit(EX_USAGE);
80 }
81
82 static struct skip_range
83 skip_get_range(char *skip_addr)
84 {
85         struct skip_range addr_range;
86         char *token;
87
88         addr_range.start = 0;
89         addr_range.end = 0;
90
91         token = strsep(&skip_addr, "..");
92         if (token) {
93                 addr_range.start = strtoul(token, 0, 16);
94                 token = strsep(&skip_addr, "..");
95                 if ((token != NULL) && !atoi(token)) {
96                         token = strsep(&skip_addr, "..");
97                         if (token)
98                                 addr_range.end = strtoul(token, 0, 16);
99                 }
100         }
101
102         return (addr_range);
103 }
104
105 /* Parse the string to get hex 7 bits addresses */
106 static int
107 skip_get_tokens(char *skip_addr, int *sk_addr, int max_index)
108 {
109         char *token;
110         int i;
111
112         for (i = 0; i < max_index; i++) {
113                 token = strsep(&skip_addr, ":");
114                 if (token == NULL)
115                         break;
116                 sk_addr[i] = strtoul(token, 0, 16);
117         }
118         return (i);
119 }
120
121 static int
122 scan_bus(struct iiccmd cmd, char *dev, int skip, char *skip_addr)
123 {
124         struct skip_range addr_range = { 0, 0 };
125         int *tokens, fd, error, i, index, j;
126         int len = 0, do_skip = 0, no_range = 1;
127
128         fd = open(dev, O_RDWR);
129         if (fd == -1) {
130                 fprintf(stderr, "Error opening I2C controller (%s) for "
131                     "scanning: %s\n", dev, strerror(errno));
132                 return (EX_NOINPUT);
133         }
134
135         if (skip) {
136                 len = strlen(skip_addr);
137                 if (strstr(skip_addr, "..") != NULL) {
138                         addr_range = skip_get_range(skip_addr);
139                         no_range = 0;
140                 } else {
141                         tokens = (int *)malloc((len / 2 + 1) * sizeof(int));
142                         if (tokens == NULL) {
143                                 fprintf(stderr, "Error allocating tokens "
144                                     "buffer\n");
145                                 error = -1;
146                                 goto out;
147                         }
148                         index = skip_get_tokens(skip_addr, tokens,
149                             len / 2 + 1);
150                 }
151
152                 if (!no_range && (addr_range.start > addr_range.end)) {
153                         fprintf(stderr, "Skip address out of range\n");
154                         error = -1;
155                         goto out;
156                 }
157         }
158
159         printf("Scanning I2C devices on %s: ", dev);
160         for (i = 1; i < 127; i++) {
161
162                 if (skip && ( addr_range.start < addr_range.end)) {
163                         if (i >= addr_range.start && i <= addr_range.end)
164                                 continue;
165
166                 } else if (skip && no_range)
167                         for (j = 0; j < index; j++) {
168                                 if (tokens[j] == i) {
169                                         do_skip = 1;
170                                         break;
171                                 }
172                         }
173
174                 if (do_skip) {
175                         do_skip = 0;
176                         continue;
177                 }
178
179                 cmd.slave = i << 1;
180                 cmd.last = 1;
181                 cmd.count = 0;
182                 error = ioctl(fd, I2CRSTCARD, &cmd);
183                 if (error)
184                         goto out;
185
186                 cmd.slave = i << 1;
187                 cmd.last = 1;
188                 error = ioctl(fd, I2CSTART, &cmd);
189                 if (!error)
190                         printf("%x ", i);
191                 cmd.slave = i << 1;
192                 cmd.last = 1;
193                 error = ioctl(fd, I2CSTOP, &cmd);
194         }
195         printf("\n");
196
197         error = ioctl(fd, I2CRSTCARD, &cmd);
198 out:
199         close(fd);
200         if (skip && no_range)
201                 free(tokens);
202
203         if (error) {
204                 fprintf(stderr, "Error scanning I2C controller (%s): %s\n",
205                     dev, strerror(errno));
206                 return (EX_NOINPUT);
207         } else
208                 return (EX_OK);
209 }
210
211 static int
212 reset_bus(struct iiccmd cmd, char *dev)
213 {
214         int fd, error;
215
216         fd = open(dev, O_RDWR);
217         if (fd == -1) {
218                 fprintf(stderr, "Error opening I2C controller (%s) for "
219                     "resetting: %s\n", dev, strerror(errno));
220                 return (EX_NOINPUT);
221         }
222
223         printf("Resetting I2C controller on %s: ", dev);
224         error = ioctl(fd, I2CRSTCARD, &cmd);
225         close (fd);
226
227         if (error) {
228                 printf("error: %s\n", strerror(errno));
229                 return (EX_IOERR);
230         } else {
231                 printf("OK\n");
232                 return (EX_OK);
233         }
234 }
235
236 static char *
237 prepare_buf(int size, uint32_t off)
238 {
239         char *buf;
240
241         buf = malloc(size);
242         if (buf == NULL)
243                 return (buf);
244
245         if (size == 1)
246                 buf[0] = off & 0xff;
247         else if (size == 2) {
248                 buf[0] = (off >> 8) & 0xff;
249                 buf[1] = off & 0xff;
250         }
251
252         return (buf);
253 }
254
255 static int
256 i2c_write(char *dev, struct options i2c_opt, char *i2c_buf)
257 {
258         struct iiccmd cmd;
259         int ch, i, error, fd, bufsize;
260         char *err_msg, *buf;
261
262         /*
263          * Read data to be written to the chip from stdin
264          */
265         if (i2c_opt.verbose && !i2c_opt.binary)
266                 fprintf(stderr, "Enter %u bytes of data: ", i2c_opt.count);
267
268         for (i = 0; i < i2c_opt.count; i++) {
269                 ch = getchar();
270                 if (ch == EOF) {
271                         free(i2c_buf);
272                         err(1, "not enough data, exiting\n");
273                 }
274                 i2c_buf[i] = ch;
275         }
276
277         fd = open(dev, O_RDWR);
278         if (fd == -1) {
279                 free(i2c_buf);
280                 err(1, "open failed");
281         }
282
283         cmd.slave = i2c_opt.addr;
284         error = ioctl(fd, I2CSTART, &cmd);
285         if (error == -1) {
286                 err_msg = "ioctl: error sending start condition";
287                 goto err1;
288         }
289
290         if (i2c_opt.width) {
291                 bufsize = i2c_opt.width / 8;
292                 buf = prepare_buf(bufsize, i2c_opt.off);
293                 if (buf == NULL) {
294                         err_msg = "error: offset malloc";
295                         goto err1;
296                 }
297         } else {
298                 bufsize = 0;
299                 buf = NULL;
300         }
301
302         switch(i2c_opt.mode) {
303         case I2C_MODE_STOP_START:
304                 /*
305                  * Write offset where the data will go
306                  */
307                 if (i2c_opt.width) {
308                         cmd.count = bufsize;
309                         cmd.buf = buf;
310                         error = ioctl(fd, I2CWRITE, &cmd);
311                         free(buf);
312                         if (error == -1) {
313                                 err_msg = "ioctl: error when write offset";
314                                 goto err1;
315                         }
316                 }
317
318                 error = ioctl(fd, I2CSTOP, &cmd);
319                 if (error == -1) {
320                         err_msg = "ioctl: error sending stop condition";
321                         goto err2;
322                 }
323                 cmd.slave = i2c_opt.addr;
324                 error = ioctl(fd, I2CSTART, &cmd);
325                 if (error == -1) {
326                         err_msg = "ioctl: error sending start condition";
327                         goto err1;
328                 }
329
330                 /*
331                  * Write the data
332                  */
333                 cmd.count = i2c_opt.count;
334                 cmd.buf = i2c_buf;
335                 cmd.last = 0;
336                 error = ioctl(fd, I2CWRITE, &cmd);
337                 if (error == -1) {
338                         err_msg = "ioctl: error when write";
339                         goto err1;
340                 }
341                 break;
342
343         case I2C_MODE_REPEATED_START:
344                 /*
345                  * Write offset where the data will go
346                  */
347                 if (i2c_opt.width) {
348                         cmd.count = bufsize;
349                         cmd.buf = buf;
350                         error = ioctl(fd, I2CWRITE, &cmd);
351                         free(buf);
352                         if (error == -1) {
353                                 err_msg = "ioctl: error when write offset";
354                                 goto err1;
355                         }
356                 }
357
358                 cmd.slave = i2c_opt.addr;
359                 error = ioctl(fd, I2CRPTSTART, &cmd);
360                 if (error == -1) {
361                         err_msg = "ioctl: error sending repeated start "
362                             "condition";
363                         goto err1;
364                 }
365
366                 /*
367                  * Write the data
368                  */
369                 cmd.count = i2c_opt.count;
370                 cmd.buf = i2c_buf;
371                 cmd.last = 0;
372                 error = ioctl(fd, I2CWRITE, &cmd);
373                 if (error == -1) {
374                         err_msg = "ioctl: error when write";
375                         goto err1;
376                 }
377                 break;
378
379         case I2C_MODE_NONE: /* fall through */
380         default:                
381                 buf = realloc(buf, bufsize + i2c_opt.count);
382                 if (buf == NULL) {
383                         err_msg = "error: data malloc";
384                         goto err1;
385                 }
386
387                 memcpy(buf + bufsize, i2c_buf, i2c_opt.count);
388                 /*
389                  * Write offset and data
390                  */
391                 cmd.count = bufsize + i2c_opt.count;
392                 cmd.buf = buf;
393                 cmd.last = 0;
394                 error = ioctl(fd, I2CWRITE, &cmd);
395                 free(buf);
396                 if (error == -1) {
397                         err_msg = "ioctl: error when write";
398                         goto err1;
399                 }
400                 break;
401         }
402         cmd.slave = i2c_opt.addr;
403         error = ioctl(fd, I2CSTOP, &cmd);
404         if (error == -1) {
405                 err_msg = "ioctl: error sending stop condition";
406                 goto err2;
407         }
408
409         close(fd);
410         return (0);
411
412 err1:
413         cmd.slave = i2c_opt.addr;
414         error = ioctl(fd, I2CSTOP, &cmd);
415         if (error == -1)
416                 fprintf(stderr, "error sending stop condtion\n");
417 err2:
418         if (err_msg)
419                 fprintf(stderr, "%s", err_msg);
420
421         close(fd);
422         return (1);
423 }
424
425 static int
426 i2c_read(char *dev, struct options i2c_opt, char *i2c_buf)
427 {
428         struct iiccmd cmd;
429         int i, fd, error, bufsize;
430         char *err_msg, data = 0, *buf;
431
432         fd = open(dev, O_RDWR);
433         if (fd == -1)
434                 err(1, "open failed");
435
436         bzero(&cmd, sizeof(cmd));
437
438         if (i2c_opt.width) {
439                 cmd.slave = i2c_opt.addr;
440                 cmd.count = 1;
441                 cmd.last = 0;
442                 cmd.buf = &data;
443                 error = ioctl(fd, I2CSTART, &cmd);
444                 if (error == -1) {
445                         err_msg = "ioctl: error sending start condition";
446                         goto err1;
447                 }
448                 bufsize = i2c_opt.width / 8;
449                 buf = prepare_buf(bufsize, i2c_opt.off);
450                 if (buf == NULL) {
451                         err_msg = "error: offset malloc";
452                         goto err1;
453                 }
454
455                 cmd.count = bufsize;
456                 cmd.buf = buf;
457                 cmd.last = 0;
458                 error = ioctl(fd, I2CWRITE, &cmd);
459                 free(buf);
460                 if (error == -1) {
461                         err_msg = "ioctl: error when write offset";
462                         goto err1;
463                 }
464
465                 if (i2c_opt.mode == I2C_MODE_STOP_START) {
466                         cmd.slave = i2c_opt.addr;
467                         error = ioctl(fd, I2CSTOP, &cmd);
468                         if (error == -1) {
469                                 err_msg = "error sending stop condtion\n";
470                                 goto err2;
471                         }
472                 }
473         }
474         cmd.slave = i2c_opt.addr;
475         cmd.count = 1;
476         cmd.last = 0;
477         cmd.buf = &data;
478         if (i2c_opt.mode == I2C_MODE_STOP_START) {
479                 error = ioctl(fd, I2CSTART, &cmd);
480                 if (error == -1) {
481                         err_msg = "ioctl: error sending start condition";
482                         goto err1;
483                 }
484         } else if (i2c_opt.mode == I2C_MODE_REPEATED_START) {
485                 error = ioctl(fd, I2CRPTSTART, &cmd);
486                 if (error == -1) {
487                         err_msg = "ioctl: error sending repeated start "
488                             "condition";
489                         goto err1;
490                 }
491         }
492         error = ioctl(fd, I2CSTOP, &cmd);
493         if (error == -1) {
494                 err_msg = "error sending stop condtion\n";
495                 goto err2;
496         }
497
498         for (i = 0; i < i2c_opt.count; i++) {
499                 error = read(fd, &i2c_buf[i], 1);
500                 if (error == -1) {
501                         err_msg = "ioctl: error while reading";
502                         goto err1;
503                 }
504         }
505
506         close(fd);
507         return (0);
508
509 err1:
510         cmd.slave = i2c_opt.addr;
511         error = ioctl(fd, I2CSTOP, &cmd);
512         if (error == -1)
513                 fprintf(stderr, "error sending stop condtion\n");
514 err2:
515         if (err_msg)
516                 fprintf(stderr, "%s", err_msg);
517
518         close(fd);
519         return (1);
520 }
521
522 int
523 main(int argc, char** argv)
524 {
525         struct iiccmd cmd;
526         struct options i2c_opt;
527         char *dev, *skip_addr, *i2c_buf;
528         int error, chunk_size, i, j, ch;
529
530         errno = 0;
531         error = 0;
532
533         /* Line-break the output every chunk_size bytes */
534         chunk_size = 16;
535
536         dev = I2C_DEV;
537
538         /* Default values */
539         i2c_opt.addr_set = 0;
540         i2c_opt.off = 0;
541         i2c_opt.verbose = 0;
542         i2c_opt.dir = 'r';      /* direction = read */
543         i2c_opt.width = 8;
544         i2c_opt.count = 1;
545         i2c_opt.binary = 0;     /* ASCII text output */
546         i2c_opt.scan = 0;       /* no bus scan */
547         i2c_opt.skip = 0;       /* scan all addresses */
548         i2c_opt.reset = 0;      /* no bus reset */
549         i2c_opt.mode = I2C_MODE_NOTSET;
550
551         while ((ch = getopt(argc, argv, "a:f:d:o:w:c:m:n:sbvrh")) != -1) {
552                 switch(ch) {
553                 case 'a':
554                         i2c_opt.addr = (strtoul(optarg, 0, 16) << 1);
555                         if (i2c_opt.addr == 0 && errno == EINVAL)
556                                 i2c_opt.addr_set = 0;
557                         else
558                                 i2c_opt.addr_set = 1;
559                         break;
560                 case 'f':
561                         dev = optarg;
562                         break;
563                 case 'd':
564                         i2c_opt.dir = optarg[0];
565                         break;
566                 case 'o':
567                         i2c_opt.off = strtoul(optarg, 0, 16);
568                         if (i2c_opt.off == 0 && errno == EINVAL)
569                                 error = 1;
570                         break;
571                 case 'w':
572                         i2c_opt.width = atoi(optarg);
573                         break;
574                 case 'c':
575                         i2c_opt.count = atoi(optarg);
576                         break;
577                 case 'm':
578                         if (!strcmp(optarg, "no"))
579                                 i2c_opt.mode = I2C_MODE_NONE;
580                         else if (!strcmp(optarg, "ss"))
581                                 i2c_opt.mode = I2C_MODE_STOP_START;
582                         else if (!strcmp(optarg, "rs"))
583                                 i2c_opt.mode = I2C_MODE_REPEATED_START;
584                         else
585                                 usage();
586                         break;
587                 case 'n':
588                         i2c_opt.skip = 1;
589                         skip_addr = optarg;
590                         break;
591                 case 's':
592                         i2c_opt.scan = 1;
593                         break;
594                 case 'b':
595                         i2c_opt.binary = 1;
596                         break;
597                 case 'v':
598                         i2c_opt.verbose = 1;
599                         break;
600                 case 'r':
601                         i2c_opt.reset = 1;
602                         break;
603                 case 'h':
604                 default:
605                         usage();
606                 }
607         }
608         argc -= optind;
609         argv += optind;
610
611         /* Set default mode if option -m is not specified */
612         if (i2c_opt.mode == I2C_MODE_NOTSET) {
613                 if (i2c_opt.dir == 'r')
614                         i2c_opt.mode = I2C_MODE_STOP_START;
615                 else if (i2c_opt.dir == 'w')
616                         i2c_opt.mode = I2C_MODE_NONE;
617         }
618
619         /* Basic sanity check of command line arguments */
620         if (i2c_opt.scan) {
621                 if (i2c_opt.addr_set)
622                         usage();
623         } else if (i2c_opt.reset) {
624                 if (i2c_opt.addr_set)
625                         usage();
626         } else if (error) {
627                 usage();
628         } else if ((i2c_opt.dir == 'r' || i2c_opt.dir == 'w')) {
629                 if ((i2c_opt.addr_set == 0) ||
630                     !(i2c_opt.width == 0 || i2c_opt.width == 8 ||
631                     i2c_opt.width == 16))
632                 usage();
633         }
634
635         if (i2c_opt.verbose)
636                 fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, "
637                     "offset: 0x%02x, width: %u, count: %u\n", dev,
638                     i2c_opt.addr >> 1, i2c_opt.dir, i2c_opt.off,
639                     i2c_opt.width, i2c_opt.count);
640
641         if (i2c_opt.scan)
642                 exit(scan_bus(cmd, dev, i2c_opt.skip, skip_addr));
643
644         if (i2c_opt.reset)
645                 exit(reset_bus(cmd, dev));
646
647         i2c_buf = malloc(i2c_opt.count);
648         if (i2c_buf == NULL)
649                 err(1, "data malloc");
650
651         if (i2c_opt.dir == 'w') {
652                 error = i2c_write(dev, i2c_opt, i2c_buf);
653                 if (error) {
654                         free(i2c_buf);
655                         return (1);
656                 }
657         }
658         if (i2c_opt.dir == 'r') {
659                 error = i2c_read(dev, i2c_opt, i2c_buf);
660                 if (error) {
661                         free(i2c_buf);
662                         return (1);
663                 }
664         }
665
666         if (i2c_opt.verbose)
667                 fprintf(stderr, "\nData %s (hex):\n", i2c_opt.dir == 'r' ?
668                     "read" : "written");
669
670         i = 0;
671         j = 0;
672         while (i < i2c_opt.count) {
673                 if (i2c_opt.verbose || (i2c_opt.dir == 'r' &&
674                     !i2c_opt.binary))
675                         fprintf (stderr, "%02hhx ", i2c_buf[i++]);
676
677                 if (i2c_opt.dir == 'r' && i2c_opt.binary) {
678                         fprintf(stdout, "%c", i2c_buf[j++]);
679                         if(!i2c_opt.verbose)
680                                 i++;
681                 }
682                 if (!i2c_opt.verbose && (i2c_opt.dir == 'w'))
683                         break;
684                 if ((i % chunk_size) == 0)
685                         fprintf(stderr, "\n");
686         }
687         if ((i % chunk_size) != 0)
688                 fprintf(stderr, "\n");
689
690         free(i2c_buf);
691         return (0);
692 }