source: mainline/uspace/srv/net/slip/slip.c@ b99f6e2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b99f6e2 was 1c635d6, checked in by Martin Sucha <sucha14@…>, 11 years ago

Do not hold a task's return value after it has disconnected.

Holding the task's return value meant that if nobody waited
for task's result, it polluted NS's memory. This was apparently
done because of a race between spawning a task and waiting for it.

We solve this problem in another way: ns discards the return value
as soon as the task disconnects from it. This typically happens
when the task finishes its execution. In order to avoid the race,
we send the wait request to ns while spawning the task (i.e. when
we talk to the loader), but before we allow the loaded program
to run.

Fixes #132

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