blob: 90e88183034183eb75ae0a1b52cf7e0776f0a596 [file] [log] [blame]
Harald Weltec12d52b2009-02-01 21:39:06 +00001/* OpenBSC BS-11 T-Link interface using POSIX serial port */
2
3/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
4 *
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 */
22
23#include <unistd.h>
24#include <stdlib.h>
25#include <stdio.h>
26#include <errno.h>
27#include <string.h>
28#include <termios.h>
29#include <fcntl.h>
30
31#include <openbsc/select.h>
32#include <openbsc/msgb.h>
33#include <openbsc/debug.h>
34#include <openbsc/gsm_data.h>
35
36/* adaption layer from GSM 08.59 + 12.21 to RS232 */
37
38struct serial_handle {
39 struct bsc_fd fd;
40 struct llist_head tx_queue;
41
42 struct msgb *rx_msg;
43 unsigned int rxmsg_bytes_missing;
44
45 unsigned int delay_ms;
46};
47
48/* FIXME: this needs to go */
49static struct serial_handle _ser_handle, *ser_handle = &_ser_handle;
50
51#define LAPD_HDR_LEN 10
52
53/* callback from abis_nm */
54int _abis_nm_sendmsg(struct msgb *msg)
55{
56 struct serial_handle *sh = ser_handle;
57 u_int8_t *lapd;
58 unsigned int len;
59
60 msg->l2h = msg->data;
61
62 /* prepend LAPD header */
63 lapd = msgb_push(msg, LAPD_HDR_LEN);
64
65 len = msg->len - 2;
66
67 lapd[0] = (len >> 8) & 0xff;
68 lapd[1] = len & 0xff; /* length of bytes startign at lapd[2] */
69 lapd[2] = 0x00;
70 lapd[3] = 0x07;
71 lapd[4] = 0x01;
72 lapd[5] = 0x3e;
73 lapd[6] = 0x00;
74 lapd[7] = 0x00;
75 lapd[8] = msg->len - 10; /* length of bytes starting at lapd[10] */
76 lapd[9] = lapd[8] ^ 0x38;
77
78 msgb_enqueue(&sh->tx_queue, msg);
79 sh->fd.when |= BSC_FD_WRITE;
80
81 return 0;
82}
83
84/* select.c callback in case we can write to the RS232 */
85static int handle_ser_write(struct bsc_fd *bfd)
86{
87 struct serial_handle *sh = bfd->data;
88 struct msgb *msg;
89 int written;
90
91 msg = msgb_dequeue(&sh->tx_queue);
92 if (!msg) {
93 bfd->when &= ~BSC_FD_WRITE;
94 return 0;
95 }
96
97 if (debug_mask & DMI) {
98 fprintf(stdout, "RS232 TX: ");
99 hexdump(msg->data, msg->len);
100 }
101
102 /* send over serial line */
103 written = write(bfd->fd, msg->data, msg->len);
104 if (written < msg->len) {
105 perror("short write:");
106 msgb_free(msg);
107 return -1;
108 }
109
110 msgb_free(msg);
111 usleep(sh->delay_ms*1000);
112
113 return 0;
114}
115
116#define SERIAL_ALLOC_SIZE 300
117
118/* select.c callback in case we can read from the RS232 */
119static int handle_ser_read(struct bsc_fd *bfd)
120{
121 struct serial_handle *sh = bfd->data;
122 struct msgb *msg;
123 int rc = 0;
124
125 if (!sh->rx_msg) {
126 sh->rx_msg = msgb_alloc(SERIAL_ALLOC_SIZE);
127 sh->rx_msg->l2h = NULL;
128 }
129 msg = sh->rx_msg;
130
131 /* first read two byes to obtain length */
132 if (msg->len < 2) {
133 rc = read(sh->fd.fd, msg->tail, 2 - msg->len);
134 if (rc < 0) {
135 perror("ERROR reading from serial port");
136 msgb_free(msg);
137 return rc;
138 }
139 msgb_put(msg, rc);
140
141 if (msg->len >= 2) {
142 /* parse LAPD payload length */
143 if (msg->data[0] != 0)
144 fprintf(stderr, "Suspicious header byte 0: 0x%02x\n",
145 msg->data[0]);
146
147 sh->rxmsg_bytes_missing = msg->data[0] << 8;
148 sh->rxmsg_bytes_missing += msg->data[1];
149
150 if (sh->rxmsg_bytes_missing < LAPD_HDR_LEN -2)
151 fprintf(stderr, "Invalid length in hdr: %u\n",
152 sh->rxmsg_bytes_missing);
153 }
154 } else {
155 /* try to read as many of the missing bytes as are available */
156 rc = read(sh->fd.fd, msg->tail, sh->rxmsg_bytes_missing);
157 if (rc < 0) {
158 perror("ERROR reading from serial port");
159 msgb_free(msg);
160 return rc;
161 }
162 msgb_put(msg, rc);
163 sh->rxmsg_bytes_missing -= rc;
164
165 if (sh->rxmsg_bytes_missing == 0) {
166 /* we have one complete message now */
167 sh->rx_msg = NULL;
168
169 if (msg->len > LAPD_HDR_LEN)
170 msg->l2h = msg->data + LAPD_HDR_LEN;
171
172 if (debug_mask & DMI) {
173 fprintf(stdout, "RS232 RX: ");
174 hexdump(msg->data, msg->len);
175 }
176 rc = handle_serial_msg(msg);
177 }
178 }
179
180 return rc;
181}
182
183/* select.c callback */
184static int serial_fd_cb(struct bsc_fd *bfd, unsigned int what)
185{
186 int rc = 0;
187
188 if (what & BSC_FD_READ)
189 rc = handle_ser_read(bfd);
190
191 if (rc < 0)
192 return rc;
193
194 if (what & BSC_FD_WRITE)
195 rc = handle_ser_write(bfd);
196
197 return rc;
198}
199
200int rs232_setup(const char *serial_port, unsigned int delay_ms)
201{
202 int rc, serial_fd;
203 struct termios tio;
204
205 serial_fd = open(serial_port, O_RDWR);
206 if (serial_fd < 0) {
207 perror("cannot open serial port:");
208 return serial_fd;
209 }
210
211 /* set baudrate */
212 rc = tcgetattr(serial_fd, &tio);
213 if (rc < 0) {
214 perror("tcgetattr()");
215 return rc;
216 }
217 cfsetispeed(&tio, B19200);
218 cfsetospeed(&tio, B19200);
219 tio.c_cflag |= (CREAD | CLOCAL | CS8);
220 tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
221 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
222 tio.c_iflag |= (INPCK | ISTRIP);
223 tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
224 tio.c_oflag &= ~(OPOST);
225 rc = tcsetattr(serial_fd, TCSADRAIN, &tio);
226 if (rc < 0) {
227 perror("tcsetattr()");
228 return rc;
229 }
230
231 INIT_LLIST_HEAD(&ser_handle->tx_queue);
232 ser_handle->fd.fd = serial_fd;
233 ser_handle->fd.when = BSC_FD_READ;
234 ser_handle->fd.cb = serial_fd_cb;
235 ser_handle->fd.data = ser_handle;
236 ser_handle->delay_ms = delay_ms;
237 rc = bsc_register_fd(&ser_handle->fd);
238 if (rc < 0) {
239 fprintf(stderr, "could not register FD: %s\n",
240 strerror(rc));
241 return rc;
242 }
243
244 return 0;
245}