【UEFI基础】EDK网络框架(通用函数和数据)

通用函数和数据

DPC

DPC全称Deferred Procedure Call。Deferred的意思是“延迟”,这个DPC的作用就是注册函数,然后在之后的某个时刻调用,所以确实是有“延迟”的意思。DPC在UEFI的实现中包括两个部分。一部分是库函数DxeDpcLib,对应代码NetworkPkg\Library\DxeDpcLib\DxeDpcLib.inf;另一部分是EFI_DPC_PROTOCOL,对应代码NetworkPkg\DpcDxe\DpcDxe.inf。

DxeDpcLib

库中只实现了两个函数:

/**Add a Deferred Procedure Call to the end of the DPC queue.@param[in]  DpcTpl        The EFI_TPL that the DPC should be invoked.@param[in]  DpcProcedure  Pointer to the DPC's function.@param[in]  DpcContext    Pointer to the DPC's context.  Passed to DpcProcedurewhen DpcProcedure is invoked.@retval EFI_SUCCESS            The DPC was queued.@retval EFI_INVALID_PARAMETER  DpcTpl is not a valid EFI_TPL.@retval EFI_INVALID_PARAMETER  DpcProcedure is NULL.@retval EFI_OUT_OF_RESOURCES   There are not enough resources available toadd the DPC to the queue.
**/
EFI_STATUS
EFIAPI
QueueDpc (IN EFI_TPL            DpcTpl,IN EFI_DPC_PROCEDURE  DpcProcedure,IN VOID               *DpcContext    OPTIONAL);/**Dispatch the queue of DPCs.  ALL DPCs that have been queued with a DpcTplvalue greater than or equal to the current TPL are invoked in the order thatthey were queued.  DPCs with higher DpcTpl values are invoked before DPCs withlower DpcTpl values.@retval EFI_SUCCESS    One or more DPCs were invoked.@retval EFI_NOT_FOUND  No DPCs were invoked.
**/
EFI_STATUS
EFIAPI
DispatchDpc (VOID);

库函数的实现只是简单调用了EFI_DPC_PROTOCOL的接口函数:

EFI_STATUS
EFIAPI
QueueDpc (IN EFI_TPL            DpcTpl,IN EFI_DPC_PROCEDURE  DpcProcedure,IN VOID               *DpcContext    OPTIONAL)
{//// Call the EFI_DPC_PROTOCOL to queue the DPC//return mDpc->QueueDpc (mDpc, DpcTpl, DpcProcedure, DpcContext);
}

我们在使用DPC的时候直接调用库函数更方便,不过在了解DPC的实现时还是要重点关注EFI_DPC_PROTOCOL

EFI_DPC_PROTOCOL

接口初始化

EFI_DPC_PROTOCOL在一个DXE模块中安装的,安装的过程也很简单,查看具体的模块入口代码(位于NetworkPkg\DpcDxe\Dpc.c,只包含重点代码,下同):

EFI_STATUS
EFIAPI
DpcDriverEntryPoint (IN EFI_HANDLE        ImageHandle,IN EFI_SYSTEM_TABLE  *SystemTable)
{//// Initialize the DPC queue for all possible TPL values//for (Index = 0; Index <= TPL_HIGH_LEVEL; Index++) {InitializeListHead (&mDpcQueue[Index]);}//// Install the EFI_DPC_PROTOCOL instance onto a new handle//Status = gBS->InstallMultipleProtocolInterfaces (&mDpcHandle,&gEfiDpcProtocolGuid, &mDpc,NULL);
}

这里唯一需要关注的是mDpcQueue这个全局变量。它是一个链表数组:

//
// An array of DPC queues.  A DPC queue is allocated for every level EFI_TPL value.
// As DPCs are queued, they are added to the end of the linked list.
// As DPCs are dispatched, they are removed from the beginning of the linked list.
//
LIST_ENTRY  mDpcQueue[TPL_HIGH_LEVEL + 1];	// TPL_HIGH_LEVEL的值是31,所以数组大小是32个元素

每一个数组元素对应一种优先级。这里创建了所有可能的优先级,但实际上常用的也就下面几种:

//
// Task priority level
//
#define TPL_APPLICATION       4
#define TPL_CALLBACK          8
#define TPL_NOTIFY            16
#define TPL_HIGH_LEVEL        31

这个链表真正的有效元素是DpcEntry,其结构如下:

//
// Internal data structure for managing DPCs.  A DPC entry is either on the free
// list or on a DPC queue at a specific EFI_TPL.
//
typedef struct {LIST_ENTRY           ListEntry;EFI_DPC_PROCEDURE    DpcProcedure;VOID                 *DpcContext;
} DPC_ENTRY;

第一个参数ListEntry用来处理链表,可以不关注;后面的两个参数,一个是DPC函数指针,一个是DPC函数的入参。EFI_DPC_PROCEDURE定义如下:

/**Invoke a Deferred Procedure Call.@param  DpcContext           The pointer to the Deferred Procedure Call's context,which is implementation dependent.
**/
typedef
VOID
(EFIAPI *EFI_DPC_PROCEDURE)(IN VOID  *DpcContext);

这个函数就是我们的主角:Deferred Procedure Call,简称DPC

接口实现

EFI_DPC_PROTOCOL包含两个接口函数,下面具体介绍。

  • 首先介绍DpcQueueDpc()
EFI_STATUS
EFIAPI
DpcQueueDpc (IN EFI_DPC_PROTOCOL   *This,IN EFI_TPL            DpcTpl,IN EFI_DPC_PROCEDURE  DpcProcedure,IN VOID               *DpcContext    OPTIONAL)

该函数的操作流程如下:

  1. 判断优先级DpcTpl是否满足要求:
  //// Make sure DpcTpl is valid//if ((DpcTpl < TPL_APPLICATION) || (DpcTpl > TPL_HIGH_LEVEL)) {return EFI_INVALID_PARAMETER;}
  1. 判断mDpcEntryFreeList的状态。这个mDpcEntryFreeList是另外的一个链表:
//
// Free list of DPC entries.  As DPCs are queued, entries are removed from this
// free list.  As DPC entries are dispatched, DPC entries are added to the free list.
// If the free list is empty and a DPC is queued, the free list is grown by allocating
// an additional set of DPC entries.
//
LIST_ENTRY  mDpcEntryFreeList = INITIALIZE_LIST_HEAD_VARIABLE (mDpcEntryFreeList);

一开始它是空的,在首次使用时会创建64个空的DPC_ENTRY,其基本代码:

  //// Check to see if there are any entries in the DPC free list//if (IsListEmpty (&mDpcEntryFreeList)) {//// Add 64 DPC entries to the free list//for (Index = 0; Index < 64; Index++) {//// Allocate a new DPC entry//DpcEntry = AllocatePool (sizeof (DPC_ENTRY));//// Add the newly allocated DPC entry to the DPC free list//InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);}}

