muxDevLoad是用来加载一个网络设备的,muxDevLoad成功执行后,我们就可以在shell里调用ifconfig命令来察看load的网卡设备了。
先看一下函数的调用流程,最后大概分析一下muxDevLoad的功能。
1726 /****************************************************************************** 1727 * 1728 * usrNetInit - network intialization routine called from usrConfig.c 1729 * 1730 */ 1731 STATUS usrNetInit 1732 ( 1733 char *bootString 1734 ) 1735 { 1736 #ifdef INCLUDE_BOOT_LINE_INIT 1737 usrBootLineParse(NULL); 1738 #endif /* INCLUDE_BOOT_LINE_INIT */ 1739 networkinit (); 1740 return (OK); 1741 }
该函数位于"vxworks-6.8/target/src/config/usrNetwork.c", 调用netWorkInit()来初始化整个网络模块。
netWorkInit()
------->usrNetEndLibInit() : "vxworks-6.8/target/src/config/usrNetwork.c"
-------->vxbDevMethodRun((UINT32)&muxDevConnect_desc, NULL); "vxworks-6.8/target/src/config/usrNetwork.c"
通过上述函数流程,可以看到vxbDevMethodRun遍历所有的注册设备,对每个设备调用muxDevConnect_desc函数,看看muxDevConnect_desc函数是哪儿定义的。
下面这段代码是从驱动:vxworks-6.8/target/src/hwif/end/an983VxbEnd.c 摘出来的。
141 LOCAL struct vxbDeviceMethod anMethods[] = 142 { 143 DEVMETHOD(miiRead, anPhyRead), 144 DEVMETHOD(miiWrite, anPhyWrite), 145 DEVMETHOD(miiMediaUpdate, anLinkUpdate), 146 DEVMETHOD(muxDevConnect, anMuxConnect), 147 DEVMETHOD(vxbDrvUnlink, anInstUnlink), 148 { 0, 0 } 149 };
anMethods是一个vxbDeviceMethod结构体数组,struct vxbDeviceMethod结构体定义如下:
266 /* 267 * An alternative method for bus controllers to provide the 268 * methods used by downstream devices. This structure is 269 * provided so that the controller can provide a null-terminated 270 * list of method/handler pairs. 271 * 272 * When a driver fetches the access methods, the standard methods 273 * are used first. If the method is not one of the standard 274 * methods (specified in the vxbAccessList structure), or if the 275 * method specified is NULL in the controller‘s standard methods, 276 * then the vxbAccessMethodsGet() routine searches through any 277 * methods listed in the pMethods field of the vxbAccessList 278 * structure. 279 * 280 * In addition, this structure is used by all devices to provide 281 * services to OS modules such as power management. 282 */ 283 284 struct vxbDeviceMethod 285 { 286 UINT32 devMethodId; 287 FUNCPTR handler; 288 };
宏DEVMETHOD的定义如下:
171 /* method related macros, for driver use and method caller use */ 172 173 #define DEVMETHOD(NAME, FUNC) { (UINT32)&NAME##_desc, (FUNCPTR) FUNC } 174 #define DEVMETHOD_END { 0, NULL } 175 #define METHOD_DECL(NAME) IMPORT char NAME##_desc[]; 176 #define DEVMETHOD_DEF(METHOD, STRING) char METHOD##_desc[]=STRING; 177 #define DEVMETHOD_CALL(METHOD) ((UINT32)(&METHOD##_desc[0]))
从vxbDeviceMethod结构体可以看出,能通过vxbDevMethodRun函数调用的函数必须有一个函数ID,DEVMETHOD可以帮助生成vxDeviceMethod实例。
到这一步,已经可以调到一个具体驱动的muxDevConnect函数了,下面分析一个具体驱动的muxDevConnect函数。还是以an983VxbEnd.c为例,代码如下:
855 /***************************************************************************** 856 * 857 * anMuxConnect - muxConnect method handler 858 * 859 * This function handles muxConnect() events, which may be triggered 860 * manually or (more likely) by the bootstrap code. Most VxBus 861 * initialization occurs before the MUX has been fully initialized, 862 * so the usual muxDevLoad()/muxDevStart() sequence must be defered 863 * until the networking subsystem is ready. This routine will ultimately 864 * trigger a call to anEndLoad() to create the END interface instance. 865 * 866 * RETURNS: N/A 867 * 868 * ERRNO: N/A 869 */ 870 871 LOCAL void anMuxConnect 872 ( 873 VXB_DEVICE_ID pDev, 874 void * unused 875 ) 876 { 877 AN_DRV_CTRL *pDrvCtrl; 878 879 /* 880 * Attach our ISR. For PCI, the index value is always 881 * 0, since the PCI bus controller dynamically sets 882 * up interrupts for us. 883 */ 884 885 vxbIntConnect (pDev, 0, anEndInt, pDev->pDrvCtrl); 886 887 pDrvCtrl = pDev->pDrvCtrl; 888 889 /* Save the cookie. */ 890 891 pDrvCtrl->anMuxDevCookie = muxDevLoad (pDev->unitNumber, 892 anEndLoad, "", TRUE, pDev); 893 894 if (pDrvCtrl->anMuxDevCookie != NULL) 895 muxDevStart (pDrvCtrl->anMuxDevCookie); 896 897 if (_func_m2PollStatsIfPoll != NULL) 898 endPollStatsInit (pDrvCtrl->anMuxDevCookie, 899 _func_m2PollStatsIfPoll); 900 901 return; 902 }
anMuxConnect首先注册中断,紧接着调用muxDevLoad函数完成网络设备的加载,传入的参数分别为设备的单元号,当同种设备有多个实例时,可以通过pDev->unitNumber区分开来。第二个参数是驱动实现的endLoad函数。最后一个是pDev指向当前网卡的设备实例指针。这三个参数比较重要。下面给出完整的muxDevLoad函数代码,从中可以看出muxDevLoad分两次调用anEndLoad函数,最终完成设备注册,通过muxDevLoad代码,可以更清楚的看到网卡实例的结构体在内核中是如何存放的。
503 /****************************************************************************** 504 * 505 * muxDevLoad - load a driver into the MUX 506 * 507 * The muxDevLoad() routine loads a network driver into the MUX. Internally, 508 * this routine calls the specified <endLoad> routine to initialize the 509 * software state of the device. After the device is initialized, 510 * muxDevStart() must be called to start the device. 511 * .IP <unit> 15 512 * Expects the unit number of the device. 513 * .IP <endLoad> 514 * Expects a pointer to the network driver‘s endLoad() or nptLoad() entry 515 * point. 516 * .IP <pInitString> 517 * Expects a pointer to an initialization string, typically a colon-delimited 518 * list of options. The muxDevLoad() routine passes this along blindly to 519 * the <endLoad> function. 520 * .IP <loaning> 521 * Currently unused. Expects a boolean value that tells the MUX whether the 522 * driver supports buffer loaning on this device. If the low-level device 523 * cannot support buffer loaning, passing in TRUE has no effect. 524 * .IP <pBSP> 525 * This argument is passed blindly to the driver, which may or may not use it. 526 * It is provided so that the BSP can pass in tables of functions that the 527 * driver can use but that are specific to the particular BSP on which 528 * it runs. 529 * 530 * RETURNS: A cookie representing the new device, or NULL if an error occurred. 531 * 532 * ERRNO: S_muxLib_LOAD_FAILED 533 */ 534 535 DEV_COOKIE muxDevLoad 536 ( 537 int unit, /* unit number of device */ 538 END_OBJ * (*endLoad) (char*, void*), /* load function of the driver */ 539 char * pInitString, /* init string for this driver */ 540 BOOL loaning, /* we loan buffers */ 541 void * pBSP /* for BSP group */ 542 ) 543 { 544 END_OBJ * pNew = NULL; 545 END_TBL_ROW * pNode = NULL; 546 NODE * enode; 547 DEV_OBJ dev; 548 char initString [END_INIT_STR_MAX]; 549 int endStyle; 550 MUX_END_STYLE_INFO * style; 551 int j; 552 553 bzero (initString, END_INIT_STR_MAX); 554 555 /* Create a fake END placeholder, mark it as invalid */ 556 557 bzero ((caddr_t)&dev, sizeof (dev)); 558 dev.unit = unit; 559 dev.muxFlags = MUX_END_FLAG_LOADING; 560 dev.refs = 1; 561 562 if (snprintf (initString, END_INIT_STR_MAX, 563 "%d:%s", unit, pInitString) >= END_INIT_STR_MAX) 564 goto muxDevLoad_errout; 565 566 /* 567 * Loading a device is a two pass algorithm. 568 * 569 * This is Pass 1. 570 * 571 * In the first pass we ask the device what its name is. 572 * The END must respect the name length limits of dev.name, 573 * END_NAME_MAX = 16 characters, including terminating NUL. 574 * Actually, the network stack imposes tighter limits in 575 * interface ioctls, for which the name (including unit number & 576 * terminating NUL) must fit in 16 bytes. 577 */ 578 579 if (endLoad (dev.name, NULL) != 0) 580 goto muxDevLoad_errout; 581 582 /* 583 * Now we verify that the device hasn‘t already been loaded, and 584 * to protect against concurrent loads of the same device, we 585 * put a temporary placeholder for the END into the MUX list. 586 * 587 * We use the temporary placeholder rather than holding muxLock 588 * across the END operations since we prefer not to establish 589 * any mutex ordering between muxLock and any semaphores taken 590 * by endLoad() or the device ioctl routine. 591 */ 592 593 if (semTake (muxLock, WAIT_FOREVER) != OK) 594 return NULL; 595 596 /* 597 * Consider replacing this data structure with a hash table. 598 * Consider sorting so that an iterator could be defined that 599 * traversed through the lists in a well-defined order. 600 * Consider interface indexes managed by the MUX (& consistent 601 * with the ipnet & other stacks). 602 */ 603 604 for (pNode = (END_TBL_ROW *)lstFirst(&endList); pNode != NULL; 605 pNode = (END_TBL_ROW *)lstNext(&pNode->node)) 606 { 607 if (strcmp (pNode->name, dev.name) == 0) 608 break; 609 } 610 611 if (pNode != NULL) /* there‘s already a row for this driver */ 612 { 613 /* 614 * pNew is used temporarily here. We do not create a new END object 615 * till later. 616 */ 617 618 for (enode = lstFirst(&pNode->units); enode != NULL; 619 enode = lstNext(enode)) 620 { 621 pNew = member_to_object (enode, END_OBJ, devObject.node); 622 if (pNew->devObject.unit == unit) 623 { 624 if (_func_logMsg) 625 _func_logMsg ("muxDevLoad: %s%d already loaded!\n", 626 dev.name, unit); 627 628 semGive (muxLock); 629 goto muxDevLoad_errout; 630 } 631 } 632 } 633 else /* There is no row yet for this device; add one. */ 634 { 635 /* MEM */ 636 pNode = malloc (sizeof(END_TBL_ROW)); 637 if (pNode == NULL) 638 { 639 semGive (muxLock); 640 return NULL; 641 } 642 643 bzero ((char *)pNode, sizeof(END_TBL_ROW)); 644 strncpy (pNode->name, dev.name, END_NAME_MAX - 1); 645 pNode->name [END_NAME_MAX - 1] = EOS; 646 lstAdd (&endList, &pNode->node); 647 } 648 649 /* 650 * Add placeholder to list before unlocking, to protect against 651 * concurrent loads of the same device. 652 */ 653 654 lstAdd (&pNode->units, &dev.node); 655 656 taskSafe (); /* stay delete-safe after unlocking */ 657 658 semGive (muxLock); 659 660 /* 661 * This is Pass 2. 662 * 663 * Now that we can determine a unique number we assign that number to 664 * the device and actually load it. 665 */ 666 667 pNew = (END_OBJ *)endLoad ( (char *)initString, pBSP); 668 669 if (pNew == NULL) 670 goto muxLoadErr; 671 672 if (pNew->pFuncTable == NULL || pNew->pFuncTable->ioctl == NULL) 673 goto muxLoadErr; 674 675 /* should this be done by END_OBJ_INIT() ? */ 676 677 pNew->devObject.dummyBinding = pNew; 678 pNew->devObject.refs = 1; 679 pNew->devObject.muxFlags = 0; 680 681 pNew->unloadInfo = NULL; 682 683 /* 684 * There used to be stuff here to decide whether to set the 685 * END_MIB_2233 flag in pNew->flags here; that‘s obsolete in 686 * more ways than one. Removed it. (TODO: What to do instead?) 687 */ 688 689 /* 690 * Determine if driver uses END or NPT interface. Default is END. 691 * END_STYLE_END2 (ipnet-native) drivers must support EIOCGSTYLE. 692 */ 693 694 endStyle = END_STYLE_END; 695 696 if (pNew->pFuncTable->ioctl (pNew, EIOCGSTYLE, 697 (caddr_t) &endStyle) != OK && 698 pNew->pFuncTable->ioctl (pNew, EIOCGNPT, NULL) == OK) 699 { 700 endStyle = END_STYLE_NPT; 701 } 702 703 pNew->endStyle = endStyle; 704 705 /* 706 * Set the native send routine and argument. 707 * 708 * TODO: may need a send wrapper to collect statistics for 709 * ENDs that don‘t set the END_MIB_2233 flag. See the old 710 * _muxTkSendEnd() and _muxTkSendNpt(). 711 * 712 * Actually, endM2Init() sets END_MIB_2233 in each END 713 * depending on whether RFC 2233 support is included in 714 * the image (pMibRtn != NULL); that‘s independent of 715 * the driver. A few older drivers that don‘t call 716 * endM2Init() might set END_MIB_2233 themselves. 717 */ 718 pNew->send[endStyle].func = pNew->pFuncTable->send; 719 pNew->send[endStyle].arg = pNew; 720 721 pNew->pollSend[endStyle].func = pNew->pFuncTable->pollSend; 722 pNew->pollSend[endStyle].arg = pNew; 723 724 pNew->pollRecv[endStyle].func = pNew->pFuncTable->pollRcv; 725 pNew->pollRecv[endStyle].arg = pNew; 726 727 style = &muxEndStyles[endStyle]; 728 729 if ((pNew->receiveRtn = style->rxRtn) == NULL) 730 { 731 if (_func_logMsg) 732 _func_logMsg ("No support for end style %d, required by %s%n\n", 733 endStyle, pNew->devObject.name, unit); 734 } 735 pNew->rxArg = pNew; /* do we need rxArg? */ 736 737 /* 738 * Install wrapper send, polled send, and polled receive 739 * functions for different supported styles 740 */ 741 for (j = 0; j < NUM_END_STYLES; ++j) 742 { 743 if (j == endStyle) /* skip the native style */ 744 continue; 745 746 if (style->sendWrappers[j] == NULL) 747 continue; 748 749 pNew->send[j].func = style->sendWrappers[j]; 750 pNew->send[j].arg = pNew; 751 pNew->pollSend[j].func = style->pollSendWrappers[j]; 752 pNew->pollSend[j].arg = pNew; 753 pNew->pollRecv[j].func = style->pollReceiveWrappers[j]; 754 pNew->pollRecv[j].arg = pNew; 755 } 756 757 semTake (muxLock, WAIT_FOREVER); 758 759 taskUnsafe (); /* remove extra deletion safety count */ 760 761 /* remove placeholder */ 762 763 lstDelete (&pNode->units, &dev.node); 764 765 lstAdd (&pNode->units, &pNew->devObject.node); 766 767 semGive (muxLock); 768 769 return (pNew); 770 771 muxLoadErr: 772 semTake (muxLock, WAIT_FOREVER); 773 774 taskUnsafe (); /* remove extra deletion safety count */ 775 776 /* remove placeholder */ 777 778 lstDelete (&pNode->units, &dev.node); 779 780 if (lstCount (&pNode->units) == 0) 781 { 782 lstDelete (&endList, &pNode->node); 783 free (pNode); 784 } 785 786 semGive (muxLock); 787 788 muxDevLoad_errout: 789 errnoSet (S_muxLib_LOAD_FAILED); 790 return (NULL); 791 }
//TODO:以后再加详细注释
下面是具体驱动的andLoad函数实现:
1080 /***************************************************************************** 1081 * 1082 * anEndLoad - END driver entry point 1083 * 1084 * This routine initializes the END interface instance associated 1085 * with this device. In traditional END drivers, this function is 1086 * the only public interface, and it‘s typically invoked by a BSP 1087 * driver configuration stub. With VxBus, the BSP stub code is no 1088 * longer needed, and this function is now invoked automatically 1089 * whenever this driver‘s muxConnect() method is called. 1090 * 1091 * For older END drivers, the load string would contain various 1092 * configuration parameters, but with VxBus this use is deprecated. 1093 * The load string should just be an empty string. The second 1094 * argument should be a pointer to the VxBus device instance 1095 * associated with this device. Like older END drivers, this routine 1096 * will still return the device name if the init string is empty, 1097 * since this behavior is still expected by the MUX. The MUX will 1098 * invoke this function twice: once to obtain the device name, 1099 * and then again to create the actual END_OBJ instance. 1100 * 1101 * When this function is called the second time, it will initialize 1102 * the END object, perform MIB2 setup, allocate a buffer pool, and 1103 * initialize the supported END capabilities. The only special 1104 * capability we support is VLAN_MTU, since we can receive slightly 1105 * larger than normal frames. 1106 * 1107 * RETURNS: An END object pointer, or NULL on error, or 0 and the name 1108 * of the device if the <loadStr> was empty. 1109 * 1110 * ERRNO: N/A 1111 */ 1112 1113 LOCAL END_OBJ *anEndLoad 1114 ( 1115 char * loadStr, 1116 void * pArg 1117 ) 1118 { 1119 AN_DRV_CTRL *pDrvCtrl; 1120 VXB_DEVICE_ID pDev; 1121 1122 /* Make the MUX happy. */ 1123 1124 if (loadStr == NULL) 1125 return NULL; 1126 1127 if (loadStr[0] == 0) 1128 { 1129 bcopy (AN_NAME, loadStr, sizeof(AN_NAME)); 1130 return NULL; 1131 } 1132 1133 pDev = pArg; 1134 pDrvCtrl = pDev->pDrvCtrl; 1135 1136 if (END_OBJ_INIT (&pDrvCtrl->anEndObj, NULL, pDev->pName, 1137 pDev->unitNumber, &anNetFuncs, 1138 "Infineon AN983 VxBus END Driver") == ERROR) 1139 { 1140 logMsg("%s%d: END_OBJ_INIT failed\n", (int)AN_NAME, 1141 pDev->unitNumber, 0, 0, 0, 0); 1142 return (NULL); 1143 } 1144 1145 endM2Init (&pDrvCtrl->anEndObj, M2_ifType_ethernet_csmacd, 1146 pDrvCtrl->anAddr, ETHER_ADDR_LEN, ETHERMTU, 100000000, 1147 IFF_NOTRAILERS | IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST); 1148 1149 /* Allocate a buffer pool */ 1150 1151 if (endPoolCreate (256, &pDrvCtrl->anEndObj.pNetPool) == ERROR) 1152 { 1153 logMsg("%s%d: pool creation failed\n", (int)AN_NAME, 1154 pDev->unitNumber, 0, 0, 0, 0); 1155 return (NULL); 1156 } 1157 1158 pDrvCtrl->anPollBuf = netTupleGet(pDrvCtrl->anEndObj.pNetPool, 1159 AN_CLSIZE, M_DONTWAIT, MT_DATA, 0); 1160 1161 /* Set up capabilities. */ 1162 1163 pDrvCtrl->anCaps.cap_available = IFCAP_VLAN_MTU; 1164 pDrvCtrl->anCaps.cap_enabled = IFCAP_VLAN_MTU; 1165 1166 return (&pDrvCtrl->anEndObj); 1167 }