天天看點

NDK USER'S GUIDEPrefaceOverviewNetwork Application DevelopmentNetwork Control FunctionsOS Adaptation Layer

目錄

本文僅為翻譯手冊,留以自己檢視,若需要深入交流,可以在個人分類中查找解析與實踐内容(可能未釋出),或與作者聯系 

Preface

About this manual

How to use this manual

Overview

Introduction

Rebuilding NDK Libraries

NDK Stack Library Design

Design Philosophy

Control Flow

Library Directory Structure

The STACK Library

NETTOOL Libraries

OS  Library

HAL Library

NETCTRL Libraries

NDK Programming APIs

Operating System Abstraction

Sockets and Stream IO API

NETTOOL Services and Support Functions

Internal Stack API

HAL API

NDK Software Directory Structure

NDK Include File Directory

TOOL Programs

Windows and Linux Test Utilities

Example Programs

Configuring NDK Modules

Network Application Development

Configuring the NDK with C Code

Required RTOS Objects

Include Files

Library Files

System Configuration

NDK Initialization

Adding NDK Hooks Using ACD Support

Creating a Task

Initializing the File Descriptor Table

Application Debug and Troubleshooting

解決常見問題

Debug Messages

Memory Corruption

Program Lockups

Memory Management Reports

Network Control Functions

Introduction to NETCTRL Source 

History

NETCTRL Source Files

Main Functions

Additional Functions 

Booting and Scheduling

NETCTRL Scheduler 

Scheduler Overview

Scheduling Options 

Scheduler Thread Priority

Tracking Events with STKEVENT

Scheduler Loop Source Code

Disabling On-Demand Services

OS Adaptation Layer

About OS Adaptation

Source Files

Task Thread Abstraction: TASK.C

TaskCreate(), TaskExit(), and TaskDestroy()

Choosing the llEnter()/llExit() Exclusion Method 

 Packer Buffer Manager: PBM.C

Packet Buffer Pool

Packet Buffer Allocation Method

Referenced Route Handles

 Memory Allocation System: MEM.C

 Embedded File System: EFS.C

General OS Support: OSSYS.C

Jumbo Packet Buffer Manager (Jumbo PBM)

Preface

About this manual

本文檔介紹了NDK應用程式程式設計以及如何在使用者系統和應用程式中建構和使用NDK。它不是一個API參考。

How to use this manual

本文檔中提供的資訊分為以下章節:

•第1章:概述介紹了堆棧和開發網絡應用程式。

•第2章:網絡應用程式開發描述了NDK軟體,以及如何立即開始開發網絡應用程式。

•第3章:網絡控制函數描述網絡控制層(NETCTRL)的内部工作方式。

•第4章:OS适配層描述了控制NDK如何使用RTOS資源的OS适配層。這包括任務,信号量,記憶體和列印。NDK旨在與各種RTOS系統配合使用 - 包括TI-RTOS核心(以前稱為SYS / BIOS)和FreeRTOS。NDK使用POSIX pthread調用和DPL函數調用來支援各種作業系統。

•附錄A:修訂曆史記錄描述了自上一版本以來對本文檔所做的更改。

Overview

本章通過簡要概述NDK的用途和構造,以及NDK部署環境中的硬體和軟體環境細節,介紹了網絡開發人員工具包(NDK)。此網絡開發人員工具包(NDK)軟體使用者指南是NDK和開發網絡應用程式的簡介。

Introduction

網絡開發者工具包(NDK)是一個用于在TI嵌入式處理器上開發和示範網絡應用的平台。此NDK版本中包含的代碼是通用C代碼,可在各種TI器件上運作。

在SimpleLink SDK中,網絡服務SlNetSock子產品将NDK配置為有線以太網通信的網絡堆棧。

NDK堆棧用作網絡和資料包處理應用程式開發的快速原型開發平台。它可用于為現有應用程式添加網絡連接配接,以進行通信,配置和控制。使用NDK中提供的元件,開發人員可以快速從開發概念轉移到連接配接到網絡的工作實作。

NDK USER'S GUIDEPrefaceOverviewNetwork Application DevelopmentNetwork Control FunctionsOS Adaptation Layer

在圖1-1中,使用者應用程式可以使用标準BSD套接字API進行調用,也可以直接調用SlNetSock層與網絡連接配接進行通信。SlNetSock層是使用者應用程式和特定于服務的堆棧之間的堆棧獨立層。

在SimpleLink SDK中,網絡服務SlNetSock子產品将NDK配置為有線以太網通信的網絡堆棧。“SlNetIfNDK”是NDK的SlNetSock接口的實作。

可以通過調用NDK的Cfg *()函數在運作時配置NDK堆棧的設定。

NDK旨在提供獨立于平台,與裝置無關且與RTOS無關的API接口,供應用程式使用。本文檔和TI網絡開發人員工具包(NDK)API參考指南(SPRU524)中描述的許多API子產品很少被應用程式使用。相反,它們可供那些正在編寫裝置驅動程式和網絡堆棧的人使用。

使用者應用程式通常使用NDK提供的以下API:

•Cfg *()函數向配置資料庫添加設定,以确定應用程式可以使用哪些網絡服務。有關詳細資訊,請參閱TI網絡開發人員套件(NDK)API參考指南(SPRU524)。

•NC _ *()函數可以初始化,啟動和停止網絡服務系統。有關詳細資訊,請參閱TI網絡開發人員套件(NDK)API參考指南(SPRU524)。

•TaskCreate()或pthread *()函數使用POSIX處理應用程式線程。在内部,POSIX可以使用SimpleLink SDK為目标裝置支援的任何RTOS。對于大多數目标,這包括TI-RTOS核心和FreeRTOS。有關詳細資訊,請參見第2.3節。

•NDK套接字API,用于執行套接字操作,如接受,發送和接收。對于純NDK應用程式,這些是類似BSD的NDK _ *()函數。

對于SimpleLink SDK應用程式,您可以使用通過SlNetSock提供的标準BSD API。有關詳細資訊,請參閱TI網絡開發人員套件(NDK)API參考指南(SPRU524)。

Rebuilding NDK Libraries

NDK安裝包括所有源檔案以及對重建其庫的完全支援。要重建NDK庫,請參閱Texas Instruments Wiki中使用gmake重建NDK核心主題中的說明。

您可以定義以下宏以導緻重建的NDK庫的行為發生變化:

•_INCLUDE_ACD_SUPPORT  - 如果已定義,如果ARP請求來自與NDK主機不同的子網上的計算機,則不會添加位址解析協定(ARP)條目。預設情況下,為所有ARP請求添加ARP表項。

•_INCLUDE_IPv6_CODE  - 如果已定義,則啟用IPv6支援。

•_INCLUDE_JUMBOFRAME_SUPPORT  - 如果已定義,則支援大于1500位元組的資料包大小。

•_INCLUDE_NAT_CODE  - 如果已定義,則堆棧提供網絡位址轉換(NAT)支援。

•_INCLUDE_PPP_CODE  - 如果已定義,則包括點對點協定(PPP)子產品。

•_INCLUDE_PPPOE_CODE  - 如果已定義,則啟用PPP over Ethernet(PPPoE)用戶端。

•_STRONG_CHECKING  - 如果已定義,則對所有句柄執行錯誤檢查。

NDK Stack Library Design

NDK旨在提供完整的TCP / IP功能環境,無論是否具有路由,都可在較小的記憶體空間内完成。

Design Philosophy

NDK通過抽象程式設計接口與本機OS和低級硬體隔離。本機OS由作業系統适配層(OS)抽象,并且通過硬體适配層(HAL)庫支援定制硬體。這些庫用于将堆棧連接配接到RTOS和系統外圍裝置。

NDK的功能包括:

•虛拟LAN(VLAN)支援。

VLAN支援使堆棧能夠接收,處理和傳輸VLAN标記的資料包。

•原始以太網套接字支援。

原始以太網套接字(與原始IPv4 / IPv6套接字不同)使任何使用NDK堆棧的應用程式都能夠使用自定義第2層(L2)協定類型發送/接收以太網資料包,即除了任何資料包之外的資料包的以太網報頭中的協定類型 衆所周知的标準協定類型,如IP(0x800),IPv6(0x806),VLAN(0x8100),PPPoE控制(0x8863)或PPPoE資料(0x8864)。

•IPv6堆棧支援。

該堆棧可用于IPv6和IPv4。

•巨型幀支援。

巨型幀的資料包大小超過1500位元組。通過連結與為Jumbo幀支援編譯的庫,可以将巨型幀支援内置到應用程式中。必須使用添加的以下預處理器定義重新編譯庫和應用程式:_INCLUDE_JUMBOFRAME_SUPPORT。

有關VLAN,IPv6,原始以太網套接字和Jumbo幀支援的更多詳細資訊,請參閱TI網絡開發人員套件(NDK)API參考指南(SPRU524)。

Control Flow

圖1-2顯示了如何根據函數調用控制流來組織堆棧包的概念圖。顯示了構成NDK的五個主要庫。這些是STACK,NETTOOL,OS,HAL和NETCTRL。這些庫在以下章節中進行了總結。NIMU相關的更改也在受影響的庫(STACK,NETCTRL和NETTOOL)中進行了讨論。

NDK USER'S GUIDEPrefaceOverviewNetwork Application DevelopmentNetwork Control FunctionsOS Adaptation Layer

Library Directory Structure

為構成/ ti / ndk目錄樹中的NDK的每個庫提供了預建構的可連結庫和源代碼。預建構的庫位于每個庫的目錄的lib子目錄中。

有關目錄樹的更多資訊,請參見第1.5節。

•提供了IPv4和IPv6庫。不包含“ipv4”的檔案名是針對IPv6編譯的。但是,在/ ti / ndk / stack / lib目錄中,為IPv4編譯不包含“6”的檔案名。

•NETCTRL庫有“min”,正常和“full”版本。例如,netctrl_min,netctrl和netctrl_full。詳細資訊請參見1.3.8節。

•OS庫包含TI-RTOS核心(“os”和“os_sem”)和FreeRTOS(“os_freertos”和“os_sem_freertos”)版本。

•NDK安裝中不包含支援Jumbo Frame的庫(大小超過1500位元組的資料包)。如果您希望啟用了Jumbo Frame支援的NDK庫,則需要#define _INCLUDE_JUMBOFRAME_SUPPORT預處理器定義并重建庫,如TI嵌入式處理器Wiki中的重建NDK核心主題中所述。

NDK提供的庫與平台無關。也就是說,為所有平台提供了這些庫的版本。任何僅針對某些平台存在的依賴于硬體的庫都分布在相應的NDK支援包(NSP)中,您可以從NDK單獨下載下傳該包。

NDK安裝包括所有源檔案以及對重建其庫的完全支援。要重建NDK庫,請參閱TI嵌入式處理器Wiki中的重建NDK核心主題中的說明。

The STACK Library

STACK庫是主要的TCP / IP網絡堆棧。它包含從頂部的套接字層到底部的以太網和點對點協定(PPP)層的所有内容。編譯庫以使用RTOS,并且在從一個平台移動到另一個平台時不需要移植。NDK中包含了幾個庫的版本。

STACK庫在/ ti / ndk / stack / lib目錄中提供。以下版本的庫包括或排除PPP,以太網PPP(PPPoE)和網絡位址轉換(NAT)等功能。

NETTOOL Libraries

網絡工具(NETTOOL)函數庫包含随NDK提供的所​​有基于套接字的網絡服務,以及一些旨在幫助開發網絡應用程式的其他工具。NETTOOL庫中最常用的元件是基于标記的配置系統。配置系統幾乎控制堆棧及其服務的每個方面。配置可以存儲在非易失性RAM中,以便在BOOT時自動加載。

NETTOOL庫在/ ti / ndk / nettools / lib目錄中提供。

NETTOOL庫中提供的工具直接使用NIMU IOCTL調用來檢索與裝置相關的資訊。

有關更多資訊,請參見第1.4.3節。

OS  Library

這些庫形成一個向上适配層,将一些抽象的OS函數調用映射到POSIX和DPL函數調用。這包括任務線程管理,記憶體配置設定,資料包緩沖區管理,列印,日志記錄,關鍵部分和巨型資料包緩沖區管理。

OS庫位于/ ti / ndk / os / lib目錄中。“os”庫是具有優先級排除的OS Adaptation Layer庫。“os_sem”庫使用信号量排除。有關更多資訊,請參見第1.4.1節。

HAL Library

HAL庫包含将硬體外圍裝置連接配接到NDK的檔案。其中包括定時器,LED訓示燈,以太網裝置和串行端口。/ ti / ndk / hal目錄中包含的驅動程式如下:

有關HAL API的資訊,請參見第1.4.5節。HAL還在TI網絡開發人員套件(NDK)API參考指南(SPRU524)和NDK支援包以太網驅動程式設計指南(SPRUFP2)中進行了讨論。

NETCTRL Libraries

NETCTRL或網絡控制庫可以被視為堆棧的中心。它控制TCP / IP與外部世界之間的互動。在所有堆棧子產品中,它對NDK的操作最重要。其職責包括:

•初始化NDK和低級裝置驅動程式

•通過配置服務提供程式回調函數引導和維護系統配置

•與低級裝置驅動程式的接口和調用驅動程式事件以調用NDK

•在退出時解除安裝系統配置和驅動程式清理

•在堆棧啟動期間初始化NIMU核心,然後初始化并啟動在NIMU核心注冊的所有裝置驅動程式。初始化NDK核心堆棧中的VLAN子產品。

•在堆棧關閉期間取消初始化NIMU核心,然後在所有已注冊的裝置驅動程式中循環并關閉它們。

•定期輪詢所有已注冊的裝置,以便允許它們執行任何例行維護活動,例如連結管理。此外,檢查來自任何已注冊裝置的任何事件,如資料包接收。

•如果在堆棧啟動期間内置,則初始化IPv6堆棧。

NETCTRL庫旨在支援使用者在其應用程式中期望的“潛在”堆棧功能(例如DHCP伺服器)。但是,這樣做的缺點是,即使應用程式從不使用這些功能,這些功能的代碼也将包含在可執行檔案中。這導緻比通常需要的更大的占地面積。

為了最大限度地減少此問題,/ ti / ndk / netctrl / lib目錄中提供了以下不同版本的NETCTRL庫:

•netctrl_min。此最小庫僅啟用DHCP用戶端。當需要最小的占地面積時,應該使用它。

•netctrl。NETCTRL庫的這個“标準”版本支援以下功能并具有中等占用空間: -  Telnet伺服器 -  HTTP伺服器 -  DHCP用戶端•netctrl_full。這個“完整”庫支援所有支援的NETCTRL功能,包括: -  Telnet伺服器 -  HTTP伺服器 -  NAT伺服器 -  DHCP用戶端 -  DHCP伺服器 -  DNS伺服器 

所有版本的NETCTRL都支援NIMU,VLAN和Raw以太網套接字。這些NETCTRL庫版本中的每一個都是為純IPv4和IPv6建構的。

如果使用XGCONF配置工具在Code Composer Studio(CCS)中配置NDK,則會根據您啟用的子產品自動選擇相應的NETCTRL庫。

您可以重建NETCTRL庫以僅包含要使用的功能。為此,請編輯/ ti / ndk / netctrl目錄中的package.bld檔案,然後重新定義以下任何選項。有關重建NDK庫的資訊,請參閱TI嵌入式處理器Wiki中的重建NDK核心主題。

NDK Programming APIs

如前所述,堆棧設計用于最佳隔離,是以可以無縫地插入到不同的運作時環境中。是以,您可能有機會使用幾種不同的程式設計接口。它們按相關性遞減順序列在此處。以下所有内容均在TI網絡開發人員套件(NDK)API參考指南(SPRU524)中詳細介紹。

Operating System Abstraction

作業系統抽象提供了支援移植到其他作業系統的服務。在大多數情況下,這些是NDK堆棧的内部,應用程式開發人員不會使用這些API。但是,有時應用程式開發人員可能需要使用Task子產品。有關這些詳細資訊,請參閱TI網絡開發人員套件(NDK)API參考指南(SPRU524)。

Sockets and Stream IO API

套接字API主要由類似BSD的套接字服務API組成,但包含一些其他有用的調用。這些函數是可重入且線程安全的。它們顯示為作業系統随附的标準IO的擴充,不應與任何本機檔案支援功能沖突。

NETTOOL Services and Support Functions

NETTOOL庫包括網絡服務和基本網絡支援功能。支援函數的API類似于Berkeley Unix的API,為自定義功能提供了一些附加功能。

NETTOOL服務包括将堆棧作為網絡伺服器或路由器運作所需的大多數網絡協定伺服器。服務的API在所有支援的服務中是标準化和統一的,并且還可以使用配置系統調用服務,完全繞過NETTOOL API。

Internal Stack API

您幾乎從不使用内部堆棧API(可以被認為是核心級API)。但是,某些類型的堆棧維護需要它,并且它由一些示例源代碼調用。

HAL API

您很可能永遠不會直接調用HAL API,但在将堆棧移動到備用硬體平台時需要它。TI網絡開發人員套件(NDK)API參考指南(SPRU524)和網絡開發人員套件(NDK)支援包以太網驅動程式(SPRUFP2)中更詳細地描述了HAL。

NDK Software Directory Structure

NDK檔案位于/ ti / ndk中,并組織到以下子目錄中。(任何其他目錄僅供内部使用。)對于每個庫,都提供源檔案和預建構庫。

NDK USER'S GUIDEPrefaceOverviewNetwork Application DevelopmentNetwork Control FunctionsOS Adaptation Layer

NDK Include File Directory

包含檔案目錄(/ ti / ndk / inc)包含可由網絡應用程式引用的所有包含檔案。必須在軟體工具預設搜尋路徑或CCStudio項目檔案的搜尋路徑中包含此目錄。後一種方法用于示例程式中。主要包含檔案如下:

NDK USER'S GUIDEPrefaceOverviewNetwork Application DevelopmentNetwork Control FunctionsOS Adaptation Layer

在HAL,NETCTRL,NETTOOLS,OS,STACK和TOOLS庫的子目錄中提供了其他包含檔案。

TOOL Programs

NDK提供了多種用于各種目的的工具。它們位于/ ti / ndk / tools目錄中。

NDK USER'S GUIDEPrefaceOverviewNetwork Application DevelopmentNetwork Control FunctionsOS Adaptation Layer

Windows and Linux Test Utilities

WINAPPS目錄包含四個非常簡單的測試應用程式,可用于驗證Console示例程式的操作。這些測試應用程式充當TCP發送,接收和回送以及UDP回送操作的網絡用戶端。大多數NDK示例包含可與這些測試應用程式通信的網絡資料伺服器。SEND,RECV,ECHOC和TESTUDP應用程式在這些示例的描述中引用,可以在這些示例中找到。

為Windows和Linux提供了這些測試程式的可執行版本。

您可以使用提供的makefile使用Microsoft Visual Studio或MinGW編譯器工具重建這些工具。

Example Programs

SDK中提供了與NDK一起使用的示例,具體取決于SDK目标。

Configuring NDK Modules

要配置NDK及其元件,NDK允許您使用C代碼調用Cfg *()函數或CCStudio中的XGCONF配置工具。選擇一種方法或另一種方法來配置您的應用程式。

•C代碼。

通過編寫調用CfgNew()的C代碼來建立配置資料庫和其他Cfg *()函數以将各種設定添加到該配置資料庫來配置應用程式。

在連結器指令檔案中完成了一些其他配置。有關詳細資訊,請參閱第2.1節。建議使用此配置方法,因為它可以與SDK支援的任何RTOS一起使用。

•XGCONF。

使用CCStudio中的圖形顯示來啟用和設定屬性。XGCONF還可用于配置TI-RTOS核心使用的對象。有關詳細資訊,請參閱第2.2節。僅當您的RTOS是TI-RTOS核心時,才能使用此配置方法。

Network Application Development

本章介紹如何開始開發網絡應用程式。它讨論了使用NDK庫開發網絡應用程式所涉及的問題和指南。

Configuring the NDK with C Code

要配置應用程式對NDK及其元件的使用,可以使用CC代碼中的C代碼或XGCONF配置工具。選擇一種方法或另一種方法來配置您的應用程式。

C代碼。通過編寫調用CfgNew()的C代碼來建立配置資料庫和其他Cfg *()函數以将各種設定添加到該配置資料庫來配置應用程式。在連結器指令檔案中完成了一些其他配置。建議使用此配置方法,因為它可以與SDK支援的任何RTOS一起使用。如果您使用的是此配置方法,請參見第2.1.1節及後面的小節。

XGCONF。使用CCStudio中的圖形顯示來啟用和設定屬性。XGCONF還可用于配置TI-RTOS核心使用的對象。僅當您的RTOS是TI-RTOS核心并且您正在使用CCStudio開發應用程式時,才能使用此配置方法。如果您使用XGCONF進行配置,請參見第2.2節。

不要混用配置方法。如果項目使用兩種方法,則兩種配置之間将存在沖突。

本文使用C code方法進行配置

Required RTOS Objects

NDK使用OS适配層通路RTOS(TI-RTOS核心或FreeRTOS)和HAL層以通路硬體。這些層需要建立以下RTOS對象才能使NDK正常工作。隻能通過修改OS和HAL層的代碼來更改此要求。

•計時器對象。HAL中的計時器驅動程式要求建立RTOS Timer對象以驅動其主計時器。必須将Timer配置為每100mS觸發一次,并調用定時器驅動程式函數llTimerTick()。有關建立和啟動計時器對象的ndk.c中的示例,請參見第2.1.4.1.1節。

(如果使用XGCONF配置NDK,則會自動建立此對象。)

Include Files

如果您使用Cfg *()函數将設定添加到配置資料庫,則您負責指向正确的包含檔案目錄。NDK安裝中的include目錄在1.5.1節中描述。您應該在項目建構選項中包含基本NDK包含目錄。例如,項目應設定為使用包含檔案路徑:/ ti / ndk / inc。

(如果使用XGCONF配置NDK,則CCStudio項目會自動引用正确的包含檔案目錄。)

Library Files

如果您使用Cfg *()函數進行配置,則您負責将正确的庫連結到項目中。如果您使用CCStudio來管理應用程式,則可以将所需的庫檔案直接添加到CCStudio項目中。這樣,連結器就會知道在哪裡找到它們。

(如果使用XGCONF配置NDK,則正确的庫會自動與應用程式連結。)

System Configuration

如果使用Cfg *()函數進行配置,則必須建立系統配置才能使用NETCTRL API。配置是一個基于句柄的對象,它包含大量系統參數。這些參數控制堆棧的操作。典型配置參數包括:

•網絡主機名

•IP位址和子網路遮罩

•預設路由的IP位址

•要執行的服務(DHCP,DNS,HTTP等)

•名稱伺服器的IP位址

•堆棧屬性(IP路由,套接字緩沖區大小,ARP逾時等)

建立配置的過程總是從調用CfgNew()開始建立配置句柄。建立配置句柄後,可以批量将配置資訊加載到句柄中,也可以一次構造一個條目。

批量加載配置需要先前建構的配置已儲存到非易失性存儲中。配置在記憶體中後,可以通過調用CfgLoad()将資訊加載到配置句柄中。另一種選擇是手動将各個項目添加到配置中以獲得各種所需的屬性。這是通過為要添加的每個條目調用CfgAddEntry()來完成的。

堆棧配置API的确切規範顯示在TI網絡開發人員套件(NDK)API參考指南(SPRU524)的初始化和配置部分。本文檔的第2.1.4.1節和NDK示例程式中提供了一些其他示例。

Configuration Examples

本節包含一些使用Cfg *()函數構造配置的示例代碼。

Constructing a Configuration for a Static IP and Gateway(原文檔中附代碼)

此示例中的ndkStackThread()函數由堆棧的主初始化線程組成。它建立一個新配置,配置IP,TCP和UDP,然後啟動堆棧。

該示例來自幾個NDK示例中的ndk.c檔案。它執行以下操作:

1.通過調用POSIX timer_create()和timer_settime()函數,建立并啟動NDK将使用的計時器對象。在内部,這些POSIX函數可以使用任何支援的RTOS,例如TI RTOS Kernel或FreeRTOS。

2.通過調用NC_SystemOpen()啟動系統會話。

3.通過調用CfgNew()建立新配置。

4.配置IP,TCP和UDP的堆棧設定。有關如何通過調用CfgAddEntry()配置這些設定的示例,請參閱ndk.c中的initIp(),initTcp()和initUdp()函數。

5.通過調用CfgAddEntry()配置用于低,正常和高優先級任務的堆棧大小。

6.通過調用NC_NetStart()使用此配置引導系統。

7.在系統關閉時釋放配置(當NC_NetStart()傳回時)并調用CfgFree()和NC_SystemClose()。

8.調用timer_delete()删除計時器對象。