这里去掉了一系列的条件判断,只保留基本的操作,就是一个分配内存给DPC_ENTRY,然后放到mDpcEntryFreeList的过程,表示的是没有使用到的DPC。

  1. mDpcEntryFreeList中拿出一个DpcEntry,为它赋值DpcProcedureDpcContext,这么做之后DPC才真正生效了:
  //// Retrieve the first node from the free list of DPCs//DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcEntryFreeList));//// Remove the first node from the free list of DPCs//RemoveEntryList (&DpcEntry->ListEntry);//// Fill in the DPC entry with the DpcProcedure and DpcContext//DpcEntry->DpcProcedure = DpcProcedure;DpcEntry->DpcContext   = DpcContext;
  1. 将初始化好的DpcEntry插入到mDpcQueue中,mDpcQueueDepth全局变量的值加1,表示又多了一个DPC:
  //// Add the DPC entry to the end of the list for the specified DplTpl.//InsertTailList (&mDpcQueue[DpcTpl], &DpcEntry->ListEntry);//// Increment the measured DPC queue depth across all TPLs//mDpcQueueDepth++;

mDpcQueuemDpcEntryFreeList的关系大致如下:

在这里插入图片描述

这里预分配资源给mDpcEntryFreeList,以及mDpcQueuemDpcEntryFreeList之间的DPC转换使用,其目的主要是能够减少资源的反复分配导致的内存碎片化,也能够实现名字中的“延迟”一说,而且执行速度上也有保证。

  • 然后介绍DpcDispatchDpc()
EFI_STATUS
EFIAPI
DpcDispatchDpc (IN EFI_DPC_PROTOCOL  *This)

该函数的操作流程如下:

  1. 判断mDpcQueueDepth值是否大于0,如果大于0,表示有DPC可以被调用,才会有后续的操作。
  //// Check to see if there are 1 or more DPCs currently queued//if (mDpcQueueDepth > 0) {
  1. 遍历mDpcQueue,找到非空的链表节点,将它从mDpcQueue中删去,mDpcQueueDepth的值相应的减1。
    //// Loop from TPL_HIGH_LEVEL down to the current TPL value//for (Tpl = TPL_HIGH_LEVEL; Tpl >= OriginalTpl; Tpl--) {//// Check to see if the DPC queue is empty//while (!IsListEmpty (&mDpcQueue[Tpl])) {//// Retrieve the first DPC entry from the DPC queue specified by Tpl//DpcEntry = (DPC_ENTRY *)(GetFirstNode (&mDpcQueue[Tpl]));//// Remove the first DPC entry from the DPC queue specified by Tpl//RemoveEntryList (&DpcEntry->ListEntry);//// Decrement the measured DPC Queue Depth across all TPLs//mDpcQueueDepth--;

注意这里的优先级是从高到低来执行对应的DPC的,这也是符合UEFI规范的。

  1. 执行那个从mDpcQueue去除下来的DpcEntry对应的DpcProcedure
        //// Invoke the DPC passing in its context//(DpcEntry->DpcProcedure)(DpcEntry->DpcContext);
  1. 将从mDpcQueue去除下来的DpcEntry在放回到mDpcEntryFreeList中,之后就可以重复利用:
        //// Add the invoked DPC entry to the DPC free list//InsertTailList (&mDpcEntryFreeList, &DpcEntry->ListEntry);

以上就是DpcDispatchDpc()的流程。注意一次DpcDispatchDpc会将mDpcQueue中的所有DPC都执行一遍。

DPC代码示例

DpcLib的使用示例大致如下:

  1. 创建事件(位于NetworkPkg\ArpDxe\ArpDriver.c):
  //// Create the event used in the RxToken.//Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL,TPL_NOTIFY,ArpOnFrameRcvd,ArpService,&ArpService->RxToken.Event);if (EFI_ERROR (Status)) {goto ERROR_EXIT;}
  1. 事件的实现(位于NetworkPkg\ArpDxe\ArpImpl.c):
VOID
EFIAPI
ArpOnFrameRcvd (IN EFI_EVENT  Event,IN VOID       *Context)
{//// Request ArpOnFrameRcvdDpc as a DPC at TPL_CALLBACK//QueueDpc (TPL_CALLBACK, ArpOnFrameRcvdDpc, Context);
}

这里就会调用QueueDpc()。这实际上很像是Linux中断处理中的上半部和下半部,上述的代码实际上是上半部,下半部就是执行DispatchDpc(),下面是一个示例(位于NetworkPkg\ArpDxe\ArpImpl.c):

UINTN
ArpAddressResolved (IN ARP_CACHE_ENTRY    *CacheEntry,IN ARP_INSTANCE_DATA  *Instance OPTIONAL,IN EFI_EVENT          UserEvent OPTIONAL)
{//// Dispatch the DPCs queued by the NotifyFunction of the Context->UserRequestEvent.//DispatchDpc ();return Count;
}

执行DispatchDpc()的位置需要根据实际的代码逻辑来确定,这个将在后续实际使用模块中进一步说明。

下面是自己写的一个使用DpcLib的简单示例,代码可以在BeniPkg\DynamicCommand\TestDynamicCommand\TestDpc.c中找到:

/**DPC function.@param  NA@retval  NA**/
VOID
EFIAPI
DpcCallback (IN  VOID                          *Context)
{Print (L"DPC callback function\r\n");
}/**Test DpcLib code.@param  NA@return  NA**/
VOID
TestDpc (VOID)
{QueueDpc (TPL_CALLBACK, DpcCallback, NULL);
}

这里执行TestDpc()之后就会注册一个DPC函数,该函数也只是简单的打印信息而已,执行结果如下:

在这里插入图片描述

有几点需要注意:

  1. 首先代码中并没有直接调用DispatchDpc(),但是我们注册的DPC函数DpcCallback()还是被执行了,这是因为当前BIOS已经包含了UEFI网络协议栈,所以会在某个网络驱动的执行代码中调用DispatchDpc(),而前面也已经说过DispatchDpc()会调用所有的DPC函数,所以这里注册的DPC也会被调用。
  2. 其次是多次执行的结果稍有不同,主要是输出信息顺序的变化,这跟上面说明的原因也类似,网络驱动事件调用的时间点有随机的成分,这也导致了DpcCallback()执行时间的变化。

Token

BIOS网络协议中的数据基本都是通过Token的形式来处理的,它有很多种不同的描述方式,整体描述如下:

在这里插入图片描述

需要注意的是Token实际上的使用者并不是提供者本身,而是提供者的上层协议,比如MNP的Token使用者是ARP、IP4等使用MNP的上层协议。

Token中比较通用的一种是:

typedef struct {EFI_EVENT     Event;EFI_STATUS    Status;// 其它结构体union {XXX_DATA     *RxData;XXX_DATA    *TxData;// 其它结构体} Packet;
} YYY;

注意结构体中写的XXXYYY并不是真实的名称,在不同协议层下有不同的表示。当然也有其它的形式(图中深色部分),后面也会说明。下面将例举出所有的Token的具体格式。

