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
netdriver_announce(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
netdriver_prepare_copy(struct netdriver_data * data,size_t off,size_t size,unsigned int * indexp)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
netdriver_copy(struct netdriver_data * data,size_t off,vir_bytes addr,size_t size,int copyin)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
netdriver_copyin(struct netdriver_data * __restrict data,size_t off,void * __restrict ptr,size_t size)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
netdriver_copyout(struct netdriver_data * __restrict data,size_t off,const void * __restrict ptr,size_t size)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
send_reply(endpoint_t endpt,message * m_ptr)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
finish_recv(int32_t result)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
netdriver_recv(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
finish_send(int32_t result)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
netdriver_send(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
do_transfer(const struct netdriver * __restrict ndp,const message * m_ptr,int do_write)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
do_conf(const struct netdriver * __restrict ndp,const message * __restrict m_ptr)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
update_link(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
send_status(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
do_status_reply(const struct netdriver * __restrict ndp __unused,const message * __restrict m_ptr)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
netdriver_link(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
netdriver_stat_oerror(uint32_t count)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
netdriver_stat_coll(uint32_t count)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
netdriver_stat_ierror(uint32_t count)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
netdriver_stat_iqdrop(uint32_t count)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
do_init(const struct netdriver * __restrict ndp,const message * __restrict m_ptr)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
netdriver_process(const struct netdriver * __restrict ndp,const message * __restrict m_ptr,int ipc_status)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
netdriver_set_name(const char * base,unsigned int instance)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 *
netdriver_name(void)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
netdriver_init(const struct netdriver * ndp)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
local_init(int type __unused,sef_init_info_t * info __unused)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
netdriver_terminate(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
got_signal(int sig)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
netdriver_task(const struct netdriver * ndp)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