static void *ndkStackThread(void *threadArgs)
{
    void *hCfg;
    int rc;
    timer_t ndkHeartBeat;
    struct sigevent sev;
    struct itimerspec its;
    struct itimerspec oldIts;
    int ndkHeartBeatCount = 0;

    /* create the NDK timer tick */
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_value.sival_ptr = &ndkHeartBeatCount;
    sev.sigev_notify_attributes = NULL;
    sev.sigev_notify_function = &llTimerTick;

    rc = timer_create(CLOCK_MONOTONIC, &sev, &ndkHeartBeat);
    if (rc != 0) {
        DbgPrintf(DBG_INFO, "ndkStackThread: failed to create timer (%d)\n");
    }
    /* start the NDK 100ms timer */
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 100000000;
    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = 100000000;

    rc = timer_settime(ndkHeartBeat, 0, &its, NULL);
    if (rc != 0) {
    DbgPrintf(DBG_INFO, "ndkStackThread: failed to set time (%d)\n");
    }

    rc = NC_SystemOpen(NC_PRIORITY_LOW, NC_OPMODE_INTERRUPT);
    if (rc) {
        DbgPrintf(DBG_ERROR, "NC_SystemOpen Failed (%d)\n");
    }

    /* create and build the system configuration from scratch. */
    hCfg = CfgNew();
    if (!hCfg) {
        DbgPrintf(DBG_INFO, "Unable to create configuration\n");
        goto main_exit;
    }

    /* IP, TCP, and UDP config */
    initIp(hCfg);
    initTcp(hCfg);
    initUdp(hCfg);

    /* config low priority tasks stack size */
    rc = 2048;
    CfgAddEntry(hCfg, CFGTAG_OS, CFGITEM_OS_TASKSTKLOW, CFG_ADDMODE_UNIQUE,
    sizeof(uint32_t), (unsigned char *)&rc, NULL);
    /* config norm priority tasks stack size */
    rc = 2048;
    CfgAddEntry(hCfg, CFGTAG_OS, CFGITEM_OS_TASKSTKNORM, CFG_ADDMODE_UNIQUE,
    sizeof(uint32_t), (unsigned char *)&rc, NULL);
    /* config high priority tasks stack size */
    rc = 2048;
    CfgAddEntry(hCfg, CFGTAG_OS, CFGITEM_OS_TASKSTKHIGH, CFG_ADDMODE_UNIQUE,
    sizeof(uint32_t), (unsigned char *)&rc, NULL);

    do {
        rc = NC_NetStart(hCfg, networkOpen, networkClose, networkIPAddr);
    } while(rc > 0);

    /* Shut down the stack */
    CfgFree(hCfg);

    main_exit:
    NC_SystemClose();
    /* stop and delete the NDK heartbeat */
    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = 0;
    rc = timer_settime(ndkHeartBeat, 0, &its, &oldIts);
    rc = timer_delete(ndkHeartBeat);
    DbgPrintf(DBG_INFO, "ndkStackThread: exiting ...\n");
    return (NULL);
}
           

Constructing a Configuration using the DHCP Client Service(原文檔中附代碼)

本節檢查上一節中調用的ndk.c中的initIp()函數。該函數告訴堆棧使用動态主機配置協定(DHCP)用戶端服務來執行其IP位址配置。

由于DHCP提供IP位址,路由,域和域名伺服器,是以您隻需提供主機名。有關使用DHCP的更多詳細資訊,請參閱TI網絡開發人員工具包(NDK)API參考指南(SPRU524)。

下面的代碼執行以下操作:1。使用hostName為本地主機名添加配置條目,該名稱聲明為static char * hostName =“tisoc”;

2.設定dhcpc的元素,其結構類型為CI_SERVICE_DHCPC。TI網絡開發人員套件(NDK)API參考指南(SPRU524)中描述了此結構。

3.添加指定要使用的DHCP用戶端服務的配置條目。

static void initIp(void *hCfg)
{
    CI_SERVICE_DHCPC dhcpc;
    unsigned char DHCP_OPTIONS[] = { DHCPOPT_SUBNET_MASK };

    /* Add global hostname to hCfg (to be claimed in all connected domains) */
    CfgAddEntry(hCfg, CFGTAG_SYSINFO, CFGITEM_DHCP_HOSTNAME, 0,
        strlen(hostName), (unsigned char *)hostName, NULL);

    /* Use DHCP to obtain IP address on interface 1 */

    /* Specify DHCP Service on IF specified by "IfIdx" */
    memset(&dhcpc, 0, sizeof(dhcpc));
    dhcpc.cisargs.Mode = CIS_FLG_IFIDXVALID;
    dhcpc.cisargs.IfIdx = 1;
    dhcpc.cisargs.pCbSrv = &serviceReport;
    dhcpc.param.pOptions = DHCP_OPTIONS;
    dhcpc.param.len = 1;
    CfgAddEntry(hCfg, CFGTAG_SERVICE, CFGITEM_SERVICE_DHCPCLIENT, 0,
        sizeof(dhcpc), (unsigned char *)&dhcpc, NULL);
}
           

Using a Statically Defined DNS Server (原文檔中附代碼)

DHCP用戶端使用的配置系統區域可能很困難。當DHCP用戶端正在使用時,它可以完全控制配置系統的系統資訊部分中的前256個條目。在極少數情況下,與DHCP共享此空間可能很有用。

例如,假設網絡應用程式需要手動将域名系統(DNS)伺服器的IP位址添加到系統配置中。當不使用DHCP時,此代碼很簡單。要添加128.114.12.2的DNS伺服器,将在配置建構過程中添加以下代碼(在調用NC_NetStart()之前)。

IPN IPTmp;

// Manually add the DNS server "128.114.12.2"

IPTmp = inet_addr("128.114.12.2");

CfgAddEntry( hCfg, CFGTAG_SYSINFO, CFGITEM_DHCP_DOMAINNAMESERVER, 0, sizeof(IPTmp), (unsigned char *)&IPTmp, 0 );
           

請注意,示例應用程式中的CLIENT示例程式使用此代碼的形式。現在,當使用DHCP用戶端時,它會清除并重置其控制的配置部分的内容。這包括DNS伺服器位址。是以,如果将上述代碼添加到使用DHCP的應用程式中,則隻要DHCP執行狀态更新,就會清除該條目。

要與DHCP共享此配置空間(或讀取DHCP配置的結果),必須使用DHCP狀态回調報告代碼。狀态回調函數在第2.1.5.5節中介紹。當DHCP報告狀态更改時,應用程式知道系統配置的DHCP部分已重置。

以下代碼也出現在CLIENT示例程式中。當DHCP用戶端正在使用時,此代碼手動添加DNS伺服器位址。請注意,此代碼是标準服務回調函數的一部分,該函數在指定DHCP用戶端服務時提供給配置。

Controlling NDK and OS Options via the Configuration

除了指定IP位址,路由和服務外,配置系統還允許您直接操作OS适配層和NDK的配置結構。OS網絡開發人員工具包(NDK)API參考指南(SPRU524)的作業系統配置部分讨論了OS配置結構,NDK配置結構将在附錄的配置堆棧部分中讨論。這些内部結構的配置界面将合并到“初始化和配置”部分中指定的單個配置API中。

盡管可以直接修改這兩種配置結構中的值,但将參數添加到系統配置中有兩個原因。首先,它為所有網絡配置提供一緻的API,其次,如果使用配置加載和儲存功能,這些配置參數将與系統配置的其餘部分一起儲存。

作為設定OS配置選項的快速示例,以下代碼對調試報告機制進行了更改。預設情況下,NDK生成的所有調試消息都将輸出到CCStudio輸出視窗。但是,可以調整OS配置以僅列印更高嚴重性級别的消息,或者完全禁用調試消息。

NDK附帶的大多數示例應用程式都會将調試消息的列印門檻值從INFO級别提高到警告級别。以下是它在源代碼中的顯示方式:

// We do not want to see debug messages less than WARNINGS

rc = DBG_WARN;

CfgAddEntry( hCfg, CFGTAG_OS, CFGITEM_OS_DBGPRINTLEVEL,CFG_ADDMODE_UNIQUE, sizeof(uint32_t), (unsigned char *)&rc, 0 );
           

Shutdown

堆棧可以通過兩種方式關閉。第一種是在應用程式調用NC_NetStop()時發生的手動關閉。這裡,函數的調用參數作為NC_NetStart()的傳回值傳回給NETCTRL線程。是以,對于示例代碼,調用NC_NetStop(1)重新啟動網絡堆棧,而調用NC_NetStop(0)則關閉網絡堆棧。

堆棧可以關閉的第二種方式是堆棧代碼檢測到緻命錯誤。緻命錯誤是高于配置中設定的緻命門檻值的錯誤。這種類型的錯誤通常表明堆棧繼續運作是不安全的。發生這種情況時,堆棧代碼調用NC_NetStop(-1)。然後由您決定接下來應該做什麼。編碼NC_NetStart()循環的方式決定了系統是否将關閉(如示例中所示),或者隻是重新啟動。

請注意,也可以禁用關閉的臨界門檻值。可以将以下代碼添加到配置中以禁用與錯誤相關的關閉:

// We do not want the stack to abort on any error

uint32_t rc = DBG_NONE;

CfgAddEntry( hCfg, CFGTAG_OS, CFGITEM_OS_DBGABORTLEVEL,CFG_ADDMODE_UNIQUE, sizeof(uint32_t), (unsigned char *)&rc, 0 )
           

Saving the Configuration

建構配置後,應用程式可以将其儲存到非易失性RAM中,以便在下次冷啟動時重新加載。這在嵌入式系統中尤其有用,在嵌入式系統中,可以使用串行電纜,Telnet或HTTP浏覽器在運作時修改配置。

如果使用XGCONF進行配置,XGCONF不會自動支援儲存和重新加載配置。但是,在内部,建構* .cfg檔案時,将填充Cfg *()C函數使用的相同配置資料庫。您可能希望使用以下小節中的函數作為挂鈎函數來儲存使用XGCONF建立的配置,并在啟動時從非易失性記憶體中重新加載。

要儲存配置,請将其轉換為線性緩沖區,然後将線性緩沖區儲存到存儲區。以下是配置儲存操作的快速示例。請注意,假設MyMemorySave()函數将線性緩沖區儲存到非易失性存儲中。

int SaveConfig( void *hCfg ) {
    unsigned char *pBuf;
    int size;
    // Get the required size to save the configuration
    CfgSave( hCfg, &size, 0 );
    if( size && (pBuf = malloc(size) ) ) 
    {
        CfgSave( hCfg, &size, pBuf );
        MyMemorySave( pBuf, size );
        Free( pBuf );
        return(1);
    }
    return(0);
}
           

儲存配置後,可以在啟動時從非易失性存儲器加載。對于最終的NetworkTest()示例,假設另一個Task在先前的執行中建立,編輯或儲存了有效配置到某些存儲媒體。在此網絡初始化例程中,所需的隻是從存儲加載配置并使用目前配置引導NDK。

對于此示例,假設函數MyMemorySize()傳回存儲的線性緩沖區中的配置大小,并且MyMemoryLoad()從非易失性存儲中加載線性緩沖區。

int NetworkTest()
{
    int rc;
    void *hCfg;
    unsigned char *pBuf;
    Int size;

    //
    // THIS MUST BE THE ABSOLUTE FIRST THING DONE IN AN APPLICATION!!
    //
    rc = NC_SystemOpen( NC_PRIORITY_LOW, NC_OPMODE_INTERRUPT );
    if( rc ) {
        printf("NC_SystemOpen Failed (%d)\n",rc);
        for(;;);
    }

    //
    // First load the linear memory block holding the configuration
    //

    // Allocate a buffer to hold the information
    size = MyMemorySize();
    if( !size )
        goto main_exit;

    pBuf = malloc( size );
    if( !pBuf )
        goto main_exit;

    // Load from non-volatile storage
    MyMemoryLoad( pBuf, size );

    //
    // Now create the configuration and load it
    //

    // Create a new configuration
    hCfg = CfgNew();

    if( !hCfg ) {
        printf("Unable to create configuration\n");
        free( pBuf );
        goto main_exit;
    }

    // Load the configuration (and then we can free the buffer)
    CfgLoad( hCfg, size, pBuf );

    mmFree( pBuf );

    //
    // Boot the system using this configuration
    //
    // We keep booting until the function returns less than 1. This allows
    // us to have a "reboot" command.
    //
    do
    {
        rc = NC_NetStart( hCfg, NetworkStart, NetworkStop, NetworkIPAddr );
    } while( rc > 0 );

    // Delete Configuration
    CfgFree( hCfg );

    // Close the OS
    main_exit:
    NC_SystemClose();
    return(0);
}
           

