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