1 static char rcsid[] = "$Id: sunether.c,v 1.2 1996/08/01 02:08:52 paul Exp $";
2
3 /*
4 * sunether.c
5 *
6 * This file contains a "ethernet device driver" for smx.
7 *
8 * The valid messages and their parameters are:
9 *
10 * m_type DL_PORT DL_PROC DL_COUNT DL_MODE DL_ADDR
11 * |------------+----------+---------+----------+---------+---------|
12 * | HARDINT | | | | | |
13 * |------------|----------|---------|----------|---------|---------|
14 * | DL_WRITE | port nr | proc nr | count | mode | address |
15 * |------------|----------|---------|----------|---------|---------|
16 * | DL_WRITEV | port nr | proc nr | count | mode | address |
17 * |------------|----------|---------|----------|---------|---------|
18 * | DL_READ | port nr | proc nr | count | | address |
19 * |------------|----------|---------|----------|---------|---------|
20 * | DL_READV | port nr | proc nr | count | | address |
21 * |------------|----------|---------|----------|---------|---------|
22 * | DL_INIT | port nr | proc nr | mode | | address |
23 * |------------|----------|---------|----------|---------|---------|
24 * | DL_GETSTAT | port nr | proc nr | | | address |
25 * |------------|----------|---------|----------|---------|---------|
26 * | DL_STOP | port_nr | | | | |
27 * |------------|----------|---------|----------|---------|---------|
28 *
29 * The messages sent are:
30 *
31 * m-type DL_POR T DL_PROC DL_COUNT DL_STAT DL_CLCK
32 * |------------|----------|---------|----------|---------|---------|
33 * |DL_TASK_REPL| port nr | proc nr | rd-count | err|stat| clock |
34 * |------------|----------|---------|----------|---------|---------|
35 *
36 * m_type m3_i1 m3_i2 m3_ca1
37 * |------------+---------+-----------+---------------|
38 * |DL_INIT_REPL| port nr | last port | ethernet addr |
39 * |------------|---------|-----------|---------------|
40 */
41
42 /*
43 * The smx ethernet device driver operates in a very simple fashion.
44 * During smx boot, a file the ETHER_FD file descriptor is opened
45 * as a UDP/IP socket connected to a relay process running under SunOS.
46 * All outgoing packets are written to this descriptor. Anything
47 * received on the descriptor is treated as an incoming ethernet packet,
48 * and is relayed back to the inet server. The entry points are:
49 *
50 * se_init - called during bootstrap
51 * dp8390_task - the ethernet device driver task's main body
52 * se_interrupt - called when an incoming packet has arrived.
53 *
54 * During the MINIX bootstrap, se_init is called to inform this driver of its
55 * "ethernet address", which is actually the UDP address bound to the
56 * ETHER_FD socket. se_init also sets up interrupt handling for
57 * incoming messages. Later in the bootstrap dp8390_task is called
58 * as the main body of the ethernet task is created (the name is kept the
59 * same as the PC function to minimise changes).
60 *
61 * Outgoing packets are sent immediately by writing them to ETHER_FD, so
62 * writing processes are never suspended. A reading process is suspended
63 * if there are no input packets waiting. Up to NR_READ_BUFFERS incoming
64 * packets can be buffered---any further packets will be dropped.
65 *
66 * ISSUES:
67 * - if packets are often dropped, then we should change the
68 * buffering, so that each packet is stored in a buffer of the
69 * appropriate size, and not one of maximum size.
70 * - currently, once do_init has been called the device driver remains
71 * in the "enabled" state forever. Should a DL_STOP diable the driver
72 * (as in the dp8390 driver)?
73 * - currently, broadcasts are permitted whenever networking is enabled.
74 * Should we allow them only if DEF_BROAD was specified when the
75 * ethernet device driver was enabled?
76 */
77
78 #include "kernel.h"
79 #include <minix/callnr.h>
80 #include <minix/com.h>
81 #include <net/gen/ether.h>
82 #include <net/gen/eth_io.h>
83
84 #include <sun/syscall.h>
85
86 #include "proc.h"
87 #include "assert.h"
88
89 #if ENABLE_NETWORKING
90
91 /*
92 * There is only 1 "ethernet port"---ETHER_FD!
93 */
94 #define SMX_PORT_NR 1
95
96
97 /*
98 * IO vector definition. The definition, plus the functions for manipulating
99 * IO vectors, are taken straight from dp8390.c.
100 */
101 #define IOVEC_NR 16
102
103 typedef struct iovec_dat {
104 iovec_t iod_iovec[IOVEC_NR];
105 int iod_iovec_s;
106 int iod_proc_nr;
107 vir_bytes iod_iovec_addr;
108 } iovec_dat_t;
109
110 /*
111 * Constants for use with se_rwiovec specifying whether copying is from
112 * the driver to the IO vector (STORE) or vice versa (LOAD).
113 */
114 #define LOAD 1
115 #define STORE 2
116
117
118 /*
119 * Information about a reader process awaiting an incoming packet, and about
120 * queued incoming packets, is held in the read_info structure. The buffered
121 * packets start in read_buffers[first_used], and go to
122 * read_buffers[(first_used + buffs_used - 1) % NR_READ_BUFFERS. The lengths
123 * of the buffered packets are stored in the corresponding entries of
124 * chars_read.
125 */
126 #define NR_READ_BUFFERS 4
127
128 typedef struct read_info {
129 int reader_waiting; /* Is there a reader waiting? */
130 int client; /* Who is it? */
131 iovec_dat_t read_iovec; /* Where should the incoming packet go? */
132
133 int buffs_used; /* number of buffer used */
134 int first_buff; /* first buffer used */
135 unsigned read_buffers[NR_READ_BUFFERS][ETH_MAX_PACK_SIZE / sizeof(unsigned) + 1];
136 int chars_read[NR_READ_BUFFERS];
137 } read_info_t;
138
139
140 static read_info_t read_info; /* Waiting read and buffered packet info */
141 static ether_addr_t smx_eaddr; /* Our "ethernet address" */
142 static eth_stat_t estats; /* Ethernet stats---most unused in smx. */
143 static int smxeth_tasknr; /* Ethernet driver task number */
144 static int eth_enabled = 0;
145
146
147 /*
148 * Local fucntions
149 */
150 static void se_interrupt(int fd);
151
152 static void do_init(message *mp);
153 static int se_ethernet_on(ether_addr_t *eaddr);
154 static void do_stop(message *mp);
155
156 static void do_vwrite(message *mp, int vectored);
157 static void do_vread(message *mp, int vectored);
158 static void do_int(void);
159 static int se_flush(int fd);
160 static int se_recv(void);
161
162 static void se_rwiovec(char *buffer, iovec_dat_t *iovp, vir_bytes count,
163 int rw);
164 static int calc_iovec_size(iovec_dat_t *iovp);
165 static void se_next_iovec(iovec_dat_t *iovp);
166 static void get_userdata(int user_proc, vir_bytes user_addr, vir_bytes count,
167 void *loc_addr);
168 static void put_userdata(int user_proc, vir_bytes user_addr, vir_bytes count,
169 void *loc_addr);
170
171 static void do_getstat(message *mp);
172
173 static void se_reply(int whoto, int err, int status, vir_bytes bytes_read);
174
175
176 /*
177 * Function: se_init
178 * Parameter: eaddr - our ethernet address.
179 *
180 * Called from kernel main() during booting. We record our ethernet
181 * address, install se_interrupt as the hander for SIGIO signals on ETHER_FD,
182 * and initialise read_info.
183 */
184 void se_init(char *eaddr)
185 {
186 smx_eaddr = *(ether_addr_t *) eaddr;
187 read_info.reader_waiting = 0;
188 read_info.buffs_used = read_info.first_buff = 0;
189 sunio_install_handler(ETHER_FD, se_interrupt);
190 }
191
192
193 /*
194 * Function: se_interrupt
195 * Parameter: fd - SunOS descriptor input is available on.
196 *
197 * An ethernet packet is avilable. Wakeup the smx ethernet driver to
198 * read the packet. This is layer 1 code.
199 */
200 static void se_interrupt(int fd)
201 {
202 interrupt(smxeth_tasknr);
203 }
204
205
206
207 /*
208 * Function: dp8390_task
209 *
210 * The main body of the smx ethernet device driver task.
211 */
212 void dp8390_task(void)
213 {
214 message m;
215 int r;
216
217 smxeth_tasknr = proc_number(proc_ptr); /* For se_interrupt to use */
218
219 while(1) {
220 if ((r = receive(ANY, &m)) != OK) {
221 panic("smx_ether: receive failed", r);
222 }
223 switch (m.m_type)
224 {
225 case DL_WRITE: do_vwrite(&m, FALSE); break;
226 case DL_WRITEV: do_vwrite(&m, TRUE); break;
227 case DL_READ: do_vread(&m, FALSE); break;
228 case DL_READV: do_vread(&m, TRUE); break;
229 case DL_INIT: do_init(&m); break;
230 case DL_GETSTAT: do_getstat(&m); break;
231 case DL_STOP: do_stop(&m); break;
232 case HARD_INT: do_int(); break;
233 default:
234 panic("smx_ether: illegal message", m.m_type);
235 }
236 }
237 }
238
239
240 /*
241 * Function: do_init
242 * Parameter: mp - incoming DL_INIT message
243 *
244 * We mark the device as enabled, and return the address and number of
245 * interfaces. Initialisation fails if the port number to be initialised
246 * is not 0 (smx supports only 1 interface), if smx has been started
247 * with networking disabled (in which case our ethernet address is all
248 * zeroes) or the mode requested involved mutlicast mode or promiscuous
249 * mode (the relay program needs modifying to handle these).
250 */
251 static void do_init(message *mp)
252 {
253 message reply_mess;
254
255 reply_mess.m_type = DL_INIT_REPLY;
256
257 if (mp->DL_PORT != 0 || !se_ethernet_on(&smx_eaddr) ||
258 (mp->DL_MODE & DL_PROMISC_REQ) || (mp->DL_MODE & DL_MULTI_REQ)) {
259 reply_mess.m3_i1 = ENXIO;
260 if (send(mp->m_source, &reply_mess) != OK) {
261 panic("smx_ether: unable to send in DL_INIT", NO_NUM);
262 }
263 return;
264 }
265
266 memset(&estats, 0, sizeof(estats));
267 reply_mess.m3_i1 = mp->DL_PORT;
268 reply_mess.m3_i2 = SMX_PORT_NR;
269 *(ether_addr_t *) reply_mess.m3_ca1 = smx_eaddr;
270 eth_enabled = 1;
271
272 if (send(mp->m_source, &reply_mess) != OK) {
273 panic("smx_ether: unable to send in DL_INIT", NO_NUM);
274 }
275 }
276
277
278 /*
279 * Function: se_ethernet_on
280 * Parameter: eaddr - our ethernet address
281 * Returns: true of networking is enabled (eaddr is non-zero), false if it
282 * is disabled.
283 *
284 * The minix bootstrap passes an all zeroes eaddr if networking is off.
285 */
286 static int se_ethernet_on(ether_addr_t *eaddr)
287 {
288 int i;
289
290 for (i = 0; i < sizeof(eaddr->ea_addr); i++) {
291 if (eaddr->ea_addr[i] != 0) {
292 return 1;
293 }
294 }
295 return 0;
296 }
297
298
299 /*
300 * Function: do_stop
301 * Parameter: mp - incoming DL_STOP message
302 *
303 * Currently we just validate the port number. Should we set eth_enabled
304 * to false here, and discard buffered input? What if a reader is waiting?
305 */
306 static void do_stop(message *mp)
307 {
308 assert(eth_enabled);
309
310 if (mp->DL_PORT != 0) {
311 panic("smx_ether: illegal port", mp->DL_PORT);
312 }
313 }
314
315
316 /*
317 * Function: do_vwrite
318 * Parameters: mp - incoming DL_WRITE or DL_WRITEV message
319 * vectored - true if DL_WRITEV, false otherwise
320 *
321 * Sets up an iovec (based on the supplied iovec for a DL_WRITEV,
322 * the buffer supplied for a DL_WRITE), copies the message into
323 * a local buffer, then writes the complete packet to ETHER_FD.
324 */
325 static void do_vwrite(message *mp, int vectored)
326 {
327 iovec_dat_t iovec, tmp_iovec;
328 int count, size;
329 static char write_buffer[ETH_MAX_PACK_SIZE];
330
331 assert(eth_enabled);
332
333 if (mp->DL_PORT != 0) {
334 panic("smx_ether: illegal port", mp->DL_PORT);
335 }
336
337 count = mp->DL_COUNT;
338 iovec.iod_proc_nr = mp->DL_PROC;
339 if (vectored) {
340 /*
341 * A vectored write. Read in the vector (or at least the first part
342 * of it if it is a long one), and calculate the total number of
343 * bytes in the iovec. We do this on a copy of the iovec because
344 * calc_iovec_size is destructive.
345 */
346 get_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR,
347 (count > IOVEC_NR ? IOVEC_NR : count) *
348 sizeof(iovec_t), iovec.iod_iovec);
349 iovec.iod_iovec_s = count;
350 iovec.iod_iovec_addr = (vir_bytes) mp->DL_ADDR;
351 tmp_iovec = iovec;
352 size = calc_iovec_size(&tmp_iovec);
353 } else {
354 iovec.iod_iovec[0].iov_addr = (vir_bytes) mp->DL_ADDR;
355 iovec.iod_iovec[0].iov_size = mp->DL_COUNT;
356 iovec.iod_iovec_s = 1;
357 iovec.iod_iovec_addr = 0;
358 size = mp->DL_COUNT;
359 }
360 if (size < ETH_MIN_PACK_SIZE || size > ETH_MAX_PACK_SIZE) {
361 panic("smx_ether: invalid packet size", size);
362 }
363
364 /*
365 * Load the packet from user space, then output it
366 */
367 se_rwiovec(write_buffer, &iovec, size, LOAD);
368 if (SunOS(SYS_write, ETHER_FD, write_buffer, size) != size) {
369 panic("smx_ether: SunOS write failed", errno);
370 }
371
372 estats.ets_packetT++;
373 se_reply(mp->DL_PROC, OK, DL_PACK_SEND, 0);
374 }
375
376
377 /*
378 * Function: do_vread
379 * Parameters: mp - incoming DL_READ or DL_READV message
380 * vectored - true if DL_READV, false otherwise
381 *
382 * First we set up an iovec (based on the supplied iovec for a DL_READV,
383 * the buffer supplied for a DL_READ). We then call se_recv which will
384 * transfer a buffered packet and send a reply message, or will tell us
385 * that there are no waiting packets.
386 */
387 static void do_vread(message *mp, int vectored)
388 {
389 iovec_dat_t tmp_iovec;
390 int count;
391 int size;
392
393 assert(eth_enabled);
394
395 count = mp->DL_COUNT;
396 if (mp->DL_PORT != 0) {
397 panic("smx_ether: illegal port", mp->DL_PORT);
398 }
399
400 if (read_info.reader_waiting) {
401 panic("smx_ether: read already in progress", NO_NUM);
402 }
403
404 read_info.read_iovec.iod_proc_nr = mp->DL_PROC;
405 if (vectored) {
406 /*
407 * A vectored read. Read in the vector (or at least the first part
408 * of it if it is a long one), and calculate the total number of
409 * bytes in the iovec. We do this on a copy of the iovec because
410 * calc_iovec_size is destructive.
411 */
412 get_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR,
413 (count > IOVEC_NR ? IOVEC_NR : count) *
414 sizeof(iovec_t), read_info.read_iovec.iod_iovec);
415 read_info.read_iovec.iod_iovec_s = count;
416 read_info.read_iovec.iod_iovec_addr = (vir_bytes) mp->DL_ADDR;
417
418 tmp_iovec = read_info.read_iovec;
419 size = calc_iovec_size(&tmp_iovec);
420 } else {
421 read_info.read_iovec.iod_iovec[0].iov_addr = (vir_bytes) mp->DL_ADDR;
422 read_info.read_iovec.iod_iovec[0].iov_size = mp->DL_COUNT;
423 read_info.read_iovec.iod_iovec_s = 1;
424 read_info.read_iovec.iod_iovec_addr = 0;
425 size = count;
426 }
427 if (size < ETH_MAX_PACK_SIZE) {
428 panic("smx_ether: wrong packet size", size);
429 }
430 read_info.reader_waiting = 1;
431
432 if (se_recv() == 0) {
433 /*
434 * Nothing was waiting---reply to that effect:
435 */
436 se_reply(mp->DL_PROC, OK, 0, 0);
437 } /* else---se_recv has already replied */
438 }
439
440
441 /*
442 * Function: do_int
443 *
444 * One or more packets are available for input. These packets are read into
445 * the available buffers following any packets already buffered. Once all
446 * packets have been read, se_recv is called to check to see whether it is now
447 * possible to reply to a previous read.
448 */
449 static void do_int(void)
450 {
451 int num_read;
452 int buff;
453
454 if (!eth_enabled) {
455 se_flush(ETHER_FD);
456 return;
457 }
458
459 if (read_info.buffs_used == 0) {
460 buff = read_info.first_buff = 0;
461 } else {
462 buff = (read_info.first_buff + read_info.buffs_used) % NR_READ_BUFFERS;
463 }
464
465 for (;;) {
466 if (read_info.buffs_used == NR_READ_BUFFERS) {
467 if (se_flush(ETHER_FD)) {
468 estats.ets_missedP++;
469 /* printf("Ethernet packet dropped because of full buffers\n");*/
470 }
471 break;
472 }
473 num_read = SunOS(SYS_read, ETHER_FD, read_info.read_buffers[buff],
474 sizeof(read_info.read_buffers[buff]));
475 if (num_read <= 0) {
476 break;
477 }
478 if (num_read < ETH_MIN_PACK_SIZE) {
479 panic("smx_ether: received ethernet packet too small", num_read);
480 }
481
482 estats.ets_packetR++;
483 read_info.chars_read[buff] = num_read;
484 read_info.buffs_used++;
485
486 buff = (buff + 1) % NR_READ_BUFFERS;
487 }
488 se_recv();
489 }
490
491
492 /*
493 * Function: se_flush
494 * Parameter: fd - SunOS descriptor
495 * Returns: 1 if anything read from fd, 0 otherwise
496 *
497 * Read and discard all input currently available on fd.
498 */
499 static int se_flush(int fd)
500 {
501 int anything_read = 0;
502 char flush_buff[100];
503
504 for (;;) {
505 if (SunOS(SYS_read, fd, flush_buff, sizeof(flush_buff)) <= 0) {
506 break;
507 }
508 anything_read = 1;
509 }
510 return anything_read;
511 }
512
513
514 /*
515 * Function: se_recv
516 * Returns: 1 if data was transferred and a reply sent to a process waiting
517 * to read a packet, 0 otherwise.
518 *
519 * If we have one or more buffered packets, and a process waiting to read
520 * a packet, then we can transfer a packet to the reader.
521 */
522 static int se_recv(void)
523 {
524 if (!read_info.reader_waiting || read_info.buffs_used == 0) {
525 return 0; /* Transfer not possible */
526 }
527
528 /*
529 * Transfer packet and reply to waiting reader.
530 */
531 se_rwiovec((char *)read_info.read_buffers[read_info.first_buff],
532 &read_info.read_iovec,
533 read_info.chars_read[read_info.first_buff], STORE);
534 se_reply(read_info.read_iovec.iod_proc_nr, OK, DL_PACK_RECV,
535 read_info.chars_read[read_info.first_buff]);
536
537 read_info.reader_waiting = 0;
538 read_info.buffs_used--;
539 if (read_info.buffs_used == 0) {
540 read_info.first_buff = 0;
541 } else {
542 read_info.first_buff = (read_info.first_buff + 1) % NR_READ_BUFFERS;
543 }
544 return 1;
545 }
546
547
548 /*
549 * Function: se_rwiovec
550 * Parameters: buffer - buffer withing smx_ether.
551 * iovp - details of an iovec in another process
552 * count - number of bytes to copy
553 * rw - LOAD if iovp -> buffer; STORE if buffer ->iovp
554 *
555 * Transfer count bytes between buffer and iovp in the direction given
556 * by rw. This operation is destructive on the information in iovp.
557 */
558 static void se_rwiovec(char *buffer, iovec_dat_t *iovp, vir_bytes count,
559 int rw)
560 {
561 int bytes, i;
562
563 i = 0;
564 while (count > 0) {
565 if (i >= IOVEC_NR) {
566 /*
567 * Need next section of the io vector
568 */
569 se_next_iovec(iovp);
570 i = 0;
571 continue;
572 }
573
574 assert(i < iovp->iod_iovec_s);
575 bytes = iovp->iod_iovec[i].iov_size;
576 if (bytes > count) {
577 bytes = count;
578 }
579
580 if (rw == LOAD) {
581 get_userdata(iovp->iod_proc_nr, iovp->iod_iovec[i].iov_addr,
582 bytes, buffer);
583 } else {
584 put_userdata(iovp->iod_proc_nr, iovp->iod_iovec[i].iov_addr,
585 bytes, buffer);
586 }
587
588 count -= bytes;
589 buffer += bytes;
590 i++;
591 }
592 assert(count == 0);
593 }
594
595
596 /*
597 * Function: calc_iovec_size
598 * Parameter: iovp - iovec whose size is to be determined.
599 * Returns: the number of bytes of storage within the iovec
600 *
601 * Calculate the size of a request. Note that the iovec_dat
602 * structure will be unusable after calc_iovec_size.
603 */
604 static int calc_iovec_size(iovec_dat_t *iovp)
605 {
606 int size;
607 int i;
608
609 size = 0;
610 i= 0;
611 while (i < iovp->iod_iovec_s) {
612 if (i >= IOVEC_NR) { /* get the next chunk */
613 se_next_iovec(iovp);
614 i = 0;
615 continue;
616 }
617 size += iovp->iod_iovec[i].iov_size;
618 i++;
619 }
620 return size;
621 }
622
623
624 /*
625 * Function: se_next_iovec
626 * Parameter: iovp - iovec_dat that we want the next chunk of
627 *
628 * Load the next chunk of the iovec whose detail are recorded in iovp
629 */
630 static void se_next_iovec(iovec_dat_t *iovp)
631 {
632 assert(iovp->iod_iovec_s > IOVEC_NR);
633
634 iovp->iod_iovec_s -= IOVEC_NR;
635
636 iovp->iod_iovec_addr += IOVEC_NR * sizeof(iovec_t);
637
638 get_userdata(iovp->iod_proc_nr, iovp->iod_iovec_addr,
639 (iovp->iod_iovec_s > IOVEC_NR ? IOVEC_NR : iovp->iod_iovec_s)
640 * sizeof(iovec_t), iovp->iod_iovec);
641 }
642
643
644 /*
645 * Function: get_userdata
646 * Parameters: user_proc - process data is coming from
647 * user_addr - address of buffer in user_proc
648 * count - number of bytes to transfer
649 * loc_addr - address of local buffer to copy the user buffer to
650 *
651 * Copy a buffer from user_proc to a local buffer.
652 */
653 static void get_userdata(int user_proc, vir_bytes user_addr, vir_bytes count,
654 void *loc_addr)
655 {
656 phys_bytes src;
657
658 src = numap(user_proc, user_addr, count);
659 if (!src) {
660 panic("smx_ether: umap failed", NO_NUM);
661 }
662
663 phys_copy(src, vir2phys(loc_addr), (phys_bytes) count);
664 }
665
666
667 /*
668 * Function: put_userdata
669 * Parameters: user_proc - process data is going to
670 * user_addr - address of buffer in user_proc
671 * count - number of bytes to transfer
672 * loc_addr - address of local buffer to copy the user buffer from
673 *
674 * Copy a buffer from a local buffe to user_proc.
675 */
676 static void put_userdata(int user_proc, vir_bytes user_addr, vir_bytes count,
677 void *loc_addr)
678 {
679 phys_bytes dst;
680
681 dst = numap(user_proc, user_addr, count);
682 if (!dst) {
683 panic("smx_ether: umap failed", NO_NUM);
684 }
685 phys_copy(vir2phys(loc_addr), dst, (phys_bytes) count);
686 }
687
688
689 /*
690 * Function: do_getstat
691 * Parameters: mp - incoming DL_GETSTAT message
692 *
693 * Copies the ethernet statistics to the requersting process.
694 */
695 static void do_getstat(message *mp)
696 {
697 if (mp->DL_PORT != 0) {
698 panic("smx_ether: illegal port", mp->DL_PORT);
699 }
700
701 assert(eth_enabled);
702
703 put_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR,
704 (vir_bytes) sizeof(estats), &estats);
705 se_reply(mp->DL_PROC, OK, 0, 0);
706 }
707
708
709
710 /*
711 * Function: se_reply
712 * Parameters: whoto - who to reply to
713 * err - the error status of the request made
714 * status - a bit map, containing details on packet
715 * send and receive
716 * bytes_read - number of bytes read (only non-zero if
717 * status includes DL_PACK_RECV).
718 *
719 * Send a reply to a previous request message.
720 */
721 static void se_reply(int whoto, int err, int status, vir_bytes bytes_read)
722 {
723 message reply;
724 int r;
725
726 reply.m_type = DL_TASK_REPLY;
727 reply.DL_PORT = 0; /* The only port under smx */
728 reply.DL_PROC = whoto;
729 reply.DL_STAT = status | ((u32_t) err << 16);
730 reply.DL_COUNT = bytes_read;
731 reply.DL_CLCK = get_uptime();
732 r = send(whoto, &reply);
733 if (r < 0) {
734 panic("smx_ether: send failed:", r);
735 }
736 }
737
738 #endif /* ENABLE_NETWORKING */
739
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.