NDK Initialization

在可以執行類似于示例的套接字應用程式之前,必須正确配置和初始化堆棧。為了便于标準初始化過程,并允許自定義,NDK中包含網絡控制子產品(NETCTRL)的源代碼。NETCTRL子產品是堆棧初始化和事件排程的中心。對NETCTRL操作的充分了解對于建構可靠的網絡應用程式至關重要。本節介紹如何在網絡應用程式中使用NETCTRL。第3章提供了有關NETCTRL如何工作以及如何調整的解釋。

TI網絡開發人員套件(NDK)API參考指南(SPRU524)的第4章詳細介紹了NDK的初始化過程。本節與該文檔的NDK軟體目錄中描述的初始化過程密切相關。在這裡,我們用更實際的傾斜描述資訊。與此處提到的功能的确切API有關的程式設計人員應參考TI網絡開發人員工具包(NDK)API參考指南(SPRU524)以獲得更精确的描述。

The NETCTRL Task Thread

如果使用Cfg *()API調用進行配置,則必須建立一個包含對NC_NetStart()調用的Task線程,該調用依次運作網絡排程程式函數。NSP示例應用程式,在其主C源檔案中提供此線程。

如果使用XGCONF進行配置,則會自動生成此線程,并且調用NC_NetStart()的代碼位于生成的C檔案中(例如,evmOMAPL138用戶端示例的client_p674.c)。

此Task線程(稱為排程程式線程)是幾乎所有NETCTRL活動都發生的線程。該線程充當程式的入口點并執行初始化操作。之後,它成為NETCTRL排程程式線程。是以,在關閉堆棧之前,不會将此線程的控制權傳回給調用方。應用程式任務 - 面向網絡或其他 - 不在此線程内執行。

Pre-Initialization

如果使用Cfg *()API調用進行配置,則應用程式必須在調用任何其他堆棧API函數之前調用主初始化函數NC_SystemOpen()。這初始化堆棧和所有堆棧元件使用的記憶體環境。兩個調用參數Priority和OpMode訓示排程程式應如何執行。例如,NSP中包含的示例應用程式包含以下代碼:

rc = NC_SystemOpen( NC_PRIORITY_LOW, NC_OPMODE_INTERRUPT );
if( rc ) {
    printf("NC_SystemOpen Failed (%d)\n",rc);
    for(;;);
}
           

Invoking New Network Tasks and Services

可以在NDK配置中指定一些标準網絡服務;它們由NETCTRL子產品自動加載和解除安裝。其他服務,包括應用程式員編寫的服務,應該從回調函數啟動。

如果使用Cfg *()API調用進行配置,則可以使用提供給NC_NetStart()的Start回調函數來添加回調函數。作為網絡啟動回調的示例,下面的NetworkStart()函數通過調用open函數來打開使用者SMTP伺服器應用程式以建立主應用程式線程。

static SMTP_Handle hSMTP;

//
// NetworkStart
//
// This function is called after the configuration has booted
//
static void NetworkStart( ) {
    // Create an SMTP server Task
    hSMTP = SMTP_open( );
}
           

上面的代碼啟動了一個不需要進一步監視的自包含應用程式,但是在系統關閉時必須關閉應用程式。這是通過NetworkStop()回調函數完成的。是以,NetworkStop()函數必須撤消在NetworkStart()中完成的操作。

//
// NetworkStop
//
// This function is called when the network is shutting down
//
static void NetworkStop()
{
    // Close our SMTP server Task
    SMTP_close( hSMTP );
}
           

上面的示例假定無論堆棧是否具有本地IP位址,都可以啟動網絡排程程式任務。對于偵聽通配符位址為0.0.0.0的伺服器,情況也是如此。在極少數情況下,任務初始化可能需要IP位址,或者可能需要某種裝置類型的IP位址。在這些情況下,NetworkIPAddr()回調函數會向應用程式發出可以安全啟動的信号。

以下示例說明了對NetworkIPAddr()回調的調用參數。請注意,可以調用IFIndexGetHandle()和IFGetType()函數來擷取添加或删除新IP位址的裝置類型(HTYPE_ETH或HTYPE_PPP)。此示例僅列印一條消息。此回調函數的最常見用途是同步需要在執行之前安裝本地IP位址的網絡任務。

//
// NetworkIPAddr
// This function is called whenever an IP address binding is
// added or removed from the system.
//
static void NetworkIPAddr( IPN IPAddr, uint32_t IfIdx, uint32_t fAdd ) {
    IPN IPTmp;

    if( fAdd )
        printf("Network Added: ");
    else
        printf("Network Removed: ");

    // Print a message
    IPTmp = ntohl( IPAddr );

    printf("If-%d:%d.%d.%d.%d\n", IfIdx, (unsigned char)(IPTmp>>24)&0xFF,
        (unsigned char)(IPTmp>>16)&0xFF, (unsigned char)(IPTmp>>8)&0xFF, (unsigned char)IPTmp&0xFF );
}
           

Network Startup

如果使用Cfg *()API調用進行配置,則應用程式必須在加載配置後調用NETCTRL函數NC_NetStart()以調用網絡排程程式。除了配置句柄外,該函數還需要三個額外的回調指針參數;指向Start回調函數,Stop函數和IP Address Event函數的指針。

前兩個回調函數隻調用一次。系統初始化并準備好執行網絡應用程式時,将調用Start回調(請注意,可能尚未安裝本地IP網絡位址)。系統關閉時會調用Stop回調,表示堆棧很快就無法執行網絡應用程式。第三個回調可以多次調用。在系統中添加或删除本地IP位址時調用它。這可用于檢測新的DHCP或PPP位址事件,或僅用于記錄本地IP位址以供本地網絡應用程式使用。在系統關閉之前,對NC_NetStart()的調用不會傳回,然後它會傳回一個關閉代碼作為其傳回值。系統如何關閉對于确定是否應重新啟動堆棧可能很重要。例如,可能需要重新啟動以加載新配置。NC_NetStart()的傳回碼可用于确定是否應再次調用NC_NetStart()(進而執行重新開機)。

舉一個簡單的例子,如果堆棧以大于零的傳回碼關閉,則以下代碼使用目前配置句柄不斷重新啟動堆棧。通過調用NC_NetStop()關閉堆棧時設定傳回碼。

//
// Boot the system using our configuration
//
// We keep booting until the function returns 0. This allows
// us to have a "reboot" command.
//
do
{
    rc = NC_NetStart( hCfg, NetworkStart, NetworkStop, NetworkIPAddr );
} while( rc > 0 );
           

Adding Status Report Services

配置系統還可用于調用NETTOOLS庫中的标準網絡服務。使用NDK的網絡應用程式可用的服務将在TI網絡開發人員工具包(NDK)API參考指南(SPRU524)的第4章中詳細讨論。本節總結了該章中描述的服務。

使用NETTOOLS庫時,會引入NETTOOLS狀态回調函數。此回調函數跟蹤通過配置啟用的服務的狀态。狀态回調函數有兩個級别。第一個回調是由NETTOOLS服務完成的。它在服務狀态發生變化時調用配置服務提供程式。然後,配置服務提供程式将自己的狀态添加到資訊中,并回調應用程式的回調函數。當應用程式将服務添加到系統配置時,将提供指向應用程式回調的指針。

如果使用Cfg *()API調用進行配置,則所有示例中使用的基本狀态回調函數如下所示:

//
// Service Status Reports
//
static char *TaskName[] = { "Telnet","HTTP","NAT","DHCPS","DHCPC","DNS" };
static char *ReportStr[] = { "","Running","Updated","Complete","Fault" };
static char *StatusStr[] = { "Disabled", "Waiting", "IPTerm", "Failed", "Enabled" }

static void ServiceReport( uint32_t Item, uint32_t Status, uint32_t Report, void *h ) {
    printf( "Service Status: %-9s: %-9s: %-9s: %03d\n",
        TaskName[Item-1], StatusStr[Status], ReportStr[Report/256], Report&0xFF );
}
           

請注意,各個服務的名稱列在TaskName []數組中。此順序由配置系統中服務項的定義指定,并且是常量。請參閱檔案INC / NETTOOLS / NETCFG.H以擷取實體聲明。

請注意,定義主報告代碼的字元串列在ReportStr []數組中。此順序由NETTOOLS标準報告機制指定并且是常量。請參閱檔案INC / NETTOOLS / NETTOOLS.H以擷取實體聲明。

請注意,定義Task狀态的字元串在StatusStr []數組中定義。此順序由配置系統中标準服務結構的定義指定。請參閱檔案INC / NETTOOLS / NETCFG.H以擷取實體聲明。

此回調函數列印的最後一個值是Report中傳遞的值的最低有效8位。此值特定于相關服務。對于大多數服務,此值是多餘的。通常,如果服務成功,則報告“完成”,如果服務失敗,則報告“故障”。對于從未完成的服務(例如,在IP租約處于活動狀态時繼續運作的DHCP用戶端),報告的高位元組表示正在運作,并且必須使用特定于服務的低位元組來确定目前狀态。

例如,使用DHCP用戶端服務時,報告的8個最低有效位中傳回的狀态代碼為:

DHCPCODE_IPADD       Client has added an IP address

DHCPCODE_IPREMOVE       IP address removed and CFG erased

DHCPCODE_IPRENEW       IP renewed, DHCP config space reset

這些DHCP用戶端特定的報告代碼在INC / NETTOOLS / INC / DHCPIF.H中定義。在大多數情況下,除了以下情況外,您不必檢查狀态報告代碼,直到這個詳細程度。使用DHCP用戶端配置堆棧時,DHCP用戶端控制CFGTAG_SYSINFO标記空間的前256個條目。這些條目對應于256個DHCP選項标記。應用程式可以檢查DHCPCODE_IPADD或DHCPCODE_IPRENEW傳回代碼,以便它可以讀取或更改DHCP用戶端擷取的資訊。這将在第2.1.4.1.2節中進一步讨論。

Adding NDK Hooks Using ACD Support

使用定義的_INCLUDE_ACD_SUPPORT宏重建NDK可以支援一些可選的鈎子函數,這些函數可以讓使用者在堆棧的IP和ARP層中獲得更多控制。這些鈎子支援那些希望将位址沖突檢測協定(ACD,RFC5227)添加到NDK的人。

但是,鈎子也可以用于其他目的。這些挂鈎可用于執行以下操作: 

•在IP位址綁定到堆棧之前,對IP位址執行運作時驗證,這些IP位址可以靜态設定或由DHCP配置。

•監控傳入的位址解析協定(ARP)資料包。

使用_INCLUDE_ACD_SUPPORT重建NDK時,它會在IP層和ARP層的關鍵位置調用使用者定義的挂鈎函數(如果它們是非NULL)。您可以按照以下小節中的說明定義此類功能并啟用它們。

Defining an IP Address Validation Function

要使用NDK的鈎子函數調用來驗證潛在的IP位址,請建立并定義一個接受IP位址的鈎子函數。如果IP位址可以使用,該函數必須傳回成功,如果不能使用,則傳回-1。該函數的簽名定義如下:

/* Hook function to check IP address per protocols like ACD RFC5227 */
typedef int(*NS_ValidateAddress)(IPN ipAddr);
           

Registering the IP Address Validation Function

一旦寫入IP位址驗證功能,就可以将其傳遞給以下API,以便将其注冊到堆棧:

extern void NS_setAddrHook(NS_ValidateAddress fxn);
           

然後,IP層在綁定潛在IP位址之前調用驗證函數“fxn”(如果它不是NULL)。如果驗證函數傳回成功,則堆棧綁定IP位址。如果不是,則綁定失敗。

必須在堆棧初始化代碼的早期在适當的位置調用NS_setAddrHook()函數。特别是,應該在調用NC_NetStart()之前在啟動代碼中調用它。為了在堆棧初始化期間進行此調用,有必要修改NDK堆棧線程的代碼以添加調用。

rc = NC_SystemOpen(NC_PRIORITY_LOW, NC_OPMODE_INTERRUPT);
if (rc) {
    System_abort("NC_SystemOpen Failed (%d)\n");
}

/* Create and build the system configuration from scratch. */
hCfg = CfgNew();
if (!hCfg) {
    System_printf("Unable to create configuration\n");
    goto main_exit;
}

/* ... */

/* call NS_setAddrHook here */
do {
    rc = NC_NetStart(hCfg, ti_ndk_config_Global_NetworkOpen,
                    ti_ndk_config_Global_NetworkClose,
                    ti_ndk_config_Global_NetworkIPAddr);
} while (rc > 0 || rc == UAT_LINKDOWN);
           

