source: mainline/uspace/srv/net/slip/slip.c@ 417a2ba1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 417a2ba1 was 417a2ba1, checked in by Jiri Svoboda <jiri@…>, 12 years ago

iplink_ev_recv() should use ip_ver_t instead of AF.

  • Property mode set to 100644
File size: 9.6 KB
Line 
1/*
2 * Copyright (c) 2013 Jakub Jermar
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 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup slip
30 * @{
31 */
32/**
33 * @file
34 * @brief IP over serial line IP link provider.
35 */
36
37#include <stdio.h>
38#include <stdint.h>
39#include <loc.h>
40#include <inet/addr.h>
41#include <inet/iplink_srv.h>
42#include <device/char_dev.h>
43#include <io/log.h>
44#include <errno.h>
45
46#define NAME "slip"
47#define CAT_IPLINK "iplink"
48
49#define SLIP_MTU 1006 /* as per RFC 1055 */
50
51#define SLIP_END 0300
52#define SLIP_ESC 0333
53#define SLIP_ESC_END 0334
54#define SLIP_ESC_ESC 0335
55
56static int slip_open(iplink_srv_t *);
57static int slip_close(iplink_srv_t *);
58static int slip_send(iplink_srv_t *, iplink_sdu_t *);
59static int slip_send6(iplink_srv_t *, iplink_sdu6_t *);
60static int slip_get_mtu(iplink_srv_t *, size_t *);
61static int slip_get_mac48(iplink_srv_t *, addr48_t *);
62static int slip_addr_add(iplink_srv_t *, inet_addr_t *);
63static int slip_addr_remove(iplink_srv_t *, inet_addr_t *);
64
65static iplink_srv_t slip_iplink;
66
67static iplink_ops_t slip_iplink_ops = {
68 .open = slip_open,
69 .close = slip_close,
70 .send = slip_send,
71 .send6 = slip_send6,
72 .get_mtu = slip_get_mtu,
73 .get_mac48 = slip_get_mac48,
74 .addr_add = slip_addr_add,
75 .addr_remove = slip_addr_remove
76};
77
78static uint8_t slip_send_buf[SLIP_MTU + 2];
79static size_t slip_send_pending;
80
81static uint8_t slip_recv_buf[SLIP_MTU + 2];
82static size_t slip_recv_pending;
83static size_t slip_recv_read;
84
85int slip_open(iplink_srv_t *srv)
86{
87 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_open()");
88 return EOK;
89}
90
91int slip_close(iplink_srv_t *srv)
92{
93 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_close()");
94 return EOK;
95}
96
97static void write_flush(async_sess_t *sess)
98{
99 size_t written = 0;
100
101 while (slip_send_pending > 0) {
102 ssize_t size;
103
104 size = char_dev_write(sess, &slip_send_buf[written],
105 slip_send_pending);
106 if (size < 0) {
107 log_msg(LOG_DEFAULT, LVL_ERROR,
108 "char_dev_write() returned %d",
109 (int) size);
110 slip_send_pending = 0;
111 break;
112 }
113 written += size;
114 slip_send_pending -= size;
115 }
116}
117
118static void write_buffered(async_sess_t *sess, uint8_t ch)
119{
120 if (slip_send_pending == sizeof(slip_send_buf))
121 write_flush(sess);
122 slip_send_buf[slip_send_pending++] = ch;
123}
124
125int slip_send(iplink_srv_t *srv, iplink_sdu_t *sdu)
126{
127 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_send()");
128
129 async_sess_t *sess = (async_sess_t *) srv->arg;
130 uint8_t *data = sdu->data;
131
132 /*
133 * Strictly speaking, this is not prescribed by the RFC, but the RFC
134 * suggests to start with sending a SLIP_END byte as a synchronization
135 * measure for dealing with previous possible noise on the line.
136 */
137 write_buffered(sess, SLIP_END);
138
139 for (size_t i = 0; i < sdu->size; i++) {
140 switch (data[i]) {
141 case SLIP_END:
142 write_buffered(sess, SLIP_ESC);
143 write_buffered(sess, SLIP_ESC_END);
144 break;
145 case SLIP_ESC:
146 write_buffered(sess, SLIP_ESC);
147 write_buffered(sess, SLIP_ESC_ESC);
148 break;
149 default:
150 write_buffered(sess, data[i]);
151 break;
152 }
153 }
154
155 write_buffered(sess, SLIP_END);
156 write_flush(sess);
157
158 return EOK;
159}
160
161int slip_send6(iplink_srv_t *srv, iplink_sdu6_t *sdu)
162{
163 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_send6()");
164
165 return ENOTSUP;
166}
167
168int slip_get_mtu(iplink_srv_t *srv, size_t *mtu)
169{
170 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_get_mtu()");
171 *mtu = SLIP_MTU;
172 return EOK;
173}
174
175int slip_get_mac48(iplink_srv_t *src, addr48_t *mac)
176{
177 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_get_mac48()");
178 return ENOTSUP;
179}
180
181int slip_addr_add(iplink_srv_t *srv, inet_addr_t *addr)
182{
183 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_addr_add()");
184 return EOK;
185}
186
187int slip_addr_remove(iplink_srv_t *srv, inet_addr_t *addr)
188{
189 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_addr_remove()");
190 return EOK;
191}
192
193static void usage(void)
194{
195 printf("Usage: " NAME " <service-name> <link-name>\n");
196}
197
198static void slip_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
199{
200 log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_client_conn()");
201 iplink_conn(iid, icall, &slip_iplink);
202}
203
204static uint8_t read_buffered(async_sess_t *sess)
205{
206 while (slip_recv_pending == 0) {
207 ssize_t size;
208
209 size = char_dev_read(sess, slip_recv_buf,
210 sizeof(slip_recv_buf));
211 if (size < 0) {
212 log_msg(LOG_DEFAULT, LVL_ERROR,
213 "char_dev_read() returned %d", (int) size);
214 return SLIP_END;
215 }
216 slip_recv_pending = size;
217 slip_recv_read = 0;
218 }
219 slip_recv_pending--;
220 return slip_recv_buf[slip_recv_read++];
221}
222
223static int slip_recv_fibril(void *arg)
224{
225 async_sess_t *sess = (async_sess_t *) arg;
226 static uint8_t recv_final[SLIP_MTU];
227 iplink_recv_sdu_t sdu;
228 uint8_t ch;
229 int rc;
230
231 sdu.data = recv_final;
232
233 while (true) {
234 for (sdu.size = 0; sdu.size < sizeof(recv_final); /**/) {
235 ch = read_buffered(sess);
236 switch (ch) {
237 case SLIP_END:
238 if (sdu.size == 0) {
239 /*
240 * Discard the empty SLIP datagram.
241 */
242 break;
243 }
244 goto pass;
245
246 case SLIP_ESC:
247 ch = read_buffered(sess);
248 if (ch == SLIP_ESC_END) {
249 recv_final[sdu.size++] = SLIP_END;
250 break;
251 } else if (ch == SLIP_ESC_ESC) {
252 recv_final[sdu.size++] = SLIP_ESC;
253 break;
254 }
255
256 /*
257 * The RFC suggests to simply insert the wrongly
258 * escaped character into the packet so we fall
259 * through.
260 */
261
262 default:
263 recv_final[sdu.size++] = ch;
264 break;
265 }
266
267 }
268
269 /*
270 * We have reached the limit of our MTU. Regardless of whether
271 * the datagram is properly ended with SLIP_END, pass it along.
272 * If the next character is really SLIP_END, nothing
273 * catastrophic happens. The algorithm will just see an
274 * artificially empty SLIP datagram and life will go on.
275 */
276
277pass:
278 rc = iplink_ev_recv(&slip_iplink, &sdu, ip_v4);
279 if (rc != EOK) {
280 log_msg(LOG_DEFAULT, LVL_ERROR,
281 "iplink_ev_recv() returned %d", rc);
282 }
283 }
284
285 return 0;
286}
287
288static int slip_init(const char *svcstr, const char *linkstr)
289{
290 service_id_t svcid;
291 service_id_t linksid;
292 category_id_t iplinkcid;
293 async_sess_t *sess_in = NULL;
294 async_sess_t *sess_out = NULL;
295 fid_t fid;
296 int rc;
297
298 iplink_srv_init(&slip_iplink);
299 slip_iplink.ops = &slip_iplink_ops;
300
301 async_set_client_connection(slip_client_conn);
302
303 rc = loc_server_register(NAME);
304 if (rc != EOK) {
305 log_msg(LOG_DEFAULT, LVL_ERROR,
306 "Failed registering server.");
307 return rc;
308 }
309
310 rc = loc_service_get_id(svcstr, &svcid, 0);
311 if (rc != EOK) {
312 log_msg(LOG_DEFAULT, LVL_ERROR,
313 "Failed getting ID for service %s", svcstr);
314 return rc;
315 }
316
317 rc = loc_category_get_id(CAT_IPLINK, &iplinkcid, 0);
318 if (rc != EOK) {
319 log_msg(LOG_DEFAULT, LVL_ERROR,
320 "Failed to get category ID for %s",
321 CAT_IPLINK);
322 return rc;
323 }
324
325 /*
326 * Create two sessions to allow to both read and write from the
327 * char_dev at the same time.
328 */
329 sess_out = loc_service_connect(EXCHANGE_SERIALIZE, svcid, 0);
330 if (!sess_out) {
331 log_msg(LOG_DEFAULT, LVL_ERROR,
332 "Failed to connect to service %s (ID=%d)",
333 svcstr, (int) svcid);
334 return ENOENT;
335 }
336 slip_iplink.arg = sess_out;
337
338 sess_in = loc_service_connect(EXCHANGE_SERIALIZE, svcid, 0);
339 if (!sess_in) {
340 log_msg(LOG_DEFAULT, LVL_ERROR,
341 "Failed to connect to service %s (ID=%d)",
342 svcstr, (int) svcid);
343 rc = ENOENT;
344 goto fail;
345 }
346
347 rc = loc_service_register(linkstr, &linksid);
348 if (rc != EOK) {
349 log_msg(LOG_DEFAULT, LVL_ERROR,
350 "Failed to register service %s",
351 linkstr);
352 goto fail;
353 }
354
355 rc = loc_service_add_to_cat(linksid, iplinkcid);
356 if (rc != EOK) {
357 log_msg(LOG_DEFAULT, LVL_ERROR,
358 "Failed to add service %d (%s) to category %d (%s).",
359 (int) linksid, linkstr, (int) iplinkcid, CAT_IPLINK);
360 goto fail;
361 }
362
363 fid = fibril_create(slip_recv_fibril, sess_in);
364 if (!fid) {
365 log_msg(LOG_DEFAULT, LVL_ERROR,
366 "Failed to create receive fibril.");
367 rc = ENOENT;
368 goto fail;
369 }
370 fibril_add_ready(fid);
371
372 return EOK;
373
374fail:
375 if (sess_out)
376 async_hangup(sess_out);
377 if (sess_in)
378 async_hangup(sess_in);
379
380 /*
381 * We assume that our registration at the location service will be
382 * cleaned up automatically as the service (i.e. this task) terminates.
383 */
384
385 return rc;
386}
387
388int main(int argc, char *argv[])
389{
390 int rc;
391
392 printf(NAME ": IP over serial line service\n");
393
394 if (argc != 3) {
395 usage();
396 return EINVAL;
397 }
398
399 rc = log_init(NAME);
400 if (rc != EOK) {
401 printf(NAME ": failed to initialize log\n");
402 return rc;
403 }
404
405 rc = slip_init(argv[1], argv[2]);
406 if (rc != EOK)
407 return 1;
408
409 printf(NAME ": Accepting connections.\n");
410 task_retval(0);
411 async_manager();
412
413 /* not reached */
414 return 0;
415}
416
417/** @}
418 */
Note: See TracBrowser for help on using the repository browser.