EFI_MANAGED_NETWORK_COMPLETION_TOKEN

typedef struct {////// This Event will be signaled after the Status field is updated/// by the MNP. The type of Event must be/// EFI_NOTIFY_SIGNAL. The Task Priority Level (TPL) of/// Event must be lower than or equal to TPL_CALLBACK.///EFI_EVENT     Event;////// The status that is returned to the caller at the end of the operation/// to indicate whether this operation completed successfully.///EFI_STATUS    Status;union {////// When this token is used for receiving, RxData is a pointer to the EFI_MANAGED_NETWORK_RECEIVE_DATA.///EFI_MANAGED_NETWORK_RECEIVE_DATA     *RxData;////// When this token is used for transmitting, TxData is a pointer to the EFI_MANAGED_NETWORK_TRANSMIT_DATA.///EFI_MANAGED_NETWORK_TRANSMIT_DATA    *TxData;} Packet;
} EFI_MANAGED_NETWORK_COMPLETION_TOKEN;

IP_IO_IP_COMPLETION_TOKEN

typedef struct {////// This Event will be signaled after the Status field is updated/// by the EFI IPv4 Protocol driver. The type of Event must be/// EFI_NOTIFY_SIGNAL. The Task Priority Level (TPL) of/// Event must be lower than or equal to TPL_CALLBACK.///EFI_EVENT     Event;////// The status that is returned to the caller at the end of the operation/// to indicate whether this operation completed successfully.///EFI_STATUS    Status;union {////// When this token is used for receiving, RxData is a pointer to the EFI_IP4_RECEIVE_DATA.///EFI_IP4_RECEIVE_DATA     *RxData;////// When this token is used for transmitting, TxData is a pointer to the EFI_IP4_TRANSMIT_DATA.///EFI_IP4_TRANSMIT_DATA    *TxData;} Packet;
} EFI_IP4_COMPLETION_TOKEN;typedef union {EFI_IP4_COMPLETION_TOKEN    Ip4Token;EFI_IP6_COMPLETION_TOKEN    Ip6Token;
} IP_IO_IP_COMPLETION_TOKEN;

TCP_IO_CONNECTION_TOKEN

typedef struct {EFI_EVENT     Event;EFI_STATUS    Status;
} EFI_TCP4_COMPLETION_TOKEN;typedef struct {////// The Status in the CompletionToken will be set to one of/// the following values if the active open succeeds or an unexpected/// error happens:/// EFI_SUCCESS:              The active open succeeds and the instance's///                           state is Tcp4StateEstablished./// EFI_CONNECTION_RESET:     The connect fails because the connection is reset///                           either by instance itself or the communication peer./// EFI_CONNECTION_REFUSED:   The connect fails because this connection is initiated with///                           an active open and the connection is refused./// EFI_ABORTED:              The active open is aborted./// EFI_TIMEOUT:              The connection establishment timer expires and///                           no more specific information is available./// EFI_NETWORK_UNREACHABLE:  The active open fails because///                           an ICMP network unreachable error is received./// EFI_HOST_UNREACHABLE:     The active open fails because an///                           ICMP host unreachable error is received./// EFI_PROTOCOL_UNREACHABLE: The active open fails///                           because an ICMP protocol unreachable error is received./// EFI_PORT_UNREACHABLE:     The connection establishment///                           timer times out and an ICMP port unreachable error is received./// EFI_ICMP_ERROR:           The connection establishment timer timeout and some other ICMP///                           error is received./// EFI_DEVICE_ERROR:         An unexpected system or network error occurred./// EFI_NO_MEDIA:             There was a media error.///EFI_TCP4_COMPLETION_TOKEN    CompletionToken;
} EFI_TCP4_CONNECTION_TOKEN;typedef union {EFI_TCP4_CONNECTION_TOKEN    Tcp4Token;EFI_TCP6_CONNECTION_TOKEN    Tcp6Token;
} TCP_IO_CONNECTION_TOKEN;

注意这个结构体不涉及到具体的数据(即Packet成员)。

TCP_IO_IO_TOKEN

typedef struct {////// When transmission finishes or meets any unexpected error it will/// be set to one of the following values:/// EFI_SUCCESS:              The receiving or transmission operation///                           completes successfully./// EFI_CONNECTION_FIN:       The receiving operation fails because the communication peer///                           has closed the connection and there is no more data in the///                           receive buffer of the instance./// EFI_CONNECTION_RESET:     The receiving or transmission operation fails///                           because this connection is reset either by instance///                           itself or the communication peer./// EFI_ABORTED:              The receiving or transmission is aborted./// EFI_TIMEOUT:              The transmission timer expires and no more///                           specific information is available./// EFI_NETWORK_UNREACHABLE:  The transmission fails///                           because an ICMP network unreachable error is received./// EFI_HOST_UNREACHABLE:     The transmission fails because an///                           ICMP host unreachable error is received./// EFI_PROTOCOL_UNREACHABLE: The transmission fails///                           because an ICMP protocol unreachable error is received./// EFI_PORT_UNREACHABLE:     The transmission fails and an///                           ICMP port unreachable error is received./// EFI_ICMP_ERROR:           The transmission fails and some other///                           ICMP error is received./// EFI_DEVICE_ERROR:         An unexpected system or network error occurs./// EFI_NO_MEDIA:             There was a media error.///EFI_TCP4_COMPLETION_TOKEN    CompletionToken;union {////// When this token is used for receiving, RxData is a pointer to EFI_TCP4_RECEIVE_DATA.///EFI_TCP4_RECEIVE_DATA     *RxData;////// When this token is used for transmitting, TxData is a pointer to EFI_TCP4_TRANSMIT_DATA.///EFI_TCP4_TRANSMIT_DATA    *TxData;} Packet;
} EFI_TCP4_IO_TOKEN;typedef union {EFI_TCP4_IO_TOKEN    Tcp4Token;EFI_TCP6_IO_TOKEN    Tcp6Token;
} TCP_IO_IO_TOKEN;

TCP_IO_LISTEN_TOKEN

typedef struct {EFI_TCP4_COMPLETION_TOKEN    CompletionToken;EFI_HANDLE                   NewChildHandle;
} EFI_TCP4_LISTEN_TOKEN;typedef union {EFI_TCP4_LISTEN_TOKEN    Tcp4Token;EFI_TCP6_LISTEN_TOKEN    Tcp6Token;
} TCP_IO_LISTEN_TOKEN;

这里的数据不再是Packet,而是一个EFI_HANDLE,它表示的是一个TCP Socket对应的虚拟Handle:

