blob: 0c8863ee7849984cd0b6f2689f41085954876189 [file] [log] [blame]
Todd Brochd4b00c12011-02-08 16:57:33 -08001// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <assert.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <ftdi.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <sys/types.h>
12#include <sys/stat.h>
13#include <termios.h>
14#include <unistd.h>
15#include <usb.h>
16
17#include "ftdi_common.h"
18#include "ftdiuart.h"
19
20// TODO(tbroch) Where are these protos and under what conditions do they
21// exist across lin/mac/win
22int grantpt(int fd);
23int unlockpt(int fd);
24int ptsname_r(int fd, char *buf, size_t buflen);
25int posix_openpt(int flags);
26
27#ifdef DARWIN
28int ptsname_r(int fd, char *buf, size_t buflen) {
Todd Broche2dfb362011-02-23 20:48:54 -080029 char *name = ptsname(fd);
30 if (name == NULL) {
31 errno = EINVAL;
Todd Brochd4b00c12011-02-08 16:57:33 -080032 return -1;
Todd Brochd4b00c12011-02-08 16:57:33 -080033 }
Todd Broche2dfb362011-02-23 20:48:54 -080034 if (strlen(name) + 1 > buflen) {
35 errno = ERANGE;
36 return -1;
37 }
38 strncpy(buf, name, buflen);
39 return 0;
Todd Brochd4b00c12011-02-08 16:57:33 -080040}
41#endif
42
43int fuart_init(struct fuart_context *fuartc, struct ftdi_context *fc) {
44 assert(fuartc);
45 memset(fuartc, 0, sizeof(*fuartc));
46
47 fuartc->fc = fc;
48 // init all inputs
49 fuartc->gpio.direction = 0;
50 fuartc->gpio.value = 0;
51 // TODO(tbroch) NO OOB signal support (CTS,RTS,DCR,DTR)
52 fuartc->gpio.mask = ~(TX_POS | RX_POS);
53
54 fuartc->error = FUART_ERR_NONE;
55 return FUART_ERR_NONE;
56}
57
58int fuart_open(struct fuart_context *fuartc,
59 struct ftdi_common_args *fargs) {
60 int rv;
61
62 struct ftdi_context *fc = fuartc->fc;
Todd Broche2dfb362011-02-23 20:48:54 -080063 assert(fc);
Todd Brochd4b00c12011-02-08 16:57:33 -080064
Todd Broche2dfb362011-02-23 20:48:54 -080065 ftdi_set_interface(fc, fargs->interface);
Todd Brochd4b00c12011-02-08 16:57:33 -080066 if (!IS_FTDI_OPEN(fc)) {
67 rv = ftdi_usb_open(fc, fargs->vendor_id, fargs->product_id);
68 if (rv < 0) {
69 ERROR_FTDI("Opening usb connection", fc);
70 return FUART_ERR_FTDI;
71 }
72 }
Todd Brochd4b00c12011-02-08 16:57:33 -080073 if (fcom_num_interfaces(fc) > 1) {
74 if ((rv = ftdi_set_interface(fc, fargs->interface))) {
75 ERROR_FTDI("setting interface", fc);
76 return FUART_ERR_FTDI;
77 }
78 }
79 CHECK_FTDI(ftdi_set_bitmode(fc, TX_POS, BITMODE_RESET),
80 "uart mode", fc);
81
82 // TODO(tbroch) Do some checking of reasonable cfg/buadrate
83 CHECK_FTDI(ftdi_set_line_property(fc, fargs->bits, fargs->sbits,
84 fargs->parity), "line props", fc);
85 CHECK_FTDI(ftdi_set_baudrate(fc, fargs->speed), "baudrate", fc);
86
87 if (fc->type == TYPE_R) {
88 int gpio_cfg = fargs->direction<<4 | fargs->value;
89 CHECK_FTDI(ftdi_set_bitmode(fc, gpio_cfg, BITMODE_CBUS),
90 "uart mode", fc);
91 }
92
93 int fd;
94 if ((fd = posix_openpt(O_RDWR | O_NOCTTY)) == -1) {
95 perror("opening pty master");
96 return FUART_ERR_OPEN;
97 }
98 if (grantpt(fd) == -1) {
99 perror("grantpt");
100 return FUART_ERR_OPEN;
101 }
102 if (unlockpt(fd) == -1) {
103 perror("unlockpt");
104 return FUART_ERR_OPEN;
105 }
106 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
107 perror("fcntl setfl -> nonblock");
108 return FUART_ERR_OPEN;
109 }
110 if (ptsname_r(fd, fuartc->name, PATH_MAX) != 0) {
111 perror("getting name of pty");
112 return FUART_ERR_OPEN;
113 }
Todd Broche2dfb362011-02-23 20:48:54 -0800114 prn_dbg("pty name = %s\n", fuartc->name);
Todd Brochd4b00c12011-02-08 16:57:33 -0800115 if (!isatty(fd)) {
116 prn_error("Not a TTY device.\n");
117 return FUART_ERR_OPEN;
118 }
119 struct termios tty_cfg;
120 cfmakeraw(&tty_cfg);
121 tcsetattr(fd, TCSANOW, &tty_cfg);
122
123 fuartc->fd = fd;
124 return FUART_ERR_NONE;
125}
126
127int fuart_wr_rd(struct fuart_context *fuartc, int nsecs) {
128
129 int rv;
130 int bytes;
131 struct ftdi_context *fc = fuartc->fc;
132 if ((bytes = read(fuartc->fd, fuartc->buf, sizeof(fuartc->buf))) > 0) {
133#ifdef DEBUG
134 fuartc->buf[bytes] = '\0';
135 printf("about to write %d bytes to ftdi %s\n", bytes, fuartc->buf);
136#endif
137 rv = ftdi_write_data(fc, fuartc->buf, bytes);
Todd Brochd4b00c12011-02-08 16:57:33 -0800138 if (rv != bytes) {
139 ERROR_FTDI("writing to uart", fc);
140 return FUART_ERR_WR;
141 }
142 }
143 usleep(nsecs);
144 // TODO(tbroch) is there a lower cost way to interrogate ftdi for data. How
145 // does the event/error_char factor into things?
146 bytes = ftdi_read_data(fc, fuartc->buf, sizeof(fuartc->buf));
147 if (bytes > 0) {
148 int bytes_remaining = bytes;
149 while ((bytes = write(fuartc->fd, fuartc->buf, bytes_remaining)) > 0) {
150 bytes_remaining -= bytes;
151 }
152 if (bytes == -1) {
153 perror("writing ftdi data to pty");
154 }
155
156 } else if (bytes < 0) {
157 perror("failed ftdi_read_data");
158 ERROR_FTDI("reading ftdi data", fuartc->fc);
159 return FUART_ERR_RD;
160 }
161 // TODO(tbroch) How do we guarantee no data loss for tx/rx
162 return FUART_ERR_NONE;
163}
164
165int fuart_close(struct fuart_context *fuartc) {
166 int rv = FUART_ERR_NONE;
167 close(fuartc->fd);
168 CHECK_FTDI(ftdi_usb_close(fuartc->fc), "fuart close", fuartc->fc);
169 ftdi_deinit(fuartc->fc);
170 return rv;
171}
172