Remove building with NOCRYPTO option
[minix3.git] / minix / lib / libnetdriver / netdriver.c
bloba42db2edfe1815e7cd255df87ee148361606f43e
1 /* The device-independent network driver framework. */
3 #include <minix/drivers.h>
4 #include <minix/netdriver.h>
5 #include <minix/ds.h>
6 #include <assert.h>
8 #include "netdriver.h"
11 * These maximum values should be at least somewhat synchronized with the
12 * values in the LWIP service's ndev module.
14 #define NETDRIVER_SENDQ_MAX 8
15 #define NETDRIVER_RECVQ_MAX 2
18 * Maximum number of multicast addresses that can be copied in from the TCP/IP
19 * service and passed to the driver. If the actual number from the service
20 * exceeds this maximum, the driver will be told to receive all multicast
21 * packets instead.
23 #define NETDRIVER_MCAST_MAX 16
25 static const struct netdriver *netdriver_table = NULL;
27 static int running;
29 static int init_expected;
31 static int up;
33 static unsigned int ticks;
35 static struct netdriver_data pending_sendq[NETDRIVER_SENDQ_MAX];
36 static unsigned int pending_sends, pending_sendtail;
38 static struct netdriver_data pending_recvq[NETDRIVER_RECVQ_MAX];
39 static unsigned int pending_recvs, pending_recvtail;
41 static int pending_status;
42 static endpoint_t status_endpt;
44 static int pending_link, pending_stat;
45 static uint32_t stat_oerror, stat_coll, stat_ierror, stat_iqdrop;
47 static char device_name[NDEV_NAME_MAX];
48 static netdriver_addr_t device_hwaddr;
49 static uint32_t device_caps;
51 static unsigned int device_link;
52 static uint32_t device_media;
55 * Announce we are up after a fresh start or restart.
57 static void
58 netdriver_announce(void)
60 const char *driver_prefix = "drv.net.";
61 char label[DS_MAX_KEYLEN];
62 char key[DS_MAX_KEYLEN];
63 int r;
65 /* Publish a driver up event. */
66 if ((r = ds_retrieve_label_name(label, sef_self())) != OK)
67 panic("netdriver: unable to get own label: %d", r);
69 snprintf(key, sizeof(key), "%s%s", driver_prefix, label);
70 if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK)
71 panic("netdriver: unable to publish driver up event: %d", r);
75 * Prepare for copying. Given a flat offset, return the vector element index
76 * and an offset into that element. Panic if the request does not fall
77 * entirely within the vector.
79 size_t
80 netdriver_prepare_copy(struct netdriver_data * data, size_t off, size_t size,
81 unsigned int * indexp)
83 unsigned int i;
85 assert(data->size > 0);
88 * In theory we could truncate when copying out, but this creates a
89 * problem for port-based I/O, where the size of the transfer is
90 * typically specified in advance. We could do extra port-based I/O
91 * to discard the extra bytes, but the driver is better off doing such
92 * truncation itself. Thus, we disallow copying (in and out) beyond
93 * the given data vector altogether.
95 if (off + size > data->size)
96 panic("netdriver: request to copy beyond data size");
99 * Find the starting offset in the vector. If this turns out to be
100 * expensive, this can be adapted to store the last <element,offset>
101 * pair in the "data" structure (this is the reason it is not 'const').
103 for (i = 0; i < data->count; i++) {
104 assert(data->iovec[i].iov_size > 0);
106 if (off >= data->iovec[i].iov_size)
107 off -= data->iovec[i].iov_size;
108 else
109 break;
112 assert(i < data->count);
114 *indexp = i;
115 return off;
119 * Copy in or out packet data from/to a vector of grants.
121 static void
122 netdriver_copy(struct netdriver_data * data, size_t off, vir_bytes addr,
123 size_t size, int copyin)
125 struct vscp_vec vec[SCPVEC_NR];
126 size_t chunk;
127 unsigned int i, v;
128 int r;
130 off = netdriver_prepare_copy(data, off, size, &i);
132 /* Generate a new vector with all the individual copies to make. */
133 for (v = 0; size > 0; v++) {
134 chunk = data->iovec[i].iov_size - off;
135 if (chunk > size)
136 chunk = size;
137 assert(chunk > 0);
140 * We should be able to fit the entire I/O request in a single
141 * copy vector. If not, MINIX3 has been misconfigured.
143 if (v >= SCPVEC_NR)
144 panic("netdriver: invalid vector size constant");
146 if (copyin) {
147 vec[v].v_from = data->endpt;
148 vec[v].v_to = SELF;
149 } else {
150 vec[v].v_from = SELF;
151 vec[v].v_to = data->endpt;
153 vec[v].v_gid = data->iovec[i].iov_grant;
154 vec[v].v_offset = off;
155 vec[v].v_addr = addr;
156 vec[v].v_bytes = chunk;
158 i++;
159 off = 0;
160 addr += chunk;
161 size -= chunk;
164 assert(v > 0 && v <= SCPVEC_NR);
167 * If only one vector element was generated, use a direct copy. This
168 * saves the kernel from having to copy in the vector.
170 if (v == 1) {
171 if (copyin)
172 r = sys_safecopyfrom(vec->v_from, vec->v_gid,
173 vec->v_offset, vec->v_addr, vec->v_bytes);
174 else
175 r = sys_safecopyto(vec->v_to, vec->v_gid,
176 vec->v_offset, vec->v_addr, vec->v_bytes);
177 } else
178 r = sys_vsafecopy(vec, v);
180 if (r != OK)
181 panic("netdriver: unable to copy data: %d", r);
185 * Copy in packet data.
187 void
188 netdriver_copyin(struct netdriver_data * __restrict data, size_t off,
189 void * __restrict ptr, size_t size)
192 netdriver_copy(data, off, (vir_bytes)ptr, size, TRUE /*copyin*/);
196 * Copy out packet data.
198 void
199 netdriver_copyout(struct netdriver_data * __restrict data, size_t off,
200 const void * __restrict ptr, size_t size)
203 netdriver_copy(data, off, (vir_bytes)ptr, size, FALSE /*copyin*/);
207 * Send a reply to a request.
209 static void
210 send_reply(endpoint_t endpt, message * m_ptr)
212 int r;
214 if ((r = asynsend(endpt, m_ptr)) != OK)
215 panic("netdriver: unable to send to %d: %d", endpt, r);
219 * A packet receive request has finished. Send a reply and clean up.
221 static void
222 finish_recv(int32_t result)
224 struct netdriver_data *data;
225 message m;
227 assert(pending_recvs > 0);
229 data = &pending_recvq[pending_recvtail];
231 memset(&m, 0, sizeof(m));
232 m.m_type = NDEV_RECV_REPLY;
233 m.m_netdriver_ndev_reply.id = data->id;
234 m.m_netdriver_ndev_reply.result = result;
236 send_reply(data->endpt, &m);
238 pending_recvtail = (pending_recvtail + 1) %
239 __arraycount(pending_recvq);
240 pending_recvs--;
244 * Resume receiving packets. In particular, if a receive request was pending,
245 * call the driver's receive function. If the call is successful, send a reply
246 * to the requesting party.
248 void
249 netdriver_recv(void)
251 struct netdriver_data *data;
252 ssize_t r;
254 assert(netdriver_table != NULL);
256 while (pending_recvs > 0) {
257 data = &pending_recvq[pending_recvtail];
260 * For convenience of driver writers: if the receive function
261 * returns zero, simply call it again, to simplify discarding
262 * invalid packets.
264 do {
265 r = netdriver_table->ndr_recv(data, data->size);
268 * The default policy is: drop undersized packets,
269 * panic on oversized packets. The driver may
270 * implement any other policy (e.g., pad small packets,
271 * drop or truncate large packets), but it should at
272 * least test against the given 'max' value. The
273 * reason that truncation should be implemented in the
274 * driver rather than here, is explained in an earlier
275 * comment about truncating copy operations.
277 if (r >= 0 && r < NDEV_ETH_PACKET_MIN)
278 r = 0;
279 else if (r > (ssize_t)data->size)
280 panic("netdriver: oversized packet returned: "
281 "%zd", r);
282 } while (r == 0);
284 if (r == SUSPEND)
285 break;
287 if (r < 0)
288 panic("netdriver: driver reported receive failure: %d",
291 assert(r >= NDEV_ETH_PACKET_MIN && (size_t)r <= data->size);
293 finish_recv(r);
298 * A packet send request has finished. Send a reply and clean up.
300 static void
301 finish_send(int32_t result)
303 struct netdriver_data *data;
304 message m;
306 assert(pending_sends > 0);
308 data = &pending_sendq[pending_sendtail];
310 memset(&m, 0, sizeof(m));
311 m.m_type = NDEV_SEND_REPLY;
312 m.m_netdriver_ndev_reply.id = data->id;
313 m.m_netdriver_ndev_reply.result = result;
315 send_reply(data->endpt, &m);
317 pending_sendtail = (pending_sendtail + 1) %
318 __arraycount(pending_sendq);
319 pending_sends--;
323 * Resume sending packets. In particular, if any send requests were pending,
324 * call the driver's send function for each of them, until the driver can take
325 * no more. For each successful request is successful, send a reply to the
326 * requesting party.
328 void
329 netdriver_send(void)
331 struct netdriver_data *data;
332 int r;
334 assert(netdriver_table != NULL);
336 while (pending_sends > 0) {
337 data = &pending_sendq[pending_sendtail];
339 r = netdriver_table->ndr_send(data, data->size);
341 if (r == SUSPEND)
342 break;
344 if (r < 0)
345 panic("netdriver: driver reported send failure: %d",
348 finish_send(r);
353 * Process a request to send or receive a packet.
355 static void
356 do_transfer(const struct netdriver * __restrict ndp, const message * m_ptr,
357 int do_write)
359 struct netdriver_data *data;
360 cp_grant_id_t grant;
361 size_t size;
362 unsigned int i;
364 /* Prepare the local data structure. */
365 if (do_write) {
366 if (pending_sends == __arraycount(pending_sendq))
367 panic("netdriver: too many concurrent send requests");
369 data = &pending_sendq[(pending_sendtail + pending_sends) %
370 __arraycount(pending_sendq)];
371 } else {
372 if (pending_recvs == __arraycount(pending_recvq))
373 panic("netdriver: too many concurrent receive "
374 "requests");
376 data = &pending_recvq[(pending_recvtail + pending_recvs) %
377 __arraycount(pending_recvq)];
380 data->endpt = m_ptr->m_source;
381 data->id = m_ptr->m_ndev_netdriver_transfer.id;
382 data->count = m_ptr->m_ndev_netdriver_transfer.count;
384 if (data->count == 0 || data->count > NDEV_IOV_MAX)
385 panic("netdriver: bad I/O vector count: %u", data->count);
387 data->size = 0;
389 for (i = 0; i < data->count; i++) {
390 grant = m_ptr->m_ndev_netdriver_transfer.grant[i];
391 size = (size_t)m_ptr->m_ndev_netdriver_transfer.len[i];
393 assert(size > 0);
395 data->iovec[i].iov_grant = grant;
396 data->iovec[i].iov_size = size;
397 data->size += size;
400 if (data->size < NDEV_ETH_PACKET_MIN ||
401 (!do_write && data->size < NDEV_ETH_PACKET_MAX_TAGGED))
402 panic("netdriver: invalid I/O vector size: %zu\n", data->size);
404 if (do_write)
405 pending_sends++;
406 else
407 pending_recvs++;
410 * If the driver is down, immediately abort the request again. This
411 * is not a common case but does occur as part of queue draining by the
412 * TCP/IP stack, and is way easier to handle here than up there..
414 if (!up) {
415 if (do_write)
416 finish_send(EINTR);
417 else
418 finish_recv(EINTR);
420 return;
423 /* Otherwise, resume sending or receiving. */
424 if (do_write)
425 netdriver_send();
426 else
427 netdriver_recv();
431 * Process a request to (re)configure the driver.
433 static void
434 do_conf(const struct netdriver * __restrict ndp,
435 const message * __restrict m_ptr)
437 netdriver_addr_t mcast_list[NETDRIVER_MCAST_MAX];
438 uint32_t set, mode;
439 unsigned int mcast_count;
440 message m;
441 int r;
443 set = m_ptr->m_ndev_netdriver_conf.set;
444 mode = m_ptr->m_ndev_netdriver_conf.mode;
447 * If the request includes taking down the interface, perform that step
448 * first: it is expected that in many cases, changing other settings
449 * requires stopping and restarting the device.
451 if ((set & NDEV_SET_MODE) && mode == NDEV_MODE_DOWN &&
452 ndp->ndr_set_mode != NULL)
453 ndp->ndr_set_mode(mode, NULL, 0);
455 if ((set & NDEV_SET_CAPS) && ndp->ndr_set_caps != NULL)
456 ndp->ndr_set_caps(m_ptr->m_ndev_netdriver_conf.caps);
458 if ((set & NDEV_SET_FLAGS) && ndp->ndr_set_flags != NULL)
459 ndp->ndr_set_flags(m_ptr->m_ndev_netdriver_conf.flags);
461 if ((set & NDEV_SET_MEDIA) && ndp->ndr_set_media != NULL)
462 ndp->ndr_set_media(m_ptr->m_ndev_netdriver_conf.media);
464 if ((set & NDEV_SET_HWADDR) && ndp->ndr_set_hwaddr != NULL) {
465 /* Save the new hardware address. */
466 memcpy(&device_hwaddr, m_ptr->m_ndev_netdriver_conf.hwaddr,
467 sizeof(device_hwaddr));
469 ndp->ndr_set_hwaddr(&device_hwaddr);
472 if ((set & NDEV_SET_MODE) && mode != NDEV_MODE_DOWN &&
473 ndp->ndr_set_mode != NULL) {
475 * If we have a multicast list, copy it in, unless it is too
476 * large: in that case, enable all-multicast receipt mode.
478 if ((mode & NDEV_MODE_MCAST_LIST) &&
479 m_ptr->m_ndev_netdriver_conf.mcast_count >
480 __arraycount(mcast_list)) {
481 mode &= ~NDEV_MODE_MCAST_LIST;
482 mode |= NDEV_MODE_MCAST_ALL;
485 if (mode & NDEV_MODE_MCAST_LIST) {
486 assert(m_ptr->m_ndev_netdriver_conf.mcast_grant !=
487 GRANT_INVALID);
489 mcast_count = m_ptr->m_ndev_netdriver_conf.mcast_count;
491 if ((r = sys_safecopyfrom(m_ptr->m_source,
492 m_ptr->m_ndev_netdriver_conf.mcast_grant, 0,
493 (vir_bytes)mcast_list,
494 mcast_count * sizeof(mcast_list[0]))) != OK)
495 panic("netdriver: unable to copy data: %d", r);
497 ndp->ndr_set_mode(mode, mcast_list, mcast_count);
498 } else
499 ndp->ndr_set_mode(mode, NULL, 0);
502 /* We always report OK: the caller cannot do anything upon failure. */
503 memset(&m, 0, sizeof(m));
504 m.m_type = NDEV_CONF_REPLY;
505 m.m_netdriver_ndev_reply.id = m_ptr->m_ndev_netdriver_conf.id;
506 m.m_netdriver_ndev_reply.result = OK;
508 send_reply(m_ptr->m_source, &m);
511 * Finally, if the device has been taken down, abort pending send and
512 * receive requests.
514 if (set & NDEV_SET_MODE) {
515 if (mode == NDEV_MODE_DOWN) {
516 while (pending_sends > 0)
517 finish_send(EINTR);
519 while (pending_recvs > 0)
520 finish_recv(EINTR);
522 up = FALSE;
523 } else
524 up = TRUE;
529 * Request an update of the link state and active media of the device. This
530 * routine may be called both from the driver and internally.
532 static void
533 update_link(void)
536 if (netdriver_table->ndr_get_link != NULL)
537 device_link = netdriver_table->ndr_get_link(&device_media);
539 pending_link = FALSE;
543 * Attempt to send a status update to the endpoint registered to receive status
544 * updates, if any.
546 static void
547 send_status(void)
549 message m;
550 int r;
552 assert(pending_link || pending_stat);
554 if (status_endpt == NONE || pending_status)
555 return;
557 if (pending_link)
558 update_link();
560 memset(&m, 0, sizeof(m));
561 m.m_type = NDEV_STATUS;
562 m.m_netdriver_ndev_status.id = 0; /* for now */
563 m.m_netdriver_ndev_status.link = device_link;
564 m.m_netdriver_ndev_status.media = device_media;
565 m.m_netdriver_ndev_status.oerror = stat_oerror;
566 m.m_netdriver_ndev_status.coll = stat_coll;
567 m.m_netdriver_ndev_status.ierror = stat_ierror;
568 m.m_netdriver_ndev_status.iqdrop = stat_iqdrop;
570 if ((r = asynsend3(status_endpt, &m, AMF_NOREPLY)) != OK)
571 panic("netdriver: unable to send status: %d", r);
574 * Do not send another status message until either the one we just sent
575 * gets acknowledged or we get a new initialization request. This way
576 * we get "natural pacing" (i.e., we avoid overflowing the asynsend
577 * message queue by design) without using timers.
579 pending_status = TRUE;
582 * The status message sends incremental updates for statistics. This
583 * means that while a restart of the TCP/IP stack means the statistics
584 * are lost (not great), a restart of the driver leaves the statistics
585 * mostly intact (more important).
587 stat_oerror = 0;
588 stat_coll = 0;
589 stat_ierror = 0;
590 stat_iqdrop = 0;
591 pending_stat = FALSE;
595 * Process a reply to a status update that we sent earlier on (supposedly).
597 static void
598 do_status_reply(const struct netdriver * __restrict ndp __unused,
599 const message * __restrict m_ptr)
602 if (m_ptr->m_source != status_endpt)
603 return;
605 if (!pending_status || m_ptr->m_ndev_netdriver_status_reply.id != 0)
606 panic("netdriver: unexpected status reply");
608 pending_status = FALSE;
611 * If the local status has changed since our last status update,
612 * send a new one right away.
614 if (pending_link || pending_stat)
615 send_status();
619 * The driver reports that the link state and/or active media may have changed.
620 * When convenient, request the new state from the driver and send a status
621 * message to the TCP/IP stack.
623 void
624 netdriver_link(void)
627 pending_link = TRUE;
629 send_status();
633 * The driver reports that a number of output errors have occurred. Update
634 * statistics accordingly.
636 void
637 netdriver_stat_oerror(uint32_t count)
640 if (count == 0)
641 return;
643 stat_oerror += count;
644 pending_stat = TRUE;
646 send_status();
650 * The driver reports that one or more packet collisions have occurred. Update
651 * statistics accordingly.
653 void
654 netdriver_stat_coll(uint32_t count)
657 if (count == 0)
658 return;
660 stat_coll += count;
661 pending_stat = TRUE;
663 send_status();
667 * The driver reports that a number of input errors have occurred. Adjust
668 * statistics accordingly.
670 void
671 netdriver_stat_ierror(uint32_t count)
674 if (count == 0)
675 return;
677 stat_ierror += count;
678 pending_stat = TRUE;
680 send_status();
684 * The driver reports that a number of input queue drops have occurred. Update
685 * statistics accordingly.
687 void
688 netdriver_stat_iqdrop(uint32_t count)
691 if (count == 0)
692 return;
694 stat_iqdrop += count;
695 pending_stat = TRUE;
697 send_status();
701 * Process an initialization request. Actual initialization has already taken
702 * place, so we simply report the information gathered at that time. If the
703 * caller (the TCP/IP stack) has crashed and restarted, we will get another
704 * initialization request message, so keep the information up-to-date.
706 static void
707 do_init(const struct netdriver * __restrict ndp,
708 const message * __restrict m_ptr)
710 message m;
713 * First of all, an initialization request is a sure indication that
714 * the caller does not have any send or receive requests pending, and
715 * will not acknowledge our previous status request, if any. Forget
716 * any such previous requests and start sending status requests to the
717 * (new) endpoint.
719 pending_sends = 0;
720 pending_recvs = 0;
721 pending_status = FALSE;
723 status_endpt = m_ptr->m_source;
726 * Update link and media now, because we are about to send the initial
727 * values of those to the caller as well.
729 update_link();
731 memset(&m, 0, sizeof(m));
732 m.m_type = NDEV_INIT_REPLY;
733 m.m_netdriver_ndev_init_reply.id = m_ptr->m_ndev_netdriver_init.id;
734 m.m_netdriver_ndev_init_reply.link = device_link;
735 m.m_netdriver_ndev_init_reply.media = device_media;
736 m.m_netdriver_ndev_init_reply.caps = device_caps;
737 strlcpy(m.m_netdriver_ndev_init_reply.name, device_name,
738 sizeof(m.m_netdriver_ndev_init_reply.name));
739 assert(sizeof(device_hwaddr) <=
740 sizeof(m.m_netdriver_ndev_init_reply.hwaddr));
741 memcpy(m.m_netdriver_ndev_init_reply.hwaddr, &device_hwaddr,
742 sizeof(device_hwaddr));
743 m.m_netdriver_ndev_init_reply.hwaddr_len = sizeof(device_hwaddr);
745 m.m_netdriver_ndev_init_reply.max_send = __arraycount(pending_sendq);
746 m.m_netdriver_ndev_init_reply.max_recv = __arraycount(pending_recvq);
748 send_reply(m_ptr->m_source, &m);
751 * Also send the current status. This is not required by the protocol
752 * and only serves to provide updated statistics to a new TCP/IP stack
753 * instance right away.
755 if (pending_stat)
756 send_status();
760 * Process an incoming message, and send a reply.
762 void
763 netdriver_process(const struct netdriver * __restrict ndp,
764 const message * __restrict m_ptr, int ipc_status)
767 netdriver_table = ndp;
769 /* Check for notifications first. */
770 if (is_ipc_notify(ipc_status)) {
771 switch (m_ptr->m_source) {
772 case HARDWARE:
773 if (ndp->ndr_intr != NULL)
774 ndp->ndr_intr(m_ptr->m_notify.interrupts);
775 break;
777 case CLOCK:
778 if (ndp->ndr_tick != NULL)
779 ndp->ndr_tick();
781 if (ticks > 0)
782 (void)sys_setalarm(ticks, FALSE /*abs_time*/);
783 break;
785 default:
786 if (ndp->ndr_other != NULL)
787 ndp->ndr_other(m_ptr, ipc_status);
790 return;
794 * Discard datalink requests preceding a first NDEV_INIT request, so
795 * that after a driver restart, any in-flight request is discarded.
796 * Note that for correct driver operation it is important that
797 * non-datalink requests, and interrupts in particular, do not go
798 * through this check.
800 if (IS_NDEV_RQ(m_ptr->m_type) && init_expected) {
801 if (m_ptr->m_type != NDEV_INIT)
802 return; /* do not send a reply */
804 init_expected = FALSE;
807 switch (m_ptr->m_type) {
808 case NDEV_INIT:
809 do_init(ndp, m_ptr);
810 break;
812 case NDEV_CONF:
813 do_conf(ndp, m_ptr);
814 break;
816 case NDEV_SEND:
817 do_transfer(ndp, m_ptr, TRUE /*do_write*/);
818 break;
820 case NDEV_RECV:
821 do_transfer(ndp, m_ptr, FALSE /*do_write*/);
822 break;
824 case NDEV_STATUS_REPLY:
825 do_status_reply(ndp, m_ptr);
826 break;
828 default:
829 if (ndp->ndr_other != NULL)
830 ndp->ndr_other(m_ptr, ipc_status);
835 * Set a name for the device, based on the base name 'base' and the instance
836 * number 'instance'.
838 static void
839 netdriver_set_name(const char * base, unsigned int instance)
841 size_t len;
843 assert(instance <= 255);
845 len = strlen(base);
846 assert(len <= sizeof(device_name) - 4);
848 memcpy(device_name, base, len);
849 if (instance >= 100)
850 device_name[len++] = '0' + instance / 100;
851 if (instance >= 10)
852 device_name[len++] = '0' + (instance % 100) / 10;
853 device_name[len++] = '0' + instance % 10;
854 device_name[len] = 0;
858 * Return the device name generated at driver initialization time.
860 const char *
861 netdriver_name(void)
864 return device_name;
868 * Perform initialization. Return OK or an error code.
871 netdriver_init(const struct netdriver * ndp)
873 unsigned int instance;
874 long v;
875 int r;
877 /* Initialize global variables. */
878 pending_sendtail = 0;
879 pending_sends = 0;
880 pending_recvtail = 0;
881 pending_recvs = 0;
883 memset(device_name, 0, sizeof(device_name));
885 memset(&device_hwaddr, 0, sizeof(device_hwaddr));
886 device_caps = 0;
888 /* Use sensible defaults for the link state and active media. */
889 device_link = NDEV_LINK_UNKNOWN;
890 device_media = IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0);
892 status_endpt = NONE;
893 pending_status = FALSE;
894 pending_link = FALSE;
896 up = FALSE;
898 ticks = 0;
900 /* Get the device instance number. */
901 v = 0;
902 (void)env_parse("instance", "d", 0, &v, 0, 255);
903 instance = (unsigned int)v;
905 /* Generate the full driver name. */
906 netdriver_set_name(ndp->ndr_name, instance);
908 /* Call the initialization routine. */
909 if ((r = ndp->ndr_init(instance, &device_hwaddr, &device_caps,
910 &ticks)) != OK)
911 return r;
913 /* Announce we are up! */
914 netdriver_announce();
916 init_expected = TRUE;
917 running = TRUE;
919 if (ticks > 0)
920 (void)sys_setalarm(ticks, FALSE /*abs_time*/);
922 return OK;
926 * Perform SEF initialization.
928 static int
929 local_init(int type __unused, sef_init_info_t * info __unused)
932 assert(netdriver_table != NULL);
934 return netdriver_init(netdriver_table);
938 * Break out of the main loop after finishing the current request.
940 void
941 netdriver_terminate(void)
944 if (netdriver_table != NULL && netdriver_table->ndr_stop != NULL)
945 netdriver_table->ndr_stop();
947 running = FALSE;
949 sef_cancel();
953 * The process has received a signal. See if we have to terminate.
955 static void
956 got_signal(int sig)
959 if (sig != SIGTERM)
960 return;
962 netdriver_terminate();
966 * Main program of any network driver.
968 void
969 netdriver_task(const struct netdriver * ndp)
971 message mess;
972 int r, ipc_status;
974 /* Perform SEF initialization. */
975 sef_setcb_init_fresh(local_init);
976 sef_setcb_signal_handler(got_signal);
978 netdriver_table = ndp;
980 sef_startup();
982 /* The main message loop. */
983 while (running) {
984 if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) {
985 if (r == EINTR)
986 continue; /* sef_cancel() was called */
988 panic("netdriver: sef_receive_status failed: %d", r);
991 netdriver_process(ndp, &mess, ipc_status);