///
/// The socket structure representing a network service access point.
///
struct _TCP_SOCKET {EFI_HANDLE                  SockHandle;    ///< The virtual handle of the socket

TCP_IO_CLOSE_TOKEN

typedef struct {////// When transmission finishes or meets any unexpected error it will/// be set to one of the following values:/// EFI_SUCCESS:              The receiving or transmission operation///                           completes successfully./// EFI_CONNECTION_FIN:       The receiving operation fails because the communication peer///                           has closed the connection and there is no more data in the///                           receive buffer of the instance./// EFI_CONNECTION_RESET:     The receiving or transmission operation fails///                           because this connection is reset either by instance///                           itself or the communication peer./// EFI_ABORTED:              The receiving or transmission is aborted./// EFI_TIMEOUT:              The transmission timer expires and no more///                           specific information is available./// EFI_NETWORK_UNREACHABLE:  The transmission fails///                           because an ICMP network unreachable error is received./// EFI_HOST_UNREACHABLE:     The transmission fails because an///                           ICMP host unreachable error is received./// EFI_PROTOCOL_UNREACHABLE: The transmission fails///                           because an ICMP protocol unreachable error is received./// EFI_PORT_UNREACHABLE:     The transmission fails and an///                           ICMP port unreachable error is received./// EFI_ICMP_ERROR:           The transmission fails and some other///                           ICMP error is received./// EFI_DEVICE_ERROR:         An unexpected system or network error occurs./// EFI_NO_MEDIA:             There was a media error.///EFI_TCP4_COMPLETION_TOKEN    CompletionToken;union {////// When this token is used for receiving, RxData is a pointer to EFI_TCP4_RECEIVE_DATA.///EFI_TCP4_RECEIVE_DATA     *RxData;////// When this token is used for transmitting, TxData is a pointer to EFI_TCP4_TRANSMIT_DATA.///EFI_TCP4_TRANSMIT_DATA    *TxData;} Packet;
} EFI_TCP4_IO_TOKEN;typedef union {EFI_TCP4_CLOSE_TOKEN    Tcp4Token;EFI_TCP6_CLOSE_TOKEN    Tcp6Token;
} TCP_IO_CLOSE_TOKEN;

UDP_COMPLETION_TOKEN

typedef struct {EFI_EVENT     Event;EFI_STATUS    Status;union {EFI_UDP4_RECEIVE_DATA     *RxData;EFI_UDP4_TRANSMIT_DATA    *TxData;} Packet;
} EFI_UDP4_COMPLETION_TOKEN;typedef union {EFI_UDP4_COMPLETION_TOKEN    Udp4;EFI_UDP6_COMPLETION_TOKEN    Udp6;
} UDP_COMPLETION_TOKEN;

EFI_DNS4_COMPLETION_TOKEN

///
/// EFI_DNS4_COMPLETION_TOKEN
///
typedef struct {////// This Event will be signaled after the Status field is updated by the EFI DNS/// protocol driver. The type of Event must be EFI_NOTIFY_SIGNAL.///EFI_EVENT    Event;////// Will be set to one of the following values:///   EFI_SUCCESS:      The host name to address translation completed successfully.///   EFI_NOT_FOUND:    No matching Resource Record (RR) is found.///   EFI_TIMEOUT:      No DNS server reachable, or RetryCount was exhausted without///                     response from all specified DNS servers.///   EFI_DEVICE_ERROR: An unexpected system or network error occurred.///   EFI_NO_MEDIA:     There was a media error.///EFI_STATUS    Status;////// Retry number if no response received after RetryInterval. If zero, use the/// parameter configured through Dns.Configure() interface.///UINT32        RetryCount;////// Minimum interval of retry is 2 second. If the retry interval is less than 2/// seconds, then use the 2 seconds. If zero, use the parameter configured through/// Dns.Configure() interface.UINT32        RetryInterval;////// DNSv4 completion token data///union {////// When the Token is used for host name to address translation, H2AData is a pointer/// to the DNS_HOST_TO_ADDR_DATA.///DNS_HOST_TO_ADDR_DATA      *H2AData;////// When the Token is used for host address to host name translation, A2HData is a/// pointer to the DNS_ADDR_TO_HOST_DATA.///DNS_ADDR_TO_HOST_DATA      *A2HData;////// When the Token is used for a general lookup function, GLookupDATA is a pointer to/// the DNS_GENERAL_LOOKUP_DATA.///DNS_GENERAL_LOOKUP_DATA    *GLookupData;} RspData;
} EFI_DNS4_COMPLETION_TOKEN;

这个Token中的数据相比其它的Token多了很多内容,不过形式并没有太大的变化,后面还有一些类似的Token。

EFI_MTFTP4_TOKEN

struct _EFI_MTFTP4_TOKEN {////// The status that is returned to the caller at the end of the operation/// to indicate whether this operation completed successfully.///EFI_STATUS                     Status;////// The event that will be signaled when the operation completes. If/// set to NULL, the corresponding function will wait until the read or/// write operation finishes. The type of Event must be/// EVT_NOTIFY_SIGNAL. The Task Priority Level (TPL) of/// Event must be lower than or equal to TPL_CALLBACK.///EFI_EVENT                      Event;////// If not NULL, the data that will be used to override the existing configure data.///EFI_MTFTP4_OVERRIDE_DATA       *OverrideData;////// The pointer to the null-terminated ASCII file name string.///UINT8                          *Filename;////// The pointer to the null-terminated ASCII mode string. If NULL, "octet" is used.///UINT8                          *ModeStr;////// Number of option/value string pairs.///UINT32                         OptionCount;////// The pointer to an array of option/value string pairs. Ignored if OptionCount is zero.///EFI_MTFTP4_OPTION              *OptionList;////// The size of the data buffer.///UINT64                         BufferSize;////// The pointer to the data buffer. Data that is downloaded from the/// MTFTPv4 server is stored here. Data that is uploaded to the/// MTFTPv4 server is read from here. Ignored if BufferSize is zero.///VOID                           *Buffer;////// The pointer to the context that will be used by CheckPacket,/// TimeoutCallback and PacketNeeded.///VOID                           *Context;////// The pointer to the callback function to check the contents of the received packet.///EFI_MTFTP4_CHECK_PACKET        CheckPacket;////// The pointer to the function to be called when a timeout occurs.///EFI_MTFTP4_TIMEOUT_CALLBACK    TimeoutCallback;////// The pointer to the function to provide the needed packet contents.///EFI_MTFTP4_PACKET_NEEDED       PacketNeeded;
};

SOCK_IO_TOKEN

typedef struct _SOCK_COMPLETION_TOKEN {EFI_EVENT     Event;          ///< The event to be issuedEFI_STATUS    Status;         ///< The status to be issued
} SOCK_COMPLETION_TOKEN;typedef struct _SOCK_IO_TOKEN {SOCK_COMPLETION_TOKEN    Token;SOCK_IO_DATA             Packet;
} SOCK_IO_TOKEN;

EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN

typedef struct {////// The completion status of transmitting and receiving.///EFI_STATUS                Status;////// If not NULL, the event that will be signaled when the collection process/// completes. If NULL, this function will busy-wait until the collection process competes.///EFI_EVENT                 CompletionEvent;////// The pointer to the server IP address. This address may be a unicast, multicast, or broadcast address.///EFI_IPv4_ADDRESS          RemoteAddress;////// The server listening port number. If zero, the default server listening port number (67) will be used.///UINT16                    RemotePort;////// The pointer to the gateway address to override the existing setting.///EFI_IPv4_ADDRESS          GatewayAddress;////// The number of entries in ListenPoints. If zero, the default station address and port number 68 are used.///UINT32                    ListenPointCount;////// An array of station address and port number pairs that are used as receiving filters./// The first entry is also used as the source address and source port of the outgoing packet.///EFI_DHCP4_LISTEN_POINT    *ListenPoints;////// The number of seconds to collect responses. Zero is invalid.///UINT32                    TimeoutValue;////// The pointer to the packet to be transmitted.///EFI_DHCP4_PACKET          *Packet;////// Number of received packets.///UINT32                    ResponseCount;////// The pointer to the allocated list of received packets.///EFI_DHCP4_PACKET          *ResponseList;
} EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN;

EFI_HTTP_TOKEN

///
/// EFI_HTTP_TOKEN
///
typedef struct {////// This Event will be signaled after the Status field is updated by the EFI HTTP/// Protocol driver. The type of Event must be EFI_NOTIFY_SIGNAL. The Task Priority/// Level (TPL) of Event must be lower than or equal to TPL_CALLBACK.///EFI_EVENT    Event;////// Status will be set to one of the following value if the HTTP request is/// successfully sent or if an unexpected error occurs:///   EFI_SUCCESS:      The HTTP request was successfully sent to the remote host.///   EFI_HTTP_ERROR:   The response message was successfully received but contains a///                     HTTP error. The response status code is returned in token.///   EFI_ABORTED:      The HTTP request was cancelled by the caller and removed from///                     the transmit queue.///   EFI_TIMEOUT:      The HTTP request timed out before reaching the remote host.///   EFI_DEVICE_ERROR: An unexpected system or network error occurred.///EFI_STATUS          Status;////// Pointer to storage containing HTTP message data.///EFI_HTTP_MESSAGE    *Message;
} EFI_HTTP_TOKEN;

UDP_RX_TOKEN

这里开始的Token的结构相比前面的Token已经有很大的差别:

typedef struct {UINT32                  Signature;UDP_IO                  *UdpIo;UDP_IO_CALLBACK         CallBack;VOID                    *Context;UINT32                  HeadLen;UDP_COMPLETION_TOKEN    Token;
} UDP_RX_TOKEN;

UDP_TX_TOKEN

typedef struct {UINT32                  Signature;LIST_ENTRY              Link;UDP_IO                  *UdpIo;UDP_IO_CALLBACK         CallBack;NET_BUF                 *Packet;VOID                    *Context;EFI_IPv4_ADDRESS        Gateway;UDP_SESSION_DATA        Session;UDP_COMPLETION_TOKEN    Token;UDP_TRANSMIT_DATA       Data;
} UDP_TX_TOKEN;

IP4_LINK_TX_TOKEN

typedef struct {UINT32                                  Signature;LIST_ENTRY                              Link;IP4_INTERFACE                           *Interface;IP4_SERVICE                             *IpSb;IP4_PROTOCOL                            *IpInstance;IP4_FRAME_CALLBACK                      CallBack;NET_BUF                                 *Packet;VOID                                    *Context;EFI_MAC_ADDRESS                         DstMac;EFI_MAC_ADDRESS                         SrcMac;EFI_MANAGED_NETWORK_COMPLETION_TOKEN    MnpToken;EFI_MANAGED_NETWORK_TRANSMIT_DATA       MnpTxData;
} IP4_LINK_TX_TOKEN;

IP4_LINK_RX_TOKEN

typedef struct {UINT32                                  Signature;IP4_INTERFACE                           *Interface;IP4_PROTOCOL                            *IpInstance;IP4_FRAME_CALLBACK                      CallBack;VOID                                    *Context;EFI_MANAGED_NETWORK_COMPLETION_TOKEN    MnpToken;
} IP4_LINK_RX_TOKEN;

SOCK_TOKEN

typedef struct _SOCK_TOKEN {LIST_ENTRY               TokenList;     ///< The entry to add in the token listSOCK_COMPLETION_TOKEN    *Token;        ///< The application's tokenUINT32                   RemainDataLen; ///< Unprocessed data lengthSOCKET                   *Sock;         ///< The pointer to the socket this token///< belongs to
} SOCK_TOKEN;

IpIoLib

UDP4和TCP4中,有不少操作实际上是放在IpIoLib库中完成的,它实际上是上层网络驱动对IP层调用的包装接口。

IpIoLib首先对IPv4和IPv6中会使用到的数据进行了包装,这样就可以用统一的接口来处理IP层,比如:

typedef union {EFI_IP4_COMPLETION_TOKEN    Ip4Token;EFI_IP6_COMPLETION_TOKEN    Ip6Token;
} IP_IO_IP_COMPLETION_TOKEN;typedef union {EFI_IP4_TRANSMIT_DATA    Ip4TxData;EFI_IP6_TRANSMIT_DATA    Ip6TxData;
} IP_IO_IP_TX_DATA;typedef union {EFI_IP4_RECEIVE_DATA    Ip4RxData;EFI_IP6_RECEIVE_DATA    Ip6RxData;
} IP_IO_IP_RX_DATA;typedef union {EFI_IP4_OVERRIDE_DATA    Ip4OverrideData;EFI_IP6_OVERRIDE_DATA    Ip6OverrideData;
} IP_IO_OVERRIDE;typedef union {EFI_IP4_CONFIG_DATA    Ip4CfgData;EFI_IP6_CONFIG_DATA    Ip6CfgData;
} IP_IO_IP_CONFIG_DATA;typedef union {EFI_IP4_HEADER    *Ip4Hdr;EFI_IP6_HEADER    *Ip6Hdr;
} IP_IO_IP_HEADER;typedef union {EFI_IP4_PROTOCOL    *Ip4;EFI_IP6_PROTOCOL    *Ip6;
} IP_IO_IP_PROTOCOL;

这里包含了收发和处理数据的结构体,配置IP的结构体等内容,另外还有一个重要的结构体IP_IO,会在后续进一步介绍。

IP_IO

IP_IO是对IP4和IP6接口的包装,这样上层的TCP和UDP就可以直接使用它而不需要分别对待IP4和IP6。该结构体位于NetworkPkg\Include\Library\IpIoLib.h:

///
/// This data structure wraps Ip4/Ip6 instances. The IpIo Library uses it for all
/// Ip4/Ip6 operations.
///
typedef struct _IP_IO {////// The node used to link this IpIo to the active IpIo list.///LIST_ENTRY                   Entry;////// The list used to maintain the IP instance for different sending purpose.///LIST_ENTRY                   IpList;EFI_HANDLE                   Controller;EFI_HANDLE                   Image;EFI_HANDLE                   ChildHandle;//// The IP instance consumed by this IP_IO//IP_IO_IP_PROTOCOL            Ip;BOOLEAN                      IsConfigured;////// Some ip configuration data can be changed.///UINT8                        Protocol;////// Token and event used to get data from IP.///IP_IO_IP_COMPLETION_TOKEN    RcvToken;////// List entry used to link the token passed to IP_IO.///LIST_ENTRY                   PendingSndList;//// User interface used to get notify from IP_IO//VOID                         *RcvdContext;     ///< See IP_IO_OPEN_DATA::RcvdContext.VOID                         *SndContext;      ///< See IP_IO_OPEN_DATA::SndContext.PKT_RCVD_NOTIFY              PktRcvdNotify;    ///< See IP_IO_OPEN_DATA::PktRcvdNotify.PKT_SENT_NOTIFY              PktSentNotify;    ///< See IP_IO_OPEN_DATA::PktSentNotify.UINT8                        IpVersion;IP4_ADDR                     StationIp;IP4_ADDR                     SubnetMask;
} IP_IO;

该结构体通过IpIoCreate()函数创建,它们在UDP和TCP模块中使用,比如TCP中:

EFI_STATUS
TcpCreateService (IN EFI_HANDLE  Controller,IN EFI_HANDLE  Image,IN UINT8       IpVersion)
{TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion);

还有UDP中:

EFI_STATUS
Udp4CreateService (IN OUT UDP4_SERVICE_DATA  *Udp4Service,IN     EFI_HANDLE         ImageHandle,IN     EFI_HANDLE         ControllerHandle)
{Udp4Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_4);

下面介绍IP_IO中比较重要的成员:

