1 /* LWIP service - ndev.c - network driver communication module */
3 * There is almost a one-to-one mapping between network device driver (ndev)
4 * objects and ethernet interface (ethif) objects, with as major difference
5 * that there may be an ndev object but not an ethif object for a driver that
6 * is known to exist but has not yet replied to our initialization request:
7 * without the information from the initialization request, there is no point
8 * creating an ethif object just yet, while we do need to track the driver
9 * process. TODO: it would be nice if unanswered init requests timed out and
10 * caused the removal of the ndev object after a while.
12 * Beyond that, this module aims to abstract away the low-level details of
13 * communication, memory grants, and driver restarts. Driver restarts are not
14 * fully transparent to the ethif module because it needs to reinitialize
15 * driver state only it knows about after a restart. Drivers that are in the
16 * process of restarting and therefore not operational are said to be disabled.
18 * From this module's point of view, a network driver is one of two states:
19 * initializing, where it has yet to respond to our initialization request, and
20 * active, where it is expected to accept and respond to all other requests.
21 * This module does not keep track of higher-level states and rules however;
22 * that is left to the ethif layer on one side, and the network driver itself
23 * on the other side. One important example is the interface being up or down:
24 * the ndev layer will happily forward send and receive requests when the
25 * interface is down, but these requests will be (resp.) dropped and rejected
26 * by the network driver in that state, and will not be generated by the ethif
27 * layer when the layer is down. Imposing barriers between configure and send
28 * requests is also left to the other parties.
30 * In this module, each active network driver has a send queue and a receive
31 * queue. The send queue is shared for packet send requests and configuration
32 * change requests. The receive queue is used for packet receive requests
33 * only. Each queue has a maximum depth, which is the minimum of a value
34 * provided by the network driver during initialization and local restrictions.
35 * These local restrictions are different for the two queue types: the receive
36 * queue is always bounded to a hardcoded value, while the send queue has a
37 * guaranteed minimum depth but may use up to the driver's maximum using spare
38 * entries. For both, a minimum depth is always available, since it is not
39 * possible to cancel individual send or receive requests after they have been
40 * sent to a particular driver. This does mean that we necessarily waste a
41 * large number of request structures in the common case.
43 * The general API model does not support the notion of blocking calls. While
44 * it would make sense to retrieve e.g. error statistics from the driver only
45 * when requested by userland, implementing this without threads would be
46 * seriously complicated, because such requests can have many origins (ioctl,
47 * PF_ROUTE message, sysctl). Instead, we rely on drivers updating us with the
48 * latest information on everything at all times, so that we can hand over a
49 * cached copy of (e.g.) those error statistics right away. We provide a means
50 * for drivers to perform rate limiting of such status updates (to prevent
51 * overflowing asynsend queues), by replying to these status messages. That
52 * means that there is a request-response combo going in the opposite direction
53 * of the regular messages.
55 * TODO: in the future we will want to obtain the list of supported media modes
56 * (IFM_) from drivers, so that userland can view the list. Given the above
57 * model, the easiest way would be to obtain a copy of the full list, limited
58 * to a configured number of entries, at driver initialization time. This
59 * would require that the initialization request also involve a memory grant.
61 * If necessary, it would not be too much work to split off this module into
62 * its own libndev library. For now, there is no point in doing this and the
63 * tighter coupling allows us to optimize just a little but (see pbuf usage).
70 #define LABEL_MAX 16 /* FIXME: this should be in a system header */
72 #define NDEV_SENDQ 2 /* minimum guaranteed send queue depth */
73 #define NDEV_RECVQ 2 /* guaranteed receive queue depth */
74 #define NREQ_SPARES 8 /* spare send queue (request) objects */
75 #define NR_NREQ ((NDEV_SENDQ + NDEV_RECVQ) * NR_NDEV + NREQ_SPARES)
77 static SIMPLEQ_HEAD(, ndev_req
) nreq_freelist
;
79 static struct ndev_req
{
80 SIMPLEQ_ENTRY(ndev_req
) nreq_next
; /* next request in queue */
81 int nreq_type
; /* type of request message */
82 cp_grant_id_t nreq_grant
[NDEV_IOV_MAX
]; /* grants for request */
83 } nreq_array
[NR_NREQ
];
85 static unsigned int nreq_spares
; /* number of free spare objects */
88 uint32_t nq_head
; /* ID of oldest pending request */
89 uint8_t nq_count
; /* current nr of pending requests */
90 uint8_t nq_max
; /* maximum nr of pending requests */
91 SIMPLEQ_HEAD(, ndev_req
) nq_req
; /* queue of pending requests */
95 endpoint_t ndev_endpt
; /* driver endpoint */
96 char ndev_label
[LABEL_MAX
]; /* driver label */
97 struct ethif
*ndev_ethif
; /* ethif object, or NULL if init'ing */
98 struct ndev_queue ndev_sendq
; /* packet send and configure queue */
99 struct ndev_queue ndev_recvq
; /* packet receive queue */
100 } ndev_array
[NR_NDEV
];
102 static ndev_id_t ndev_max
; /* highest driver count ever seen */
105 * This macro checks whether the network driver is active rather than
106 * initializing. See above for more information.
108 #define NDEV_ACTIVE(ndev) ((ndev)->ndev_sendq.nq_max > 0)
110 static int ndev_pending
; /* number of initializing drivers */
112 /* The CTL_MINIX MINIX_LWIP "drivers" subtree. Dynamically numbered. */
113 static struct rmib_node minix_lwip_drivers_table
[] = {
114 RMIB_INTPTR(RMIB_RO
, &ndev_pending
, "pending",
115 "Number of drivers currently initializing"),
118 static struct rmib_node minix_lwip_drivers_node
=
119 RMIB_NODE(RMIB_RO
, minix_lwip_drivers_table
, "drivers",
120 "Network driver information");
123 * Initialize the network driver communication module.
131 /* Initialize local variables. */
134 SIMPLEQ_INIT(&nreq_freelist
);
136 for (slot
= 0; slot
< __arraycount(nreq_array
); slot
++)
137 SIMPLEQ_INSERT_TAIL(&nreq_freelist
, &nreq_array
[slot
],
140 nreq_spares
= NREQ_SPARES
;
143 * Preallocate the total number of grants that we could possibly need
144 * concurrently. Even though it is extremely unlikely that we will
145 * ever need that many grants in practice, the alternative is runtime
146 * dynamic memory (re)allocation which is something we prefer to avoid
147 * altogether. At time of writing, we end up preallocating 320 grants
148 * using up a total of a bit under 9KB of memory.
150 cpf_prealloc(NR_NREQ
* NDEV_IOV_MAX
);
154 * Not needed, just for ultimate safety: start off all queues with
155 * wildly different request sequence numbers, to minimize the chance
156 * that any two replies will ever be confused.
158 for (slot
= 0; slot
< __arraycount(ndev_array
); slot
++) {
159 ndev_array
[slot
].ndev_sendq
.nq_head
= slot
<< 21;
160 ndev_array
[slot
].ndev_recvq
.nq_head
= (slot
* 2 + 1) << 20;
163 /* Subscribe to Data Store (DS) events from network drivers. */
164 if ((r
= ds_subscribe("drv\\.net\\..*",
165 DSF_INITIAL
| DSF_OVERWRITE
)) != OK
)
166 panic("unable to subscribe to driver events: %d", r
);
169 * Keep track of how many drivers are in "pending" state, which means
170 * that they have not yet replied to our initialization request.
174 /* Register the minix.lwip.drivers subtree. */
175 mibtree_register_lwip(&minix_lwip_drivers_node
);
179 * Initialize a queue for first use.
182 ndev_queue_init(struct ndev_queue
* nq
)
186 * Only ever increase sequence numbers, to minimize the chance that
187 * two (e.g. from different driver instances) happen to be the same.
193 SIMPLEQ_INIT(&nq
->nq_req
);
197 * Advance the given request queue, freeing up the request at the head of the
198 * queue including any grants in use for it.
201 ndev_queue_advance(struct ndev_queue
* nq
)
203 struct ndev_req
* nreq
;
207 nreq
= SIMPLEQ_FIRST(&nq
->nq_req
);
209 for (i
= 0; i
< __arraycount(nreq
->nreq_grant
); i
++) {
210 grant
= nreq
->nreq_grant
[i
];
212 if (!GRANT_VALID(grant
))
215 /* TODO: make the safecopies code stop using errno. */
216 if (cpf_revoke(grant
) != 0)
217 panic("unable to revoke grant: %d", -errno
);
220 if (nreq
->nreq_type
!= NDEV_RECV
&& nq
->nq_count
> NDEV_SENDQ
) {
223 assert(nreq_spares
<= NREQ_SPARES
);
226 SIMPLEQ_REMOVE_HEAD(&nq
->nq_req
, nreq_next
);
228 SIMPLEQ_INSERT_HEAD(&nreq_freelist
, nreq
, nreq_next
);
235 * Clear any outstanding requests from the given queue and reset it to a
236 * pre-initialization state.
239 ndev_queue_reset(struct ndev_queue
* nq
)
242 while (nq
->nq_count
> 0) {
243 assert(!SIMPLEQ_EMPTY(&nq
->nq_req
));
245 ndev_queue_advance(nq
);
252 * Obtain a request object for use in a new request. Return the request
253 * object, with its request type field set to 'type', and with the request
254 * sequence ID returned in 'seq'. Return NULL if no request objects are
255 * available for the given request type. If the caller does send off the
256 * request, a call to ndev_queue_add() must follow immediately after. If the
257 * caller fails to send off the request for other reasons, it need not do
258 * anything: this function does not perform any actions that need to be undone.
260 static struct ndev_req
*
261 ndev_queue_get(struct ndev_queue
* nq
, int type
, uint32_t * seq
)
263 struct ndev_req
*nreq
;
265 /* Has the hard queue depth limit been reached? */
266 if (nq
->nq_count
== nq
->nq_max
)
270 * For send requests, we may use request objects from a shared "spares"
271 * pool, if available.
273 if (type
!= NDEV_RECV
&& nq
->nq_count
>= NDEV_SENDQ
&&
277 assert(!SIMPLEQ_EMPTY(&nreq_freelist
));
278 nreq
= SIMPLEQ_FIRST(&nreq_freelist
);
280 nreq
->nreq_type
= type
;
282 *seq
= nq
->nq_head
+ nq
->nq_count
;
288 * Add a successfully sent request to the given queue. The request must have
289 * been obtained using ndev_queue_get() directly before the call to this
290 * function. This function never fails.
293 ndev_queue_add(struct ndev_queue
* nq
, struct ndev_req
* nreq
)
296 if (nreq
->nreq_type
!= NDEV_RECV
&& nq
->nq_count
>= NDEV_SENDQ
) {
297 assert(nreq_spares
> 0);
302 SIMPLEQ_REMOVE_HEAD(&nreq_freelist
, nreq_next
);
304 SIMPLEQ_INSERT_TAIL(&nq
->nq_req
, nreq
, nreq_next
);
310 * Remove the head of the given request queue, but only if it matches the given
311 * request type and sequence ID. Return TRUE if the head was indeed removed,
312 * or FALSE if the head of the request queue (if any) did not match the given
313 * type and/or sequence ID.
316 ndev_queue_remove(struct ndev_queue
* nq
, int type
, uint32_t seq
)
318 struct ndev_req
*nreq
;
320 if (nq
->nq_count
< 1 || nq
->nq_head
!= seq
)
323 assert(!SIMPLEQ_EMPTY(&nq
->nq_req
));
324 nreq
= SIMPLEQ_FIRST(&nq
->nq_req
);
326 if (nreq
->nreq_type
!= type
)
329 ndev_queue_advance(nq
);
335 * Send an initialization request to a driver. If this is a new driver, the
336 * ethif module does not get to know about the driver until it answers to this
337 * request, as the ethif module needs much of what the reply contains. On the
338 * other hand, if this is a restarted driver, it will stay disabled until the
339 * init reply comes in.
342 ndev_send_init(struct ndev
* ndev
)
347 memset(&m
, 0, sizeof(m
));
348 m
.m_type
= NDEV_INIT
;
349 m
.m_ndev_netdriver_init
.id
= ndev
->ndev_sendq
.nq_head
;
351 if ((r
= asynsend3(ndev
->ndev_endpt
, &m
, AMF_NOREPLY
)) != OK
)
352 panic("asynsend to driver failed: %d", r
);
356 * A network device driver has been started or restarted.
359 ndev_up(const char * label
, endpoint_t endpt
)
361 static int reported
= FALSE
;
366 * First see if we already had an entry for this driver. If so, it has
367 * been restarted, and we need to report it as not running to ethif.
371 for (slot
= 0; slot
< ndev_max
; slot
++) {
372 if (ndev_array
[slot
].ndev_endpt
== NONE
) {
374 ndev
= &ndev_array
[slot
];
379 if (!strcmp(ndev_array
[slot
].ndev_label
, label
)) {
380 /* Cancel any ongoing requests. */
381 ndev_queue_reset(&ndev_array
[slot
].ndev_sendq
);
382 ndev_queue_reset(&ndev_array
[slot
].ndev_recvq
);
384 if (ndev_array
[slot
].ndev_ethif
!= NULL
) {
385 ethif_disable(ndev_array
[slot
].ndev_ethif
);
390 ndev_array
[slot
].ndev_endpt
= endpt
;
392 /* Attempt to resume communication. */
393 ndev_send_init(&ndev_array
[slot
]);
401 * If there is no free slot for this driver in our table, we
402 * necessarily have to ignore the driver altogether. We report
403 * such cases once, so that the user can recompile if desired.
405 if (ndev_max
== __arraycount(ndev_array
)) {
407 printf("LWIP: not enough ndev slots!\n");
414 ndev
= &ndev_array
[ndev_max
++];
417 /* Initialize the slot. */
418 ndev
->ndev_endpt
= endpt
;
419 strlcpy(ndev
->ndev_label
, label
, sizeof(ndev
->ndev_label
));
420 ndev
->ndev_ethif
= NULL
;
421 ndev_queue_init(&ndev
->ndev_sendq
);
422 ndev_queue_init(&ndev
->ndev_recvq
);
424 ndev_send_init(ndev
);
430 * A network device driver has been terminated.
433 ndev_down(struct ndev
* ndev
)
436 /* Cancel any ongoing requests. */
437 ndev_queue_reset(&ndev
->ndev_sendq
);
438 ndev_queue_reset(&ndev
->ndev_recvq
);
441 * If this ndev object had a corresponding ethif object, tell the ethif
442 * layer that the device is really gone now.
444 if (ndev
->ndev_ethif
!= NULL
)
445 ethif_remove(ndev
->ndev_ethif
);
449 /* Remove the driver from our own administration. */
450 ndev
->ndev_endpt
= NONE
;
452 while (ndev_max
> 0 && ndev_array
[ndev_max
- 1].ndev_endpt
== NONE
)
457 * The DS service has notified us of changes to our subscriptions. That means
458 * that network drivers may have been started, restarted, and/or shut down.
459 * Find out what has changed, and act accordingly.
464 static const char *prefix
= "drv.net.";
465 char key
[DS_MAX_KEYLEN
], *label
;
472 prefixlen
= strlen(prefix
);
474 /* Check whether any drivers have been (re)started. */
475 while ((r
= ds_check(key
, NULL
, &endpt
)) == OK
) {
476 if (strncmp(key
, prefix
, prefixlen
) != 0 || endpt
== NONE
)
479 if (ds_retrieve_u32(key
, &val
) != OK
|| val
!= DS_DRIVER_UP
)
482 label
= &key
[prefixlen
];
483 if (label
[0] == '\0' || memchr(label
, '\0', LABEL_MAX
) == NULL
)
486 ndev_up(label
, endpt
);
490 printf("LWIP: DS check failed (%d)\n", r
);
493 * Check whether the drivers we currently know about are still up. The
494 * ones that are not are really gone. It is no problem that we recheck
495 * any drivers that have just been reported by ds_check() above.
496 * However, we cannot check the same key: while the driver is being
497 * restarted, its driver status is already gone from DS. Instead, see
498 * if there is still an entry for its label, as that entry remains in
499 * existence during the restart. The associated endpoint may still
500 * change however, so do not check that part: in such cases we will get
501 * a driver-up announcement later anyway.
503 for (slot
= 0; slot
< ndev_max
; slot
++) {
504 if (ndev_array
[slot
].ndev_endpt
== NONE
)
507 if (ds_retrieve_label_endpt(ndev_array
[slot
].ndev_label
,
509 ndev_down(&ndev_array
[slot
]);
514 * A network device driver has sent a reply to our initialization request.
517 ndev_init_reply(struct ndev
* ndev
, const message
* m_ptr
)
519 struct ndev_hwaddr hwaddr
;
520 uint8_t hwaddr_len
, max_send
, max_recv
;
525 * Make sure that we were waiting for a reply to an initialization
526 * request, and that this is the reply to that request.
528 if (NDEV_ACTIVE(ndev
) ||
529 m_ptr
->m_netdriver_ndev_init_reply
.id
!= ndev
->ndev_sendq
.nq_head
)
533 * Do just enough sanity checking on the data to pass it up to the
534 * ethif layer, which will check the rest (e.g., name duplicates).
536 if (memchr(m_ptr
->m_netdriver_ndev_init_reply
.name
, '\0',
537 sizeof(m_ptr
->m_netdriver_ndev_init_reply
.name
)) == NULL
||
538 m_ptr
->m_netdriver_ndev_init_reply
.name
[0] == '\0') {
539 printf("LWIP: driver %d provided invalid name\n",
547 hwaddr_len
= m_ptr
->m_netdriver_ndev_init_reply
.hwaddr_len
;
548 if (hwaddr_len
< 1 || hwaddr_len
> __arraycount(hwaddr
.nhwa_addr
)) {
549 printf("LWIP: driver %d provided invalid HW-addr length\n",
557 if ((max_send
= m_ptr
->m_netdriver_ndev_init_reply
.max_send
) < 1 ||
558 (max_recv
= m_ptr
->m_netdriver_ndev_init_reply
.max_recv
) < 1) {
559 printf("LWIP: driver %d provided invalid queue maximum\n",
568 * If the driver is new, allocate a new ethif object for it. On
569 * success, or if the driver was restarted, (re)enable the interface.
570 * Both calls may fail, in which case we should forget about the
571 * driver. It may continue to send us messages, which we should then
574 name
= m_ptr
->m_netdriver_ndev_init_reply
.name
;
576 if (ndev
->ndev_ethif
== NULL
) {
577 ndev
->ndev_ethif
= ethif_add((ndev_id_t
)(ndev
- ndev_array
),
578 name
, m_ptr
->m_netdriver_ndev_init_reply
.caps
);
582 if (ndev
->ndev_ethif
!= NULL
) {
584 * Set the maximum numbers of pending requests (for each
585 * direction) first, because enabling the interface may cause
586 * the ethif layer to start sending requests immediately.
588 ndev
->ndev_sendq
.nq_max
= max_send
;
589 ndev
->ndev_sendq
.nq_head
++;
592 * Limit the maximum number of concurrently pending receive
593 * requests to our configured maximum. For send requests, we
594 * use a more dynamic approach with spare request objects.
596 if (max_recv
> NDEV_RECVQ
)
597 max_recv
= NDEV_RECVQ
;
598 ndev
->ndev_recvq
.nq_max
= max_recv
;
599 ndev
->ndev_recvq
.nq_head
++;
601 memset(&hwaddr
, 0, sizeof(hwaddr
));
602 memcpy(hwaddr
.nhwa_addr
,
603 m_ptr
->m_netdriver_ndev_init_reply
.hwaddr
, hwaddr_len
);
606 * Provide a NULL pointer for the name if we have only just
607 * added the interface at all. The callee may use this to
608 * determine whether the driver is new or has been restarted.
610 enabled
= ethif_enable(ndev
->ndev_ethif
, name
, &hwaddr
,
611 m_ptr
->m_netdriver_ndev_init_reply
.hwaddr_len
,
612 m_ptr
->m_netdriver_ndev_init_reply
.caps
,
613 m_ptr
->m_netdriver_ndev_init_reply
.link
,
614 m_ptr
->m_netdriver_ndev_init_reply
.media
);
619 * If we did not manage to enable the interface, remove it again,
620 * possibly also from the ethif layer.
629 * Request that a network device driver change its configuration. This
630 * function allows for configuration of various different driver and device
631 * aspects: the I/O mode (and multicast receipt list), the enabled (sub)set of
632 * capabilities, the driver-specific flags, and the hardware address. Each of
633 * these settings may be changed by setting the corresponding NDEV_SET_ flag in
634 * the 'set' field of the given configuration structure. It is explicitly
635 * allowed to generate a request with no NDEV_SET_ flags; such a request will
636 * be sent to the driver and ultimately generate a response. Return OK if the
637 * configuration request was sent to the driver, EBUSY if no (more) requests
638 * can be sent to the driver right now, or ENOMEM on grant allocation failure.
641 ndev_conf(ndev_id_t id
, const struct ndev_conf
* nconf
)
644 struct ndev_req
*nreq
;
650 assert(id
< __arraycount(ndev_array
));
651 ndev
= &ndev_array
[id
];
653 assert(ndev
->ndev_endpt
!= NONE
);
654 assert(NDEV_ACTIVE(ndev
));
656 if ((nreq
= ndev_queue_get(&ndev
->ndev_sendq
, NDEV_CONF
,
660 memset(&m
, 0, sizeof(m
));
661 m
.m_type
= NDEV_CONF
;
662 m
.m_ndev_netdriver_conf
.id
= seq
;
663 m
.m_ndev_netdriver_conf
.set
= nconf
->nconf_set
;
665 grant
= GRANT_INVALID
;
667 if (nconf
->nconf_set
& NDEV_SET_MODE
) {
668 m
.m_ndev_netdriver_conf
.mode
= nconf
->nconf_mode
;
670 if (nconf
->nconf_mode
& NDEV_MODE_MCAST_LIST
) {
671 assert(nconf
->nconf_mclist
!= NULL
);
672 assert(nconf
->nconf_mccount
!= 0);
674 grant
= cpf_grant_direct(ndev
->ndev_endpt
,
675 (vir_bytes
)nconf
->nconf_mclist
,
676 sizeof(nconf
->nconf_mclist
[0]) *
677 nconf
->nconf_mccount
, CPF_READ
);
679 if (!GRANT_VALID(grant
))
682 m
.m_ndev_netdriver_conf
.mcast_count
=
683 nconf
->nconf_mccount
;
687 m
.m_ndev_netdriver_conf
.mcast_grant
= grant
;
689 if (nconf
->nconf_set
& NDEV_SET_CAPS
)
690 m
.m_ndev_netdriver_conf
.caps
= nconf
->nconf_caps
;
692 if (nconf
->nconf_set
& NDEV_SET_FLAGS
)
693 m
.m_ndev_netdriver_conf
.flags
= nconf
->nconf_flags
;
695 if (nconf
->nconf_set
& NDEV_SET_MEDIA
)
696 m
.m_ndev_netdriver_conf
.media
= nconf
->nconf_media
;
698 if (nconf
->nconf_set
& NDEV_SET_HWADDR
)
699 memcpy(m
.m_ndev_netdriver_conf
.hwaddr
,
700 nconf
->nconf_hwaddr
.nhwa_addr
,
701 __arraycount(m
.m_ndev_netdriver_conf
.hwaddr
));
703 if ((r
= asynsend3(ndev
->ndev_endpt
, &m
, AMF_NOREPLY
)) != OK
)
704 panic("asynsend to driver failed: %d", r
);
706 nreq
->nreq_grant
[0] = grant
; /* may also be invalid */
707 nreq
->nreq_grant
[1] = GRANT_INVALID
;
709 ndev_queue_add(&ndev
->ndev_sendq
, nreq
);
715 * The network device driver has sent a reply to a configuration request.
718 ndev_conf_reply(struct ndev
* ndev
, const message
* m_ptr
)
722 * Was this the request we were waiting for? If so, remove it from the
723 * send queue. Otherwise, ignore this reply message.
725 if (!NDEV_ACTIVE(ndev
) || !ndev_queue_remove(&ndev
->ndev_sendq
,
726 NDEV_CONF
, m_ptr
->m_netdriver_ndev_reply
.id
))
729 /* Tell the ethif layer about the updated configuration. */
730 assert(ndev
->ndev_ethif
!= NULL
);
732 ethif_configured(ndev
->ndev_ethif
,
733 m_ptr
->m_netdriver_ndev_reply
.result
);
737 * Construct a packet send or receive request and send it off to a network
738 * driver. The given pbuf chain may be part of a queue. Return OK if the
739 * request was successfully sent, or ENOMEM on grant allocation failure.
742 ndev_transfer(struct ndev
* ndev
, const struct pbuf
* pbuf
, int do_send
,
743 uint32_t seq
, struct ndev_req
* nreq
)
751 memset(&m
, 0, sizeof(m
));
752 m
.m_type
= (do_send
) ? NDEV_SEND
: NDEV_RECV
;
753 m
.m_ndev_netdriver_transfer
.id
= seq
;
755 left
= pbuf
->tot_len
;
757 for (i
= 0; left
> 0; i
++) {
758 assert(i
< NDEV_IOV_MAX
);
760 grant
= cpf_grant_direct(ndev
->ndev_endpt
,
761 (vir_bytes
)pbuf
->payload
, pbuf
->len
,
762 (do_send
) ? CPF_READ
: CPF_WRITE
);
764 if (!GRANT_VALID(grant
)) {
766 (void)cpf_revoke(nreq
->nreq_grant
[i
]);
771 m
.m_ndev_netdriver_transfer
.grant
[i
] = grant
;
772 m
.m_ndev_netdriver_transfer
.len
[i
] = pbuf
->len
;
774 nreq
->nreq_grant
[i
] = grant
;
776 assert(left
>= pbuf
->len
);
781 m
.m_ndev_netdriver_transfer
.count
= i
;
784 * Unless the array is full, an invalid grant marks the end of the list
787 if (i
< __arraycount(nreq
->nreq_grant
))
788 nreq
->nreq_grant
[i
] = GRANT_INVALID
;
790 if ((r
= asynsend3(ndev
->ndev_endpt
, &m
, AMF_NOREPLY
)) != OK
)
791 panic("asynsend to driver failed: %d", r
);
797 * Send a packet to the given network driver. Return OK if the packet is sent
798 * off to the driver, EBUSY if no (more) packets can be sent to the driver at
799 * this time, or ENOMEM on grant allocation failure.
801 * The use of 'pbuf' in this interface is a bit ugly, but it saves us from
802 * having to go through an intermediate representation (e.g. an iovec array)
803 * for the data being sent. The same applies to ndev_receive().
806 ndev_send(ndev_id_t id
, const struct pbuf
* pbuf
)
809 struct ndev_req
*nreq
;
813 assert(id
< __arraycount(ndev_array
));
814 ndev
= &ndev_array
[id
];
816 assert(ndev
->ndev_endpt
!= NONE
);
817 assert(NDEV_ACTIVE(ndev
));
819 if ((nreq
= ndev_queue_get(&ndev
->ndev_sendq
, NDEV_SEND
,
823 if ((r
= ndev_transfer(ndev
, pbuf
, TRUE
/*do_send*/, seq
, nreq
)) != OK
)
826 ndev_queue_add(&ndev
->ndev_sendq
, nreq
);
832 * The network device driver has sent a reply to a send request.
835 ndev_send_reply(struct ndev
* ndev
, const message
* m_ptr
)
839 * Was this the request we were waiting for? If so, remove it from the
840 * send queue. Otherwise, ignore this reply message.
842 if (!NDEV_ACTIVE(ndev
) || !ndev_queue_remove(&ndev
->ndev_sendq
,
843 NDEV_SEND
, m_ptr
->m_netdriver_ndev_reply
.id
))
846 /* Tell the ethif layer about the result of the transmission. */
847 assert(ndev
->ndev_ethif
!= NULL
);
849 ethif_sent(ndev
->ndev_ethif
,
850 m_ptr
->m_netdriver_ndev_reply
.result
);
854 * Return TRUE if a new receive request can be spawned for a particular network
855 * driver, or FALSE if its queue of receive requests is full. This call exists
856 * merely to avoid needless buffer allocatin in the case that ndev_recv() is
857 * going to return EBUSY anyway.
860 ndev_can_recv(ndev_id_t id
)
864 assert(id
< __arraycount(ndev_array
));
865 ndev
= &ndev_array
[id
];
867 assert(ndev
->ndev_endpt
!= NONE
);
868 assert(NDEV_ACTIVE(ndev
));
870 return (ndev
->ndev_recvq
.nq_count
< ndev
->ndev_recvq
.nq_max
);
874 * Start the process of receiving a packet from a network driver. The packet
875 * will be stored in the given pbuf chain upon completion. Return OK if the
876 * receive request is sent to the driver, EBUSY if the maximum number of
877 * concurrent receive requests has been reached for this driver, or ENOMEM on
878 * grant allocation failure.
881 ndev_recv(ndev_id_t id
, struct pbuf
* pbuf
)
884 struct ndev_req
*nreq
;
888 assert(id
< __arraycount(ndev_array
));
889 ndev
= &ndev_array
[id
];
891 assert(ndev
->ndev_endpt
!= NONE
);
892 assert(NDEV_ACTIVE(ndev
));
894 if ((nreq
= ndev_queue_get(&ndev
->ndev_recvq
, NDEV_RECV
,
898 if ((r
= ndev_transfer(ndev
, pbuf
, FALSE
/*do_send*/, seq
,
902 ndev_queue_add(&ndev
->ndev_recvq
, nreq
);
908 * The network device driver has sent a reply to a receive request.
911 ndev_recv_reply(struct ndev
* ndev
, const message
* m_ptr
)
915 * Was this the request we were waiting for? If so, remove it from the
916 * receive queue. Otherwise, ignore this reply message.
918 if (!NDEV_ACTIVE(ndev
) || !ndev_queue_remove(&ndev
->ndev_recvq
,
919 NDEV_RECV
, m_ptr
->m_netdriver_ndev_reply
.id
))
922 /* Tell the ethif layer about the result of the receipt. */
923 assert(ndev
->ndev_ethif
!= NULL
);
925 ethif_received(ndev
->ndev_ethif
,
926 m_ptr
->m_netdriver_ndev_reply
.result
);
930 * A network device driver sent a status report to us. Process it and send a
934 ndev_status(struct ndev
* ndev
, const message
* m_ptr
)
939 if (!NDEV_ACTIVE(ndev
))
942 /* Tell the ethif layer about the status update. */
943 assert(ndev
->ndev_ethif
!= NULL
);
945 ethif_status(ndev
->ndev_ethif
, m_ptr
->m_netdriver_ndev_status
.link
,
946 m_ptr
->m_netdriver_ndev_status
.media
,
947 m_ptr
->m_netdriver_ndev_status
.oerror
,
948 m_ptr
->m_netdriver_ndev_status
.coll
,
949 m_ptr
->m_netdriver_ndev_status
.ierror
,
950 m_ptr
->m_netdriver_ndev_status
.iqdrop
);
953 * Send a reply, so that the driver knows it can send a new status
954 * update without risking asynsend queue overflows. The ID of these
955 * messages is chosen by the driver and and we simply echo it.
957 memset(&m
, 0, sizeof(m
));
958 m
.m_type
= NDEV_STATUS_REPLY
;
959 m
.m_ndev_netdriver_status_reply
.id
= m_ptr
->m_netdriver_ndev_status
.id
;
961 if ((r
= asynsend(m_ptr
->m_source
, &m
)) != OK
)
962 panic("asynsend to driver failed: %d", r
);
966 * Process a network driver reply message.
969 ndev_process(const message
* m_ptr
, int ipc_status
)
975 /* Find the slot of the driver that sent the message, if any. */
976 endpt
= m_ptr
->m_source
;
978 for (slot
= 0, ndev
= ndev_array
; slot
< ndev_max
; slot
++, ndev
++)
979 if (ndev
->ndev_endpt
== endpt
)
983 * If we cannot find a slot for the driver, drop the message. We may
984 * be ignoring the driver because it misbehaved or we are out of slots.
986 if (slot
== ndev_max
)
990 * Process the reply message. For future compatibility, ignore any
991 * unrecognized message types.
993 switch (m_ptr
->m_type
) {
994 case NDEV_INIT_REPLY
:
995 ndev_init_reply(ndev
, m_ptr
);
999 case NDEV_CONF_REPLY
:
1000 ndev_conf_reply(ndev
, m_ptr
);
1004 case NDEV_SEND_REPLY
:
1005 ndev_send_reply(ndev
, m_ptr
);
1009 case NDEV_RECV_REPLY
:
1010 ndev_recv_reply(ndev
, m_ptr
);
1015 ndev_status(ndev
, m_ptr
);