xref: /minix/minix/lib/libnetdriver/netdriver.c (revision fb9c64b2)
1 /* The device-independent network driver framework. */
2 
3 #include <minix/drivers.h>
4 #include <minix/netdriver.h>
5 #include <minix/ds.h>
6 #include <assert.h>
7 
8 #include "netdriver.h"
9 
10 /*
11  * These maximum values should be at least somewhat synchronized with the
12  * values in the LWIP service's ndev module.
13  */
14 #define NETDRIVER_SENDQ_MAX	8
15 #define NETDRIVER_RECVQ_MAX	2
16 
17 /*
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.
22  */
23 #define NETDRIVER_MCAST_MAX	16
24 
25 static const struct netdriver *netdriver_table = NULL;
26 
27 static int running;
28 
29 static int init_expected;
30 
31 static int up;
32 
33 static unsigned int ticks;
34 
35 static struct netdriver_data pending_sendq[NETDRIVER_SENDQ_MAX];
36 static unsigned int pending_sends, pending_sendtail;
37 
38 static struct netdriver_data pending_recvq[NETDRIVER_RECVQ_MAX];
39 static unsigned int pending_recvs, pending_recvtail;
40 
41 static int pending_status;
42 static endpoint_t status_endpt;
43 
44 static int pending_link, pending_stat;
45 static uint32_t stat_oerror, stat_coll, stat_ierror, stat_iqdrop;
46 
47 static char device_name[NDEV_NAME_MAX];
48 static netdriver_addr_t device_hwaddr;
49 static uint32_t device_caps;
50 
51 static unsigned int device_link;
52 static uint32_t device_media;
53 
54 /*
55  * Announce we are up after a fresh start or restart.
56  */
57 static void
58 netdriver_announce(void)
59 {
60 	const char *driver_prefix = "drv.net.";
61 	char label[DS_MAX_KEYLEN];
62 	char key[DS_MAX_KEYLEN];
63 	int r;
64 
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);
68 
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);
72 }
73 
74 /*
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.
78  */
79 size_t
80 netdriver_prepare_copy(struct netdriver_data * data, size_t off, size_t size,
81 	unsigned int * indexp)
82 {
83 	unsigned int i;
84 
85 	assert(data->size > 0);
86 
87 	/*
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.
94 	 */
95 	if (off + size > data->size)
96 		panic("netdriver: request to copy beyond data size");
97 
98 	/*
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').
102 	 */
103 	for (i = 0; i < data->count; i++) {
104 		assert(data->iovec[i].iov_size > 0);
105 
106 		if (off >= data->iovec[i].iov_size)
107 			off -= data->iovec[i].iov_size;
108 		else
109 			break;
110 	}
111 
112 	assert(i < data->count);
113 
114 	*indexp = i;
115 	return off;
116 }
117 
118 /*
119  * Copy in or out packet data from/to a vector of grants.
120  */
121 static void
122 netdriver_copy(struct netdriver_data * data, size_t off, vir_bytes addr,
123 	size_t size, int copyin)
124 {
125 	struct vscp_vec vec[SCPVEC_NR];
126 	size_t chunk;
127 	unsigned int i, v;
128 	int r;
129 
130 	off = netdriver_prepare_copy(data, off, size, &i);
131 
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);
138 
139 		/*
140 		 * We should be able to fit the entire I/O request in a single
141 		 * copy vector.  If not, MINIX3 has been misconfigured.
142 		 */
143 		if (v >= SCPVEC_NR)
144 			panic("netdriver: invalid vector size constant");
145 
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;
152 		}
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;
157 
158 		i++;
159 		off = 0;
160 		addr += chunk;
161 		size -= chunk;
162 	}
163 
164 	assert(v > 0 && v <= SCPVEC_NR);
165 
166 	/*
167 	 * If only one vector element was generated, use a direct copy.  This
168 	 * saves the kernel from having to copy in the vector.
169 	 */
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);
179 
180 	if (r != OK)
181 		panic("netdriver: unable to copy data: %d", r);
182 }
183 
184 /*
185  * Copy in packet data.
186  */
187 void
188 netdriver_copyin(struct netdriver_data * __restrict data, size_t off,
189 	void * __restrict ptr, size_t size)
190 {
191 
192 	netdriver_copy(data, off, (vir_bytes)ptr, size, TRUE /*copyin*/);
193 }
194 
195 /*
196  * Copy out packet data.
197  */
198 void
199 netdriver_copyout(struct netdriver_data * __restrict data, size_t off,
200 	const void * __restrict ptr, size_t size)
201 {
202 
203 	netdriver_copy(data, off, (vir_bytes)ptr, size, FALSE /*copyin*/);
204 }
205 
206 /*
207  * Send a reply to a request.
208  */
209 static void
210 send_reply(endpoint_t endpt, message * m_ptr)
211 {
212 	int r;
213 
214 	if ((r = asynsend(endpt, m_ptr)) != OK)
215 		panic("netdriver: unable to send to %d: %d", endpt, r);
216 }
217 
218 /*
219  * A packet receive request has finished.  Send a reply and clean up.
220  */
221 static void
222 finish_recv(int32_t result)
223 {
224 	struct netdriver_data *data;
225 	message m;
226 
227 	assert(pending_recvs > 0);
228 
229 	data = &pending_recvq[pending_recvtail];
230 
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;
235 
236 	send_reply(data->endpt, &m);
237 
238 	pending_recvtail = (pending_recvtail + 1) %
239 	    __arraycount(pending_recvq);
240 	pending_recvs--;
241 }
242 
243 /*
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.
247  */
248 void
249 netdriver_recv(void)
250 {
251 	struct netdriver_data *data;
252 	ssize_t r;
253 
254 	assert(netdriver_table != NULL);
255 
256 	while (pending_recvs > 0) {
257 		data = &pending_recvq[pending_recvtail];
258 
259 		/*
260 		 * For convenience of driver writers: if the receive function
261 		 * returns zero, simply call it again, to simplify discarding
262 		 * invalid packets.
263 		 */
264 		do {
265 			r = netdriver_table->ndr_recv(data, data->size);
266 
267 			/*
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.
276 			 */
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);
283 
284 		if (r == SUSPEND)
285 			break;
286 
287 		if (r < 0)
288 			panic("netdriver: driver reported receive failure: %d",
289 			    r);
290 
291 		assert(r >= NDEV_ETH_PACKET_MIN && (size_t)r <= data->size);
292 
293 		finish_recv(r);
294 	}
295 }
296 
297 /*
298  * A packet send request has finished.  Send a reply and clean up.
299  */
300 static void
301 finish_send(int32_t result)
302 {
303 	struct netdriver_data *data;
304 	message m;
305 
306 	assert(pending_sends > 0);
307 
308 	data = &pending_sendq[pending_sendtail];
309 
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;
314 
315 	send_reply(data->endpt, &m);
316 
317 	pending_sendtail = (pending_sendtail + 1) %
318 	    __arraycount(pending_sendq);
319 	pending_sends--;
320 }
321 
322 /*
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.
327  */
328 void
329 netdriver_send(void)
330 {
331 	struct netdriver_data *data;
332 	int r;
333 
334 	assert(netdriver_table != NULL);
335 
336 	while (pending_sends > 0) {
337 		data = &pending_sendq[pending_sendtail];
338 
339 		r = netdriver_table->ndr_send(data, data->size);
340 
341 		if (r == SUSPEND)
342 			break;
343 
344 		if (r < 0)
345 			panic("netdriver: driver reported send failure: %d",
346 			    r);
347 
348 		finish_send(r);
349 	}
350 }
351 
352 /*
353  * Process a request to send or receive a packet.
354  */
355 static void
356 do_transfer(const struct netdriver * __restrict ndp, const message * m_ptr,
357 	int do_write)
358 {
359 	struct netdriver_data *data;
360 	cp_grant_id_t grant;
361 	size_t size;
362 	unsigned int i;
363 
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");
368 
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");
375 
376 		data = &pending_recvq[(pending_recvtail + pending_recvs) %
377 		    __arraycount(pending_recvq)];
378 	}
379 
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;
383 
384 	if (data->count == 0 || data->count > NDEV_IOV_MAX)
385 		panic("netdriver: bad I/O vector count: %u", data->count);
386 
387 	data->size = 0;
388 
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];
392 
393 		assert(size > 0);
394 
395 		data->iovec[i].iov_grant = grant;
396 		data->iovec[i].iov_size = size;
397 		data->size += size;
398 	}
399 
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);
403 
404 	if (do_write)
405 		pending_sends++;
406 	else
407 		pending_recvs++;
408 
409 	/*
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..
413 	 */
414 	if (!up) {
415 		if (do_write)
416 			finish_send(EINTR);
417 		else
418 			finish_recv(EINTR);
419 
420 		return;
421 	}
422 
423 	/* Otherwise, resume sending or receiving. */
424 	if (do_write)
425 		netdriver_send();
426 	else
427 		netdriver_recv();
428 }
429 
430 /*
431  * Process a request to (re)configure the driver.
432  */
433 static void
434 do_conf(const struct netdriver * __restrict ndp,
435 	const message * __restrict m_ptr)
436 {
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;
442 
443 	set = m_ptr->m_ndev_netdriver_conf.set;
444 	mode = m_ptr->m_ndev_netdriver_conf.mode;
445 
446 	/*
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.
450 	 */
451 	if ((set & NDEV_SET_MODE) && mode == NDEV_MODE_DOWN &&
452 	    ndp->ndr_set_mode != NULL)
453 		ndp->ndr_set_mode(mode, NULL, 0);
454 
455 	if ((set & NDEV_SET_CAPS) && ndp->ndr_set_caps != NULL)
456 		ndp->ndr_set_caps(m_ptr->m_ndev_netdriver_conf.caps);
457 
458 	if ((set & NDEV_SET_FLAGS) && ndp->ndr_set_flags != NULL)
459 		ndp->ndr_set_flags(m_ptr->m_ndev_netdriver_conf.flags);
460 
461 	if ((set & NDEV_SET_MEDIA) && ndp->ndr_set_media != NULL)
462 		ndp->ndr_set_media(m_ptr->m_ndev_netdriver_conf.media);
463 
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));
468 
469 		ndp->ndr_set_hwaddr(&device_hwaddr);
470 	}
471 
472 	if ((set & NDEV_SET_MODE) && mode != NDEV_MODE_DOWN &&
473 	    ndp->ndr_set_mode != NULL) {
474 		/*
475 		 * If we have a multicast list, copy it in, unless it is too
476 		 * large: in that case, enable all-multicast receipt mode.
477 		 */
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;
483 		}
484 
485 		if (mode & NDEV_MODE_MCAST_LIST) {
486 			assert(m_ptr->m_ndev_netdriver_conf.mcast_grant !=
487 			    GRANT_INVALID);
488 
489 			mcast_count = m_ptr->m_ndev_netdriver_conf.mcast_count;
490 
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);
496 
497 			ndp->ndr_set_mode(mode, mcast_list, mcast_count);
498 		} else
499 			ndp->ndr_set_mode(mode, NULL, 0);
500 	}
501 
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;
507 
508 	send_reply(m_ptr->m_source, &m);
509 
510 	/*
511 	 * Finally, if the device has been taken down, abort pending send and
512 	 * receive requests.
513 	 */
514 	if (set & NDEV_SET_MODE) {
515 		if (mode == NDEV_MODE_DOWN) {
516 			while (pending_sends > 0)
517 				finish_send(EINTR);
518 
519 			while (pending_recvs > 0)
520 				finish_recv(EINTR);
521 
522 			up = FALSE;
523 		} else
524 			up = TRUE;
525 	}
526 }
527 
528 /*
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.
531  */
532 static void
533 update_link(void)
534 {
535 
536 	if (netdriver_table->ndr_get_link != NULL)
537 		device_link = netdriver_table->ndr_get_link(&device_media);
538 
539 	pending_link = FALSE;
540 }
541 
542 /*
543  * Attempt to send a status update to the endpoint registered to receive status
544  * updates, if any.
545  */
546 static void
547 send_status(void)
548 {
549 	message m;
550 	int r;
551 
552 	assert(pending_link || pending_stat);
553 
554 	if (status_endpt == NONE || pending_status)
555 		return;
556 
557 	if (pending_link)
558 		update_link();
559 
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;
569 
570 	if ((r = asynsend3(status_endpt, &m, AMF_NOREPLY)) != OK)
571 		panic("netdriver: unable to send status: %d", r);
572 
573 	/*
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.
578 	 */
579 	pending_status = TRUE;
580 
581 	/*
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).
586 	 */
587 	stat_oerror = 0;
588 	stat_coll = 0;
589 	stat_ierror = 0;
590 	stat_iqdrop = 0;
591 	pending_stat = FALSE;
592 }
593 
594 /*
595  * Process a reply to a status update that we sent earlier on (supposedly).
596  */
597 static void
598 do_status_reply(const struct netdriver * __restrict ndp __unused,
599 	const message * __restrict m_ptr)
600 {
601 
602 	if (m_ptr->m_source != status_endpt)
603 		return;
604 
605 	if (!pending_status || m_ptr->m_ndev_netdriver_status_reply.id != 0)
606 		panic("netdriver: unexpected status reply");
607 
608 	pending_status = FALSE;
609 
610 	/*
611 	 * If the local status has changed since our last status update,
612 	 * send a new one right away.
613 	 */
614 	if (pending_link || pending_stat)
615 		send_status();
616 }
617 
618 /*
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.
622  */
623 void
624 netdriver_link(void)
625 {
626 
627 	pending_link = TRUE;
628 
629 	send_status();
630 }
631 
632 /*
633  * The driver reports that a number of output errors have occurred.  Update
634  * statistics accordingly.
635  */
636 void
637 netdriver_stat_oerror(uint32_t count)
638 {
639 
640 	if (count == 0)
641 		return;
642 
643 	stat_oerror += count;
644 	pending_stat = TRUE;
645 
646 	send_status();
647 }
648 
649 /*
650  * The driver reports that one or more packet collisions have occurred.  Update
651  * statistics accordingly.
652  */
653 void
654 netdriver_stat_coll(uint32_t count)
655 {
656 
657 	if (count == 0)
658 		return;
659 
660 	stat_coll += count;
661 	pending_stat = TRUE;
662 
663 	send_status();
664 }
665 
666 /*
667  * The driver reports that a number of input errors have occurred.  Adjust
668  * statistics accordingly.
669  */
670 void
671 netdriver_stat_ierror(uint32_t count)
672 {
673 
674 	if (count == 0)
675 		return;
676 
677 	stat_ierror += count;
678 	pending_stat = TRUE;
679 
680 	send_status();
681 }
682 
683 /*
684  * The driver reports that a number of input queue drops have occurred.  Update
685  * statistics accordingly.
686  */
687 void
688 netdriver_stat_iqdrop(uint32_t count)
689 {
690 
691 	if (count == 0)
692 		return;
693 
694 	stat_iqdrop += count;
695 	pending_stat = TRUE;
696 
697 	send_status();
698 }
699 
700 /*
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.
705  */
706 static void
707 do_init(const struct netdriver * __restrict ndp,
708 	const message * __restrict m_ptr)
709 {
710 	message m;
711 
712 	/*
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.
718 	 */
719 	pending_sends = 0;
720 	pending_recvs = 0;
721 	pending_status = FALSE;
722 
723 	status_endpt = m_ptr->m_source;
724 
725 	/*
726 	 * Update link and media now, because we are about to send the initial
727 	 * values of those to the caller as well.
728 	 */
729 	update_link();
730 
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);
744 
745 	m.m_netdriver_ndev_init_reply.max_send = __arraycount(pending_sendq);
746 	m.m_netdriver_ndev_init_reply.max_recv = __arraycount(pending_recvq);
747 
748 	send_reply(m_ptr->m_source, &m);
749 
750 	/*
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.
754 	 */
755 	if (pending_stat)
756 		send_status();
757 }
758 
759 /*
760  * Process an incoming message, and send a reply.
761  */
762 void
763 netdriver_process(const struct netdriver * __restrict ndp,
764 	const message * __restrict m_ptr, int ipc_status)
765 {
766 
767 	netdriver_table = ndp;
768 
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;
776 
777 		case CLOCK:
778 			if (ndp->ndr_tick != NULL)
779 				ndp->ndr_tick();
780 
781 			if (ticks > 0)
782 				(void)sys_setalarm(ticks, FALSE /*abs_time*/);
783 			break;
784 
785 		default:
786 			if (ndp->ndr_other != NULL)
787 				ndp->ndr_other(m_ptr, ipc_status);
788 		}
789 
790 		return;
791 	}
792 
793 	/*
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.
799 	 */
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 */
803 
804 		init_expected = FALSE;
805 	}
806 
807 	switch (m_ptr->m_type) {
808 	case NDEV_INIT:
809 		do_init(ndp, m_ptr);
810 		break;
811 
812 	case NDEV_CONF:
813 		do_conf(ndp, m_ptr);
814 		break;
815 
816 	case NDEV_SEND:
817 		do_transfer(ndp, m_ptr, TRUE /*do_write*/);
818 		break;
819 
820 	case NDEV_RECV:
821 		do_transfer(ndp, m_ptr, FALSE /*do_write*/);
822 		break;
823 
824 	case NDEV_STATUS_REPLY:
825 		do_status_reply(ndp, m_ptr);
826 		break;
827 
828 	default:
829 		if (ndp->ndr_other != NULL)
830 			ndp->ndr_other(m_ptr, ipc_status);
831 	}
832 }
833 
834 /*
835  * Set a name for the device, based on the base name 'base' and the instance
836  * number 'instance'.
837  */
838 static void
839 netdriver_set_name(const char * base, unsigned int instance)
840 {
841 	size_t len;
842 
843 	assert(instance <= 255);
844 
845 	len = strlen(base);
846 	assert(len <= sizeof(device_name) - 4);
847 
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;
855 }
856 
857 /*
858  * Return the device name generated at driver initialization time.
859  */
860 const char *
861 netdriver_name(void)
862 {
863 
864 	return device_name;
865 }
866 
867 /*
868  * Perform initialization.  Return OK or an error code.
869  */
870 int
871 netdriver_init(const struct netdriver * ndp)
872 {
873 	unsigned int instance;
874 	long v;
875 	int r;
876 
877 	/* Initialize global variables. */
878 	pending_sendtail = 0;
879 	pending_sends = 0;
880 	pending_recvtail = 0;
881 	pending_recvs = 0;
882 
883 	memset(device_name, 0, sizeof(device_name));
884 
885 	memset(&device_hwaddr, 0, sizeof(device_hwaddr));
886 	device_caps = 0;
887 
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);
891 
892 	status_endpt = NONE;
893 	pending_status = FALSE;
894 	pending_link = FALSE;
895 
896 	up = FALSE;
897 
898 	ticks = 0;
899 
900 	/* Get the device instance number. */
901 	v = 0;
902 	(void)env_parse("instance", "d", 0, &v, 0, 255);
903 	instance = (unsigned int)v;
904 
905 	/* Generate the full driver name. */
906 	netdriver_set_name(ndp->ndr_name, instance);
907 
908 	/* Call the initialization routine. */
909 	if ((r = ndp->ndr_init(instance, &device_hwaddr, &device_caps,
910 	    &ticks)) != OK)
911 		return r;
912 
913 	/* Announce we are up! */
914 	netdriver_announce();
915 
916 	init_expected = TRUE;
917 	running = TRUE;
918 
919 	if (ticks > 0)
920 		(void)sys_setalarm(ticks, FALSE /*abs_time*/);
921 
922 	return OK;
923 }
924 
925 /*
926  * Perform SEF initialization.
927  */
928 static int
929 local_init(int type __unused, sef_init_info_t * info __unused)
930 {
931 
932 	assert(netdriver_table != NULL);
933 
934 	return netdriver_init(netdriver_table);
935 }
936 
937 /*
938  * Break out of the main loop after finishing the current request.
939  */
940 void
941 netdriver_terminate(void)
942 {
943 
944 	if (netdriver_table != NULL && netdriver_table->ndr_stop != NULL)
945 		netdriver_table->ndr_stop();
946 
947 	running = FALSE;
948 
949 	sef_cancel();
950 }
951 
952 /*
953  * The process has received a signal.  See if we have to terminate.
954  */
955 static void
956 got_signal(int sig)
957 {
958 
959 	if (sig != SIGTERM)
960 		return;
961 
962 	netdriver_terminate();
963 }
964 
965 /*
966  * Main program of any network driver.
967  */
968 void
969 netdriver_task(const struct netdriver * ndp)
970 {
971 	message mess;
972 	int r, ipc_status;
973 
974 	/* Perform SEF initialization. */
975 	sef_setcb_init_fresh(local_init);
976 	sef_setcb_signal_handler(got_signal);
977 
978 	netdriver_table = ndp;
979 
980 	sef_startup();
981 
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 */
987 
988 			panic("netdriver: sef_receive_status failed: %d", r);
989 		}
990 
991 		netdriver_process(ndp, &mess, ipc_status);
992 	}
993 }
994