  • IpList:IP实例链表,它在IpIoCreate()中初始化,并在IpIoAddIp()中添加链表成员:
IP_IO_IP_INFO *
EFIAPI
IpIoAddIp (IN OUT IP_IO  *IpIo)
{InsertTailList (&IpIo->IpList, &IpInfo->Entry);

这里涉及到另外一个结构体IP_IO_IP_INFO

typedef struct _IP_IO_IP_INFO {EFI_IP_ADDRESS               Addr;IP_IO_IP_MASK                PreMask;LIST_ENTRY                   Entry;EFI_HANDLE                   ChildHandle;IP_IO_IP_PROTOCOL            Ip;IP_IO_IP_COMPLETION_TOKEN    DummyRcvToken;INTN                         RefCnt;UINT8                        IpVersion;
} IP_IO_IP_INFO;

这个结构体也是在IpIoAddIp()中创建的,这个函数虽然说是增加IP_IO_IP_INFO,但是其中只是对IP_IO_IP_INFO做了初始化而已,其中最重要的代码是:

IP_IO_IP_INFO *
EFIAPI
IpIoAddIp (IN OUT IP_IO  *IpIo)
{Status = IpIoCreateIpChildOpenProtocol (IpIo->Controller,IpIo->Image,&IpInfo->ChildHandle,IpInfo->IpVersion,(VOID **)&IpInfo->Ip);// 中间略InsertTailList (&IpIo->IpList, &IpInfo->Entry);

IpIoCreateIpChildOpenProtocol()打开了gEfiIp4ProtocolGuid对应的EFI_IP4_PROTOCOL(当然实际代码中是IP_IO_IP_PROTOCOL)。所以IP_IO_IP_INFO这个结构体的重点是对IP通信接口EFI_IP4_PROTOCOL的包装。最终IpIoAddIp()函数会被上层的TCP和UDP使用,来创建它们的IP通信接口:

// TCP模块中:
EFI_STATUS
TcpAttachPcb (IN SOCKET  *Sk)
{Tcb->IpInfo = IpIoAddIp (IpIo);// UDP模块中:
EFI_STATUS
EFIAPI
Udp4ServiceBindingCreateChild (IN EFI_SERVICE_BINDING_PROTOCOL  *This,IN EFI_HANDLE                    *ChildHandle)
{Instance->IpInfo = IpIoAddIp (Udp4Service->IpIo);

通过上述的方式,UPD和TCP就与其下层的IP联系到了一起:

在这里插入图片描述

  • Ip:对IP4和IP6的Protocol的包装,我们主要关注IP4就可以了:
typedef union {EFI_IP4_PROTOCOL    *Ip4;EFI_IP6_PROTOCOL    *Ip6;
} IP_IO_IP_PROTOCOL;
  • RcvToken:如注释描述,是从IP4获取数据的Token。
  • PendingSndList:需要传递给IP4的Token的列表。
  • RcvdContextSndContextPktRcvdNotifyPktSentNotify:对应数据处理的函数,它们来自另外的一个结构体IP_IO_OPEN_DATA
///
/// The struct is for the user to pass IP configuration and callbacks to IP_IO.
/// It is used by IpIoOpen().
///
typedef struct _IP_IO_OPEN_DATA {IP_IO_IP_CONFIG_DATA    IpConfigData;  ///< Configuration of the IP instance.VOID                    *RcvdContext;  ///< Context data used by receive callback.VOID                    *SndContext;   ///< Context data used by send callback.PKT_RCVD_NOTIFY         PktRcvdNotify; ///< Receive callback.PKT_SENT_NOTIFY         PktSentNotify; ///< Send callback.
} IP_IO_OPEN_DATA;

IP_IO_OPEN_DATA其实是一个临时参数,会在打开IP_IO的时候使用,比如UDP模块中:

EFI_STATUS
Udp4CreateService (IN OUT UDP4_SERVICE_DATA  *Udp4Service,IN     EFI_HANDLE         ImageHandle,IN     EFI_HANDLE         ControllerHandle)
{OpenData.RcvdContext           = (VOID *)Udp4Service;OpenData.SndContext            = NULL;OpenData.PktRcvdNotify         = Udp4DgramRcvd;OpenData.PktSentNotify         = Udp4DgramSent;Status = IpIoOpen (Udp4Service->IpIo, &OpenData);

还有TCP中:

EFI_STATUS
TcpCreateService (IN EFI_HANDLE  Controller,IN EFI_HANDLE  Image,IN UINT8       IpVersion)
{ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA));if (IpVersion == IP_VERSION_4) {CopyMem (&OpenData.IpConfigData.Ip4CfgData,&mIp4IoDefaultIpConfigData,sizeof (EFI_IP4_CONFIG_DATA));OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;} else {CopyMem (&OpenData.IpConfigData.Ip6CfgData,&mIp6IoDefaultIpConfigData,sizeof (EFI_IP6_CONFIG_DATA));OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;}OpenData.PktRcvdNotify = TcpRxCallback;Status                 = IpIoOpen (TcpServiceData->IpIo, &OpenData);

都是在创建TCP或者UDP服务时作为参数使用。这个数据中最重要的是两个回调函数,分别对应IP数据收发的处理:

/**The prototype is called back when an IP packet is received.@param[in] Status        The result of the receive request.@param[in] IcmpErr       Valid when Status is EFI_ICMP_ERROR.@param[in] NetSession    The IP session for the received packet.@param[in] Pkt           The packet received.@param[in] Context       The data provided by the user for the received packet whenthe callback is registered in IP_IO_OPEN_DATA::RcvdContext.**/
typedef
VOID
(EFIAPI *PKT_RCVD_NOTIFY)(IN EFI_STATUS           Status,IN UINT8                IcmpErr,IN EFI_NET_SESSION_DATA *NetSession,IN NET_BUF              *Pkt,IN VOID                 *Context);/**The prototype is called back when an IP packet is sent.@param[in] Status        Result of the IP packet being sent.@param[in] Context       The data provided by user for the received packet whenthe callback is registered in IP_IO_OPEN_DATA::SndContext.@param[in] Sender        A Union type to specify a pointer of EFI_IP4_PROTOCOLor EFI_IP6_PROTOCOL.@param[in] NotifyData    The Context data specified when calling IpIoSend()**/
typedef
VOID
(EFIAPI *PKT_SENT_NOTIFY)(IN EFI_STATUS        Status,IN VOID              *Context,IN IP_IO_IP_PROTOCOL Sender,IN VOID              *NotifyData);
  • IpVersion:对应IP_VERSION_4或者IP_VERSION_6,值分别是4和6,表示IPv4和IPv6。
  • StationIpSubnetMask:IP使用的地址和掩码。

函数

比较重要的函数:

/**Create a new IP_IO instance.If IpVersion is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().This function uses IP4/IP6 service binding protocol in Controller to createan IP4/IP6 child (aka IP4/IP6 instance).@param[in]  Image             The image handle of the driver or application thatconsumes IP_IO.@param[in]  Controller        The controller handle that has IP4 or IP6 servicebinding protocol installed.@param[in]  IpVersion         The version of the IP protocol to use, eitherIPv4 or IPv6.@return The pointer to a newly created IP_IO instance, or NULL if failed.**/
IP_IO *
EFIAPI
IpIoCreate (IN EFI_HANDLE  Image,IN EFI_HANDLE  Controller,IN UINT8       IpVersion);/**Open an IP_IO instance for use.If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().This function is called after IpIoCreate(). It is used for configuring the IPinstance and register the callbacks and their context data for sending andreceiving IP packets.@param[in, out]  IpIo               The pointer to an IP_IO instance that needsto open.@param[in]       OpenData           The configuration data and callbacks forthe IP_IO instance.@retval          EFI_SUCCESS            The IP_IO instance opened with OpenDatasuccessfully.@retval          EFI_ACCESS_DENIED      The IP_IO instance is configured, avoid toreopen it.@retval          EFI_UNSUPPORTED        IPv4 RawData mode is no supported.@retval          EFI_INVALID_PARAMETER  Invalid input parameter.@retval          Others                 Error condition occurred.**/
EFI_STATUS
EFIAPI
IpIoOpen (IN OUT IP_IO            *IpIo,IN     IP_IO_OPEN_DATA  *OpenData);/**Send out an IP packet.This function is called after IpIoOpen(). The data to be sent is wrapped inPkt. The IP instance wrapped in IpIo is used for sending by default but can beoverridden by Sender. Other sending configs, like source address and gatewayaddress etc., are specified in OverrideData.@param[in, out]  IpIo                  Pointer to an IP_IO instance used for sending IPpacket.@param[in, out]  Pkt                   Pointer to the IP packet to be sent.@param[in]       Sender                The IP protocol instance used for sending.@param[in]       Context               Optional context data.@param[in]       NotifyData            Optional notify data.@param[in]       Dest                  The destination IP address to send this packet to.This parameter is optional when using IPv6.@param[in]       OverrideData          The data to override some configuration of the IPinstance used for sending.@retval          EFI_SUCCESS           The operation is completed successfully.@retval          EFI_INVALID_PARAMETER The input parameter is not correct.@retval          EFI_NOT_STARTED       The IpIo is not configured.@retval          EFI_OUT_OF_RESOURCES  Failed due to resource limit.@retval          Others                Error condition occurred.**/
EFI_STATUS
EFIAPI
IpIoSend (IN OUT IP_IO           *IpIo,IN OUT NET_BUF         *Pkt,IN     IP_IO_IP_INFO   *Sender        OPTIONAL,IN     VOID            *Context       OPTIONAL,IN     VOID            *NotifyData    OPTIONAL,IN     EFI_IP_ADDRESS  *Dest          OPTIONAL,IN     IP_IO_OVERRIDE  *OverrideData  OPTIONAL);/**Add a new IP instance for sending data.If IpIo is NULL, then ASSERT().If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().The function is used to add the IP_IO to the IP_IO sending list. The callercan later use IpIoFindSender() to get the IP_IO and call IpIoSend() to senddata.@param[in, out]  IpIo               The pointer to an IP_IO instance to add a new IPinstance for sending purposes.@return The pointer to the created IP_IO_IP_INFO structure; NULL if failed.**/
IP_IO_IP_INFO *
EFIAPI
IpIoAddIp (IN OUT IP_IO  *IpIo);/**Configure the IP instance of this IpInfo and start the receiving if IpConfigDatais not NULL.If Ip version is not IP_VERSION_4 or IP_VERSION_6, then ASSERT().@param[in, out]  IpInfo          The pointer to the IP_IO_IP_INFO instance.@param[in, out]  IpConfigData    The IP4 or IP6 configure data used to configurethe IP instance. If NULL, the IP instance is reset.If UseDefaultAddress is set to TRUE, and the configureoperation succeeds, the default address informationis written back in this IpConfigData.@retval          EFI_SUCCESS     The IP instance of this IpInfo was configured successfully,or there is no need to reconfigure it.@retval          Others          The configuration failed.**/
EFI_STATUS
EFIAPI
IpIoConfigIp (IN OUT IP_IO_IP_INFO  *IpInfo,IN OUT VOID           *IpConfigData OPTIONAL);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/231401.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023高级人工智能期末总结

1、人工智能概念的一般描述 人工智能是那些与人的思维相关的活动&#xff0c;诸如决策、问题求解和学习等的自动化&#xff1b; 人工智能是一种计算机能够思维&#xff0c;使机器具有智力的激动人心的新尝试&#xff1b; 人工智能是研究如何让计算机做现阶段只有人才能做得好的…

【Storm实战】1.1 图解Storm的抽象概念

文章目录 0. 前言1. Storm 中的抽象概念1.1 流 (Stream)1.2 拓扑 (Topology)1.3 Spout1.4 Bolt1.5 任务 (Task)1.6 工作者 (Worker) 2. 形象的理解Storm的抽象概念2.1 流 (Stream)2.2 拓扑 (Topology)2.3 Spout2.4 Bolt2.5 任务 (Task)2.6 工作者 (Worker)场景1场景2 3.参考文档…

Mysql 将表里的两列值数据互换

示例&#xff1a; 需要将表中的 两个订单号互换 方案&#xff1a; 将同一张表数据做 临时数据 和主表 做数据交互 。 update 表 as main, 表 as temp set main.bill_no temp.track_bill_no, main.track_bill_no temp.bill_no where main.id temp.id…

实时记录和查看Apache 日志

Apache 是一个开源的、广泛使用的、跨平台的 Web 服务器&#xff0c;保护 Apache Web 服务器平台在很大程度上取决于监控其上发生的活动和事件&#xff0c;监视 Apache Web 服务器的最佳方法之一是收集和分析其访问日志文件。 Apache 访问日志提供了有关用户如何与您的网站交互…

HarmonyOS应用开发之DevEco Studio安装与初次使用

1、DevEco Studio介绍 DevEco Studio是基于IntelliJ IDEA Community开源版本打造&#xff0c;面向华为终端全场景多设备的一站式集成开发环境&#xff08;IDE&#xff09;&#xff0c;为开发者提供工程模板创建、开发、编译、调试、发布等E2E的HarmonyOS应用/服务的开发工具。…

如何通过HACS+Cpolar实现远程控制米家和HomeKit等智能家居设备

文章目录 基本条件一、下载HACS源码二、添加HACS集成三、绑定米家设备 ​ 上文介绍了如何实现群晖Docker部署HomeAssistant&#xff0c;通过内网穿透在户外控制家庭中枢。本文将介绍如何安装HACS插件商店&#xff0c;将米家&#xff0c;果家设备接入 Home Assistant。 基本条件…

在Google Colab中调用Gemini的API实现智能问答

一、引言 Google终于放出大招&#xff0c;在2023年12月6日正式推出规模最大、功能最强大的人工智能模型Gemini&#xff0c;对标ChatGPT&#xff0c;甚至有要赶超ChatGPT-4.0的节奏。 相比之前的Bard&#xff0c;Gemini的文本理解能力、图片识别能力和语义抽取能力大大增强&am…

Spring见解3 AOP

4.Spring AOP 4.1.为什么要学习AOP? 案例&#xff1a;有一个接口Service有一个insert方法&#xff0c;在insert被调用时打印调用前的毫秒数与调用后的毫秒数&#xff0c;其实现为&#xff1a; public class UserServiceImpl implements UserService {private UserDao userDao…

新年福利|这款价值数万的报表工具永久免费了

随着数据资产的价值逐渐凸显&#xff0c;越来越多的企业会希望采用报表工具来处理数据分析&#xff0c;了解业务经营状况&#xff0c;从而辅助经营决策。不过&#xff0c;企业在选型报表工具的时候经常会遇到以下几个问题&#xff1a; 各个报表工具有很多功能和特性&#xff0c…

金蝶云星空数据库根据仓库和仓位查询内码(SQL脚本)

SELECT a.仓库ID,a.仓库名称,d.仓位ID,d.仓位名称,c.内码FROM ( SELECT a.FSTOCKID 仓库ID,b.FNAME 仓库名称FROM T_BD_STOCK aINNER JOIN T_BD_STOCK_L bON a.FSTOCKID b.FSTOCKID --仓库列表) aLEFT JOIN ( SELECT FENTRYID 仓位值表FENTRYID,FSTOC…

C++ 释放指针

在C中&#xff0c;释放指针通常使用delete或delete[]操作符&#xff1b; 如果指针指向的是单个对象&#xff0c;可以使用delete操作符进行释放&#xff1b; 在释放完内存后&#xff0c;最好将指针置为nullptr&#xff0c;以避免出现悬空指针&#xff08;dangling pointer&#…

软件测试基础理论学习-软件测试方法论

软件测试方法论 软件测试的方法应该建立在不同的软件测试类型上&#xff0c;不同的测试类型会存在不同的方法。本文以软件测试中常见的黑盒测试为例&#xff0c;简述常见软件测试方法。 黑盒测试用例设计方法包括等价类划分法、边界值分析法、因果图法、判定表驱动法、正交试…

一、Qt介绍

一、Qt介绍 1、介绍 Qt是一套程序开发库&#xff0c;但是与MFC&#xff08;依赖于Windows API&#xff09;不同&#xff0c;Qt是跨平台开发库。 Qt获取&#xff1a;[Qt下载地址](https://download.qt.io/archive/qt/)2、Qt安装 QtMinGWSourcesQt ChartsQt Data Visualizatio…

c语言题目之统计二级制数中1的个数

文章目录 题目一、方法1二、方法2三&#xff0c;方法3总结 题目 统计二进制数中1的个数 输入一行&#xff0c;输出一行 输入&#xff1a; 输入一个整数 输出&#xff1a; 输出存储在内存中二进制的1的个数 一、方法1 之前的文章中&#xff0c;小编写了有关于内存在二进制中的存…

cissp 第10章 : 物理安全要求

10.1 站点与设施设计的安全原则 物理控制是安全防护的第一条防线&#xff0c;而人员是最后一道防线。 10.1.1 安全设施计划 安全设施计划通过关键路径分析完成。 关键路径分析用于找出关键应用、流程、运营以及所有必要支撑元索间的关系。 技术融合指的是各种技术、解决方案…

MongoDB 面试题

MongoDB 面试题 1. 什么是MongoDB&#xff1f; MongoDB是一种非关系型数据库&#xff0c;被广泛用于大型数据存储和分布式系统的构建。MongoDB支持的数据模型比传统的关系型数据库更加灵活&#xff0c;支持动态查询和索引&#xff0c;也支持BSON格式的数据存储&#xff0c;这…

ARCGIS PRO SDK Geoprocessing

调用原型&#xff1a;Dim gpResult AS IGPResult await Geoprocessing.ExecuteToolAsync(调用工具名称, GPValue数组, environment, null, null, executeFlags) 一、调用工具名称&#xff1a;地理处理工具名称。如面转线&#xff1a;management.PolygonToLine&#xff0c;而非…

解析为什么Go语言要使用[]rune而不是string来表示中文字符

众所周知&#xff0c;Go语言中有以下这些数据类型。但rune32这个go语言特有的数据类型&#xff0c;比较有意思却经常遭到忽视。所以今天探索学习一下这个数据类型的功能、用法。 Go基本数据类型 布尔&#xff1a;bool 字符串&#xff1a;string 整数&#xff1a; int int8 …

群晖Docker部署HomeAssistant容器结合内网穿透远程控制家中智能设备

目录 一、下载HomeAssistant镜像 二、内网穿透HomeAssistant&#xff0c;实现异地控制智能家居 三、使用固定域名访问HomeAssistant 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。 点击跳转到网站 Ho…

Ubuntu20.04 上启用 VCAN 用作本地调试

目录 一、启用本机的 VCAN​ 编辑 1.1 加载本机的 vcan 1.2 添加本机的 vcan0 1.3 查看添加的 vcan0 1.4 开启本机的 vcan0 1.5 关闭本机的 vcan0 1.6 删除本机的 vcan0 二、测试本机的 VCAN 2.1 CAN 发送数据 代码 2.2 CAN 接收数据 代码 2.3 CMakeLists.…