1 /* The device-independent network driver framework. */
3 #include <minix/drivers.h>
4 #include <minix/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
23 #define NETDRIVER_MCAST_MAX 16
25 static const struct netdriver
*netdriver_table
= NULL
;
29 static int init_expected
;
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.
58 netdriver_announce(void)
60 const char *driver_prefix
= "drv.net.";
61 char label
[DS_MAX_KEYLEN
];
62 char key
[DS_MAX_KEYLEN
];
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.
80 netdriver_prepare_copy(struct netdriver_data
* data
, size_t off
, size_t size
,
81 unsigned int * indexp
)
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
;
112 assert(i
< data
->count
);
119 * Copy in or out packet data from/to a vector of grants.
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
];
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
;
140 * We should be able to fit the entire I/O request in a single
141 * copy vector. If not, MINIX3 has been misconfigured.
144 panic("netdriver: invalid vector size constant");
147 vec
[v
].v_from
= data
->endpt
;
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
;
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.
172 r
= sys_safecopyfrom(vec
->v_from
, vec
->v_gid
,
173 vec
->v_offset
, vec
->v_addr
, vec
->v_bytes
);
175 r
= sys_safecopyto(vec
->v_to
, vec
->v_gid
,
176 vec
->v_offset
, vec
->v_addr
, vec
->v_bytes
);
178 r
= sys_vsafecopy(vec
, v
);
181 panic("netdriver: unable to copy data: %d", r
);
185 * Copy in packet data.
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.
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.
210 send_reply(endpoint_t endpt
, message
* m_ptr
)
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.
222 finish_recv(int32_t result
)
224 struct netdriver_data
*data
;
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
);
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.
251 struct netdriver_data
*data
;
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
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
)
279 else if (r
> (ssize_t
)data
->size
)
280 panic("netdriver: oversized packet returned: "
288 panic("netdriver: driver reported receive failure: %d",
291 assert(r
>= NDEV_ETH_PACKET_MIN
&& (size_t)r
<= data
->size
);
298 * A packet send request has finished. Send a reply and clean up.
301 finish_send(int32_t result
)
303 struct netdriver_data
*data
;
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
);
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
331 struct netdriver_data
*data
;
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
);
345 panic("netdriver: driver reported send failure: %d",
353 * Process a request to send or receive a packet.
356 do_transfer(const struct netdriver
* __restrict ndp
, const message
* m_ptr
,
359 struct netdriver_data
*data
;
364 /* Prepare the local data structure. */
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
)];
372 if (pending_recvs
== __arraycount(pending_recvq
))
373 panic("netdriver: too many concurrent receive "
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
);
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
];
395 data
->iovec
[i
].iov_grant
= grant
;
396 data
->iovec
[i
].iov_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
);
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..
423 /* Otherwise, resume sending or receiving. */
431 * Process a request to (re)configure the driver.
434 do_conf(const struct netdriver
* __restrict ndp
,
435 const message
* __restrict m_ptr
)
437 netdriver_addr_t mcast_list
[NETDRIVER_MCAST_MAX
];
439 unsigned int mcast_count
;
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
!=
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
);
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
514 if (set
& NDEV_SET_MODE
) {
515 if (mode
== NDEV_MODE_DOWN
) {
516 while (pending_sends
> 0)
519 while (pending_recvs
> 0)
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.
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
552 assert(pending_link
|| pending_stat
);
554 if (status_endpt
== NONE
|| pending_status
)
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).
591 pending_stat
= FALSE
;
595 * Process a reply to a status update that we sent earlier on (supposedly).
598 do_status_reply(const struct netdriver
* __restrict ndp __unused
,
599 const message
* __restrict m_ptr
)
602 if (m_ptr
->m_source
!= status_endpt
)
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
)
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.
633 * The driver reports that a number of output errors have occurred. Update
634 * statistics accordingly.
637 netdriver_stat_oerror(uint32_t count
)
643 stat_oerror
+= count
;
650 * The driver reports that one or more packet collisions have occurred. Update
651 * statistics accordingly.
654 netdriver_stat_coll(uint32_t count
)
667 * The driver reports that a number of input errors have occurred. Adjust
668 * statistics accordingly.
671 netdriver_stat_ierror(uint32_t count
)
677 stat_ierror
+= count
;
684 * The driver reports that a number of input queue drops have occurred. Update
685 * statistics accordingly.
688 netdriver_stat_iqdrop(uint32_t count
)
694 stat_iqdrop
+= count
;
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.
707 do_init(const struct netdriver
* __restrict ndp
,
708 const message
* __restrict m_ptr
)
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
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.
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.
760 * Process an incoming message, and send a reply.
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
) {
773 if (ndp
->ndr_intr
!= NULL
)
774 ndp
->ndr_intr(m_ptr
->m_notify
.interrupts
);
778 if (ndp
->ndr_tick
!= NULL
)
782 (void)sys_setalarm(ticks
, FALSE
/*abs_time*/);
786 if (ndp
->ndr_other
!= NULL
)
787 ndp
->ndr_other(m_ptr
, ipc_status
);
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
) {
817 do_transfer(ndp
, m_ptr
, TRUE
/*do_write*/);
821 do_transfer(ndp
, m_ptr
, FALSE
/*do_write*/);
824 case NDEV_STATUS_REPLY
:
825 do_status_reply(ndp
, m_ptr
);
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
839 netdriver_set_name(const char * base
, unsigned int instance
)
843 assert(instance
<= 255);
846 assert(len
<= sizeof(device_name
) - 4);
848 memcpy(device_name
, base
, len
);
850 device_name
[len
++] = '0' + instance
/ 100;
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.
868 * Perform initialization. Return OK or an error code.
871 netdriver_init(const struct netdriver
* ndp
)
873 unsigned int instance
;
877 /* Initialize global variables. */
878 pending_sendtail
= 0;
880 pending_recvtail
= 0;
883 memset(device_name
, 0, sizeof(device_name
));
885 memset(&device_hwaddr
, 0, sizeof(device_hwaddr
));
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);
893 pending_status
= FALSE
;
894 pending_link
= FALSE
;
900 /* Get the device instance number. */
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
,
913 /* Announce we are up! */
914 netdriver_announce();
916 init_expected
= TRUE
;
920 (void)sys_setalarm(ticks
, FALSE
/*abs_time*/);
926 * Perform SEF initialization.
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.
941 netdriver_terminate(void)
944 if (netdriver_table
!= NULL
&& netdriver_table
->ndr_stop
!= NULL
)
945 netdriver_table
->ndr_stop();
953 * The process has received a signal. See if we have to terminate.
962 netdriver_terminate();
966 * Main program of any network driver.
969 netdriver_task(const struct netdriver
* ndp
)
974 /* Perform SEF initialization. */
975 sef_setcb_init_fresh(local_init
);
976 sef_setcb_signal_handler(got_signal
);
978 netdriver_table
= ndp
;
982 /* The main message loop. */
984 if ((r
= sef_receive_status(ANY
, &mess
, &ipc_status
)) != OK
) {
986 continue; /* sef_cancel() was called */
988 panic("netdriver: sef_receive_status failed: %d", r
);
991 netdriver_process(ndp
, &mess
, ipc_status
);