ichthyic-passthrough.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // License: CC0 (https://creativecommons.org/publicdomain/zero/1.0/)
  2. //
  3. // To the extent possible under law, all copyright and related or neighboring rights are waived
  4. // on this file.
  5. // This work is published from: United States.
  6. //
  7. // Example usage:
  8. //
  9. // socat -d -d pty,raw,echo=0,link=/tmp/ttyPIUich pty,raw,echo=0,link=/tmp/ttyPIU
  10. // ./ichthyic-passthrough -i /tmp/ttyPIUich -I /dev/i2c-1
  11. //
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <sys/types.h>
  16. #include <sys/stat.h>
  17. #include <fcntl.h>
  18. #include <sys/ioctl.h>
  19. #include <unistd.h>
  20. #include <time.h>
  21. #include <sys/time.h>
  22. #include <linux/i2c-dev.h>
  23. #include <i2c/smbus.h>
  24. #include <errno.h>
  25. #include <getopt.h>
  26. #include <stdint.h>
  27. #define ICHTHYIC_MAX_BUF 256
  28. #define ICHTHYIC_VERSION "0.1.0"
  29. #define ICHTHYIC_DEFAULT_I2C_ADDR 0x08
  30. int g_verbose = 0;
  31. struct option g_long_option[] = {
  32. {"pty", required_argument, 0, 'i'},
  33. {"i2c", required_argument, 0, 'I'},
  34. {"log", required_argument, 0, 'L'},
  35. {"verbose", no_argument, 0, 'v'},
  36. {"version", no_argument, 0, 'V'},
  37. {"help", no_argument, 0, 'h'},
  38. {0, 0, 0, 0}
  39. };
  40. typedef struct ichthyic_type {
  41. uint8_t *i2c_fn;
  42. int i2c_adapter;
  43. int i2c_worker_addr;
  44. int i2c_fd;
  45. uint32_t i2c_from_buf_n;
  46. uint8_t *i2c_from_buf;
  47. uint32_t i2c_to_buf_n;
  48. uint8_t *i2c_to_buf;
  49. } ichthyic_t;
  50. void ichthyic_init(ichthyic_t *ich) {
  51. ich->i2c_fn = (uint8_t *)malloc(sizeof(uint8_t)*ICHTHYIC_MAX_BUF);
  52. ich->i2c_from_buf = (uint8_t *)malloc(sizeof(uint8_t)*ICHTHYIC_MAX_BUF);
  53. ich->i2c_to_buf = (uint8_t *)malloc(sizeof(uint8_t)*ICHTHYIC_MAX_BUF);
  54. ich->i2c_from_buf[0] = '\0';
  55. ich->i2c_fn[0] = '\0';
  56. ich->i2c_adapter = -1;
  57. ich->i2c_worker_addr = -1;
  58. ich->i2c_fd = -1;
  59. ich->i2c_to_buf_n = 0;
  60. ich->i2c_to_buf[0] = '\0';
  61. ich->i2c_from_buf_n = 0;
  62. ich->i2c_from_buf[0] = '\0';
  63. }
  64. void ichthyic_clear(ichthyic_t *ich) {
  65. if (ich) {
  66. if (ich->i2c_fn) { free(ich->i2c_fn); }
  67. if (ich->i2c_from_buf) { free(ich->i2c_from_buf); }
  68. if (ich->i2c_to_buf) { free(ich->i2c_to_buf); }
  69. }
  70. }
  71. void ichthyic_free(ichthyic_t *ich) {
  72. ichthyic_clear(ich);
  73. if (ich) { free(ich); }
  74. }
  75. ichthyic_t *ichthyic_alloc() {
  76. ichthyic_t *ich;
  77. ich = malloc(sizeof(ichthyic_t));
  78. ichthyic_init(ich);
  79. return ich;
  80. }
  81. int ichthyic_i2c_appendbuf(ichthyic_t *ich, uint8_t *buf, uint32_t nbuf) {
  82. int i, p;
  83. p = ich->i2c_to_buf_n;
  84. for (i=0, p=ich->i2c_to_buf_n; (i<nbuf) && (p<(ICHTHYIC_MAX_BUF-1)); i++, p++) {
  85. ich->i2c_to_buf[p] = buf[i];
  86. }
  87. ich->i2c_to_buf[p] = '\0';
  88. ich->i2c_to_buf_n = p;
  89. }
  90. int ichthyic_i2c_queuebuf(ichthyic_t *ich, uint8_t *buf, uint32_t nbuf) {
  91. int i;
  92. for (i=0; i<nbuf; i++) {
  93. ich->i2c_to_buf[i] = buf[i];
  94. }
  95. ich->i2c_to_buf[nbuf] = '\0';
  96. ich->i2c_to_buf_n = nbuf;
  97. }
  98. int ichthyic_i2c_read(ichthyic_t *ich) {
  99. int32_t res, dn=0;
  100. res = i2c_smbus_read_byte(ich->i2c_fd);
  101. if (res<0) { return -1; }
  102. if (res==0) { return 0; }
  103. dn=1;
  104. while (ich->i2c_from_buf_n < (ICHTHYIC_MAX_BUF-1)) {
  105. ich->i2c_from_buf[ich->i2c_from_buf_n] = res;
  106. ich->i2c_from_buf_n++;
  107. ich->i2c_from_buf[ich->i2c_from_buf_n] = '\0';
  108. res = i2c_smbus_read_byte(ich->i2c_fd);
  109. if (res<0) { return -1; }
  110. if (res==0) { break; }
  111. dn++;
  112. }
  113. return dn;
  114. }
  115. int ichthyic_i2c_write(ichthyic_t *ich) {
  116. int i;
  117. int32_t res;
  118. for (i=0; ich->i2c_to_buf[i]; i++) {
  119. res = i2c_smbus_write_byte(ich->i2c_fd, ich->i2c_to_buf[i]);
  120. }
  121. ich->i2c_to_buf_n = 0;
  122. ich->i2c_to_buf[0] = '\0';
  123. }
  124. int ichthyic_i2c_writebuf(ichthyic_t *ich, uint8_t *buf, uint32_t nbuf) {
  125. uint32_t i;
  126. int32_t res;
  127. for (i=0; i<nbuf; i++) {
  128. res = i2c_smbus_write_byte(ich->i2c_fd, buf[i]);
  129. }
  130. }
  131. void show_version(FILE *fp) {
  132. fprintf(fp, "ichthyic-passthrough version: %s\n", ICHTHYIC_VERSION);
  133. }
  134. void show_help(FILE *fp, char *i2c_fn, char *pty_fn) {
  135. show_version(fp);
  136. fprintf(fp, "\nusage:\n\n ichthyic-passthrough [-i pty] [-I i2c] [-L logfn] [-h] [-v] [-V]\n\n");
  137. fprintf(fp, " [-D] use defaults (i2c:%s, pty:%s)\n", i2c_fn, pty_fn);
  138. fprintf(fp, " [-i pty] pty (--pty)\n");
  139. fprintf(fp, " [-I i2c] I2C device interface (--i2c)\n");
  140. fprintf(fp, " [-L logfn] log file (--log)\n");
  141. fprintf(fp, " [-v] verbosity level (can be used multiple times to increase verbosity) (--verbose)\n");
  142. fprintf(fp, " [-V] print out version and exit (--version)\n");
  143. fprintf(fp, " [-h] help (this screen) (--help)\n");
  144. fprintf(fp, "\n");
  145. }
  146. void _print_buf(FILE *fp, char *buf, int buf_n) {
  147. int i;
  148. for (i=0; i<buf_n; i++) {
  149. if (buf[i] == '\n') { fprintf(fp, "`"); }
  150. else if (buf[i] == '\r') { fprintf(fp, "~"); }
  151. else if ((buf[i] >= ' ') &&
  152. (buf[i] <= '~')) {
  153. fprintf(fp,"%c", buf[i]);
  154. }
  155. else { fprintf(fp, "<%x>", buf[i]); }
  156. }
  157. fprintf(fp, "\n");
  158. fflush(fp);
  159. }
  160. int main(int argc, char **argv) {
  161. int i, ret, fd, adapter_nr, ch, dn;
  162. int opt_idx;
  163. char fn[32];
  164. int worker_addr = 0x08;
  165. struct timespec _sleepy, _sleepy_rem;
  166. char *pty_fn;
  167. int pty_fd=-1, pty_fd_flag = 0;
  168. int _n=0, _buf_n=32, _s=0;
  169. char _buf[32];
  170. FILE *log_fp = stdout;
  171. char *log_fn;
  172. int _ok = 0;
  173. ichthyic_t ich;
  174. pty_fn = (char *)malloc(sizeof(char)*ICHTHYIC_MAX_BUF);
  175. log_fn = (char *)malloc(sizeof(char)*ICHTHYIC_MAX_BUF);
  176. ichthyic_init(&ich);
  177. ich.i2c_worker_addr = ICHTHYIC_DEFAULT_I2C_ADDR;
  178. ich.i2c_adapter = 1;
  179. snprintf(ich.i2c_fn, 31, "/dev/i2c-%i", ich.i2c_adapter);
  180. g_verbose = 0;
  181. pty_fn[0] = '\0';
  182. log_fn[0] = '\0';
  183. while ((ch = getopt_long(argc, argv, "hi:I:L:vVD", g_long_option, &opt_idx)) >= 0) {
  184. switch(ch) {
  185. case 0:
  186. break;
  187. case 'D':
  188. _ok = 1;
  189. break;
  190. case 'i':
  191. strncpy(pty_fn, optarg, ICHTHYIC_MAX_BUF-1);
  192. pty_fn[ICHTHYIC_MAX_BUF-1] = '\0';
  193. _ok=1;
  194. break;
  195. case 'I':
  196. strncpy(ich.i2c_fn, optarg, ICHTHYIC_MAX_BUF-1);
  197. ich.i2c_fn[ICHTHYIC_MAX_BUF-1] = '\0';
  198. _ok=1;
  199. break;
  200. case 'L':
  201. strncpy(log_fn, optarg, ICHTHYIC_MAX_BUF-1);
  202. log_fn[ICHTHYIC_MAX_BUF-1] = '\0';
  203. break;
  204. case 'v':
  205. g_verbose++;
  206. _ok=1;
  207. break;
  208. case 'V':
  209. show_version(stdout);
  210. exit(0);
  211. case 'h':
  212. show_help(stdout, ich.i2c_fn, pty_fn);
  213. exit(0);
  214. default:
  215. show_help(stderr, ich.i2c_fn, pty_fn);
  216. exit(-1);
  217. break;
  218. }
  219. }
  220. if (_ok==0) {
  221. show_help(stderr, ich.i2c_fn, pty_fn);
  222. exit(-1);
  223. }
  224. if ((g_verbose > 0) && (strlen(log_fn) > 0)) {
  225. log_fp = fopen(log_fn, "w");
  226. if (!log_fp) {
  227. perror(log_fn);
  228. exit(-1);
  229. }
  230. }
  231. // 1000000000 ns in 1 sec
  232. //
  233. _sleepy.tv_sec = 0;
  234. _sleepy.tv_nsec = 1000000;
  235. // stdin
  236. //
  237. if (strlen((const char *)pty_fn) == 0) {
  238. pty_fd = 0;
  239. }
  240. else {
  241. pty_fd = open(pty_fn, O_RDWR);
  242. if (pty_fd < 0) {
  243. perror(pty_fn);
  244. exit(-1);
  245. }
  246. }
  247. pty_fd_flag = fcntl(pty_fd, F_GETFL, 0);
  248. fcntl(pty_fd, F_SETFL, pty_fd_flag | O_NONBLOCK);
  249. ich.i2c_fd = open(ich.i2c_fn, O_RDWR);
  250. if (ich.i2c_fd < 0) {
  251. perror(ich.i2c_fn);
  252. exit(-1);
  253. }
  254. if (ioctl(ich.i2c_fd, I2C_SLAVE, ich.i2c_worker_addr) < 0) {
  255. perror(ich.i2c_fn);
  256. exit(-2);
  257. }
  258. while (1) {
  259. _n = read(pty_fd, _buf, _buf_n);
  260. if (_n<0) {
  261. // pty_fd is non blocking, so only non-EAGAIN
  262. // errors are real
  263. //
  264. if (errno != EAGAIN) {
  265. perror("stdin:");
  266. }
  267. }
  268. // A 0-byte read means the file descriptor has been closed
  269. //
  270. else if (_n==0) {
  271. if (g_verbose>0) {
  272. fprintf(log_fp, "# input closed, cleaning up\n");
  273. fflush(log_fp);
  274. }
  275. break;
  276. }
  277. // Otherwise we have actual data that we want to append
  278. // to our buffer
  279. //
  280. if (_n>0) {
  281. if (g_verbose > 0) {
  282. fprintf(log_fp, "#[%i]:", _n);
  283. for (i=0; i<_n; i++) { fprintf(log_fp, "%c", _buf[i]); }
  284. fprintf(log_fp, "\n");
  285. fflush(log_fp);
  286. }
  287. ichthyic_i2c_appendbuf(&ich, _buf, _n);
  288. }
  289. // Write any data to our I2C Line
  290. //
  291. ichthyic_i2c_write(&ich);
  292. // And read any I2C data that might be ready
  293. //
  294. ret = ichthyic_i2c_read(&ich);
  295. if (ret<0) { perror("i2c read"); }
  296. else if (ret > 0) {
  297. if (g_verbose > 1) {
  298. fprintf(log_fp, "## read +%i (%i,%i) i2c bytes\n", ret, ret, ich.i2c_from_buf_n);
  299. fflush(log_fp);
  300. }
  301. }
  302. if (ich.i2c_from_buf_n > 0) {
  303. // If our buffer has a lf or cr at the end, flush it
  304. //
  305. if ((ich.i2c_from_buf[ ich.i2c_from_buf_n-1 ] == '\r') ||
  306. (ich.i2c_from_buf[ ich.i2c_from_buf_n-1 ] == '\n')) {
  307. _n = ich.i2c_from_buf_n;
  308. _s = 0;
  309. do {
  310. dn = write(pty_fd, ich.i2c_from_buf + _s, _n);
  311. if (dn<0) {
  312. perror("write error");
  313. exit(-1);
  314. }
  315. _n -= dn;
  316. _s += dn;
  317. } while (_n > 0);
  318. if (g_verbose > 0) {
  319. fprintf(log_fp, "# [%i]>:", ich.i2c_from_buf_n);
  320. _print_buf(log_fp, ich.i2c_from_buf, ich.i2c_from_buf_n);
  321. }
  322. ich.i2c_from_buf[0] = '\0';
  323. ich.i2c_from_buf_n = 0;
  324. }
  325. // If we've hit the maximum size of our buffer, flush it regardless
  326. //
  327. else if (ich.i2c_from_buf_n >= (ICHTHYIC_MAX_BUF-1)) {
  328. _n = ich.i2c_from_buf_n;
  329. _s = 0;
  330. do {
  331. dn = write(pty_fd, ich.i2c_from_buf + _s, _n);
  332. if (dn<0) {
  333. perror("write error while flushing");
  334. exit(-1);
  335. }
  336. _n -= dn;
  337. _s += dn;
  338. } while (_n > 0);
  339. if (g_verbose > 0) {
  340. fprintf(log_fp, "# i2c_from_buf_n (%i) == %i, flushing: ", ich.i2c_from_buf_n, ICHTHYIC_MAX_BUF);
  341. _print_buf(log_fp, ich.i2c_from_buf, ich.i2c_from_buf_n);
  342. }
  343. ich.i2c_from_buf[0] = '\0';
  344. ich.i2c_from_buf_n = 0;
  345. }
  346. }
  347. nanosleep(&_sleepy, &_sleepy_rem);
  348. }
  349. close(ich.i2c_fd);
  350. ichthyic_clear(&ich);
  351. if (log_fp != stdout) { fclose(log_fp); }
  352. }