Defining an ARP Packet Monitoring Function

類似地,還可以定義和注冊鈎子函數以監視接收的ARP分組。要使用NDK的挂鈎函數調用來監視收到的ARP資料包,請定義一個接受ARP資料包的挂鈎函數。在驅動程式Rx處理期間将調用鈎子函數。該函數可以執行此鈎子函數中所需的任何操作。例如,它可以掃描ARP資料包的資料或對資料包進行排隊(使用SB隊列)以供以後處理。

該函數的定義如下:

/* Hook function to give access to received ARP packets */
typedef void(*LLI_ReportARP)(void * data);
           

Registering the ARP Packet Monitoring Function

一旦編寫了ARP資料包監控功能,就可以将其傳遞給以下API,以便将其注冊到堆棧:

extern void LLI_setARPHook(LLI_ReportARP fxn);
           

然後,ARP層将為堆棧接收的ARP資料包調用驗證函數“fxn”(如果它不為NULL)。

調用LLI_setARPHook()函數的步驟與第2.1.6.2節中的步驟類似。

Creating a Task

使用NDK的應用程式可以通過以下任一方式建立任務線程:

•按照TI網絡開發人員工具包(NDK)API參考指南(SPRU524)中的說明調用TaskCreate()。

•按照TI處理器wiki的POSIX線程(pthread)支援頁面中的說明調用POSIX pthread API。請注意,如果直接使用pthreads,則代碼需要通過在開頭調用fdOpenSession()并在結尾調用fdCloseSession()來初始化檔案描述符表(參見第2.3.1節)。

在内部,TaskCreate()使用POSIX pthreads,是以您的應用程式實際上使用POSIX去進行任務建立方法。

基于優先級的排除使您的應用程式僅使用已配置的NDK優先級範圍内的優先級(OS_TASKPRILOW到OS_TASKPRIHIGH)非常重要。将線程設定為比NDK的高優先級線程級别更高的優先級可能會破壞系統并線上程調用任何與堆棧相關的函數時導緻不可預測的行為。

以下示例使用TaskCreate()建立具有普通優先級的Task:

void *taskHandle = NULL;

taskHandle = TaskCreate( entrypoint, "TaskName", OS_TASKPRINORM, stacksize, arg1, arg2, arg3 );
           

以下示例使用pthread API建立供NDK使用的任務。該線程具有普通優先級,堆棧大小為2048

#include <pthread.h>
#include <ti/ndk/inc/netmain.h>

void mySocketThreadFxn()
{
    fdOpenSession((void *)pthread_self());
    /* do socket calls */
    fdCloseSession((void *)pthread_self());
}

void createNdkThread()
{
    int status;
    pthread_attr_t pthreadAttrs;
    struct sched_param schedParams;
    pthread_t task;

    pthread_attr_init(&pthreadAttrs);

    schedParams.sched_priority = OS_TASKPRINORM;
        status = pthread_attr_setschedparam(&pthreadAttrs, &schedParams);
    if (status != 0) {
        /* error */
    }

    status = pthread_attr_setstacksize(&pthreadAttrs, 2048);
    if (status != 0) {
        /* error */
    }

    status = pthread_attr_setdetachstate(&pthreadAttrs, PTHREAD_CREATE_DETACHED);
    if (status != 0) {
        /* error */
    }

    status = pthread_create(&task, &pthreadAttrs, mySocketThreadFxn, NULL);
    if (status != 0) {
        /* error */
    }

    pthread_attr_destroy(&pthreadAttrs);
    if(status!= 0)
    {
    / * error * /
    }
}
           

Initializing the File Descriptor Table

使用套接字或檔案API的每個Task線程必須配置設定檔案描述符表并将該表與Task句柄相關聯。TI網絡開發人員套件(NDK)API參考指南(SPRU524)中詳細介紹了此過程。要實作這一點,必須在使用任何面向檔案描述符的函數之前執行對fdOpenSession()的調用,然後在不再需要這些函數時調用fdCloseSession()。

void *mySocketsFunction(void *arg)
{
    fdOpenSession((void *)pthread_self());
    /* do socket calls */
    fdCloseSession((void *)pthread_self());
    return (NULL);
}
           

Application Debug and Troubleshooting

雖然沒有即時或簡單的方法來調試NDK應用程式,但以下部分提供了一些潛在問題區域的快速描述。其中一些主題也在文檔的其他地方讨論。

解決常見問題

NDK最常見的支援請求之一是處理無法發送或接收網絡資料包的問題。這也可能采取丢包或一般性能不佳的形式。這種行為有很多原因。有關潛在的排程問題,請參見第2.2.7.2節。還建議應用程式員完全了解NETCTRL子產品的工作原理。為此,請參閱第3章。

這是一個快速清單。如果使用XGCONF進行配置,則不會發生許多潛在的配置問題。

All socket calls return “error” (-1)

•確定在使用套接字之前在Task中調用fdOpenSession(),并在Task終止時調用fdCloseSession()。

No link indication, or will not re-link when cable is disconnected and reconnected.

•確定配置中有一個Timer對象,每100 ms調用一次驅動程式函數llTimerTick()。

Not receiving any packets – ever

•通過以非阻塞方式調用NDK_recv(),fdPoll()或fdSelect()來輪詢資料時,請確定沒有任何排程問題。當NETCTRL排程程式以低優先級運作時,不允許網絡應用程式在不阻塞的情況下進行輪詢。嘗試以高優先級運作排程程式(通過NC_SystemOpen())。

•NDK假定存在一些L2緩存。如果DSP或ARM配置為内部存儲器而沒有任何剩餘的L2緩存,則NDK驅動程式将無法正常工作。

Performance is sluggish. Very slow ping response.

•確定配置中有一個Timer對象,每100 ms調用一次驅動程式函數llTimerTick()。

•如果在中斷模式下移植以太網驅動程式并運作NETCTRL,請確定您的裝置正确檢測到中斷。確定中斷極性正确。

UDP application drops packets on NDK_send() calls.

•如果發送到新IP位址,則第一次發送可能會在ARP層中保留,而堆棧将确定資料包目标的MAC位址。在此模式下,後續發送将被丢棄。

•使用UDP并一次發送多個資料包時,請確定有足夠的資料包緩沖區可用(參見第4.3.1節)。

•确認您沒有任何計劃問題。嘗試以高優先級運作排程程式(通過NC_SystemOpen())。

UDP application drops packets on NDK_recv() calls.

•確定有足夠的資料包緩沖區(參見第4.3.1節)。

•確定UDP的資料包門檻值足夠高,以儲存在NDK_recv()調用之間接收的所有UDP資料(請參閱NDK程式員參考指南中的CFGITEM_IP_SOCKUDPRXLIMIT)。

•确認您沒有任何計劃問題。嘗試以高優先級運作排程程式(通過NC_SystemOpen())。

•以太網裝置驅動程式可能會丢棄資料包。某些裝置驅動程式具有可調整的RX隊列深度,而其他驅動程式則沒有有關更多詳細資訊,請參閱以太網裝置驅動程式的源代碼(硬體平台的NDK支援包中提供了裝置驅動程式源代碼)。

Pings to NDK target Fails Beyond 3012 Size

NDK的預設配置允許重新組裝最大為“3012”位元組的資料包。為了能夠ping更大的大小,需要重新配置堆棧,如下所示:

