1 /*
2 *
3 * Copyright (c) 2003 The Regents of the University of California. All
4 * rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * - Neither the name of the University nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31
32 /*
33 * utilities that help link providers (i.e. the device drivers themselves)
34 *
35 * $Id: link_provider.c,v 1.31 2007-12-08 04:05:04 girod Exp $
36 */
37
38 char link_provider_cvsid[] = "$Id: link_provider.c,v 1.31 2007-12-08 04:05:04 girod Exp $";
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <time.h>
43
44 #include "emrun/emsim.h"
45 #include "libdev/packet_dev.h"
46 #include "libdev/status_dev.h"
47 #include "libdev/directory_client.h"
48 #include "liblink_i.h"
49
50 static int link_fill_status(lp_context_t *lp);
51
52 gint link_standard_send(pd_context_t *pd, const void *packet,
53 int packetlen, int loop_needed)
54 {
55 lp_context_t *lp = (lp_context_t *) pd_data(pd);
56 int retval=0;
57
58 /* if no calback provided, return errno 'function not implemented' */
59 if (lp->opts.send == NULL)
60 return -ENOSYS;
61
62 /* check for short packet */
63 if (packetlen < sizeof(link_pkt_t)) {
64 elog(LOG_WARNING, "Dropping short packet (%d/%d)", packetlen, (int)sizeof(link_pkt_t));
65 goto out;
66 }
67
68 /* if client has a send callback, call it */
69 retval = lp->opts.send(lp, (link_pkt_t *)packet,
70 packetlen-sizeof(link_pkt_t), loop_needed);
71
72 /* check if LP_BLOCKED and switch to the pd constant */
73 if (retval == LP_BLOCKED)
74 retval = PD_BLOCKED;
75
76 out:
77 return retval;
78 }
79
80
81 gint link_standard_enqueue(pd_context_t *pd, const void *packet,
82 int packetlen)
83 {
84 lp_context_t *lp = (lp_context_t *) pd_data(pd);
85 int retval = 0;
86 int data_len = packetlen-sizeof(link_pkt_t);
87
88 /* check for short packet */
89 if (packetlen < sizeof(link_pkt_t)) {
90 elog(LOG_ERR, "short packet (%d bytes) written to %s", packetlen,
91 lp_name(lp, NULL));
92 return -EINVAL;
93 }
94
95 /* check for over MTU */
96 if (!lp->opts.no_auto_mtu_check && (data_len > lp->status.MTU)) {
97 elog(LOG_WARNING, "packet of %d bytes over MTU %d, written to %s", data_len,
98 lp->status.MTU, lp_name(lp, NULL));
99 return -EMSGSIZE;
100 }
101
102 /* if calback provided, call it */
103 if (lp->opts.enqueue != NULL)
104 retval = lp->opts.enqueue(lp, (link_pkt_t*)packet, packetlen-sizeof(link_pkt_t));
105
106 return retval;
107 }
108
109
110 gint link_standard_ioctl(pd_context_t *pd, int cmd, void *arg)
111 {
112 lp_context_t *lp = (lp_context_t *) pd_data(pd);
113 int retval;
114
115 /* handle the PROMISC and STATUS ioctl */
116 switch (cmd) {
117 case LINK_GET_STATUS:
118 if (link_fill_status(lp) < 0) {
119 elog(LOG_WARNING, "Error requesting link status");
120 return -ENOSYS;
121 }
122 /* copy the info to the buffer */
123 memmove(arg, &lp->status, sizeof(lp->status));
124 return 0;
125
126 case LINK_SET_PROMISC: {
127 int set_enable = *((uint32_t *) arg);
128 if (set_enable >= LINK_PROMISC_MODE_MAX)
129 return -EINVAL;
130
131 pd_client_t *curr = pd_curr_client(pd);
132 int curr_enable = (int)(size_t)pd_client_data(curr);
133 int notify = 0;
134
135 int set_active = boolify(set_enable == LINK_PROMISC_MODE_ACTIVE);
136 int curr_active = boolify(curr_enable == LINK_PROMISC_MODE_ACTIVE);
137
138 elog(LOG_DEBUG(10), "set promisc %d, %d, clients=%d", curr_enable, set_enable, lp->promisc_clients);
139
140 /* update lower */
141 if (set_active != curr_active) {
142 if (set_active) {
143 if (lp->promisc_clients == 0) notify = 1;
144 lp->promisc_clients++;
145 }
146 else {
147 if (lp->promisc_clients == 1) notify = 1;
148 if (lp->promisc_clients == 0)
149 elog(LOG_CRIT, "promisc_clients would go neg?");
150 else lp->promisc_clients--;
151 }
152 }
153
154 /* save value */
155 pd_set_client_data(curr, (void*)(size_t)set_enable);
156
157 /* notify lower on change */
158 if (notify) {
159 if (lp->opts.promisc_mode) lp->opts.promisc_mode(lp, set_enable);
160 elog(LOG_DEBUG(1), "app set %s mode on link %s",
161 set_active ? "promiscuous" : "not promiscuous", lp->opts.opts.name);
162 }
163 return 0;
164 }
165
166 default:
167 break;
168 }
169
170 /* if no calback provided, return errno 'function not implemented' */
171 if (lp->opts.ioctl == NULL)
172 return -ENOSYS;
173
174 /* if client has a send callback, call it */
175 retval = lp->opts.ioctl(lp, cmd, arg);
176
177 return retval;
178 }
179
180
181 int link_standard_close(pd_context_t *pd)
182 {
183 int zero = 0;
184 link_standard_ioctl(pd, LINK_SET_PROMISC, &zero);
185 return EVENT_RENEW;
186 }
187
188
189 gint link_standard_filter(pd_context_t *pd,
190 const void *packet, int packetlen,
191 const pd_filter_t *filter)
192 {
193 lp_context_t *lp = (lp_context_t *) pd_data(pd);
194 link_pkt_t *link_pkt = (link_pkt_t *) packet;
195 link_pkt_type_t *filter_pkt_type = (link_pkt_type_t *) filter->data;
196 int i;
197 int f_count;
198
199 /* reject short packets */
200 if (packetlen < sizeof(link_pkt_t))
201 return 0;
202
203 /* how many requested types? */
204 f_count = filter ? filter->len/sizeof(link_pkt_type_t) : 0;
205
206 /* drop if not for us and promisc not enabled, or type is MAC_CTRL */
207 pd_client_t *curr = pd_curr_client(pd);
208 int promisc_enable = (int)(size_t)pd_client_data(curr);
209 if (!promisc_enable && /* not promisc mode */
210 (lp->status.if_id != 0) && /* we have an address */
211 (link_pkt->type != PKT_TYPE_MAC_CTRL) && /* not a mac control packet */
212 (link_pkt->dst.id != LINK_BROADCAST) && /* not bcast */
213 (link_pkt->dst.id != lp->status.if_id)) { /* not to us */
214 elog(LOG_DEBUG(4), "Dropping packet destined for %s, our if is %s",
215 print_if_id(link_pkt->dst.id), print_if_id(lp->status.if_id));
216 return 0;
217 }
218
219 /* if no filter was provided, accept the packet */
220 if (f_count == 0)
221 return 1;
222
223 /* filter */
224 for (i=0; i<f_count; i++)
225 if (filter_pkt_type[i] == link_pkt->type)
226 return 1;
227
228 return 0;
229 }
230
231
232 /* link device destructor */
233 void lp_destroy(lp_context_t *context)
234 {
235 if (context) {
236 /* clear reference */
237 if (context->ref)
238 *(context->ref) = NULL;
239
240 /* destroy the underlying data, status and command devices */
241 g_packet_dev_destroy(context->pdcp);
242 g_status_dev_destroy(context->scp);
243 g_status_dev_destroy(context->ccp);
244
245 /* dealloc memory */
246 free(context->opts.opts.name);
247 if (context->opts.description) free(context->opts.description);
248 if (context->opts.root_trace) free(context->opts.root_trace);
249 if (context->opts.opts.if_class) free(context->opts.opts.if_class);
250 free(context);
251 context = NULL;
252 }
253 };
254
255
256 /*
257 * Status device handlers
258 */
259
260
261 static int link_fill_status(lp_context_t *lp)
262 {
263 pd_stats_t *pds = pd_get_device_stats(lp->pdcp);
264
265 if (pds == NULL)
266 return -1;
267
268 /* reentry preotection */
269 lp->in_status_callback = 1;
270
271 /* if we want notification on all status requests
272 * and callback is present, enable update */
273 if (lp->opts.stat_notify_all_requests) {
274 if(lp->opts.status_request)
275 lp->opts.status_request(lp);
276 }
277
278 lp->status.packets_rx = pds->packets_rx;
279 lp->status.packets_tx = pds->packets_tx;
280 lp->status.bytes_rx = pds->bytes_rx - (pds->packets_rx * sizeof(link_pkt_t));
281 lp->status.bytes_tx = pds->bytes_tx - (pds->packets_tx * sizeof(link_pkt_t));
282 lp->status.errors_rx = pds->drop_rx + lp->rx_errors;
283 lp->status.errors_tx = pds->drop_tx + lp->tx_errors;
284 lp->status.promisc = boolify(lp->promisc_clients);
285
286 lp->in_status_callback = 0;
287
288 return 0;
289 }
290
291
292 static
293 int link_stat_printable(status_context_t *info, buf_t *buf)
294 {
295 lp_context_t *lp = (lp_context_t *) sd_data(info);
296
297 if (link_fill_status(lp) < 0) {
298 elog(LOG_WARNING, "Error requesting link status");
299 }
300
301 link_status_to_buf(buf, &(lp->status));
302
303 return STATUS_MSG_COMPLETE;
304 }
305
306
307 static
308 int link_stat_binary(status_context_t *info, buf_t *buf)
309 {
310 lp_context_t *lp = (lp_context_t *) sd_data(info);
311
312 if (link_fill_status(lp) < 0) {
313 elog(LOG_WARNING, "Error requesting link status");
314 }
315
316 /* copy the info to the buffer */
317 bufcpy(buf, &lp->status, sizeof(lp->status));
318
319 return STATUS_MSG_COMPLETE;
320 }
321
322
323 /* push a new status struct and notify the client */
324 void lp_push_status(lp_context_t *lp, link_status_t *new_status)
325 {
326 memmove(&(lp->status), new_status, sizeof(link_status_t));
327 memmove(&(lp->orig_status), new_status, sizeof(link_status_t));
328
329 /* fill trace and descriptions */
330 if (lp->opts.we_are_root) {
331 strcpy(lp->status.root, lp->opts.description);
332 if (lp->opts.root_trace)
333 sprintf(lp->status.trace, "[%s]", lp->opts.root_trace);
334 else
335 lp->status.trace[0] = 0;
336 lp->status.top[0] = 0;
337 }
338 else
339 strcpy(lp->status.top, lp->opts.description);
340
341 sprintf(lp->status.trace+strlen(lp->status.trace),
342 ",%s", lp->opts.opts.name);
343
344 if (!lp->in_status_callback)
345 g_status_dev_notify(lp->scp);
346 }
347
348
349 /* update root trace string */
350 void lp_update_root_trace(lp_context_t *context, const char *trace, const char *description)
351 {
352 if (trace) {
353 if (context->opts.root_trace) free(context->opts.root_trace);
354 context->opts.root_trace = strdup(trace);
355 }
356 if (description) {
357 if (context->opts.description) free(context->opts.description);
358 context->opts.description = strdup(description);
359 }
360 lp_push_status(context, &(context->status));
361 }
362
363
364 int lp_trigger_refresh(void *data, int interval, g_event_t *event)
365 {
366 lp_context_t *lp = (lp_context_t *)data;
367 lp->opts.status_request(lp);
368 return TIMER_RENEW;
369 }
370
371 /*
372 * Command device handlers
373 */
374
375 static
376 int link_cmd_command(status_context_t *info, char *command, size_t buf_size)
377 {
378 lp_context_t *lp = (lp_context_t *)sd_data(info);
379 parser_state_t *ps = misc_parse_init(command, MISC_PARSE_COLON_SCHEME);
380 int retval = EVENT_RENEW;
381 if (lp->opts.command_request)
382 retval = lp->opts.command_request(lp, ps);
383 misc_parse_cleanup(ps);
384 return retval;
385 }
386
387
388 static
389 int link_cmd_usage(status_context_t *info, buf_t *buf)
390 {
391 lp_context_t *lp = (lp_context_t *)sd_data(info);
392 buf_t *tmp = buf_new();
393 bufprintf(tmp,
394 "Link %s command usage summary:\n", lp->opts.opts.name);
395
396 if (lp->opts.usage)
397 lp->opts.usage(lp, tmp);
398
399 /* uniquify! */
400 misc_usage_uniquify(tmp, buf);
401 buf_free(tmp);
402
403 return STATUS_MSG_COMPLETE;
404 }
405
406
407 void lp_add_standard_usage(buf_t *buf, char *cmd)
408 {
409 if (strcmp(LINK_CMD_POWER, cmd)==0)
410 bufprintf(buf, " %s=<level>: Sets the power level\n", LINK_CMD_POWER);
411 else if (strcmp(LINK_CMD_IFID, cmd)==0)
412 bufprintf(buf, " %s=<id>: Sets the interface ID\n", LINK_CMD_IFID);
413 else if (strcmp(LINK_CMD_ACTIVE, cmd)==0)
414 bufprintf(buf, " %s=<1,0>: Activates or deactivates interface\n", LINK_CMD_ACTIVE);
415 else if (strcmp(LINK_CMD_RESET, cmd)==0)
416 bufprintf(buf, " %s: Resets the interface\n", LINK_CMD_RESET);
417 else if (strcmp(LINK_CMD_LPL_RX, cmd)==0)
418 bufprintf(buf, " %s=<int>: Sets BMAC LPL RX mode\n", LINK_CMD_LPL_RX);
419 else if (strcmp(LINK_CMD_LPL_TX, cmd)==0)
420 bufprintf(buf, " %s=<int>: Sets BMAC LPL TX mode\n", LINK_CMD_LPL_TX);
421 else
422 elog(LOG_WARNING, "unknown command %s", cmd);
423 }
424
425
426 /*
427 * Interface to simulation framework..
428 */
429
430 int lp_check_sim(lp_opts_t *opts) {
431 /* if we are in sim */
432 if (in_sim && (my_node_id_real != SIM_COMPONENT_ID)) {
433 char buf[128];
434
435 sprintf(buf, "/dev/radio/%s/command", opts->opts.name);
436
437 /* try to contact simulation channel */
438 if (printf_to_file(group_path(buf), "id=%d:x_off=%f:y_off=%f:class=%s",
439 my_node_id,
440 opts->antenna_offset.x,
441 opts->antenna_offset.y,
442 opts->opts.if_class) < 0) {
443 elog(LOG_NOTICE, "No channel model present for %s: driver starting.",
444 opts->opts.name);
445 }
446
447 else {
448 elog(LOG_NOTICE, "Channel model present for %s: driver going dormant.",
449 opts->opts.name);
450 return 1;
451 }
452 }
453
454 /* ok, operate normally! */
455 return 0;
456 }
457
458
459 void lp_register_in_class(char *class_name, char *link_name)
460 {
461 char buf[128];
462 sprintf(buf, "/dev/link/classes/%s", class_name);
463 char *dirname = strdup(sim_path(buf));
464 sprintf(buf, "\"%s\" Class Link Devices", class_name);
465 remote_dir_create(dirname, buf, 1);
466
467 /* add us to the class */
468 remote_dir_lookup_by_name_or_create(dirname, link_name);
469 free(dirname);
470 }
471
472
473 /*
474 * called by link providers (e.g. motenic, simulator) that want to
475 * create a link interface usable by other processes
476 */
477 gint lp_register(lp_opts_t *opts, lp_context_t **ref)
478 {
479 if (!lp_check_sim(opts)) {
480 int retval=0;
481
482 packet_dev_opts_t pd_opts = {
483 send: link_standard_send,
484 enqueue: link_standard_enqueue,
485 ioctl: link_standard_ioctl,
486 close: link_standard_close,
487 filter: link_standard_filter,
488 always_invoke_filter: 1
489 };
490
491 status_dev_opts_t sd_opts = {
492 printable: link_stat_printable,
493 binary: link_stat_binary
494 };
495
496 status_dev_opts_t cd_opts = {
497 write: link_cmd_command,
498 printable: link_cmd_usage
499 };
500
501 /* link device state */
502 lp_context_t *lp = g_new0(lp_context_t, 1);
503
504 /* make sure we allocated this sucker */
505 if (lp == NULL) {
506 retval = -ENOMEM;
507 goto done;
508 }
509
510 /* make sure the options are valid */
511 if (opts == NULL) {
512 elog(LOG_ERR, "invalid link options (null)");
513 retval = -EINVAL;
514 goto done;
515 }
516
517 /* store ref */
518 lp->ref = ref;
519
520 /* copy the options struct */
521 lp->opts = *opts;
522
523 /* copy the strings */
524 lp->opts.opts.name = strdup(opts->opts.name);
525 lp->opts.opts.if_class =
526 strdup((opts->opts.if_class && strlen(opts->opts.if_class) > 0) ?
527 opts->opts.if_class : "none");
528 lp->opts.description =
529 strdup(opts->description ? opts->description : "??No Description Set!!");
530 if (opts->root_trace)
531 lp->opts.root_trace = strdup(opts->root_trace);
532
533 /* register the packet device */
534 pd_opts.device.devname = lp_name(lp, LINK_DATA_SUBDEV);
535 pd_opts.device.device_info = lp;
536 if (g_packet_dev(&pd_opts, &lp->pdcp) < 0) {
537 elog(LOG_INFO, "Unable to register data device %s: %m",
538 pd_opts.device.devname);
539 retval = -errno;
540 goto done;
541 }
542
543 /* register the status device */
544 sd_opts.device.devname = lp_name(lp, LINK_STATUS_SUBDEV);
545 sd_opts.device.device_info = lp;
546 if (g_status_dev(&sd_opts, &lp->scp) < 0) {
547 elog(LOG_INFO, "Unable to register status device %s: %m",
548 sd_opts.device.devname);
549 retval = -errno;
550 goto done;
551 }
552
553 /* register the command device */
554 cd_opts.device.devname = lp_name(lp, LINK_COMMAND_SUBDEV);
555 cd_opts.device.device_info = lp;
556 if (g_status_dev(&cd_opts, &lp->ccp) < 0) {
557 elog(LOG_INFO, "Unable to register command device %s: %m",
558 cd_opts.device.devname);
559 retval = -errno;
560 goto done;
561 }
562
563 /* set the root timer if we are root */
564 if (lp->opts.we_are_root) {
565 if (lp->opts.root_refresh_interval == 0)
566 lp->opts.root_refresh_interval = DEFAULT_ROOT_REFRESH;
567 if (lp->opts.root_refresh_interval > 0) {
568 g_timer_add(lp->opts.root_refresh_interval, lp_trigger_refresh, lp, NULL, &(lp->root_refresh));
569 if (lp->opts.status_request == NULL)
570 elog(LOG_CRIT, "Root device must have status request callback!");
571 }
572 }
573
574 /* register a new class if needed and enter us in the directory */
575 lp_register_in_class(lp->opts.opts.if_class, lp->opts.opts.name);
576
577 /* initialize the status info with the initial status stuff */
578 lp_push_status(lp, &(opts->initial_status));
579
580 done:
581
582 /* if we've had an error, close and dealloc */
583 if (retval < 0) {
584 if (lp) {
585 lp_destroy(lp);
586 lp = NULL;
587 }
588 }
589
590 /* write the link provider context (or, a NULL link provider context
591 pointer) to the user's provided reference, if any */
592 if (ref) {
593 if (*ref) {
594 elog(LOG_WARNING, "Destroying link dev before reusing reference");
595 lp_destroy(*ref);
596 }
597 *ref = lp;
598 }
599
600 /* return either success or failure with errno properly set */
601 if (retval < 0) {
602 errno = -retval;
603 return -1;
604 } else {
605 errno = 0;
606 return 0;
607 }
608 }
609 return LP_CHANNEL_MODEL_ACTIVE;
610 }
611
612 /*
613 * this is called by the link provider when a send() is complete.
614 * it records the fact that the send is finished, then kicks off
615 * another send if necessary
616 */
617 void lp_unblock(lp_context_t *lp, int retval)
618 {
619 if (lp)
620 pd_maybe_unblock(lp->pdcp, retval);
621 }
622
623
624 /* send a packet up to all clients waiting for data */
625 void lp_receive(lp_context_t *lp, const link_pkt_t *pkt, int data_len)
626 {
627 if(lp) pd_receive(lp->pdcp, pkt, data_len+sizeof(link_pkt_t));
628 }
629
630
631 /* send a packet up to clients waiting for loopback data */
632 void lp_loop_receive(lp_context_t *lp, const link_pkt_t *pkt, int data_len)
633 {
634 if(lp) pd_loop_receive(lp->pdcp, pkt, data_len+sizeof(link_pkt_t));
635 }
636
637
638 /* Accessor functions used to get info out of a link provider context */
639 lp_opts_t *lp_opts(lp_context_t *lp)
640 {
641 return (lp) ? &lp->opts : NULL;
642 }
643
644
645 /* get the name of the link */
646 char *lp_name(lp_context_t *lp, char *suffix)
647 {
648 return lp ? link_name(&(lp->opts.opts), suffix) : NULL;
649 }
650
651 /* get the private data of a link */
652 void *lp_data(lp_context_t *lp)
653 {
654 return (lp) ? lp->opts.opts.data : NULL;
655 }
656
657
658 int lp_loop_needed(lp_context_t *lp)
659 {
660 return lp && pd_loop_needed(lp->pdcp);
661 }
662
663 link_pkt_t *lp_get_outgoing_pkt(lp_context_t *lp)
664 {
665 return (lp && lp->pdcp) ? pd_get_outgoing_pkt(lp->pdcp) : NULL;
666 }
667
668
669 int lp_get_outgoing_datalen(lp_context_t *lp)
670 {
671 return (lp && lp->pdcp) ? pd_get_outgoing_pktlen(lp->pdcp) - sizeof(link_pkt_t) : -1;
672 }
673
674
675 int lp_get_promisc_state(lp_context_t *lp)
676 {
677 return lp->promisc_clients;
678 }
679
680
681 void lp_report_tx_error(lp_context_t *lp)
682 {
683 lp->tx_errors++;
684 }
685
686 void lp_report_rx_error(lp_context_t *lp)
687 {
688 lp->rx_errors++;
689 }
690
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.