blob: 2fd2a09ba10fba752ab9c132ac2a40ed5efd8fcf [file] [log] [blame]
Pablo Neira Ayuso7e0d0062011-08-19 11:36:15 +02001/* T-Link interface using POSIX serial port */
2
3/* (C) 2008-2011 by Harald Welte <laforge@gnumonks.org>
4 *
5 * All Rights Reserved
6 *
7 * Authors: Harald Welte <laforge@gnumonks.org>
8 * Pablo Neira Ayuso <pablo@gnumonks.org>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
19 *
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#include <unistd.h>
26#include <stdlib.h>
27#include <stdio.h>
28#include <errno.h>
29#include <string.h>
30#include <termios.h>
31#include <fcntl.h>
32
33#include <osmocom/core/select.h>
34#include <osmocom/core/msgb.h>
35#include <osmocom/core/logging.h>
36#include <osmocom/core/talloc.h>
37#include <osmocom/abis/e1_input.h>
38
39static void *tall_rs232_ctx;
40
41struct serial_handle {
42 struct e1inp_line *line;
43
44 struct msgb *rx_msg;
45 unsigned int rxmsg_bytes_missing;
46
47 unsigned int delay_ms;
48};
49
50#define CRAPD_HDR_LEN 10
51
52static int handle_ser_write(struct osmo_fd *bfd);
53
54static void rs232_build_msg(struct msgb *msg)
55{
56 uint8_t *crapd;
57 unsigned int len;
58
59 msg->l2h = msg->data;
60
61 /* prepend CRAPD header */
62 crapd = msgb_push(msg, CRAPD_HDR_LEN);
63
64 len = msg->len - 2;
65
66 crapd[0] = (len >> 8) & 0xff;
67 crapd[1] = len & 0xff; /* length of bytes startign at crapd[2] */
68 crapd[2] = 0x00;
69 crapd[3] = 0x07;
70 crapd[4] = 0x01;
71 crapd[5] = 0x3e;
72 crapd[6] = 0x00;
73 crapd[7] = 0x00;
74 crapd[8] = msg->len - 10; /* length of bytes starting at crapd[10] */
75 crapd[9] = crapd[8] ^ 0x38;
76}
77
78/* select.c callback in case we can write to the rs232 */
79static int handle_ser_write(struct osmo_fd *bfd)
80{
81 struct serial_handle *sh = bfd->data;
82 struct e1inp_ts *e1i_ts = &sh->line->ts[0];
83 struct e1inp_sign_link *sign_link;
84 struct msgb *msg;
85 int written;
86
87 bfd->when &= ~BSC_FD_WRITE;
88
89 /* get the next msg for this timeslot */
90 msg = e1inp_tx_ts(e1i_ts, &sign_link);
91 if (!msg) {
92 /* no message after tx delay timer */
93 return 0;
94 }
95 DEBUGP(DLMI, "rs232 TX: %s\n", osmo_hexdump(msg->data, msg->len));
96
97 rs232_build_msg(msg);
98
99 /* send over serial line */
100 written = write(bfd->fd, msg->data, msg->len);
101 if (written < msg->len) {
102 LOGP(DLMI, LOGL_ERROR, "rs232: short write\n");
103 msgb_free(msg);
104 return -1;
105 }
106
107 msgb_free(msg);
108 usleep(sh->delay_ms*1000);
109
110 return 0;
111}
112
113#define SERIAL_ALLOC_SIZE 300
114
115/* select.c callback in case we can read from the rs232 */
116static int handle_ser_read(struct osmo_fd *bfd)
117{
118 struct serial_handle *sh = bfd->data;
119 struct msgb *msg;
120 int rc = 0;
121
122 if (!sh->rx_msg) {
123 sh->rx_msg = msgb_alloc(SERIAL_ALLOC_SIZE, "rs232 Rx");
124 sh->rx_msg->l2h = NULL;
125 }
126 msg = sh->rx_msg;
127
128 /* first read two byes to obtain length */
129 if (msg->len < 2) {
130 rc = read(bfd->fd, msg->tail, 2 - msg->len);
131 if (rc < 0) {
132 LOGP(DLMI, LOGL_ERROR, "rs232: error reading from "
133 "serial port: %s\n", strerror(errno));
134 msgb_free(msg);
135 return rc;
136 }
137 msgb_put(msg, rc);
138
139 if (msg->len >= 2) {
140 /* parse CRAPD payload length */
141 if (msg->data[0] != 0) {
142 LOGP(DLMI, LOGL_ERROR,
143 "Suspicious header byte 0: 0x%02x\n",
144 msg->data[0]);
145 }
146 sh->rxmsg_bytes_missing = msg->data[0] << 8;
147 sh->rxmsg_bytes_missing += msg->data[1];
148
149 if (sh->rxmsg_bytes_missing < CRAPD_HDR_LEN -2) {
150 LOGP(DLMI, LOGL_ERROR,
151 "Invalid length in hdr: %u\n",
152 sh->rxmsg_bytes_missing);
153 }
154 }
155 } else {
156 /* try to read as many of the missing bytes as are available */
157 rc = read(bfd->fd, msg->tail, sh->rxmsg_bytes_missing);
158 if (rc < 0) {
159 LOGP(DLMI, LOGL_ERROR, "rs232: error reading from "
160 "serial port: %s", strerror(errno));
161 msgb_free(msg);
162 return rc;
163 }
164 msgb_put(msg, rc);
165 sh->rxmsg_bytes_missing -= rc;
166
167 if (sh->rxmsg_bytes_missing == 0) {
168 struct e1inp_ts *e1i_ts = &sh->line->ts[0];
169
170 /* we have one complete message now */
171 sh->rx_msg = NULL;
172
173 if (msg->len > CRAPD_HDR_LEN)
174 msg->l2h = msg->data + CRAPD_HDR_LEN;
175
176 DEBUGP(DLMI, "rs232 RX: %s",
177 osmo_hexdump(msg->data, msg->len));
178
179 /* don't use e1inp_tx_ts() here, this header does not
180 * contain any SAPI and TEI values. */
181 if (!e1i_ts->line->ops->sign_link) {
182 LOGP(DLMI, LOGL_ERROR, "rs232: no callback set, "
183 "skipping message.\n");
184 return -EINVAL;
185 }
186 e1i_ts->line->ops->sign_link(msg);
187 }
188 }
189
190 return rc;
191}
192
193/* select.c callback */
194static int serial_fd_cb(struct osmo_fd *bfd, unsigned int what)
195{
196 int rc = 0;
197
198 if (what & BSC_FD_READ)
199 rc = handle_ser_read(bfd);
200
201 if (rc < 0)
202 return rc;
203
204 if (what & BSC_FD_WRITE)
205 rc = handle_ser_write(bfd);
206
207 return rc;
208}
209
210static int rs232_want_write(struct e1inp_ts *e1i_ts)
211{
212 e1i_ts->driver.rs232.fd.when |= BSC_FD_WRITE;
213
214 return 0;
215}
216
217static int
218rs232_setup(struct e1inp_line *line, const char *serial_port, unsigned int delay_ms)
219{
220 int rc;
221 struct osmo_fd *bfd = &line->ts[0].driver.rs232.fd;
222 struct serial_handle *ser_handle;
223 struct termios tio;
224
225 rc = open(serial_port, O_RDWR);
226 if (rc < 0) {
227 LOGP(DLMI, LOGL_ERROR, "rs232: cannot open serial port: %s",
228 strerror(errno));
229 return rc;
230 }
231 bfd->fd = rc;
232
233 /* set baudrate */
234 rc = tcgetattr(bfd->fd, &tio);
235 if (rc < 0) {
236 LOGP(DLMI, LOGL_ERROR, "rs232: tcgetattr says: %s",
237 strerror(errno));
238 return rc;
239 }
240 cfsetispeed(&tio, B19200);
241 cfsetospeed(&tio, B19200);
242 tio.c_cflag |= (CREAD | CLOCAL | CS8);
243 tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
244 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
245 tio.c_iflag |= (INPCK | ISTRIP);
246 tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
247 tio.c_oflag &= ~(OPOST);
248 rc = tcsetattr(bfd->fd, TCSADRAIN, &tio);
249 if (rc < 0) {
250 LOGP(DLMI, LOGL_ERROR, "rs232: tcsetattr says: %s",
251 strerror(errno));
252 return rc;
253 }
254
255 ser_handle = talloc_zero(tall_rs232_ctx, struct serial_handle);
256 if (ser_handle == NULL) {
257 close(bfd->fd);
258 LOGP(DLMI, LOGL_ERROR, "rs232: cannot allocate memory for "
259 "serial handler\n");
260 return -ENOMEM;
261 }
262 ser_handle->line = line;
263 ser_handle->delay_ms = delay_ms;
264
265 bfd->when = BSC_FD_READ;
266 bfd->cb = serial_fd_cb;
267 bfd->data = ser_handle;
268
269 rc = osmo_fd_register(bfd);
270 if (rc < 0) {
271 close(bfd->fd);
272 LOGP(DLMI, LOGL_ERROR, "rs232: could not register FD: %s\n",
273 strerror(rc));
274 return rc;
275 }
276
277 return 0;
278}
279
Pablo Neira Ayuso4e862cb2011-08-19 18:43:38 +0200280static int rs232_line_update(struct e1inp_line *line);
Pablo Neira Ayuso7e0d0062011-08-19 11:36:15 +0200281
282static struct e1inp_driver rs232_driver = {
283 .name = "rs232",
284 .want_write = rs232_want_write,
285 .line_update = rs232_line_update,
286};
287
Pablo Neira Ayuso4e862cb2011-08-19 18:43:38 +0200288static int rs232_line_update(struct e1inp_line *line)
Pablo Neira Ayuso7e0d0062011-08-19 11:36:15 +0200289{
290 if (line->driver != &rs232_driver)
291 return -EINVAL;
292
Pablo Neira Ayuso4e862cb2011-08-19 18:43:38 +0200293 return rs232_setup(line, line->ops->cfg.rs232.port,
294 line->ops->cfg.rs232.delay);
Pablo Neira Ayuso7e0d0062011-08-19 11:36:15 +0200295}
296
297int e1inp_rs232_init(void)
298{
299 return e1inp_driver_register(&rs232_driver);
300}