•更改“pbm.c”檔案中的“MMALLOC_MAXSIZE”定義。(即#define MMALLOC_MAXSIZE 65500)并重建庫。

•在全局配置的“緩沖區”頁籤中增加“記憶體管理器緩沖區頁面大小”。

•增加IP子產品配置的最大IP重組大小屬性。

Sending and Receiving UDP Datagrams over MTU Size

發送和接收UDP資料報的大小取決于以下NDK配置選項,套接字選項和OS适配層定義:

•NDK配置選項:

- 增加IP子產品套接字配置的最小發送大小屬性。請參閱TI網絡開發人員套件(NDK)API參考指南(SPRU524)。

- 增加IP子產品套接字配置的最小讀取大小屬性。

- 如果使用Cfg *()API調用進行配置,則可以使用以下C代碼配置這些IP子產品屬性:

uint32_t tmp = 65500;

// configure NDK
CfgAddEntry(hCfg, CFGTAG_IP, CFGITEM_IP_IPREASMMAXSIZE,
            CFG_ADDMODE_UNIQUE, sizeof(uint32_t), (unsigned char*) &tmp, 0);
CfgAddEntry(hCfg, CFGTAG_IP, CFGITEM_IP_SOCKUDPRXLIMIT,
            CFG_ADDMODE_UNIQUE, sizeof(uint32_t), (unsigned char*) &tmp, 0);

// set socket options
NDK_setsockopt(s, SOL_SOCKET, SO_RCVBUF, &tmp, sizeof(int) );
NDK_setsockopt(s, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(int) );
           

•套接字選項:

-  SO_SNDBUF:參見TI網絡開發人員套件(NDK)API參考指南(SPRU524)

-  SO_RCVBUF  - 請參閱TI網絡開發人員套件(NDK)API參考指南(SPRU524)

•OS适配層定義:

- 更改“pbm.c”檔案中的“MMALLOC_MAXSIZE”定義。(即#define MMALLOC_MAXSIZE 65500)并重建庫

- 在Global配置的Buffers頁籤中增加Memory Manager Buffer Page Size。

- 如果使用Cfg *()API調用進行配置,則可以編輯pbm.c檔案中的MMALLOC_MAXSIZE定義和mem.c檔案中的RAW_PAGE_SIZE定義。然後在/ ti / ndk / os / lib中重建相應的OS Adaptation Layer庫。

Timestamping UDP Datagram Payloads

NDK允許應用程式更新UDP資料報的有效負載。這種情況的典型用法是更新資料報的時間戳資訊。這樣,發送和接收端可以根據系統的運作時間特性的變化更準确地調整傳送延遲。

在發送端: 

•應用程式可以使用NDK_setsockopt()函數為每個插槽注冊一個調出功能。

•在将資料報插入驅動程式的傳輸隊列之前,堆棧會調用call-out函數。

•呼出功能負責更新标頭中的UDP校驗和資訊。

•以下代碼部分是如何控制它的示例:

void myTxTimestampFxn(unsigned char *pIpHdr) {
    ...
}

NDK_setsockopt(s, SOL_SOCKET, SO_TXTIMESTAMP, (void*) myTxTimestampFxn, sizeof(void*));
           

在接收端:

•應用程式可以使用EtherConfig()函數在每個接口的基礎上注冊調出功能。它在“netctrl.c”的NC_NetStart()函數中設定。

•在處理資料包之前,堆棧排程程式會調用call-out函數。

•呼出功能負責更新标頭中的UDP校驗和資訊。

•以下代碼部分是如何控制它的示例:

void myRcvTimestampFxn(unsigned char *pIpHdr) {
    ...
}

EtherConfig( hEther[i], 1518, 14, 0, 6, 12, 4, myRcvTimestampFxn);
           

一般來說

•請勿嘗試調整定時器功能頻率。確定它每100毫秒調用llTimerTick()。

•注意記憶體不足的情況。這些可以通過某些功能的傳回來檢測,但也會在啟用消息時列印出警告消息。這些消息包含記憶體不足的首字母縮寫OOM。(記憶體不足可能是由許多因素引起的,但NDK中最常見的原因是在不使用SO_LINGER套接字選項的情況下非常快速地建立和關閉TCP套接字。這會使許多套接字處于TCP timewait狀态,進而耗盡了暫存記憶體解決方案是使用SO_LINGER套接字選項。)

Debug Messages

TI-RTOS核心的調試消息使用System_printf()API處理,該API由XDCtools提供,供TI-RTOS使用。

目前不支援FreeRTOS的調試輸出,是以後面的小節不适用于使用FreeRTOS的應用程式。您可以修改OS Adaptation Layer源代碼以根據需要添加其他類型的程式輸出。

Controlling Debug Messages

TI-RTOS核心的調試消息還包括關聯的嚴重性級别。這些級别是DBG_INFO,DBG_WARN和DBG_ERROR。嚴重性級别用于兩個目的。首先,它确定是否将列印調試消息,其次,它确定調試消息是否将導緻NDK關閉。

預設情況下,将列印所有調試消息,并且級别為DBG_ERROR的消息會導緻堆棧關閉。可以在2.2.10節中描述的配置中修改此行為。或者,您可以通過第2.1.4.2節和第2.1.4.3節中所述的系統配置對其進行修改。另請參閱TI網絡開發人員套件(NDK)API參考指南(SPRU524)。

Interpreting Debug Messages

以下是堆棧操作期間可能發生的一些TI-RTOS核心調試消息的清單,以及最常見的相關原因。

TCP: Retransmit Timeout: Level DBG_INFO

此消息由TCP在向網絡對等方發送資料包時生成,并且對等方未在預期的時間内回複。這可以是任何事情;對等體已經關閉,網絡繁忙,網絡資料包被丢棄或損壞,等等。

FunctionName:Buffer OOM:Level DBG_WARN

當出現意外的記憶體不足情況時,某些子產品會生成此消息。堆棧有一個内部資源恢複例程來幫助處理這些情況;但是,大量的這些消息也可能表明沒有足夠的大塊記憶體可用,或者存在記憶體洩漏。有關更多詳細資訊,請參閱本節中有關記憶體管理器報告的說明。

 mmFree: Double Free: Level DBG_WARN

在未标記為已配置設定的記憶體塊上調用mmFree()函數時,會出現雙重釋放消息。這可能是由于為同一記憶體實體調用兩次mmFree()引起的,但更常見的是由記憶體損壞引起的。有關可能的原因,請參閱第2.4.3節。

FunctionName: HTYPE nnnn: Level DBG_ERROR

此消息僅由堆棧的強檢查版本生成。将句柄傳遞給不是正确句柄類型的函數時會導緻它。由于堆棧的面向對象特性對網絡應用程式編寫器是隐藏的,是以不應發生此錯誤。如果它不是由嘗試調用内部堆棧函數引起的,那麼很可能是記憶體損壞的結果。有關可能的原因,請參閱本節中有關記憶體損壞的說明。

mmAlloc: PIT ???? Sync: Level DBG_ERROR

該消息由暫存存儲器配置設定系統生成。PIT是頁面資訊表的首字母縮寫。表同步錯誤隻能由記憶體損壞引起。有關可能的原因,請參閱第2.4.3節。

PBM_enq: Invalid Packet: Level DBG_ERROR

此消息由OS适配層中的資料包緩沖區管理器(PBM)子產品驅動程式生成。

當PBM子產品最初配置設定其資料包緩沖池時,它會使用幻數标記每個資料包緩沖區。在正常操作期間,資料包被推入和彈出各種隊列。在每次按下操作時,将檢查資料包的幻數。當幻數無效時,将顯示此消息。使用非複制套接字API擴充時,可能會将無效資料包引入系統,但更常見的原因是記憶體損壞。有關可能的原因,請參閱本節中有關記憶體損壞的說明。

Memory Corruption

NDK調試消息可能會發生記憶體損壞錯誤。這是因為很容易破壞緩存裝置上的記憶體。NDK中包含的大多數示例程式都使用完整的L2緩存運作。在此模式下,對CPU内部存儲器範圍的任何讀或寫通路都可能導緻高速緩存損壞,進而導緻記憶體損壞。由于内部存儲器範圍從位址0x00000000開始,是以使用完全緩存時,NULL指針可能會導緻問題。

要檢查是否由NULL指針引起損壞,請更改緩存模式以使用較少的緩存。當有一些内部存儲器可用時,對位址0x0的讀取或寫入不會導緻高速緩存損壞(應用程式仍可能無法正常工作,但錯誤消息應該停止)。

追蹤任何類型的緩存損壞的另一種方法是中斷CPU讀取或寫入整個緩存範圍。Code Composer Studio能夠捕獲對一系列記憶體的讀取或寫入,但兩者都無法同時檢查。是以,可能需要進行幾次試驗。

當然,記憶體損壞可能與堆棧無關。它可能是一個狂野的指針。但是,由于破壞緩存可能會破壞整個系統的記憶體,是以緩存是第一個啟動的地方。

Program Lockups

大多數鎖定條件是由任務堆棧大小不足引起的。例如,在編寫HTTP CGI函數時,CGI函數Task線程隻有大約5000位元組的總任務堆棧。是以,不建議使用大量堆棧。通常,請勿使用以下代碼:

myTask()
{
    char TempBuffer[2000];
    myFun( TempBuffer );
}
           

但相反,請使用以下内容:

myTask()
{
    char *pTempBuf;
    pTempBuf = Memory_alloc( NULL, 2000, 0, &eb )
    if (pTempBuf != NULL)
    {
        myFun( pTempBuf );
        Memory_free( NULL, pTempBuf, 2000 );
    } 
}
           

如果調用記憶體配置設定函數的速度開銷過高,請考慮使用外部緩沖區。

這隻是一個例子,稍加考慮你可以消除所有可能的堆棧溢出條件,并消除程式鎖定的可能性。

Memory Management Reports

管理NDK中暫存記憶體的記憶體管理器具有内置報告系統。它跟蹤臨時存儲器的使用(調用mmAlloc()和mmFree()),并跟蹤對配置設定的大塊存儲器的調用(調用mmBulkAlloc()和mmBulkFree())。請注意,批量配置設定函數隻需調用malloc()和free()。可以通過調整記憶體管理器來更改此行為。

記憶體報告如下所示。它列出了每個大小桶配置設定的最大塊數,對malloc和free的調用次數,以及已配置設定記憶體的清單。示例報告如下所示: 

48:48 ( 75%) 18:96 ( 56%) 8:128 ( 33%) 28:256 ( 77%)
1:512 ( 16%) 0:1536 0:3072
(21504/46080 mmAlloc: 61347036/0/61346947, mmBulk: 25/0/17)

1 blocks alloced in 512 byte page
38 blocks alloced in 48 byte page
18 blocks alloced in 96 byte page
8 blocks alloced in 128 byte page
12 blocks alloced in 256 byte page
12 blocks alloced in 256 byte page
           

這裡,條目18:96(56%)表示最多在96位元組桶中配置設定了18個塊。記憶體管理器上的頁面大小為3072,是以使用了56%的頁面。條目21504/46080表示最多配置設定了21,504個位元組,總共有46,080個位元組可用。

條目mmAlloc:61347036/0/61346947表示對mmAlloc()進行了61,347,036次調用,其中0次失敗,并且對mmFree()進行了61,346,947次調用。請注意,在任何時候,對mmAlloc的調用加上失敗必須等于對mmFree的調用加上任何未完成的配置設定。是以,在報告為mmAlloc:n1 / n2 / n3的最終報告中,n1 + n2應等于n3。如果沒有,則存在記憶體洩漏。

使用大多數示例應用程式附帶的telnet控制台程式時,有幾種方法可以擷取記憶體報告。控制台'mem'指令列印出目前報告,但更重要的是,控制台'shutdown'指令關閉堆棧并列印出最終報告。如果根據本文檔中的規範建立和銷毀所有網絡應用程式,則最終報告中不應檢測到記憶體洩漏。調用以擷取記憶體報告的函數定義如下。

mmCheck – Generate Memory Manager Report

NDK USER'S GUIDEPrefaceOverviewNetwork Application DevelopmentNetwork Control FunctionsOS Adaptation Layer

Network Control Functions

Introduction to NETCTRL Source 

History

NETCTRL子產品最初是推薦的初始化和排程方法來執行NDK。雖然大多數都很簡單,但這個代碼成了标最終,它被分離到NETCTRL庫中。

NETCTRL子產品是NDK的中心,因為它将HAL和OS适配層連接配接到NDK。它控制初始化以及如何在堆棧内排程事件。了解NETCTRL子產品的工作原理有助于您調整DSP或ARM網絡應用程式以獲得理想的性能。

NETCTRL Source Files

NETCTRL庫的源代碼由位于/ ti / ndk / netctrl目錄下的兩個C檔案組成:

NETCTRL.C網絡控制(初始化和排程)子產品

NETSRV.C配置服務子產品(系統配置服務提供者)

有兩個包含檔案與/ INC / NETCTRL目錄中的NETCTRL相關聯:

NETCTRL.H NETCTRL的接口規範

NETSRV.H NETSRV的接口規範

Main Functions

NETCTRL.C源子產品包含具有NC_字首的所有函數的源代碼。NETCTRL子產品的功能有三個基本部分。

NETCTRL.C的第一個功能是在調用任何其他堆棧函數之前執行必要的系統初始化和關閉。這些函數聲明為NC_SystemOpen()和NC_SystemClose()。

NETCTRL.C的第二個功能是執行啟動堆棧功能所需的驅動程式環境初始化和配置引導程式。此啟動函數及其關閉對應項聲明為NC_NetStart()和NC_NetStop()。

從調用者隐藏的NETCTRL.C的最後一個功能是實作堆棧的事件排程,它是堆棧操作的中心。

NETSRV.C子產品包含引導堆棧上所有服務的代碼。此代碼擷取存儲在堆棧配置中的内容,并實作必要的堆棧功能以使配置保持最新。當配置中的活動項更改時,NETSRV子產品中的代碼将在NDK中執行該更改。

Additional Functions 

TI網絡開發人員套件(NDK)API參考指南(SPRU524)中未記錄一些其他NETCTRL功能。這些函數是NC_BootComplete()和NC_IPUpdate()。

它們都是從NETSRV子產品調用的。

NC_NetStart()函數通過建立一個入口點為NS_BootTask()(來自NETSRV.C)的引導線程來啟動配置引導過程。配置引導完成後,配置引導線程将調用NC_BootComplete()函數。它向NETCTRL發出信号,它現在可以調用由應用程式傳遞給NC_NetStart()的NetworkStart()應用程式回調。從NC_BootComplete()傳回時,引導線程終止。是以,應用程式員可以控制NetworkStart()回調線程,但不建議這樣做。

當向系統添加位址或從系統中删除位址時,NETSRV會調用IP位址更新功能。然後,此函數調用最初傳遞給NC_NetStart()的NetworkIPAddr()應用程式回調。

Booting and Scheduling

第2.1.5節讨論了使用網絡控制(NETCTRL)子產品。本節介紹主NETCTRL子產品的内部源代碼和事件排程程式的操作。

堆棧事件排程程式是調用堆棧以處理資料包和計時器事件的例程。排程程式從NC_NetStart()内部調用,直到堆棧關閉才傳回,這解釋了為什麼NC_NetStart()函數在系統關閉且排程程式終止之前不會傳回應用程式。

NC_NetStart()的基本流程如下:

NC_NetStart()
{
    Initialize_Devices();

    CreateConfigurationBootThread() ;
    NetScheduler();
    CloseConfiguration();
    CloseDevices();
}
           

在上面列出的NC_NetStart()的功能階段中,最關心的兩個是建立引導線程和網絡事件排程程式的實作。

引導線程由名為NETSRV.C的NETCTRL庫中的第二個C子產品處理。此名稱是Network Service Manager的縮寫。NETSRV子產品作為配置服務提供程式挂接到配置系統。配置系統子產品隻是一個活動資料庫。相反,網絡服務子產品将配置條目轉換為實際的NDK對象。可以改變服務子產品以适應特定需要。這可能涉及為配置系統建立自定義配置标記。但是,完全了解NETSRV中的代碼需要基本了解TI網絡開發人員工具包(NDK)API參考指南(SPRU524)中讨論的幾乎所有API函數。

您應該最關心NetScheduler()函數,因為此排程程式運作NDK。它查找需要由NDK處理的事件,并執行開始處理所需的工作。

NETCTRL Scheduler 

Scheduler Overview

NETCTRL排程程式代碼是一個名為NetScheduler()的無限循環函數,出現在源檔案NETCTRL.C的末尾。它從低級裝置驅動程式中查找活動事件,并在檢測到事件時執行操作。當通過外部調用NC_NetStop()設定靜态變量時,循環終止。

盡管NDK提供了一個可重入的環境,但堆棧的核心并不是可重入的。必須保護部分代碼不被重入調用通路。該軟體不是使用阻止所有其他任務執行的關鍵部分,而是定義稱為核心模式的操作模式。定義核心模式,使得在任何給定時間隻有一個Task可以處于核心模式。它不會阻止運作不使用NDK的任務。這為堆棧提供了保護,而不會影響不相關代碼的執行。定義了兩個函數來進入和退出核心模式,llEnter()和llExit()。它們是OS适配層的一部分,将在4.2.2節中詳細讨論。簡而言之,必須在調用堆棧之前調用llEnter(),并且在完成調用堆棧函數時必須調用llExit()。

排程程式循環的基本流程可以通過以下僞代碼彙總:

static void NetScheduler()
{
    SetSchedulingPriority();

    while( !NetHaltFlag )
    {
        WaitOrPollForEvents();
        ServiceDeviceDrivers();

        // Process current events in Kernel Mode
        if( StackEvents ) 
        {
            // Enter Kernel Mode
            llEnter();
            ServiceStackEvents();

            // Exit Kernel Mode llExit();
        } 
    }
}
           

以下各節依次介紹每個突出顯示的功能。請注意,代碼将繼續運作,直到設定NetHaltFlag。當應用程式調用NC_NetStop()函數時,将設定此标志。 

Scheduling Options 

運作排程程式有三種基本方法。它們可以被視為三種操作模式:

1.排程程式以低優先級運作,僅在有網絡事件要處理時運作。

2.排程程式以低優先級連續運作,輪詢裝置驅動程式以查找事件。

3.排程程式運作高優先級,但僅在有網絡事件要處理時才運作。

運作排程程式的最佳方式取決于應用程式和系統體系結構。

模式1是運作NDK的最有效方式。這裡,排程程式循環以低優先級運作。這使得可能具有實時要求的應用程式優先于網絡,其中實時限制更加寬松。此外,排程循環僅在存在網絡相關活動時運作;是以,也可以使用空閑循環。

當阻止裝置驅動程式使用中斷時,使用模式2。這對于實時任務最佳,但對網絡性能最差。由于排程程式線程連續運作,是以它也可以防止使用空閑循環。這是NETCTRL在使用需要輪詢的裝置驅動程式時必須使用的模式。

模式3是最像Unix的環境。這裡,網絡排程程式任務的運作優先級高于系統中的任何其他網絡任務。隻要檢測到新的網絡相關事件,就會運作堆棧,進而可能會使用堆棧搶占其他任務。這是保持網絡環境最新的最佳方法,而不會限制網絡應用程式的編寫方式。

當應用程式首次調用NC_SystemOpen()時,将設定優先級和輪詢或中斷驅動的排程。這将在第2.1.5.2節和NDK程式員參考指南中進一步讨論。

Scheduler Thread Priority

NetScheduler()實際實作的第一行包括以下代碼:

// Set the scheduler priority
TaskSetPri(TaskSelf(), SchedulerPriority);
           

 此代碼更改調用NC_NetStart()的Task線程的優先級,以便有一個控制點來設定排程程式優先級。使用的優先級是傳遞給NC_SystemOpen()函數的優先級。這将在第2.1.5.2節和NDK程式員參考指南中進一步讨論。

排程程式優先級(相對于網絡應用程式線程優先級)會影響網絡應用程式的程式設計方式。例如,當以低優先級運作排程程式時,網絡應用程式無法通過以非阻塞方式連續調用NDK_recv()來輪詢資料。這是因為如果應用程式線程永遠不會阻塞,則網絡排程程式線程永遠不會運作,并且NDK永遠不會處理傳入的資料包。

Tracking Events with STKEVENT

如前所述,NETCTRL子產品是堆棧與HAL層中的裝置驅動程式之間的接口。在舊版本的NDK中,裝置驅動程式通過全局信号量向NETCTRL子產品發出信号。為了稍微改進這個過程,簡單的信号量被封裝到一個名為STKEVENT的對象中。

從裝置驅動程式的角度來看,此事件對象是一個傳遞給名為STKEVENT_signal()的函數的句柄。實際上,這個函數隻是一個在STKEVENT類型結構上運作的MACRO。NETCTRL子產品直接在此結構上運作。STKEVENT結構定義如下:

// Stack Event Object
typedef struct _stkevent {
    void *hSemEvent;
    uint32_t EventCodes[STKEVENT_NUMEVENTS];
} STKEVENT;

#define STKEVENT_NUMEVENTS 5

#define STKEVENT_TIMER 0
#define STKEVENT_ETHERNET 1
#define STKEVENT_SERIAL 2
#define STKEVENT_LINKUP 3
#define STKEVENT_LINKDOWN 4
           

 該結構有兩個部分,一個信号量句柄和一組事件。每個驅動程式通過在EventCode []數組中為其事件類型設定一個标志來發出事件信号,然後可選地用信号通知事件信号量。隻有當驅動程式檢測到中斷條件時才會發出信号。如果在驅動程式輪詢期間檢測到事件(定期輪詢或僅輪詢驅動程式時為常量),則設定事件,但不發信号通知信号量。

當驅動程式發出STKEVENT_LINKUP或STKEVENT_LINKDOWN事件信号時,您可以提供挂鈎功能,這意味着連結已經出現或已經關閉。請注意,隻有在驅動程式具有調用STKEVENT_signal({STKEVENT_LINKUP})和STKEVENT_signal({STKEVENT_LINKDOWN})的代碼時,才會調用此類挂鈎函數。

鈎子函數應該接受單個int狀态參數。如果函數收到0,則連結現在已關閉(例如,因為電纜斷開連接配接)。如果函數收到1,則連結現在已啟動。要注冊鈎子函數,請按如下方式調用NC_setLinkHook():

NC_setLinkHook( void (*LinkHook)(int) );
           

NETCTRL子產品建立STKEVENT結構的私有執行個體,它作為STKEVENT_Handle類型的句柄傳遞給裝置驅動程式。由NETCTRL直接操作的私有執行個體聲明為:

// Static Event Object
static STKEVENT stkEvent;
           

在随後的NetScheduler()的完整源代碼中,STKEVENT結構由其執行個體stkEvent引用。

Scheduler Loop Source Code

NDK中包含的示例排程程式實作的代碼如下所示。此實作使用本節中描述的方法和對象來充實第3.2.1節中所示的僞代碼。在此代碼中,串行端口裝置和以太網裝置的數量作為調用參數傳入。當裝置驅動程式要求枚舉其實體裝置時,将從裝置驅動程式擷取此裝置數。

#define FLAG_EVENT_TIMER 1
#define FLAG_EVENT_ETHERNET 2
#define FLAG_EVENT_SERIAL 4
#define FLAG_EVENT_LINKUP 8
#define FLAG_EVENT_LINKDOWN 16

static void NetScheduler( uint32_t const SerialCnt, uint32_t const EtherCnt ) 
{
    register int fEvents;

    /* Set the scheduler priority */
    TaskSetPri( TaskSelf(), SchedulerPriority );

    /* Enter scheduling loop */
    while( !NetHaltFlag ) 
    {
        if( stkEvent.hSemEvent )
        {
            SemPend( stkEvent.hSemEvent, SEM_FOREVER );
        }

        /* Clear our event flags */
        fEvents = 0;

        /* First we do driver polling. This is done from outside */
        /* kernel mode since pure "polling" drivers cannot spend */
        /* 100% of their time in kernel mode. */

        /* Check for a timer event and flag it. EventCodes[STKEVENT_TIMER] */
        /* is set as a result of llTimerTick() (NDK heartbeat) */
        if( stkEvent.EventCodes[STKEVENT_TIMER] )
        {
            stkEvent.EventCodes[STKEVENT_TIMER] = 0;
            fEvents |= FLAG_EVENT_TIMER;
        }

        /* Poll only once every timer event for ISR based drivers, */
        /* and continuously for polling drivers. Note that "fEvents" */
        /* can only be set to FLAG_EVENT_TIMER at this point. */
        if( fEvents || !stkEvent.hSemEvent ) 
        {
            NIMUPacketServiceCheck (fEvents);
            /* Poll Serial Port Devices */
            if( SerialCnt )
                _llSerialServiceCheck( fEvents );
        }

        /* Note we check for Ethernet and Serial events after */
        /* polling since the ServiceCheck() functions may */
        /* have passively set them. */

        /* Was an Ethernet event signaled? */
        if(stkEvent.EventCodes[STKEVENT_ETHERNET])
        {
            /* We call service check on an event to allow the */
            /* driver to do any processing outside of kernel */
            /* mode that it requires, but don't call it if we */
            /* already called it due to a timer event or by polling */
            if (!(fEvents & FLAG_EVENT_TIMER) && stkEvent.hSemEvent)
                NIMUPacketServiceCheck (0);

            /* Clear the event and record it in our flags */
            stkEvent.EventCodes[STKEVENT_ETHERNET] = 0;
            fEvents |= FLAG_EVENT_ETHERNET;
        }

        /* Check for a Serial event and flag it */
        if(SerialCnt && stkEvent.EventCodes[STKEVENT_SERIAL] ) 
        {
            /* We call service check on an event to allow the */
            /* driver to do any processing outside of kernel */
            /* mode that it requires, but don't call it if we */
            /* already called it due to a timer event or by polling */
            if( !(fEvents & FLAG_EVENT_TIMER) && stkEvent.hSemEvent )
                _llSerialServiceCheck( 0 );

            /* Clear the event and record it in our flags */
            stkEvent.EventCodes[STKEVENT_SERIAL] = 0;
            fEvents |= FLAG_EVENT_SERIAL;
        }

        /* Check if link went up */
        if(stkEvent.EventCodes[STKEVENT_LINKUP])
        {
            /* Clear the event and record it in our flags */
            stkEvent.EventCodes[STKEVENT_LINKUP] = 0;
            fEvents |= FLAG_EVENT_LINKUP;
        }

        /* Check if link went down */
        if(stkEvent.EventCodes[STKEVENT_LINKDOWN])
        {
            /* Clear the event and record it in our flags */
            stkEvent.EventCodes[STKEVENT_LINKDOWN] = 0;
            fEvents |= FLAG_EVENT_LINKDOWN;
        }

        /* Process current events in Kernel Mode */
        if( fEvents ) 
        {
            /* Enter Kernel Mode */
            llEnter();

            /* Check for timer event. Timer event flag is set as a result of */
            /* llTimerTick() (NDK heartbeat) */
            if( fEvents & FLAG_EVENT_TIMER )
                ExecTimer();
        
            /* Check for packet event */
            if( fEvents & FLAG_EVENT_ETHERNET )
                NIMUPacketService();

            /* Check for serial port event */
            if( fEvents & FLAG_EVENT_SERIAL )
                llSerialService();

            /* Exit Kernel Mode */
            llExit();

            /* Check for a change in link status. Do this outside of above */
            /* llEnter/llExit pair as to avoid illegal reentrant calls to */
            /* kernel mode by user's callback. */

            /* Check for link up status */
            if( fEvents & FLAG_EVENT_LINKUP ) 
            {
                /* Call the link status callback, if user registered one */
                if (NetLinkHook) 
                {
                    /* Pass callback function a link status of "up" */
                    (*NetLinkHook)(1);
                } 
            }

            /* Check for link down status */
            if( fEvents & FLAG_EVENT_LINKDOWN ) 
            {
                /* Call the link status callback, if user registered one */
                if (NetLinkHook) 
                {
                    /* Pass callback function a link status of "down" */
                    (*NetLinkHook)(0);
                }
            } 
        } 
    } 
}
           

Disabling On-Demand Services

NETCTRL庫被設計為支援使用者在應用程式(例如DHCP伺服器)内期望的“潛在”堆棧特征。但是,這樣做的缺點是,即使應用程式從不使用這些功能,這些功能的代碼也将包含在可執行檔案中。這導緻比通常需要的更大的占地面積。為了最大限度地減少此問題,可以使用以下不同版本的NETCTRL庫:

•netctrl_min。此最小庫僅啟用DHCP用戶端。當需要最小的占地面積時,應該使用它。

•netctrl。NETCTRL庫的這個“标準”版本支援以下功能并具有中等占用空間: -  Telnet伺服器 -  HTTP伺服器 -  DHCP用戶端•netctrl_full。這個“完整”庫支援所有支援的NETCTRL功能,包括: -  Telnet伺服器 -  HTTP伺服器 -  NAT伺服器 -  DHCP用戶端 -  DHCP伺服器 -  DNS伺服器 

這些NETCTRL庫版本中的大部分版本都是為純IPv4和IPv6建構的。

如果使用XGCONF配置工具在CCStudio中配置NDK,則會根據您啟用的子產品自動選擇相應的NETCTRL庫。

如果您需要更多地控制應用程式使用的NETCTRL庫中可用的功能,可以在/ti/ndk/inc/netctrl/netsrv.h中定義以下常量,這些常量控制帶入NETCTRL庫的功能如果未定義“_NDK_EXTERN_CONFIG”。

#define NETSRV_ENABLE_TELNET 1 
#define NETSRV_ENABLE_HTTP 1 
#define NETSRV_ENABLE_NAT 0 
#define NETSRV_ENABLE_DHCPCLIENT 1 
#define NETSRV_ENABLE_DHCPSERVER 1 
#define NETSRV_ENABLE_DNSSERVER 1
           

通過将上述任何一項設定為0并重建相應的NETCTRL庫,可以從可執行檔案中清除各個服務。

OS Adaptation Layer

OS适配層控制NDK如何使用RTOS資源。這包括使用任務,信号量和記憶體。在大多數情況下,本章中描述的NDK應用程式将調用的唯一API是TaskCreate()。

About OS Adaptation

NDK使用OS适配層,以便應用程式可以使用任何支援的RTOS。目前,NDK應用支援TI-RTOS核心和FreeRTOS。這種可移植性是通過轉換為POSIX pthread調用的OS适配API實作的。反過來,POSIX層可以在内部使用TI RTOS核心或FreeRTOS。

本章介紹執行OS适配的API。

在大多數情況下,您的使用者應用程式代碼将僅調用TaskCreate()。但是,您可以根據需要修改适配層的源代碼。OS适配層的源代碼在/ ti / ndk / os目錄中提供:

Source Files

OS庫的源代碼由以下檔案組成,這些檔案位于/ ti / ndk / os目錄中:

efs.c嵌入式(基于RAM)檔案系統

mem.c記憶體配置設定和記憶體複制函數

mem_data.c記憶體配置設定定義和#pragmas

oem.c OEM緩存和系統函數

ossys.c額外的OS支援(調試日志,stricmp()函數)

semaphore.c信号量抽象

task.c任務線程抽象

兩個額外的包含檔案位于/ ti / ndk /inc / os目錄:

osif.h适配庫的接口規範

oskern.h由NETCTRL等函數使用的半私有聲明

Task Thread Abstraction: TASK.C

task.c子產品包含TI網絡開發人員工具包(NDK)API參考指南(SPRU524)中記錄的任務抽象API的子集。它還包含堆棧排除方法函數的源代碼:llEnter()和llExit()。後者在本檔案的第4.2.2節中讨論。

在大多數情況下,您的使用者應用程式代碼将僅調用TaskCreate()。

TI網絡開發人員工具包(NDK)API參考指南(SPRU524)中定義的大多數任務和信号量函數都是調用RTOS的宏。宏在inc / os / osif.h中定義。

可能需要特殊處理的功能将在後面的小節中介紹。

TaskCreate(), TaskExit(), and TaskDestroy()

create,exit和destroy函數都調用它們的POSIX pthread對應函數。

如果您使用TI-RTOS核心,TaskExit()和TaskDestroy()按預期運作,則Task.deleteTerminatedTasks配置參數必須設定為“true”。此參數設定訓示任務子產品删除空閑任務中已完成的任務。部厘清理涉及釋放任務堆棧記憶體。

NDK配置自動将Task.deleteTerminatedTasks設定為“true”。如果您的應用程式不使用NDK Global子產品進行配置,則必須手動設定此參數。例如:

var Task = xdc.useModule('ti.sysbios.knl.Task');
Task.deleteTerminatedTasks = true;
           

Choosing the llEnter()/llExit() Exclusion Method 

盡管NDK提供了一個可重入的環境,但堆棧的核心并不是可重入的。必須保護部分代碼不被重入調用通路。該軟體不是使用阻止所有其他任務執行的關鍵部分,而是定義稱為核心模式的操作模式。定義核心模式,使得在任何給定時間隻有一個Task可以處于核心模式。它不會阻止運作不使用NDK的任務。這為堆棧軟體提供了保護,而不會影響不相關代碼的執行。

llEnter()和llExit()函數在整個堆棧代碼中用于進入和退出核心模式,并提供代碼排除而不使用臨界分段。它們相當于splhigh()/ splx()Unix函數及其多個衍生函數。

NDK中包含llEnter()和llExit()函數的兩個示例實作。示例實作通過任務優先級或使用信号量提供排除。兩個實作的源代碼包含在Task抽象源檔案中:src / os / task.c

一種排除方法是優先級方法。這裡,調用llEnter()的Task被提升到OS_TASKPRIKERN的優先級,這保證它不會被搶占,因為另一個Task不可能運作(所有可能調用堆棧的任務都有一個較低的優先級)。對堆棧進行編碼,以便核心模式優先級的Task永遠不會阻塞。調用llExit()時,将恢複任務的原始優先級。請注意,可以為時間關鍵任務配置設定高于OS_TASKPRIKERN的優先級,但不允許它們調用NDK。

基于優先級的排除使您的應用程式使用NDK定義的任務優先級之一變得非常重要。如果使用的優先級大于NDK的最高定義優先級(OS_TASKPRIHIGH),則基于優先級的排除可能會中斷。如果線程調用任何與堆棧相關的函數,則将線程設定為比NDK的高優先級線程級别更高的優先級可能會中斷系統并導緻不可預測的行為。

enter和exit函數的替代實作使用初始計數為1的信号量。

當調用llEnter()時,任務調用信号量上的挂起操作。如果某個其他任務目前正在核心模式下執行,則新任務将挂起,直到原始任務調用llExit()。對llExit()的調用會導緻一個post操作,釋放一個Task進入堆棧。這種形式的函數對比優先級方法更安全,但也可能更慢。通常,信号量操作比任務優先級更改慢一點。但是,這種方法也有其優點。信号量方法的主要優點是可以更自由地為任務配置設定優先級。如果高優先級任務要調用NDK,則無需限制任務優先級或擔心。

通過更改兩個實作的#if語句,系統開發人員可以選擇使用任一實作。

 Packer Buffer Manager: PBM.C

資料包緩沖區管理器(PBM)負責管理系統中的所有資料包緩沖區。NDK和裝置驅動程式使用資料包緩沖區來承載網絡資料包資料。PBK程式設計抽象在NDK程式員參考指南中讨論。本節讨論NDK中提供的實作。

Packet Buffer Pool

PBM緩沖區在XGCONF中的全局子產品配置的緩沖區頁籤中配置。您可以設定幀數(預設值= 192),幀緩沖區大小(預設值= 1536位元組)以及存儲緩沖區的記憶體部分。

請注意,當聲明記憶體時,它将放置在緩存對齊的邊界上。此外,每個資料包緩沖區的大小必須是偶數個高速緩存行,以便可以可靠地重新整理它,而不會有與其他緩沖區沖突的風險。

Packet Buffer Allocation Method

緩沖區配置設定的基本方法是緩沖池。調用PBM_alloc()函數時會配置設定緩沖區。可以在中斷時調用此函數,是以必須確定僅作為結果進行非阻塞調用。但是,隻有裝置驅動程式才能從ISR進行調用,裝置驅動程式永遠不會要求大于PKT_SIZE_FRAMEBUF的緩沖區。是以,用于配置設定較大緩沖區的回退方法在技術上可以進行阻塞調用,盡管NDK中包含的實作在任何情況下都不會進行阻塞調用。

配置設定的基本方法是檢查大小。當大小小于或等于PKT_SIZE_FRAMEBUF時,則從空閑隊列中擷取資料包緩沖區。如果隊列中沒有空閑資料包緩沖區,則該函數傳回NULL。請注意,可以修改PBM子產品以增加空閑池或使用記憶體配置設定作為回退,但是由于大小小于或等于PKT_SIZE_FRAMEBUF的請求而提供的任何緩沖區必須遵守在上一節。

對于大于PKT_SIZE_FRAMEBUF的資料包緩沖區,可以使用标準記憶體。這些配置設定請求僅用于重新組裝大型IP資料包。生成的資料包無法在未分段的情況下送出給硬體裝置。是以,分組緩沖器不需要與硬體傳輸相容。

Referenced Route Handles

PBM結構中的一個字段是用于将資料包路由到其最終目的地的路由的引用句柄。在釋放資料包緩沖區或複制資料包緩沖區時,PBM子產品必須知道此句柄。

當通過調用PBM_free()釋放資料包緩沖區時,PBM子產品必須檢查資料包緩沖區持有的路由句柄,并取消引用句柄(如果存在)。例如:

if( pPkt->hRoute )

{

    RtDeRef( pPkt->hRoute );

    pPkt->hRoute = 0;

}
           

如PBM.C的源代碼中所述,函數RtDeRef()隻能從核心模式調用。但是,PBM子產品不依賴于定義PBM_free()函數的兩個版本,而是依賴于裝置驅動程式永遠不會被賦予包含路由的資料包緩沖區這一事實。是以,必須在核心模式下調用對緩沖區包含路由的PBM_free()的任何調用。是以,調用RtDeRef()是安全的。

使用PBM_copy()複制資料包緩沖區時,也會複制有關資料包的所有資訊。該資訊可以包括引用的路由句柄。如果在複制資料包緩沖區的過程中複制了路由句柄,則還必須通過調用RtRef()函數添加對該句柄的引用。PBM子產品不需要擔心核心模式,原因與PBM_free()沒有相同。

 Memory Allocation System: MEM.C

記憶體配置設定系統包括用于小塊記憶體,大塊以及初始化和複制記憶體塊的配置設定函數。該子產品中包含的檔案的API定義在TI網絡開發人員工具包(NDK)API參考指南(SPRU524)中定義。這些功能在整個堆棧中使用。提供源代碼以便系統程式員可以調整存儲器系統以适應特定需要。

可以配置此記憶體管理器緩沖區陣列的大小和位置。頁面大小取決于各種堆棧實體,是以在更改它時應該小心。可以向上或向下調整使用的頁數以增加或減少暫存器記憶體大小。

不應更改小記憶體塊(mmAlloc()和mmFree())的配置設定函數。

NDK使用這些函數來配置設定和釋放暫存器類型的記憶體。它們可以在中斷時調用,不允許阻塞。記憶體目前是從靜态數組中配置設定的。

記憶體操作函數mmZeroInit()和mmCopy()都用C編碼。系統程式員可以在彙編中重新編碼這些函數,或者使用EDMA通道來移動記憶體。

大記憶體塊(mmBulkAlloc()和mmBulkFree())的配置設定函數目前定義為使用malloc()和free()。可以更改這些功能以使用任何選擇的記憶體配置設定系統。它們不會在中斷時被調用并被允許阻塞。

 Embedded File System: EFS.C

EFS檔案系統為HTTP伺服器提供基于RAM的檔案支援,并為應用程式程式設計人員提供任何CGI功能。此API在TI網絡開發人員套件(NDK)API參考指南(SPRU524)中定義。提供源代碼用于調整功能以支援實體存儲媒體。這允許HTTP伺服器在不移植伺服器的情況下在實體裝置上工作。

General OS Support: OSSYS.C

對于在其他地方沒有家的功能,OSSYS檔案是一個通用的catch-all。目前,該子產品包含DbgPrintf() - 一個調試日志功能和stricmp(),它不包含在RTS中。

Jumbo Packet Buffer Manager (Jumbo PBM)

巨型資料包緩沖區管理器負責處理大小大于MMALLOC_MAXSIZE(3068位元組)的資料包緩沖區的記憶體配置設定和解除配置設定。當應用程式打算使用Jumbo幀時,該資料包緩沖區管理器很有用,即大小超過1500位元組的資料包通常不能在中斷上下文中使用PBM或RTOS API進行配置設定。

以下是Jumbo PBM的一些主要功能:

•Jumbo PBM實作大緻類似于PBM實作本身,除了它可以處理的塊大小大于PBM中的塊大小,預設情況下介于3K和10K位元組之間。

•Jumbo PBM不使用任何RTOS API或動态記憶體配置設定方法進行記憶體配置設定,是以可以在中斷上下文中安全使用。它使用靜态記憶體配置設定方法,即它在裝置記憶體的“遠”部分保留一塊記憶體,并進一步使用它來配置設定所需的資料包緩沖區。

•Jumbo PBM從記憶體中的單獨部分配置設定記憶體而不是PBM本身。PBM使用記憶體部分“NDK_PACKETMEM”,“NDK_MMBUFFER”進行記憶體配置設定。另一方面,Jumbo PBM定義并使用名為“NDK_JMMBUFFER”的部分進行記憶體配置設定。此部分的大小及其位置均可自定義。

•NDK OS AL中提供了Jumbo PBM的示例實作。客戶需要根據應用程式需求和系統記憶體限制來定制此實作。記憶體部分大小,塊大小和配置設定方法本身都可以進行自定義。

•“預計不會直接調用Jumbo PBM API。應用程式和驅動程式必須僅調用PBM_alloc()/ PBM_free()API。如果請求的記憶體大于PBM本身可以處理的記憶體,即3K位元組,則這些API依次調用Jumbo PBM API來配置設定/清理記憶體。

有關Jumbo PBM的示例實作,請參閱/ ti / ndk / stack / pbm目錄中的源檔案JUMBO_PBM.C。

繼續閱讀