FreeRTOSを動かしてみる(3)

[前回](https://qiita.com/t-yano-tryhard/items/f17f73041e293b6d0052)の続き

# NICの実装
connectが弾かれる原因を調べる

NetworkInterface.cはgitに含まれないので現時点のものを転記
<details>
<summary>NetworkInterface.c</summary>

```
/*
 * FreeRTOS+TCP V3.1.0
 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://aws.amazon.com/freertos
 * http://www.FreeRTOS.org
 */

/*****************************************************************************
 * Note: This file is Not! to be used as is. The purpose of this file is to provide
 * a template for writing a network interface. Each network interface will have to provide
 * concrete implementations of the functions in this file.
 *
 * See the following URL for an explanation of this file and its functions:
 * https://freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Porting.html
 *
 *****************************************************************************/

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "list.h"

/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"

#include "dlog.h"

/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
 * driver will filter incoming packets and only pass the stack those packets it
 * considers need processing. */
#if (ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0)
#define ipCONSIDER_FRAME_FOR_PROCESSING(pucEthernetBuffer) eProcessBuffer
#else
#define ipCONSIDER_FRAME_FOR_PROCESSING(pucEthernetBuffer) eConsiderFrameForProcessing((pucEthernetBuffer))
#endif

BaseType_t xNetworkInterfaceInitialise(void)
{
    /* FIX ME. */
    d("");
    return pdTRUE;
}

BaseType_t xNetworkInterfaceOutput(NetworkBufferDescriptor_t *const pxNetworkBuffer,
                                   BaseType_t xReleaseAfterSend)
{
    if (pxNetworkBuffer)
    {
        d("eb:%p ip:%lu bp:%u p:%u"
          " ct:%p ow:%p n:%p p:%p iv:%lu len:%u aft:%ld",
          pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->ulIPAddress, pxNetworkBuffer->usBoundPort, pxNetworkBuffer->usPort, pxNetworkBuffer->xBufferListItem.pvContainer, pxNetworkBuffer->xBufferListItem.pvOwner, pxNetworkBuffer->xBufferListItem.pxNext, pxNetworkBuffer->xBufferListItem.pxPrevious, pxNetworkBuffer->xBufferListItem.xItemValue, pxNetworkBuffer->xDataLength, xReleaseAfterSend);
    }
    else
    {
        d("");
    }
    return pdTRUE;
}

void vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])
{
    (void)pxNetworkBuffers;
    /* FIX ME. */
    d("");
}

BaseType_t xGetPhyLinkStatus(void)
{
    /* FIX ME. */
    d("");
    return pdFALSE;
}

BaseType_t xApplicationGetRandomNumber(uint32_t *pulNumber)
{
    d("");
    return *pulNumber + 1;
}
uint32_t ulApplicationGetNextSequenceNumber(uint32_t ulSourceAddress,
                                            uint16_t usSourcePort,
                                            uint32_t ulDestinationAddress,
                                            uint16_t usDestinationPort)
{
    (void)ulSourceAddress;
    (void)usSourcePort;
    (void)ulDestinationAddress;
    (void)usDestinationPort;
    uint32_t pulNumber = 0;

    d("");
    xApplicationGetRandomNumber(&pulNumber);
    return pulNumber;
}

struct xNetworkInterface *pxFillInterfaceDescriptor(BaseType_t xEMACIndex,
                                                    struct xNetworkInterface *pxInterface)
{
    d("mac:%lx", (uint32_t)xEMACIndex);
    return pxInterface;
}
```

</details>


## connectが弾かれる原因

こんな感じで呼ばれているけど 
```
(gdb) bt 
#0  prvTCPConnectStart (pxSocket=pxSocket@entry=0x20012d90, pxAddress=pxAddress@entry=0x20012a1c) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_Sockets.c:3660
#1  0x0000a686 in FreeRTOS_connect (xClientSocket=xClientSocket@entry=0x20012d90, pxAddress=0x20012a1c, pxAddress@entry=0x20012a60, xAddressLength=xAddressLength@entry=24) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_Sockets.c:3792
#2  0x0000df04 in vTCPSend (pcBufferToTransmit=pcBufferToTransmit@entry=0x20011414 <buf> "aiueo", xTotalLengthToSend=xTotalLengthToSend@entry=5) at app_client.c:55
#3  0x0000e000 in client_task (param=<optimized out>) at app_client.c:32
#4  0x00000280 in prvPortStartFirstTask () at /home/voyager/workspace/freertos/FreeRTOSv202212.01/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c:247
```
`prvTCPConnectStart`にはすでに接続済みじゃなければ
設定だけしてつないでくれるのを待っているっぽい

```
3714                     #if ( ipconfigUSE_IPv4 != 0 )
3715                         case FREERTOS_AF_INET4:
3716                             pxSocket->bits.bIsIPv6 = pdFALSE_UNSIGNED;
3717                             FreeRTOS_printf( ( "FreeRTOS_connect: %u to %xip:%u\n",
3718                                                pxSocket->usLocalPort, ( unsigned int ) FreeRTOS_ntohl( pxAddress->     sin_address.ulIP_IPv4 ), FreeRTOS_ntohs( pxAddress->sin_port ) ) );
3719                             pxSocket->u.xTCP.xRemoteIP.ulIP_IPv4 = FreeRTOS_ntohl( pxAddress->sin_address.ulIP_IPv     4 );
3720                             break;
3721                     #endif /* ( ipconfigUSE_IPv4 != 0 ) */

```

なので誰か(まず間違いなくNIC)から接続完了を送ってやる必要がある

[ドキュメント](https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Porting.html)をよく読んで見る。

`xSendEventStructToIPTask`を呼び出す必要があるらしい。

実機につながっていないので 偽の`ether_task`を作成し、
そこから呼び出すようにする。

あとTCP部分(NetworkInerface.c)は別にした
https://github.com/tyano461/freertos_cm3/commit/d0065333f02b60a60f010da731308fe2cfa8bc88
https://github.com/tyano461/freertos_tcpv4

```
sudo qemu-system-arm -machine mps2-an385 -monitor null -semihosting --semihosting-config enable=on,target=native -kernel ./build/RTOSDemo.axf -serial stdio -nographic 
[0000] NetworkInterface.c(127) pxFillInterfaceDescriptor mac:0 fr:0x5437 
[1c18] NetworkInterface.c(170) ether_task  fr:0x281 
[1c18] NetworkInterface.c(176) ether_task i:0 ep:0 ip:0 po:0 bp:0 fr:0x281 
[67ac] FreeRTOS_IP.c(1397) xSendEventStructToIPTask et:0 fr:0x574d 
[2450] app_server.c(14) server_task  fr:0x281 
[2450] app_server.c(31) vCreateTCPServerSocket sock:536948040 fr:0xd8bd 
[2450] FreeRTOS_IP.c(1397) xSendEventStructToIPTask et:9 fr:0x99f7 
[2c88] app_client.c(28) client_task  fr:0x281 
[2c88] app_client.c(44) vTCPSend IN fr:0xd7a1 
[2c88] app_client.c(54) vTCPSend socket fr:0xd7a1 
[2c88] FreeRTOS_Sockets.c(3783) FreeRTOS_connect  fr:0xd6a5 
[2c88] FreeRTOS_IP.c(1397) xSendEventStructToIPTask et:9 fr:0x99f7 
[67ac] NetworkInterface.c(103) xApplicationGetRandomNumber  fr:0x89a7 
[2450] app_server.c(45) vCreateTCPServerSocket bind fr:0xd8bd 
[2450] app_server.c(48) vCreateTCPServerSocket listen fr:0xd8bd 
[2c88] FreeRTOS_Sockets.c(3694) prvTCPConnectStart  fr:0x9d1f 
[2c88] FreeRTOS_Sockets.c(3701) prvTCPConnectStart fam:2 fr:0x9d1f 
[2c88] FreeRTOS_Sockets.c(3717) prvTCPConnectStart FreeRTOS_connect: lp:1024 -> a0a0ac8:10000 fr:0x9d1f 
[2c88] FreeRTOS_IP.c(1397) xSendEventStructToIPTask et:6 fr:0x57c3 
[67ac] NetworkInterface.c(151) pfOutput  fr:0x3db9 
[2c88] FreeRTOS_Sockets.c(3797) FreeRTOS_connect prvTCPConnectStart:0 fr:0xd6a5 
[67ac] NetworkInterface.c(151) pfOutput  fr:0x3db9 
[1c18] NetworkInterface.c(176) ether_task i:0 ep:0 ip:0 po:0 bp:0 fr:0x281 
[1c18] FreeRTOS_IP.c(1397) xSendEventStructToIPTask et:1 fr:0xd3b1 
[67ac] NetworkInterface.c(151) pfOutput  fr:0x3db9 
[2c88] app_client.c(84) vTCPSend not connected fr:0xd7a1 
[2c88] FreeRTOS_IP.c(1397) xSendEventStructToIPTask et:10 fr:0x9e53 
[2c88] app_client.c(105) vTCPSend OUT fr:0xd7a1 
[1c18] NetworkInterface.c(176) ether_task i:0 ep:0 ip:0 po:0 bp:0 fr:0x281 
[1c18] FreeRTOS_IP.c(1397) xSendEventStructToIPTask et:1 fr:0xd3b1 
```

うーんあまり代わり映えがない。
ちゃんとしたパケットを送ってやらないとだめっぽい。（そりゃそうか）


## ARP Request
pfOutputが呼ばれているのでどこから何を呼ばれているかよく見てみる
0x3db9は−1して0x3db8から呼ばれているはず

```
arm-none-eabi-objdump -d build/RTOSDemo.axf  | grep 3db8: -B15
    3d9a:	bf00      	nop
    3d9c:	00010734 	.word	0x00010734

00003da0 <FreeRTOS_OutputARPRequest>:
    3da0:	b570      	push	{r4, r5, r6, lr}
    3da2:	b082      	sub	sp, #8
    3da4:	4606      	mov	r6, r0
    3da6:	2000      	movs	r0, #0
    3da8:	f004 f93a 	bl	8020 <FreeRTOS_FirstEndPoint>
    3dac:	4604      	mov	r4, r0
    3dae:	e008      	b.n	3dc2 <FreeRTOS_OutputARPRequest+0x22>
    3db0:	68c3      	ldr	r3, [r0, #12]
    3db2:	2201      	movs	r2, #1
    3db4:	4629      	mov	r1, r5
    3db6:	4798      	blx	r3
    3db8:	4621      	mov	r1, r4
```

`FreeRTOS_OutputARPRequest`から呼ばれている
ARP Requestが行われているらしい。

gdb でも確認してみる。
```
(gdb) bt 
#0  pfOutput (pxDescriptor=0x20006804 <xInterfaces.6>, pxNetworkBuffer=0x20010a2c <xNetworkBufferDescriptors.0+56>, xReleaseAfterSend=1) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/portable/NetworkInterface/virtether/NetworkInterface.c:147
#1  0x00003db8 in FreeRTOS_OutputARPRequest (ulIPAddress=3356101130) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_ARP.c:1316
#2  0x0000bdec in prvTCPPrepareConnect_IPV4 (pxSocket=0x20012f30) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission_IPv4.c:367
#3  0x0000b59a in prvTCPPrepareConnect (pxSocket=<optimized out>) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission.c:496
#4  0x0000b5b6 in prvTCPMakeSurePrepared (pxSocket=<optimized out>) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission.c:87
#5  0x0000ba2c in prvTCPSendPacket (pxSocket=pxSocket@entry=0x20012f30) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_Transmission.c:139
#6  0x0000a3fe in xTCPSocketCheck (pxSocket=pxSocket@entry=0x20012f30) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_IP.c:226
#7  0x00009246 in xTCPTimerCheck (xWillSleep=xWillSleep@entry=1) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_Sockets.c:4824
#8  0x00005f14 in vCheckNetworkTimers () at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_IP_Timers.c:302
#9  0x00005bbc in prvProcessIPEventsAndTimers () at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_IP.c:268
#10 0x00005ce6 in prvIPTask (pvParameters=<optimized out>) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_IP.c:249
#11 0x00000280 in prvPortStartFirstTask () at /home/voyager/workspace/freertos/FreeRTOSv202212.01/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c:247
```

pxEndPoint->pxNetworkInterface->pfOutputから呼ばれているで間違いなさそう
```
(gdb) f 1
#1  0x00003db8 in FreeRTOS_OutputARPRequest (ulIPAddress=3356101130) at /home/voyager/workspace/freertos/FreeRTOS-Plus-TCP/source/FreeRTOS_ARP.c:1316
1316	                        ( void ) pxEndPoint->pxNetworkInterface->pfOutput( pxEndPoint->pxNetworkInterface, pxNetworkBuffer, pdTRUE );
(gdb) l
1311	                    iptraceNETWORK_INTERFACE_OUTPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer );
1312	
1313	                    /* Only the IP-task is allowed to call this function directly. */
1314	                    if( pxEndPoint->pxNetworkInterface != NULL )
1315	                    {
1316	                        ( void ) pxEndPoint->pxNetworkInterface->pfOutput( pxEndPoint->pxNetworkInterface, pxNetworkBuffer, pdTRUE );
1317	                    }
1318	                }
1319	                else
1320	                {

```
なので受け側で内容をよく見てみる。

```
[67ac] NetworkInterface.c(150) pfOutput ip:c80a0a0a po:0 bp:0 fr:0x3db9 
```

IPが今回設定したものになっている。
arpなのでportとかは設定されていない。

詳細はこんな感じ。~~ARPパケットとは微妙に違うっぽい気がするけど~~
etherフレームがくっついてきているらしい
```
[67ac] NetworkInterface.c(151) pfOutput ip:c80a0a0a po:0 bp:0 len:42 data:
ff ff ff ff ff ff 00 11 22 33 44 55 08 06 00 01
08 00 06 04 00 01 00 11 22 33 44 55 0a 0a 0a c8
00 00 00 00 00 00 0a 0a 0a c8 
```

ここの0x14番目の `00 01`が `usOperation`で `ipARP_REQUEST`に該当するらしい
https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/d70a21ce03569118e030b348844477b92f5fd227/source/FreeRTOS_ARP.c#L1420

なので `00 01`を受け取ったらARP応答を投げれば良さそう？？？

`pfOutput`で受け取ったものをこんな感じで埋めていって投げてみる

```c
if (descriptor)
        {
            ev.eEventType = eNetworkRxEvent;
            memcpy(descriptor, &g_desc, sizeof(NetworkBufferDescriptor_t));
            descriptor->pucEthernetBuffer = (uint8_t *)&g_arp;
            ev.pvData = descriptor;
            arp = (ARPPacket_t *)descriptor->pucEthernetBuffer;
            ah = &arp->xARPHeader;
            eth = &arp->xEthernetHeader;
            if (ah->usOperation == ipARP_REQUEST)
            {
                mac = ah->xSenderHardwareAddress;
                from = *(uint32_t *)ah->ucSenderProtocolAddress;
                addr = ah->ulTargetProtocolAddress;

                ah->usOperation = ipARP_REPLY;
                ah->xSenderHardwareAddress = connectedMac;
                memcpy(ah->ucSenderProtocolAddress, &addr, sizeof(addr));
                ah->ulTargetProtocolAddress = from;
                ah->xTargetHardwareAddress = mac;

                eth->xDestinationAddress = mac;
                eth->xSourceAddress = connectedMac;

                d("send %s", b2s(arp, sizeof(ARPPacket_t)));
                xSendEventStructToIPTask(&ev, 0);
            }
            else
            {
                d("arp op:%d", arp->xARPHeader.usOperation);
            }

            descriptor = NULL;
        }
        else
        {
            d("descriptor is null");
        }
```

受信パケットはこんな感じ

送信パケットはこんな感じ
```
[1d48] NetworkInterface.c(220) ether_task send 
00 11 22 33 44 55 00 11 22 33 44 55 08 06 00 01
08 00 06 04 00 02 00 11 22 33 44 55 0a 0a 0a c8
00 11 22 33 44 55 0a 0a 0a c8 
```

```
[67ac] NetworkInterface.c(169) pfOutput ip:c80a0a0a po:0 bp:0 len:42 data:
ff ff ff ff ff ff 00 11 22 33 44 55 08 06 00 01
08 00 06 04 00 01 00 11 22 33 44 55 0a 0a 0a c8
00 00 00 00 00 00 0a 0a 0a c8 
 r:1 fr:0x3db9 
```

FreeRTOS_connectの中でなぜか固まった
```
(gdb) n
3849	                uxEvents = xEventGroupWaitBits( pxSocket->xEventGroup,
(gdb) 
^C
Program received signal SIGINT, Interrupt.
prvGetRegistersFromStack (warning: no PSP thread stack unwinding supported.
pulFaultStackAddress=0x20010430 <xIPTaskStack.2+39696>) at init/startup.c:96
96	    for( ; ; )

```

ここまでのcommit
https://github.com/tyano461/freertos_cm3/commit/d55447f7e5790668ca861a3af3140a5de31a389f
https://github.com/tyano461/freertos_tcpv4/commit/e431dbde42f8d252b094a96f9a99b65fc6e0dba6


----
# 参考
https://github.com/renesas-rx/rx65n-cloud-kit/blob/master/lib/FreeRTOS-Plus-TCP/source/portable/NetworkInterface/RX/NetworkInterface.c