~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
cvs/emstar/link/liblink/link_provider.c


  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.