1dbcce9ddSDavid van Moolenbroek /* The device-independent network driver framework. */
2433d6423SLionel Sambuc
3433d6423SLionel Sambuc #include <minix/drivers.h>
4433d6423SLionel Sambuc #include <minix/netdriver.h>
5433d6423SLionel Sambuc #include <minix/ds.h>
6dbcce9ddSDavid van Moolenbroek #include <assert.h>
7433d6423SLionel Sambuc
8dbcce9ddSDavid van Moolenbroek #include "netdriver.h"
9433d6423SLionel Sambuc
10*f7df02e7SDavid van Moolenbroek /*
11*f7df02e7SDavid van Moolenbroek * These maximum values should be at least somewhat synchronized with the
12*f7df02e7SDavid van Moolenbroek * values in the LWIP service's ndev module.
13*f7df02e7SDavid van Moolenbroek */
14*f7df02e7SDavid van Moolenbroek #define NETDRIVER_SENDQ_MAX 8
15*f7df02e7SDavid van Moolenbroek #define NETDRIVER_RECVQ_MAX 2
16*f7df02e7SDavid van Moolenbroek
17*f7df02e7SDavid van Moolenbroek /*
18*f7df02e7SDavid van Moolenbroek * Maximum number of multicast addresses that can be copied in from the TCP/IP
19*f7df02e7SDavid van Moolenbroek * service and passed to the driver. If the actual number from the service
20*f7df02e7SDavid van Moolenbroek * exceeds this maximum, the driver will be told to receive all multicast
21*f7df02e7SDavid van Moolenbroek * packets instead.
22*f7df02e7SDavid van Moolenbroek */
23*f7df02e7SDavid van Moolenbroek #define NETDRIVER_MCAST_MAX 16
24*f7df02e7SDavid van Moolenbroek
25dbcce9ddSDavid van Moolenbroek static const struct netdriver *netdriver_table = NULL;
26dbcce9ddSDavid van Moolenbroek
27dbcce9ddSDavid van Moolenbroek static int running;
28dbcce9ddSDavid van Moolenbroek
29*f7df02e7SDavid van Moolenbroek static int init_expected;
30dbcce9ddSDavid van Moolenbroek
31*f7df02e7SDavid van Moolenbroek static int up;
32dbcce9ddSDavid van Moolenbroek
33*f7df02e7SDavid van Moolenbroek static unsigned int ticks;
34dbcce9ddSDavid van Moolenbroek
35*f7df02e7SDavid van Moolenbroek static struct netdriver_data pending_sendq[NETDRIVER_SENDQ_MAX];
36*f7df02e7SDavid van Moolenbroek static unsigned int pending_sends, pending_sendtail;
37*f7df02e7SDavid van Moolenbroek
38*f7df02e7SDavid van Moolenbroek static struct netdriver_data pending_recvq[NETDRIVER_RECVQ_MAX];
39*f7df02e7SDavid van Moolenbroek static unsigned int pending_recvs, pending_recvtail;
40*f7df02e7SDavid van Moolenbroek
41*f7df02e7SDavid van Moolenbroek static int pending_status;
42*f7df02e7SDavid van Moolenbroek static endpoint_t status_endpt;
43*f7df02e7SDavid van Moolenbroek
44*f7df02e7SDavid van Moolenbroek static int pending_link, pending_stat;
45*f7df02e7SDavid van Moolenbroek static uint32_t stat_oerror, stat_coll, stat_ierror, stat_iqdrop;
46*f7df02e7SDavid van Moolenbroek
47*f7df02e7SDavid van Moolenbroek static char device_name[NDEV_NAME_MAX];
48*f7df02e7SDavid van Moolenbroek static netdriver_addr_t device_hwaddr;
49*f7df02e7SDavid van Moolenbroek static uint32_t device_caps;
50*f7df02e7SDavid van Moolenbroek
51*f7df02e7SDavid van Moolenbroek static unsigned int device_link;
52*f7df02e7SDavid van Moolenbroek static uint32_t device_media;
53dbcce9ddSDavid van Moolenbroek
54dbcce9ddSDavid van Moolenbroek /*
55dbcce9ddSDavid van Moolenbroek * Announce we are up after a fresh start or restart.
56dbcce9ddSDavid van Moolenbroek */
57*f7df02e7SDavid van Moolenbroek static void
netdriver_announce(void)58dbcce9ddSDavid van Moolenbroek netdriver_announce(void)
59433d6423SLionel Sambuc {
6065f76edbSDavid van Moolenbroek const char *driver_prefix = "drv.net.";
61dbcce9ddSDavid van Moolenbroek char label[DS_MAX_KEYLEN];
62dbcce9ddSDavid van Moolenbroek char key[DS_MAX_KEYLEN];
63dbcce9ddSDavid van Moolenbroek int r;
64433d6423SLionel Sambuc
65433d6423SLionel Sambuc /* Publish a driver up event. */
66dbcce9ddSDavid van Moolenbroek if ((r = ds_retrieve_label_name(label, sef_self())) != OK)
67dbcce9ddSDavid van Moolenbroek panic("netdriver: unable to get own label: %d", r);
68dbcce9ddSDavid van Moolenbroek
69dbcce9ddSDavid van Moolenbroek snprintf(key, sizeof(key), "%s%s", driver_prefix, label);
70dbcce9ddSDavid van Moolenbroek if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK)
71dbcce9ddSDavid van Moolenbroek panic("netdriver: unable to publish driver up event: %d", r);
72433d6423SLionel Sambuc }
73433d6423SLionel Sambuc
74dbcce9ddSDavid van Moolenbroek /*
75dbcce9ddSDavid van Moolenbroek * Prepare for copying. Given a flat offset, return the vector element index
76dbcce9ddSDavid van Moolenbroek * and an offset into that element. Panic if the request does not fall
77dbcce9ddSDavid van Moolenbroek * entirely within the vector.
78dbcce9ddSDavid van Moolenbroek */
79dbcce9ddSDavid van Moolenbroek size_t
netdriver_prepare_copy(struct netdriver_data * data,size_t off,size_t size,unsigned int * indexp)80dbcce9ddSDavid van Moolenbroek netdriver_prepare_copy(struct netdriver_data * data, size_t off, size_t size,
81dbcce9ddSDavid van Moolenbroek unsigned int * indexp)
82433d6423SLionel Sambuc {
83dbcce9ddSDavid van Moolenbroek unsigned int i;
84433d6423SLionel Sambuc
85dbcce9ddSDavid van Moolenbroek assert(data->size > 0);
86433d6423SLionel Sambuc
87dbcce9ddSDavid van Moolenbroek /*
88dbcce9ddSDavid van Moolenbroek * In theory we could truncate when copying out, but this creates a
89dbcce9ddSDavid van Moolenbroek * problem for port-based I/O, where the size of the transfer is
90dbcce9ddSDavid van Moolenbroek * typically specified in advance. We could do extra port-based I/O
91dbcce9ddSDavid van Moolenbroek * to discard the extra bytes, but the driver is better off doing such
92dbcce9ddSDavid van Moolenbroek * truncation itself. Thus, we disallow copying (in and out) beyond
93dbcce9ddSDavid van Moolenbroek * the given data vector altogether.
94dbcce9ddSDavid van Moolenbroek */
95dbcce9ddSDavid van Moolenbroek if (off + size > data->size)
96dbcce9ddSDavid van Moolenbroek panic("netdriver: request to copy beyond data size");
97433d6423SLionel Sambuc
98dbcce9ddSDavid van Moolenbroek /*
99dbcce9ddSDavid van Moolenbroek * Find the starting offset in the vector. If this turns out to be
100dbcce9ddSDavid van Moolenbroek * expensive, this can be adapted to store the last <element,offset>
101dbcce9ddSDavid van Moolenbroek * pair in the "data" structure (this is the reason it is not 'const').
102dbcce9ddSDavid van Moolenbroek */
103dbcce9ddSDavid van Moolenbroek for (i = 0; i < data->count; i++) {
104dbcce9ddSDavid van Moolenbroek assert(data->iovec[i].iov_size > 0);
105433d6423SLionel Sambuc
106dbcce9ddSDavid van Moolenbroek if (off >= data->iovec[i].iov_size)
107dbcce9ddSDavid van Moolenbroek off -= data->iovec[i].iov_size;
108dbcce9ddSDavid van Moolenbroek else
109433d6423SLionel Sambuc break;
110433d6423SLionel Sambuc }
111433d6423SLionel Sambuc
112dbcce9ddSDavid van Moolenbroek assert(i < data->count);
113dbcce9ddSDavid van Moolenbroek
114dbcce9ddSDavid van Moolenbroek *indexp = i;
115dbcce9ddSDavid van Moolenbroek return off;
116dbcce9ddSDavid van Moolenbroek }
117dbcce9ddSDavid van Moolenbroek
118dbcce9ddSDavid van Moolenbroek /*
119dbcce9ddSDavid van Moolenbroek * Copy in or out packet data from/to a vector of grants.
120dbcce9ddSDavid van Moolenbroek */
121dbcce9ddSDavid van Moolenbroek static void
netdriver_copy(struct netdriver_data * data,size_t off,vir_bytes addr,size_t size,int copyin)122dbcce9ddSDavid van Moolenbroek netdriver_copy(struct netdriver_data * data, size_t off, vir_bytes addr,
123dbcce9ddSDavid van Moolenbroek size_t size, int copyin)
124dbcce9ddSDavid van Moolenbroek {
125dbcce9ddSDavid van Moolenbroek struct vscp_vec vec[SCPVEC_NR];
126dbcce9ddSDavid van Moolenbroek size_t chunk;
127dbcce9ddSDavid van Moolenbroek unsigned int i, v;
128dbcce9ddSDavid van Moolenbroek int r;
129dbcce9ddSDavid van Moolenbroek
130dbcce9ddSDavid van Moolenbroek off = netdriver_prepare_copy(data, off, size, &i);
131dbcce9ddSDavid van Moolenbroek
132dbcce9ddSDavid van Moolenbroek /* Generate a new vector with all the individual copies to make. */
133dbcce9ddSDavid van Moolenbroek for (v = 0; size > 0; v++) {
134dbcce9ddSDavid van Moolenbroek chunk = data->iovec[i].iov_size - off;
135dbcce9ddSDavid van Moolenbroek if (chunk > size)
136dbcce9ddSDavid van Moolenbroek chunk = size;
137dbcce9ddSDavid van Moolenbroek assert(chunk > 0);
138dbcce9ddSDavid van Moolenbroek
139dbcce9ddSDavid van Moolenbroek /*
140dbcce9ddSDavid van Moolenbroek * We should be able to fit the entire I/O request in a single
141dbcce9ddSDavid van Moolenbroek * copy vector. If not, MINIX3 has been misconfigured.
142dbcce9ddSDavid van Moolenbroek */
143dbcce9ddSDavid van Moolenbroek if (v >= SCPVEC_NR)
144dbcce9ddSDavid van Moolenbroek panic("netdriver: invalid vector size constant");
145dbcce9ddSDavid van Moolenbroek
146dbcce9ddSDavid van Moolenbroek if (copyin) {
147dbcce9ddSDavid van Moolenbroek vec[v].v_from = data->endpt;
148dbcce9ddSDavid van Moolenbroek vec[v].v_to = SELF;
149dbcce9ddSDavid van Moolenbroek } else {
150dbcce9ddSDavid van Moolenbroek vec[v].v_from = SELF;
151dbcce9ddSDavid van Moolenbroek vec[v].v_to = data->endpt;
152dbcce9ddSDavid van Moolenbroek }
153dbcce9ddSDavid van Moolenbroek vec[v].v_gid = data->iovec[i].iov_grant;
154dbcce9ddSDavid van Moolenbroek vec[v].v_offset = off;
155dbcce9ddSDavid van Moolenbroek vec[v].v_addr = addr;
156dbcce9ddSDavid van Moolenbroek vec[v].v_bytes = chunk;
157dbcce9ddSDavid van Moolenbroek
158dbcce9ddSDavid van Moolenbroek i++;
159dbcce9ddSDavid van Moolenbroek off = 0;
160dbcce9ddSDavid van Moolenbroek addr += chunk;
161dbcce9ddSDavid van Moolenbroek size -= chunk;
162dbcce9ddSDavid van Moolenbroek }
163dbcce9ddSDavid van Moolenbroek
164dbcce9ddSDavid van Moolenbroek assert(v > 0 && v <= SCPVEC_NR);
165dbcce9ddSDavid van Moolenbroek
166dbcce9ddSDavid van Moolenbroek /*
167dbcce9ddSDavid van Moolenbroek * If only one vector element was generated, use a direct copy. This
168dbcce9ddSDavid van Moolenbroek * saves the kernel from having to copy in the vector.
169dbcce9ddSDavid van Moolenbroek */
170dbcce9ddSDavid van Moolenbroek if (v == 1) {
171dbcce9ddSDavid van Moolenbroek if (copyin)
172dbcce9ddSDavid van Moolenbroek r = sys_safecopyfrom(vec->v_from, vec->v_gid,
173dbcce9ddSDavid van Moolenbroek vec->v_offset, vec->v_addr, vec->v_bytes);
174dbcce9ddSDavid van Moolenbroek else
175dbcce9ddSDavid van Moolenbroek r = sys_safecopyto(vec->v_to, vec->v_gid,
176dbcce9ddSDavid van Moolenbroek vec->v_offset, vec->v_addr, vec->v_bytes);
177dbcce9ddSDavid van Moolenbroek } else
178dbcce9ddSDavid van Moolenbroek r = sys_vsafecopy(vec, v);
179dbcce9ddSDavid van Moolenbroek
180dbcce9ddSDavid van Moolenbroek if (r != OK)
181dbcce9ddSDavid van Moolenbroek panic("netdriver: unable to copy data: %d", r);
182dbcce9ddSDavid van Moolenbroek }
183dbcce9ddSDavid van Moolenbroek
184dbcce9ddSDavid van Moolenbroek /*
185dbcce9ddSDavid van Moolenbroek * Copy in packet data.
186dbcce9ddSDavid van Moolenbroek */
187dbcce9ddSDavid van Moolenbroek void
netdriver_copyin(struct netdriver_data * __restrict data,size_t off,void * __restrict ptr,size_t size)188dbcce9ddSDavid van Moolenbroek netdriver_copyin(struct netdriver_data * __restrict data, size_t off,
189dbcce9ddSDavid van Moolenbroek void * __restrict ptr, size_t size)
190dbcce9ddSDavid van Moolenbroek {
191dbcce9ddSDavid van Moolenbroek
192dbcce9ddSDavid van Moolenbroek netdriver_copy(data, off, (vir_bytes)ptr, size, TRUE /*copyin*/);
193dbcce9ddSDavid van Moolenbroek }
194dbcce9ddSDavid van Moolenbroek
195dbcce9ddSDavid van Moolenbroek /*
196dbcce9ddSDavid van Moolenbroek * Copy out packet data.
197dbcce9ddSDavid van Moolenbroek */
198dbcce9ddSDavid van Moolenbroek void
netdriver_copyout(struct netdriver_data * __restrict data,size_t off,const void * __restrict ptr,size_t size)199dbcce9ddSDavid van Moolenbroek netdriver_copyout(struct netdriver_data * __restrict data, size_t off,
200dbcce9ddSDavid van Moolenbroek const void * __restrict ptr, size_t size)
201dbcce9ddSDavid van Moolenbroek {
202dbcce9ddSDavid van Moolenbroek
203dbcce9ddSDavid van Moolenbroek netdriver_copy(data, off, (vir_bytes)ptr, size, FALSE /*copyin*/);
204dbcce9ddSDavid van Moolenbroek }
205dbcce9ddSDavid van Moolenbroek
206dbcce9ddSDavid van Moolenbroek /*
207dbcce9ddSDavid van Moolenbroek * Send a reply to a request.
208dbcce9ddSDavid van Moolenbroek */
209dbcce9ddSDavid van Moolenbroek static void
send_reply(endpoint_t endpt,message * m_ptr)210dbcce9ddSDavid van Moolenbroek send_reply(endpoint_t endpt, message * m_ptr)
211dbcce9ddSDavid van Moolenbroek {
212dbcce9ddSDavid van Moolenbroek int r;
213dbcce9ddSDavid van Moolenbroek
214*f7df02e7SDavid van Moolenbroek if ((r = asynsend(endpt, m_ptr)) != OK)
215dbcce9ddSDavid van Moolenbroek panic("netdriver: unable to send to %d: %d", endpt, r);
216dbcce9ddSDavid van Moolenbroek }
217dbcce9ddSDavid van Moolenbroek
218dbcce9ddSDavid van Moolenbroek /*
219*f7df02e7SDavid van Moolenbroek * A packet receive request has finished. Send a reply and clean up.
220dbcce9ddSDavid van Moolenbroek */
221dbcce9ddSDavid van Moolenbroek static void
finish_recv(int32_t result)222*f7df02e7SDavid van Moolenbroek finish_recv(int32_t result)
223dbcce9ddSDavid van Moolenbroek {
224*f7df02e7SDavid van Moolenbroek struct netdriver_data *data;
225*f7df02e7SDavid van Moolenbroek message m;
226dbcce9ddSDavid van Moolenbroek
227*f7df02e7SDavid van Moolenbroek assert(pending_recvs > 0);
228dbcce9ddSDavid van Moolenbroek
229*f7df02e7SDavid van Moolenbroek data = &pending_recvq[pending_recvtail];
230dbcce9ddSDavid van Moolenbroek
231*f7df02e7SDavid van Moolenbroek memset(&m, 0, sizeof(m));
232*f7df02e7SDavid van Moolenbroek m.m_type = NDEV_RECV_REPLY;
233*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_reply.id = data->id;
234*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_reply.result = result;
235dbcce9ddSDavid van Moolenbroek
236*f7df02e7SDavid van Moolenbroek send_reply(data->endpt, &m);
237dbcce9ddSDavid van Moolenbroek
238*f7df02e7SDavid van Moolenbroek pending_recvtail = (pending_recvtail + 1) %
239*f7df02e7SDavid van Moolenbroek __arraycount(pending_recvq);
240*f7df02e7SDavid van Moolenbroek pending_recvs--;
241dbcce9ddSDavid van Moolenbroek }
242dbcce9ddSDavid van Moolenbroek
243dbcce9ddSDavid van Moolenbroek /*
244dbcce9ddSDavid van Moolenbroek * Resume receiving packets. In particular, if a receive request was pending,
245*f7df02e7SDavid van Moolenbroek * call the driver's receive function. If the call is successful, send a reply
246*f7df02e7SDavid van Moolenbroek * to the requesting party.
247dbcce9ddSDavid van Moolenbroek */
248dbcce9ddSDavid van Moolenbroek void
netdriver_recv(void)249dbcce9ddSDavid van Moolenbroek netdriver_recv(void)
250dbcce9ddSDavid van Moolenbroek {
251*f7df02e7SDavid van Moolenbroek struct netdriver_data *data;
252dbcce9ddSDavid van Moolenbroek ssize_t r;
253dbcce9ddSDavid van Moolenbroek
254dbcce9ddSDavid van Moolenbroek assert(netdriver_table != NULL);
255dbcce9ddSDavid van Moolenbroek
256*f7df02e7SDavid van Moolenbroek while (pending_recvs > 0) {
257*f7df02e7SDavid van Moolenbroek data = &pending_recvq[pending_recvtail];
258dbcce9ddSDavid van Moolenbroek
259dbcce9ddSDavid van Moolenbroek /*
260*f7df02e7SDavid van Moolenbroek * For convenience of driver writers: if the receive function
261*f7df02e7SDavid van Moolenbroek * returns zero, simply call it again, to simplify discarding
262*f7df02e7SDavid van Moolenbroek * invalid packets.
263dbcce9ddSDavid van Moolenbroek */
264*f7df02e7SDavid van Moolenbroek do {
265*f7df02e7SDavid van Moolenbroek r = netdriver_table->ndr_recv(data, data->size);
266*f7df02e7SDavid van Moolenbroek
267*f7df02e7SDavid van Moolenbroek /*
268*f7df02e7SDavid van Moolenbroek * The default policy is: drop undersized packets,
269*f7df02e7SDavid van Moolenbroek * panic on oversized packets. The driver may
270*f7df02e7SDavid van Moolenbroek * implement any other policy (e.g., pad small packets,
271*f7df02e7SDavid van Moolenbroek * drop or truncate large packets), but it should at
272*f7df02e7SDavid van Moolenbroek * least test against the given 'max' value. The
273*f7df02e7SDavid van Moolenbroek * reason that truncation should be implemented in the
274*f7df02e7SDavid van Moolenbroek * driver rather than here, is explained in an earlier
275*f7df02e7SDavid van Moolenbroek * comment about truncating copy operations.
276*f7df02e7SDavid van Moolenbroek */
277*f7df02e7SDavid van Moolenbroek if (r >= 0 && r < NDEV_ETH_PACKET_MIN)
278dbcce9ddSDavid van Moolenbroek r = 0;
279*f7df02e7SDavid van Moolenbroek else if (r > (ssize_t)data->size)
280*f7df02e7SDavid van Moolenbroek panic("netdriver: oversized packet returned: "
281*f7df02e7SDavid van Moolenbroek "%zd", r);
282dbcce9ddSDavid van Moolenbroek } while (r == 0);
283dbcce9ddSDavid van Moolenbroek
284dbcce9ddSDavid van Moolenbroek if (r == SUSPEND)
285*f7df02e7SDavid van Moolenbroek break;
286*f7df02e7SDavid van Moolenbroek
287dbcce9ddSDavid van Moolenbroek if (r < 0)
288*f7df02e7SDavid van Moolenbroek panic("netdriver: driver reported receive failure: %d",
289*f7df02e7SDavid van Moolenbroek r);
290dbcce9ddSDavid van Moolenbroek
291*f7df02e7SDavid van Moolenbroek assert(r >= NDEV_ETH_PACKET_MIN && (size_t)r <= data->size);
292dbcce9ddSDavid van Moolenbroek
293*f7df02e7SDavid van Moolenbroek finish_recv(r);
294*f7df02e7SDavid van Moolenbroek }
295dbcce9ddSDavid van Moolenbroek }
296dbcce9ddSDavid van Moolenbroek
297dbcce9ddSDavid van Moolenbroek /*
298*f7df02e7SDavid van Moolenbroek * A packet send request has finished. Send a reply and clean up.
299*f7df02e7SDavid van Moolenbroek */
300*f7df02e7SDavid van Moolenbroek static void
finish_send(int32_t result)301*f7df02e7SDavid van Moolenbroek finish_send(int32_t result)
302*f7df02e7SDavid van Moolenbroek {
303*f7df02e7SDavid van Moolenbroek struct netdriver_data *data;
304*f7df02e7SDavid van Moolenbroek message m;
305*f7df02e7SDavid van Moolenbroek
306*f7df02e7SDavid van Moolenbroek assert(pending_sends > 0);
307*f7df02e7SDavid van Moolenbroek
308*f7df02e7SDavid van Moolenbroek data = &pending_sendq[pending_sendtail];
309*f7df02e7SDavid van Moolenbroek
310*f7df02e7SDavid van Moolenbroek memset(&m, 0, sizeof(m));
311*f7df02e7SDavid van Moolenbroek m.m_type = NDEV_SEND_REPLY;
312*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_reply.id = data->id;
313*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_reply.result = result;
314*f7df02e7SDavid van Moolenbroek
315*f7df02e7SDavid van Moolenbroek send_reply(data->endpt, &m);
316*f7df02e7SDavid van Moolenbroek
317*f7df02e7SDavid van Moolenbroek pending_sendtail = (pending_sendtail + 1) %
318*f7df02e7SDavid van Moolenbroek __arraycount(pending_sendq);
319*f7df02e7SDavid van Moolenbroek pending_sends--;
320*f7df02e7SDavid van Moolenbroek }
321*f7df02e7SDavid van Moolenbroek
322*f7df02e7SDavid van Moolenbroek /*
323*f7df02e7SDavid van Moolenbroek * Resume sending packets. In particular, if any send requests were pending,
324*f7df02e7SDavid van Moolenbroek * call the driver's send function for each of them, until the driver can take
325*f7df02e7SDavid van Moolenbroek * no more. For each successful request is successful, send a reply to the
326*f7df02e7SDavid van Moolenbroek * requesting party.
327dbcce9ddSDavid van Moolenbroek */
328dbcce9ddSDavid van Moolenbroek void
netdriver_send(void)329dbcce9ddSDavid van Moolenbroek netdriver_send(void)
330dbcce9ddSDavid van Moolenbroek {
331*f7df02e7SDavid van Moolenbroek struct netdriver_data *data;
332dbcce9ddSDavid van Moolenbroek int r;
333dbcce9ddSDavid van Moolenbroek
334dbcce9ddSDavid van Moolenbroek assert(netdriver_table != NULL);
335dbcce9ddSDavid van Moolenbroek
336*f7df02e7SDavid van Moolenbroek while (pending_sends > 0) {
337*f7df02e7SDavid van Moolenbroek data = &pending_sendq[pending_sendtail];
338*f7df02e7SDavid van Moolenbroek
339*f7df02e7SDavid van Moolenbroek r = netdriver_table->ndr_send(data, data->size);
340dbcce9ddSDavid van Moolenbroek
341dbcce9ddSDavid van Moolenbroek if (r == SUSPEND)
342*f7df02e7SDavid van Moolenbroek break;
343*f7df02e7SDavid van Moolenbroek
344dbcce9ddSDavid van Moolenbroek if (r < 0)
345*f7df02e7SDavid van Moolenbroek panic("netdriver: driver reported send failure: %d",
346*f7df02e7SDavid van Moolenbroek r);
347dbcce9ddSDavid van Moolenbroek
348*f7df02e7SDavid van Moolenbroek finish_send(r);
349*f7df02e7SDavid van Moolenbroek }
350dbcce9ddSDavid van Moolenbroek }
351dbcce9ddSDavid van Moolenbroek
352dbcce9ddSDavid van Moolenbroek /*
353*f7df02e7SDavid van Moolenbroek * Process a request to send or receive a packet.
354dbcce9ddSDavid van Moolenbroek */
355dbcce9ddSDavid van Moolenbroek static void
do_transfer(const struct netdriver * __restrict ndp,const message * m_ptr,int do_write)356*f7df02e7SDavid van Moolenbroek do_transfer(const struct netdriver * __restrict ndp, const message * m_ptr,
357*f7df02e7SDavid van Moolenbroek int do_write)
358dbcce9ddSDavid van Moolenbroek {
359dbcce9ddSDavid van Moolenbroek struct netdriver_data *data;
360*f7df02e7SDavid van Moolenbroek cp_grant_id_t grant;
361*f7df02e7SDavid van Moolenbroek size_t size;
362dbcce9ddSDavid van Moolenbroek unsigned int i;
363dbcce9ddSDavid van Moolenbroek
364*f7df02e7SDavid van Moolenbroek /* Prepare the local data structure. */
365*f7df02e7SDavid van Moolenbroek if (do_write) {
366*f7df02e7SDavid van Moolenbroek if (pending_sends == __arraycount(pending_sendq))
367*f7df02e7SDavid van Moolenbroek panic("netdriver: too many concurrent send requests");
368dbcce9ddSDavid van Moolenbroek
369*f7df02e7SDavid van Moolenbroek data = &pending_sendq[(pending_sendtail + pending_sends) %
370*f7df02e7SDavid van Moolenbroek __arraycount(pending_sendq)];
371*f7df02e7SDavid van Moolenbroek } else {
372*f7df02e7SDavid van Moolenbroek if (pending_recvs == __arraycount(pending_recvq))
373*f7df02e7SDavid van Moolenbroek panic("netdriver: too many concurrent receive "
374*f7df02e7SDavid van Moolenbroek "requests");
375dbcce9ddSDavid van Moolenbroek
376*f7df02e7SDavid van Moolenbroek data = &pending_recvq[(pending_recvtail + pending_recvs) %
377*f7df02e7SDavid van Moolenbroek __arraycount(pending_recvq)];
378*f7df02e7SDavid van Moolenbroek }
379dbcce9ddSDavid van Moolenbroek
380*f7df02e7SDavid van Moolenbroek data->endpt = m_ptr->m_source;
381*f7df02e7SDavid van Moolenbroek data->id = m_ptr->m_ndev_netdriver_transfer.id;
382*f7df02e7SDavid van Moolenbroek data->count = m_ptr->m_ndev_netdriver_transfer.count;
383dbcce9ddSDavid van Moolenbroek
384*f7df02e7SDavid van Moolenbroek if (data->count == 0 || data->count > NDEV_IOV_MAX)
385*f7df02e7SDavid van Moolenbroek panic("netdriver: bad I/O vector count: %u", data->count);
386dbcce9ddSDavid van Moolenbroek
387*f7df02e7SDavid van Moolenbroek data->size = 0;
388dbcce9ddSDavid van Moolenbroek
389*f7df02e7SDavid van Moolenbroek for (i = 0; i < data->count; i++) {
390*f7df02e7SDavid van Moolenbroek grant = m_ptr->m_ndev_netdriver_transfer.grant[i];
391*f7df02e7SDavid van Moolenbroek size = (size_t)m_ptr->m_ndev_netdriver_transfer.len[i];
392*f7df02e7SDavid van Moolenbroek
393*f7df02e7SDavid van Moolenbroek assert(size > 0);
394*f7df02e7SDavid van Moolenbroek
395*f7df02e7SDavid van Moolenbroek data->iovec[i].iov_grant = grant;
396*f7df02e7SDavid van Moolenbroek data->iovec[i].iov_size = size;
397*f7df02e7SDavid van Moolenbroek data->size += size;
398*f7df02e7SDavid van Moolenbroek }
399*f7df02e7SDavid van Moolenbroek
400*f7df02e7SDavid van Moolenbroek if (data->size < NDEV_ETH_PACKET_MIN ||
401*f7df02e7SDavid van Moolenbroek (!do_write && data->size < NDEV_ETH_PACKET_MAX_TAGGED))
402dbcce9ddSDavid van Moolenbroek panic("netdriver: invalid I/O vector size: %zu\n", data->size);
403dbcce9ddSDavid van Moolenbroek
404*f7df02e7SDavid van Moolenbroek if (do_write)
405*f7df02e7SDavid van Moolenbroek pending_sends++;
406*f7df02e7SDavid van Moolenbroek else
407*f7df02e7SDavid van Moolenbroek pending_recvs++;
408dbcce9ddSDavid van Moolenbroek
409*f7df02e7SDavid van Moolenbroek /*
410*f7df02e7SDavid van Moolenbroek * If the driver is down, immediately abort the request again. This
411*f7df02e7SDavid van Moolenbroek * is not a common case but does occur as part of queue draining by the
412*f7df02e7SDavid van Moolenbroek * TCP/IP stack, and is way easier to handle here than up there..
413*f7df02e7SDavid van Moolenbroek */
414*f7df02e7SDavid van Moolenbroek if (!up) {
415*f7df02e7SDavid van Moolenbroek if (do_write)
416*f7df02e7SDavid van Moolenbroek finish_send(EINTR);
417*f7df02e7SDavid van Moolenbroek else
418*f7df02e7SDavid van Moolenbroek finish_recv(EINTR);
419dbcce9ddSDavid van Moolenbroek
420*f7df02e7SDavid van Moolenbroek return;
421*f7df02e7SDavid van Moolenbroek }
422*f7df02e7SDavid van Moolenbroek
423*f7df02e7SDavid van Moolenbroek /* Otherwise, resume sending or receiving. */
4247c48de6cSDavid van Moolenbroek if (do_write)
425dbcce9ddSDavid van Moolenbroek netdriver_send();
426dbcce9ddSDavid van Moolenbroek else
427dbcce9ddSDavid van Moolenbroek netdriver_recv();
428dbcce9ddSDavid van Moolenbroek }
429dbcce9ddSDavid van Moolenbroek
430dbcce9ddSDavid van Moolenbroek /*
431*f7df02e7SDavid van Moolenbroek * Process a request to (re)configure the driver.
432dbcce9ddSDavid van Moolenbroek */
433dbcce9ddSDavid van Moolenbroek static void
do_conf(const struct netdriver * __restrict ndp,const message * __restrict m_ptr)434dbcce9ddSDavid van Moolenbroek do_conf(const struct netdriver * __restrict ndp,
435dbcce9ddSDavid van Moolenbroek const message * __restrict m_ptr)
436dbcce9ddSDavid van Moolenbroek {
437*f7df02e7SDavid van Moolenbroek netdriver_addr_t mcast_list[NETDRIVER_MCAST_MAX];
438*f7df02e7SDavid van Moolenbroek uint32_t set, mode;
439*f7df02e7SDavid van Moolenbroek unsigned int mcast_count;
440*f7df02e7SDavid van Moolenbroek message m;
441*f7df02e7SDavid van Moolenbroek int r;
442dbcce9ddSDavid van Moolenbroek
443*f7df02e7SDavid van Moolenbroek set = m_ptr->m_ndev_netdriver_conf.set;
444*f7df02e7SDavid van Moolenbroek mode = m_ptr->m_ndev_netdriver_conf.mode;
445dbcce9ddSDavid van Moolenbroek
446*f7df02e7SDavid van Moolenbroek /*
447*f7df02e7SDavid van Moolenbroek * If the request includes taking down the interface, perform that step
448*f7df02e7SDavid van Moolenbroek * first: it is expected that in many cases, changing other settings
449*f7df02e7SDavid van Moolenbroek * requires stopping and restarting the device.
450*f7df02e7SDavid van Moolenbroek */
451*f7df02e7SDavid van Moolenbroek if ((set & NDEV_SET_MODE) && mode == NDEV_MODE_DOWN &&
452*f7df02e7SDavid van Moolenbroek ndp->ndr_set_mode != NULL)
453*f7df02e7SDavid van Moolenbroek ndp->ndr_set_mode(mode, NULL, 0);
454dbcce9ddSDavid van Moolenbroek
455*f7df02e7SDavid van Moolenbroek if ((set & NDEV_SET_CAPS) && ndp->ndr_set_caps != NULL)
456*f7df02e7SDavid van Moolenbroek ndp->ndr_set_caps(m_ptr->m_ndev_netdriver_conf.caps);
457*f7df02e7SDavid van Moolenbroek
458*f7df02e7SDavid van Moolenbroek if ((set & NDEV_SET_FLAGS) && ndp->ndr_set_flags != NULL)
459*f7df02e7SDavid van Moolenbroek ndp->ndr_set_flags(m_ptr->m_ndev_netdriver_conf.flags);
460*f7df02e7SDavid van Moolenbroek
461*f7df02e7SDavid van Moolenbroek if ((set & NDEV_SET_MEDIA) && ndp->ndr_set_media != NULL)
462*f7df02e7SDavid van Moolenbroek ndp->ndr_set_media(m_ptr->m_ndev_netdriver_conf.media);
463*f7df02e7SDavid van Moolenbroek
464*f7df02e7SDavid van Moolenbroek if ((set & NDEV_SET_HWADDR) && ndp->ndr_set_hwaddr != NULL) {
465*f7df02e7SDavid van Moolenbroek /* Save the new hardware address. */
466*f7df02e7SDavid van Moolenbroek memcpy(&device_hwaddr, m_ptr->m_ndev_netdriver_conf.hwaddr,
467*f7df02e7SDavid van Moolenbroek sizeof(device_hwaddr));
468*f7df02e7SDavid van Moolenbroek
469*f7df02e7SDavid van Moolenbroek ndp->ndr_set_hwaddr(&device_hwaddr);
470*f7df02e7SDavid van Moolenbroek }
471*f7df02e7SDavid van Moolenbroek
472*f7df02e7SDavid van Moolenbroek if ((set & NDEV_SET_MODE) && mode != NDEV_MODE_DOWN &&
473*f7df02e7SDavid van Moolenbroek ndp->ndr_set_mode != NULL) {
474*f7df02e7SDavid van Moolenbroek /*
475*f7df02e7SDavid van Moolenbroek * If we have a multicast list, copy it in, unless it is too
476*f7df02e7SDavid van Moolenbroek * large: in that case, enable all-multicast receipt mode.
477*f7df02e7SDavid van Moolenbroek */
478*f7df02e7SDavid van Moolenbroek if ((mode & NDEV_MODE_MCAST_LIST) &&
479*f7df02e7SDavid van Moolenbroek m_ptr->m_ndev_netdriver_conf.mcast_count >
480*f7df02e7SDavid van Moolenbroek __arraycount(mcast_list)) {
481*f7df02e7SDavid van Moolenbroek mode &= ~NDEV_MODE_MCAST_LIST;
482*f7df02e7SDavid van Moolenbroek mode |= NDEV_MODE_MCAST_ALL;
483*f7df02e7SDavid van Moolenbroek }
484*f7df02e7SDavid van Moolenbroek
485*f7df02e7SDavid van Moolenbroek if (mode & NDEV_MODE_MCAST_LIST) {
486*f7df02e7SDavid van Moolenbroek assert(m_ptr->m_ndev_netdriver_conf.mcast_grant !=
487*f7df02e7SDavid van Moolenbroek GRANT_INVALID);
488*f7df02e7SDavid van Moolenbroek
489*f7df02e7SDavid van Moolenbroek mcast_count = m_ptr->m_ndev_netdriver_conf.mcast_count;
490*f7df02e7SDavid van Moolenbroek
491*f7df02e7SDavid van Moolenbroek if ((r = sys_safecopyfrom(m_ptr->m_source,
492*f7df02e7SDavid van Moolenbroek m_ptr->m_ndev_netdriver_conf.mcast_grant, 0,
493*f7df02e7SDavid van Moolenbroek (vir_bytes)mcast_list,
494*f7df02e7SDavid van Moolenbroek mcast_count * sizeof(mcast_list[0]))) != OK)
495*f7df02e7SDavid van Moolenbroek panic("netdriver: unable to copy data: %d", r);
496*f7df02e7SDavid van Moolenbroek
497*f7df02e7SDavid van Moolenbroek ndp->ndr_set_mode(mode, mcast_list, mcast_count);
498*f7df02e7SDavid van Moolenbroek } else
499*f7df02e7SDavid van Moolenbroek ndp->ndr_set_mode(mode, NULL, 0);
500*f7df02e7SDavid van Moolenbroek }
501*f7df02e7SDavid van Moolenbroek
502*f7df02e7SDavid van Moolenbroek /* We always report OK: the caller cannot do anything upon failure. */
503*f7df02e7SDavid van Moolenbroek memset(&m, 0, sizeof(m));
504*f7df02e7SDavid van Moolenbroek m.m_type = NDEV_CONF_REPLY;
505*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_reply.id = m_ptr->m_ndev_netdriver_conf.id;
506*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_reply.result = OK;
507*f7df02e7SDavid van Moolenbroek
508*f7df02e7SDavid van Moolenbroek send_reply(m_ptr->m_source, &m);
509*f7df02e7SDavid van Moolenbroek
510*f7df02e7SDavid van Moolenbroek /*
511*f7df02e7SDavid van Moolenbroek * Finally, if the device has been taken down, abort pending send and
512*f7df02e7SDavid van Moolenbroek * receive requests.
513*f7df02e7SDavid van Moolenbroek */
514*f7df02e7SDavid van Moolenbroek if (set & NDEV_SET_MODE) {
515*f7df02e7SDavid van Moolenbroek if (mode == NDEV_MODE_DOWN) {
516*f7df02e7SDavid van Moolenbroek while (pending_sends > 0)
517*f7df02e7SDavid van Moolenbroek finish_send(EINTR);
518*f7df02e7SDavid van Moolenbroek
519*f7df02e7SDavid van Moolenbroek while (pending_recvs > 0)
520*f7df02e7SDavid van Moolenbroek finish_recv(EINTR);
521*f7df02e7SDavid van Moolenbroek
522*f7df02e7SDavid van Moolenbroek up = FALSE;
523*f7df02e7SDavid van Moolenbroek } else
524*f7df02e7SDavid van Moolenbroek up = TRUE;
525*f7df02e7SDavid van Moolenbroek }
526dbcce9ddSDavid van Moolenbroek }
527dbcce9ddSDavid van Moolenbroek
528dbcce9ddSDavid van Moolenbroek /*
529*f7df02e7SDavid van Moolenbroek * Request an update of the link state and active media of the device. This
530*f7df02e7SDavid van Moolenbroek * routine may be called both from the driver and internally.
531dbcce9ddSDavid van Moolenbroek */
532dbcce9ddSDavid van Moolenbroek static void
update_link(void)533*f7df02e7SDavid van Moolenbroek update_link(void)
534dbcce9ddSDavid van Moolenbroek {
535*f7df02e7SDavid van Moolenbroek
536*f7df02e7SDavid van Moolenbroek if (netdriver_table->ndr_get_link != NULL)
537*f7df02e7SDavid van Moolenbroek device_link = netdriver_table->ndr_get_link(&device_media);
538*f7df02e7SDavid van Moolenbroek
539*f7df02e7SDavid van Moolenbroek pending_link = FALSE;
540*f7df02e7SDavid van Moolenbroek }
541*f7df02e7SDavid van Moolenbroek
542*f7df02e7SDavid van Moolenbroek /*
543*f7df02e7SDavid van Moolenbroek * Attempt to send a status update to the endpoint registered to receive status
544*f7df02e7SDavid van Moolenbroek * updates, if any.
545*f7df02e7SDavid van Moolenbroek */
546*f7df02e7SDavid van Moolenbroek static void
send_status(void)547*f7df02e7SDavid van Moolenbroek send_status(void)
548*f7df02e7SDavid van Moolenbroek {
549*f7df02e7SDavid van Moolenbroek message m;
550dbcce9ddSDavid van Moolenbroek int r;
551dbcce9ddSDavid van Moolenbroek
552*f7df02e7SDavid van Moolenbroek assert(pending_link || pending_stat);
553dbcce9ddSDavid van Moolenbroek
554*f7df02e7SDavid van Moolenbroek if (status_endpt == NONE || pending_status)
555*f7df02e7SDavid van Moolenbroek return;
556dbcce9ddSDavid van Moolenbroek
557*f7df02e7SDavid van Moolenbroek if (pending_link)
558*f7df02e7SDavid van Moolenbroek update_link();
559dbcce9ddSDavid van Moolenbroek
560*f7df02e7SDavid van Moolenbroek memset(&m, 0, sizeof(m));
561*f7df02e7SDavid van Moolenbroek m.m_type = NDEV_STATUS;
562*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_status.id = 0; /* for now */
563*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_status.link = device_link;
564*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_status.media = device_media;
565*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_status.oerror = stat_oerror;
566*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_status.coll = stat_coll;
567*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_status.ierror = stat_ierror;
568*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_status.iqdrop = stat_iqdrop;
569dbcce9ddSDavid van Moolenbroek
570*f7df02e7SDavid van Moolenbroek if ((r = asynsend3(status_endpt, &m, AMF_NOREPLY)) != OK)
571*f7df02e7SDavid van Moolenbroek panic("netdriver: unable to send status: %d", r);
572*f7df02e7SDavid van Moolenbroek
573*f7df02e7SDavid van Moolenbroek /*
574*f7df02e7SDavid van Moolenbroek * Do not send another status message until either the one we just sent
575*f7df02e7SDavid van Moolenbroek * gets acknowledged or we get a new initialization request. This way
576*f7df02e7SDavid van Moolenbroek * we get "natural pacing" (i.e., we avoid overflowing the asynsend
577*f7df02e7SDavid van Moolenbroek * message queue by design) without using timers.
578*f7df02e7SDavid van Moolenbroek */
579*f7df02e7SDavid van Moolenbroek pending_status = TRUE;
580*f7df02e7SDavid van Moolenbroek
581*f7df02e7SDavid van Moolenbroek /*
582*f7df02e7SDavid van Moolenbroek * The status message sends incremental updates for statistics. This
583*f7df02e7SDavid van Moolenbroek * means that while a restart of the TCP/IP stack means the statistics
584*f7df02e7SDavid van Moolenbroek * are lost (not great), a restart of the driver leaves the statistics
585*f7df02e7SDavid van Moolenbroek * mostly intact (more important).
586*f7df02e7SDavid van Moolenbroek */
587*f7df02e7SDavid van Moolenbroek stat_oerror = 0;
588*f7df02e7SDavid van Moolenbroek stat_coll = 0;
589*f7df02e7SDavid van Moolenbroek stat_ierror = 0;
590*f7df02e7SDavid van Moolenbroek stat_iqdrop = 0;
591*f7df02e7SDavid van Moolenbroek pending_stat = FALSE;
592*f7df02e7SDavid van Moolenbroek }
593*f7df02e7SDavid van Moolenbroek
594*f7df02e7SDavid van Moolenbroek /*
595*f7df02e7SDavid van Moolenbroek * Process a reply to a status update that we sent earlier on (supposedly).
596*f7df02e7SDavid van Moolenbroek */
597*f7df02e7SDavid van Moolenbroek static void
do_status_reply(const struct netdriver * __restrict ndp __unused,const message * __restrict m_ptr)598*f7df02e7SDavid van Moolenbroek do_status_reply(const struct netdriver * __restrict ndp __unused,
599*f7df02e7SDavid van Moolenbroek const message * __restrict m_ptr)
600*f7df02e7SDavid van Moolenbroek {
601*f7df02e7SDavid van Moolenbroek
602*f7df02e7SDavid van Moolenbroek if (m_ptr->m_source != status_endpt)
603*f7df02e7SDavid van Moolenbroek return;
604*f7df02e7SDavid van Moolenbroek
605*f7df02e7SDavid van Moolenbroek if (!pending_status || m_ptr->m_ndev_netdriver_status_reply.id != 0)
606*f7df02e7SDavid van Moolenbroek panic("netdriver: unexpected status reply");
607*f7df02e7SDavid van Moolenbroek
608*f7df02e7SDavid van Moolenbroek pending_status = FALSE;
609*f7df02e7SDavid van Moolenbroek
610*f7df02e7SDavid van Moolenbroek /*
611*f7df02e7SDavid van Moolenbroek * If the local status has changed since our last status update,
612*f7df02e7SDavid van Moolenbroek * send a new one right away.
613*f7df02e7SDavid van Moolenbroek */
614*f7df02e7SDavid van Moolenbroek if (pending_link || pending_stat)
615*f7df02e7SDavid van Moolenbroek send_status();
616*f7df02e7SDavid van Moolenbroek }
617*f7df02e7SDavid van Moolenbroek
618*f7df02e7SDavid van Moolenbroek /*
619*f7df02e7SDavid van Moolenbroek * The driver reports that the link state and/or active media may have changed.
620*f7df02e7SDavid van Moolenbroek * When convenient, request the new state from the driver and send a status
621*f7df02e7SDavid van Moolenbroek * message to the TCP/IP stack.
622*f7df02e7SDavid van Moolenbroek */
623*f7df02e7SDavid van Moolenbroek void
netdriver_link(void)624*f7df02e7SDavid van Moolenbroek netdriver_link(void)
625*f7df02e7SDavid van Moolenbroek {
626*f7df02e7SDavid van Moolenbroek
627*f7df02e7SDavid van Moolenbroek pending_link = TRUE;
628*f7df02e7SDavid van Moolenbroek
629*f7df02e7SDavid van Moolenbroek send_status();
630*f7df02e7SDavid van Moolenbroek }
631*f7df02e7SDavid van Moolenbroek
632*f7df02e7SDavid van Moolenbroek /*
633*f7df02e7SDavid van Moolenbroek * The driver reports that a number of output errors have occurred. Update
634*f7df02e7SDavid van Moolenbroek * statistics accordingly.
635*f7df02e7SDavid van Moolenbroek */
636*f7df02e7SDavid van Moolenbroek void
netdriver_stat_oerror(uint32_t count)637*f7df02e7SDavid van Moolenbroek netdriver_stat_oerror(uint32_t count)
638*f7df02e7SDavid van Moolenbroek {
639*f7df02e7SDavid van Moolenbroek
640*f7df02e7SDavid van Moolenbroek if (count == 0)
641*f7df02e7SDavid van Moolenbroek return;
642*f7df02e7SDavid van Moolenbroek
643*f7df02e7SDavid van Moolenbroek stat_oerror += count;
644*f7df02e7SDavid van Moolenbroek pending_stat = TRUE;
645*f7df02e7SDavid van Moolenbroek
646*f7df02e7SDavid van Moolenbroek send_status();
647*f7df02e7SDavid van Moolenbroek }
648*f7df02e7SDavid van Moolenbroek
649*f7df02e7SDavid van Moolenbroek /*
650*f7df02e7SDavid van Moolenbroek * The driver reports that one or more packet collisions have occurred. Update
651*f7df02e7SDavid van Moolenbroek * statistics accordingly.
652*f7df02e7SDavid van Moolenbroek */
653*f7df02e7SDavid van Moolenbroek void
netdriver_stat_coll(uint32_t count)654*f7df02e7SDavid van Moolenbroek netdriver_stat_coll(uint32_t count)
655*f7df02e7SDavid van Moolenbroek {
656*f7df02e7SDavid van Moolenbroek
657*f7df02e7SDavid van Moolenbroek if (count == 0)
658*f7df02e7SDavid van Moolenbroek return;
659*f7df02e7SDavid van Moolenbroek
660*f7df02e7SDavid van Moolenbroek stat_coll += count;
661*f7df02e7SDavid van Moolenbroek pending_stat = TRUE;
662*f7df02e7SDavid van Moolenbroek
663*f7df02e7SDavid van Moolenbroek send_status();
664*f7df02e7SDavid van Moolenbroek }
665*f7df02e7SDavid van Moolenbroek
666*f7df02e7SDavid van Moolenbroek /*
667*f7df02e7SDavid van Moolenbroek * The driver reports that a number of input errors have occurred. Adjust
668*f7df02e7SDavid van Moolenbroek * statistics accordingly.
669*f7df02e7SDavid van Moolenbroek */
670*f7df02e7SDavid van Moolenbroek void
netdriver_stat_ierror(uint32_t count)671*f7df02e7SDavid van Moolenbroek netdriver_stat_ierror(uint32_t count)
672*f7df02e7SDavid van Moolenbroek {
673*f7df02e7SDavid van Moolenbroek
674*f7df02e7SDavid van Moolenbroek if (count == 0)
675*f7df02e7SDavid van Moolenbroek return;
676*f7df02e7SDavid van Moolenbroek
677*f7df02e7SDavid van Moolenbroek stat_ierror += count;
678*f7df02e7SDavid van Moolenbroek pending_stat = TRUE;
679*f7df02e7SDavid van Moolenbroek
680*f7df02e7SDavid van Moolenbroek send_status();
681*f7df02e7SDavid van Moolenbroek }
682*f7df02e7SDavid van Moolenbroek
683*f7df02e7SDavid van Moolenbroek /*
684*f7df02e7SDavid van Moolenbroek * The driver reports that a number of input queue drops have occurred. Update
685*f7df02e7SDavid van Moolenbroek * statistics accordingly.
686*f7df02e7SDavid van Moolenbroek */
687*f7df02e7SDavid van Moolenbroek void
netdriver_stat_iqdrop(uint32_t count)688*f7df02e7SDavid van Moolenbroek netdriver_stat_iqdrop(uint32_t count)
689*f7df02e7SDavid van Moolenbroek {
690*f7df02e7SDavid van Moolenbroek
691*f7df02e7SDavid van Moolenbroek if (count == 0)
692*f7df02e7SDavid van Moolenbroek return;
693*f7df02e7SDavid van Moolenbroek
694*f7df02e7SDavid van Moolenbroek stat_iqdrop += count;
695*f7df02e7SDavid van Moolenbroek pending_stat = TRUE;
696*f7df02e7SDavid van Moolenbroek
697*f7df02e7SDavid van Moolenbroek send_status();
698*f7df02e7SDavid van Moolenbroek }
699*f7df02e7SDavid van Moolenbroek
700*f7df02e7SDavid van Moolenbroek /*
701*f7df02e7SDavid van Moolenbroek * Process an initialization request. Actual initialization has already taken
702*f7df02e7SDavid van Moolenbroek * place, so we simply report the information gathered at that time. If the
703*f7df02e7SDavid van Moolenbroek * caller (the TCP/IP stack) has crashed and restarted, we will get another
704*f7df02e7SDavid van Moolenbroek * initialization request message, so keep the information up-to-date.
705*f7df02e7SDavid van Moolenbroek */
706*f7df02e7SDavid van Moolenbroek static void
do_init(const struct netdriver * __restrict ndp,const message * __restrict m_ptr)707*f7df02e7SDavid van Moolenbroek do_init(const struct netdriver * __restrict ndp,
708*f7df02e7SDavid van Moolenbroek const message * __restrict m_ptr)
709*f7df02e7SDavid van Moolenbroek {
710*f7df02e7SDavid van Moolenbroek message m;
711*f7df02e7SDavid van Moolenbroek
712*f7df02e7SDavid van Moolenbroek /*
713*f7df02e7SDavid van Moolenbroek * First of all, an initialization request is a sure indication that
714*f7df02e7SDavid van Moolenbroek * the caller does not have any send or receive requests pending, and
715*f7df02e7SDavid van Moolenbroek * will not acknowledge our previous status request, if any. Forget
716*f7df02e7SDavid van Moolenbroek * any such previous requests and start sending status requests to the
717*f7df02e7SDavid van Moolenbroek * (new) endpoint.
718*f7df02e7SDavid van Moolenbroek */
719*f7df02e7SDavid van Moolenbroek pending_sends = 0;
720*f7df02e7SDavid van Moolenbroek pending_recvs = 0;
721*f7df02e7SDavid van Moolenbroek pending_status = FALSE;
722*f7df02e7SDavid van Moolenbroek
723*f7df02e7SDavid van Moolenbroek status_endpt = m_ptr->m_source;
724*f7df02e7SDavid van Moolenbroek
725*f7df02e7SDavid van Moolenbroek /*
726*f7df02e7SDavid van Moolenbroek * Update link and media now, because we are about to send the initial
727*f7df02e7SDavid van Moolenbroek * values of those to the caller as well.
728*f7df02e7SDavid van Moolenbroek */
729*f7df02e7SDavid van Moolenbroek update_link();
730*f7df02e7SDavid van Moolenbroek
731*f7df02e7SDavid van Moolenbroek memset(&m, 0, sizeof(m));
732*f7df02e7SDavid van Moolenbroek m.m_type = NDEV_INIT_REPLY;
733*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_init_reply.id = m_ptr->m_ndev_netdriver_init.id;
734*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_init_reply.link = device_link;
735*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_init_reply.media = device_media;
736*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_init_reply.caps = device_caps;
737*f7df02e7SDavid van Moolenbroek strlcpy(m.m_netdriver_ndev_init_reply.name, device_name,
738*f7df02e7SDavid van Moolenbroek sizeof(m.m_netdriver_ndev_init_reply.name));
739*f7df02e7SDavid van Moolenbroek assert(sizeof(device_hwaddr) <=
740*f7df02e7SDavid van Moolenbroek sizeof(m.m_netdriver_ndev_init_reply.hwaddr));
741*f7df02e7SDavid van Moolenbroek memcpy(m.m_netdriver_ndev_init_reply.hwaddr, &device_hwaddr,
742*f7df02e7SDavid van Moolenbroek sizeof(device_hwaddr));
743*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_init_reply.hwaddr_len = sizeof(device_hwaddr);
744*f7df02e7SDavid van Moolenbroek
745*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_init_reply.max_send = __arraycount(pending_sendq);
746*f7df02e7SDavid van Moolenbroek m.m_netdriver_ndev_init_reply.max_recv = __arraycount(pending_recvq);
747*f7df02e7SDavid van Moolenbroek
748*f7df02e7SDavid van Moolenbroek send_reply(m_ptr->m_source, &m);
749*f7df02e7SDavid van Moolenbroek
750*f7df02e7SDavid van Moolenbroek /*
751*f7df02e7SDavid van Moolenbroek * Also send the current status. This is not required by the protocol
752*f7df02e7SDavid van Moolenbroek * and only serves to provide updated statistics to a new TCP/IP stack
753*f7df02e7SDavid van Moolenbroek * instance right away.
754*f7df02e7SDavid van Moolenbroek */
755*f7df02e7SDavid van Moolenbroek if (pending_stat)
756*f7df02e7SDavid van Moolenbroek send_status();
757dbcce9ddSDavid van Moolenbroek }
758dbcce9ddSDavid van Moolenbroek
759dbcce9ddSDavid van Moolenbroek /*
760dbcce9ddSDavid van Moolenbroek * Process an incoming message, and send a reply.
761dbcce9ddSDavid van Moolenbroek */
762dbcce9ddSDavid van Moolenbroek void
netdriver_process(const struct netdriver * __restrict ndp,const message * __restrict m_ptr,int ipc_status)763dbcce9ddSDavid van Moolenbroek netdriver_process(const struct netdriver * __restrict ndp,
764dbcce9ddSDavid van Moolenbroek const message * __restrict m_ptr, int ipc_status)
765dbcce9ddSDavid van Moolenbroek {
766dbcce9ddSDavid van Moolenbroek
767dbcce9ddSDavid van Moolenbroek netdriver_table = ndp;
768dbcce9ddSDavid van Moolenbroek
769dbcce9ddSDavid van Moolenbroek /* Check for notifications first. */
770dbcce9ddSDavid van Moolenbroek if (is_ipc_notify(ipc_status)) {
771*f7df02e7SDavid van Moolenbroek switch (m_ptr->m_source) {
772dbcce9ddSDavid van Moolenbroek case HARDWARE:
773dbcce9ddSDavid van Moolenbroek if (ndp->ndr_intr != NULL)
774dbcce9ddSDavid van Moolenbroek ndp->ndr_intr(m_ptr->m_notify.interrupts);
775dbcce9ddSDavid van Moolenbroek break;
776dbcce9ddSDavid van Moolenbroek
777dbcce9ddSDavid van Moolenbroek case CLOCK:
778*f7df02e7SDavid van Moolenbroek if (ndp->ndr_tick != NULL)
779*f7df02e7SDavid van Moolenbroek ndp->ndr_tick();
780*f7df02e7SDavid van Moolenbroek
781*f7df02e7SDavid van Moolenbroek if (ticks > 0)
782*f7df02e7SDavid van Moolenbroek (void)sys_setalarm(ticks, FALSE /*abs_time*/);
783dbcce9ddSDavid van Moolenbroek break;
784dbcce9ddSDavid van Moolenbroek
785dbcce9ddSDavid van Moolenbroek default:
786dbcce9ddSDavid van Moolenbroek if (ndp->ndr_other != NULL)
787dbcce9ddSDavid van Moolenbroek ndp->ndr_other(m_ptr, ipc_status);
788dbcce9ddSDavid van Moolenbroek }
789dbcce9ddSDavid van Moolenbroek
790*f7df02e7SDavid van Moolenbroek return;
791dbcce9ddSDavid van Moolenbroek }
792dbcce9ddSDavid van Moolenbroek
793dbcce9ddSDavid van Moolenbroek /*
794*f7df02e7SDavid van Moolenbroek * Discard datalink requests preceding a first NDEV_INIT request, so
795*f7df02e7SDavid van Moolenbroek * that after a driver restart, any in-flight request is discarded.
796*f7df02e7SDavid van Moolenbroek * Note that for correct driver operation it is important that
797*f7df02e7SDavid van Moolenbroek * non-datalink requests, and interrupts in particular, do not go
798*f7df02e7SDavid van Moolenbroek * through this check.
799dbcce9ddSDavid van Moolenbroek */
800*f7df02e7SDavid van Moolenbroek if (IS_NDEV_RQ(m_ptr->m_type) && init_expected) {
801*f7df02e7SDavid van Moolenbroek if (m_ptr->m_type != NDEV_INIT)
802dbcce9ddSDavid van Moolenbroek return; /* do not send a reply */
803dbcce9ddSDavid van Moolenbroek
804*f7df02e7SDavid van Moolenbroek init_expected = FALSE;
805dbcce9ddSDavid van Moolenbroek }
806dbcce9ddSDavid van Moolenbroek
807dbcce9ddSDavid van Moolenbroek switch (m_ptr->m_type) {
808*f7df02e7SDavid van Moolenbroek case NDEV_INIT:
809*f7df02e7SDavid van Moolenbroek do_init(ndp, m_ptr);
810*f7df02e7SDavid van Moolenbroek break;
811*f7df02e7SDavid van Moolenbroek
812*f7df02e7SDavid van Moolenbroek case NDEV_CONF:
813dbcce9ddSDavid van Moolenbroek do_conf(ndp, m_ptr);
814dbcce9ddSDavid van Moolenbroek break;
815dbcce9ddSDavid van Moolenbroek
816*f7df02e7SDavid van Moolenbroek case NDEV_SEND:
817*f7df02e7SDavid van Moolenbroek do_transfer(ndp, m_ptr, TRUE /*do_write*/);
818dbcce9ddSDavid van Moolenbroek break;
819dbcce9ddSDavid van Moolenbroek
820*f7df02e7SDavid van Moolenbroek case NDEV_RECV:
821*f7df02e7SDavid van Moolenbroek do_transfer(ndp, m_ptr, FALSE /*do_write*/);
822dbcce9ddSDavid van Moolenbroek break;
823dbcce9ddSDavid van Moolenbroek
824*f7df02e7SDavid van Moolenbroek case NDEV_STATUS_REPLY:
825*f7df02e7SDavid van Moolenbroek do_status_reply(ndp, m_ptr);
826dbcce9ddSDavid van Moolenbroek break;
827dbcce9ddSDavid van Moolenbroek
828dbcce9ddSDavid van Moolenbroek default:
829dbcce9ddSDavid van Moolenbroek if (ndp->ndr_other != NULL)
830dbcce9ddSDavid van Moolenbroek ndp->ndr_other(m_ptr, ipc_status);
831dbcce9ddSDavid van Moolenbroek }
832dbcce9ddSDavid van Moolenbroek }
833dbcce9ddSDavid van Moolenbroek
834dbcce9ddSDavid van Moolenbroek /*
835*f7df02e7SDavid van Moolenbroek * Set a name for the device, based on the base name 'base' and the instance
836*f7df02e7SDavid van Moolenbroek * number 'instance'.
837*f7df02e7SDavid van Moolenbroek */
838*f7df02e7SDavid van Moolenbroek static void
netdriver_set_name(const char * base,unsigned int instance)839*f7df02e7SDavid van Moolenbroek netdriver_set_name(const char * base, unsigned int instance)
840*f7df02e7SDavid van Moolenbroek {
841*f7df02e7SDavid van Moolenbroek size_t len;
842*f7df02e7SDavid van Moolenbroek
843*f7df02e7SDavid van Moolenbroek assert(instance <= 255);
844*f7df02e7SDavid van Moolenbroek
845*f7df02e7SDavid van Moolenbroek len = strlen(base);
846*f7df02e7SDavid van Moolenbroek assert(len <= sizeof(device_name) - 4);
847*f7df02e7SDavid van Moolenbroek
848*f7df02e7SDavid van Moolenbroek memcpy(device_name, base, len);
849*f7df02e7SDavid van Moolenbroek if (instance >= 100)
850*f7df02e7SDavid van Moolenbroek device_name[len++] = '0' + instance / 100;
851*f7df02e7SDavid van Moolenbroek if (instance >= 10)
852*f7df02e7SDavid van Moolenbroek device_name[len++] = '0' + (instance % 100) / 10;
853*f7df02e7SDavid van Moolenbroek device_name[len++] = '0' + instance % 10;
854*f7df02e7SDavid van Moolenbroek device_name[len] = 0;
855*f7df02e7SDavid van Moolenbroek }
856*f7df02e7SDavid van Moolenbroek
857*f7df02e7SDavid van Moolenbroek /*
858*f7df02e7SDavid van Moolenbroek * Return the device name generated at driver initialization time.
859*f7df02e7SDavid van Moolenbroek */
860*f7df02e7SDavid van Moolenbroek const char *
netdriver_name(void)861*f7df02e7SDavid van Moolenbroek netdriver_name(void)
862*f7df02e7SDavid van Moolenbroek {
863*f7df02e7SDavid van Moolenbroek
864*f7df02e7SDavid van Moolenbroek return device_name;
865*f7df02e7SDavid van Moolenbroek }
866*f7df02e7SDavid van Moolenbroek
867*f7df02e7SDavid van Moolenbroek /*
868dbcce9ddSDavid van Moolenbroek * Perform initialization. Return OK or an error code.
869dbcce9ddSDavid van Moolenbroek */
870dbcce9ddSDavid van Moolenbroek int
netdriver_init(const struct netdriver * ndp)871dbcce9ddSDavid van Moolenbroek netdriver_init(const struct netdriver * ndp)
872dbcce9ddSDavid van Moolenbroek {
873dbcce9ddSDavid van Moolenbroek unsigned int instance;
874dbcce9ddSDavid van Moolenbroek long v;
875dbcce9ddSDavid van Moolenbroek int r;
876dbcce9ddSDavid van Moolenbroek
877dbcce9ddSDavid van Moolenbroek /* Initialize global variables. */
878*f7df02e7SDavid van Moolenbroek pending_sendtail = 0;
879*f7df02e7SDavid van Moolenbroek pending_sends = 0;
880*f7df02e7SDavid van Moolenbroek pending_recvtail = 0;
881*f7df02e7SDavid van Moolenbroek pending_recvs = 0;
882dbcce9ddSDavid van Moolenbroek
883*f7df02e7SDavid van Moolenbroek memset(device_name, 0, sizeof(device_name));
884*f7df02e7SDavid van Moolenbroek
885*f7df02e7SDavid van Moolenbroek memset(&device_hwaddr, 0, sizeof(device_hwaddr));
886*f7df02e7SDavid van Moolenbroek device_caps = 0;
887*f7df02e7SDavid van Moolenbroek
888*f7df02e7SDavid van Moolenbroek /* Use sensible defaults for the link state and active media. */
889*f7df02e7SDavid van Moolenbroek device_link = NDEV_LINK_UNKNOWN;
890*f7df02e7SDavid van Moolenbroek device_media = IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0);
891*f7df02e7SDavid van Moolenbroek
892*f7df02e7SDavid van Moolenbroek status_endpt = NONE;
893*f7df02e7SDavid van Moolenbroek pending_status = FALSE;
894*f7df02e7SDavid van Moolenbroek pending_link = FALSE;
895*f7df02e7SDavid van Moolenbroek
896*f7df02e7SDavid van Moolenbroek up = FALSE;
897*f7df02e7SDavid van Moolenbroek
898*f7df02e7SDavid van Moolenbroek ticks = 0;
899*f7df02e7SDavid van Moolenbroek
900*f7df02e7SDavid van Moolenbroek /* Get the device instance number. */
901dbcce9ddSDavid van Moolenbroek v = 0;
902dbcce9ddSDavid van Moolenbroek (void)env_parse("instance", "d", 0, &v, 0, 255);
903dbcce9ddSDavid van Moolenbroek instance = (unsigned int)v;
904dbcce9ddSDavid van Moolenbroek
905*f7df02e7SDavid van Moolenbroek /* Generate the full driver name. */
906*f7df02e7SDavid van Moolenbroek netdriver_set_name(ndp->ndr_name, instance);
907dbcce9ddSDavid van Moolenbroek
908*f7df02e7SDavid van Moolenbroek /* Call the initialization routine. */
909*f7df02e7SDavid van Moolenbroek if ((r = ndp->ndr_init(instance, &device_hwaddr, &device_caps,
910*f7df02e7SDavid van Moolenbroek &ticks)) != OK)
911dbcce9ddSDavid van Moolenbroek return r;
912dbcce9ddSDavid van Moolenbroek
913dbcce9ddSDavid van Moolenbroek /* Announce we are up! */
914dbcce9ddSDavid van Moolenbroek netdriver_announce();
915dbcce9ddSDavid van Moolenbroek
916*f7df02e7SDavid van Moolenbroek init_expected = TRUE;
917*f7df02e7SDavid van Moolenbroek running = TRUE;
918*f7df02e7SDavid van Moolenbroek
919*f7df02e7SDavid van Moolenbroek if (ticks > 0)
920*f7df02e7SDavid van Moolenbroek (void)sys_setalarm(ticks, FALSE /*abs_time*/);
921*f7df02e7SDavid van Moolenbroek
922433d6423SLionel Sambuc return OK;
923433d6423SLionel Sambuc }
924433d6423SLionel Sambuc
925dbcce9ddSDavid van Moolenbroek /*
926*f7df02e7SDavid van Moolenbroek * Perform SEF initialization.
927dbcce9ddSDavid van Moolenbroek */
928dbcce9ddSDavid van Moolenbroek static int
local_init(int type __unused,sef_init_info_t * info __unused)929*f7df02e7SDavid van Moolenbroek local_init(int type __unused, sef_init_info_t * info __unused)
930dbcce9ddSDavid van Moolenbroek {
931dbcce9ddSDavid van Moolenbroek
932*f7df02e7SDavid van Moolenbroek assert(netdriver_table != NULL);
933dbcce9ddSDavid van Moolenbroek
934*f7df02e7SDavid van Moolenbroek return netdriver_init(netdriver_table);
935dbcce9ddSDavid van Moolenbroek }
936dbcce9ddSDavid van Moolenbroek
937dbcce9ddSDavid van Moolenbroek /*
938dbcce9ddSDavid van Moolenbroek * Break out of the main loop after finishing the current request.
939dbcce9ddSDavid van Moolenbroek */
940dbcce9ddSDavid van Moolenbroek void
netdriver_terminate(void)941dbcce9ddSDavid van Moolenbroek netdriver_terminate(void)
942dbcce9ddSDavid van Moolenbroek {
943dbcce9ddSDavid van Moolenbroek
944dbcce9ddSDavid van Moolenbroek if (netdriver_table != NULL && netdriver_table->ndr_stop != NULL)
945dbcce9ddSDavid van Moolenbroek netdriver_table->ndr_stop();
946dbcce9ddSDavid van Moolenbroek
947dbcce9ddSDavid van Moolenbroek running = FALSE;
948dbcce9ddSDavid van Moolenbroek
949dbcce9ddSDavid van Moolenbroek sef_cancel();
950dbcce9ddSDavid van Moolenbroek }
951dbcce9ddSDavid van Moolenbroek
952dbcce9ddSDavid van Moolenbroek /*
953dbcce9ddSDavid van Moolenbroek * The process has received a signal. See if we have to terminate.
954dbcce9ddSDavid van Moolenbroek */
955dbcce9ddSDavid van Moolenbroek static void
got_signal(int sig)956dbcce9ddSDavid van Moolenbroek got_signal(int sig)
957dbcce9ddSDavid van Moolenbroek {
958dbcce9ddSDavid van Moolenbroek
959dbcce9ddSDavid van Moolenbroek if (sig != SIGTERM)
960dbcce9ddSDavid van Moolenbroek return;
961dbcce9ddSDavid van Moolenbroek
962dbcce9ddSDavid van Moolenbroek netdriver_terminate();
963dbcce9ddSDavid van Moolenbroek }
964dbcce9ddSDavid van Moolenbroek
965dbcce9ddSDavid van Moolenbroek /*
966dbcce9ddSDavid van Moolenbroek * Main program of any network driver.
967dbcce9ddSDavid van Moolenbroek */
968dbcce9ddSDavid van Moolenbroek void
netdriver_task(const struct netdriver * ndp)969dbcce9ddSDavid van Moolenbroek netdriver_task(const struct netdriver * ndp)
970dbcce9ddSDavid van Moolenbroek {
971dbcce9ddSDavid van Moolenbroek message mess;
972dbcce9ddSDavid van Moolenbroek int r, ipc_status;
973dbcce9ddSDavid van Moolenbroek
974dbcce9ddSDavid van Moolenbroek /* Perform SEF initialization. */
975*f7df02e7SDavid van Moolenbroek sef_setcb_init_fresh(local_init);
976dbcce9ddSDavid van Moolenbroek sef_setcb_signal_handler(got_signal);
977dbcce9ddSDavid van Moolenbroek
978dbcce9ddSDavid van Moolenbroek netdriver_table = ndp;
979dbcce9ddSDavid van Moolenbroek
980dbcce9ddSDavid van Moolenbroek sef_startup();
981dbcce9ddSDavid van Moolenbroek
982dbcce9ddSDavid van Moolenbroek /* The main message loop. */
983dbcce9ddSDavid van Moolenbroek while (running) {
984dbcce9ddSDavid van Moolenbroek if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) {
985dbcce9ddSDavid van Moolenbroek if (r == EINTR)
986dbcce9ddSDavid van Moolenbroek continue; /* sef_cancel() was called */
987dbcce9ddSDavid van Moolenbroek
988dbcce9ddSDavid van Moolenbroek panic("netdriver: sef_receive_status failed: %d", r);
989dbcce9ddSDavid van Moolenbroek }
990dbcce9ddSDavid van Moolenbroek
991dbcce9ddSDavid van Moolenbroek netdriver_process(ndp, &mess, ipc_status);
992dbcce9ddSDavid van Moolenbroek }
993dbcce9ddSDavid van Moolenbroek }
994