RAのUART動作確認エミュレータ(ローカル版)

- [サマリ](#サマリ)
- [環境](#環境)
  - [実行イメージ](#実行イメージ)
- [事前準備](#事前準備)
  - [要らなくなったら](#要らなくなったら)
- [ビルド](#ビルド)
- [実行](#実行)
  - [ルネサスサンプルコードのダウンロード](#ルネサスサンプルコードのダウンロード)
  - [e2studioでサンプルコードのビルド](#e2studioでサンプルコードのビルド)
    - [ダウンロード](#ダウンロード)
      - [e2studio](#e2studio)
      - [FSP](#fsp)
    - [ビルド](#ビルド-1)
      - [`sci_uart`プロジェクトの読み込み](#sci_uartプロジェクトの読み込み)
      - [configuration.xml](#configurationxml)
      - [ビルド](#ビルド-2)
      - [その他](#その他)
  - [QEMUと対向UARTデバイス(もどき)の起動](#qemuと対向uartデバイスもどきの起動)
    - [QEMUの起動](#qemuの起動)
    - [対向UARTデバイス(もどき)の起動](#対向uartデバイスもどきの起動)
  - [確認](#確認)
    - [abortの説明](#abortの説明)
- [アプリケーションデバッグ](#アプリケーションデバッグ)
- [疑問点など](#疑問点など)
- [動作イメージ](#動作イメージ)


# サマリ

前に作ったものはUSB接続した対向のUART機器を用意する必要があったので
PCだけでUARTの動作確認が出来るものを作ってみました。

https://github.com/tyano463/qemu/commit/b4cc9e2656ab9c0666db29b4250bdcd61d998c8e

以下が前に作ったもの

https://qiita.com/ta_yano/items/5e717d8bb4c22360a55b

# 環境

- Fedora 40 + Xfce(*1)
- gcc 14.2.1
- [プロセス間通信用キャラクタデバイス](https://github.com/tyano463/cdev_ipc.git)(*2)

(*1)
確認にe2studioを使用しているので、ubuntuの方が良いかもしれない。

(*2)
Linux専用です。

## 実行イメージ

![kroki](https://kroki.io/graphviz/svg/eNptkk1LAzEQhu_9FWHPasFrqeDBk3hQ7KlK2TZpG7pfJtliKQWbXFToSdGjXiyWehOFitAfE7D0X5jJfriocwjZmfedfWYIph3mRl1U2z06RsMSMhGEmNS5GHik6rAwDjDBzgbiXTcyiWZ47pxaGcEdUseUQU50TdJmedxMGra8mAvCamlTCM9tEg9VkaPVVKtPLd_NuZp9rO9unFKuogGPSEvUrbpqtHOtJlq-abmAU71qdW-SJwFv0UbsMpHyQJwRP86MX7eL9cXj6urycO-gVtDg2PcHeXe5BBL1oNU1_Ek-2_uTVi-WcFkwCpHbypj0y-YbWm9noxfg0eaOZfkpDJkb9Mzs3PVJxdYqCUlllIhG_-9v__f-gHlumScG-M_2MKN9wjJOTv3II402bYdbvbAIauABMpEXAQANKoVp4W1oKfV4pseTdCGZH0YoGes3nEbSWw==)

# 事前準備

プロセス間通信用キャラクタデバイスを作成する

```shell
git clone https://github.com/tyano463/cdev_ipc.git
cd cdev_ipc
make
sudo insmod driver/simple_fifo.ko
```

確認
```shell
$ sudo dmesg -T | grep regist | tail -1
[Tue Dec 10 17:49:19 2024] FIFO: Device has been registered
$ sudo ls -l /dev/ttyQEMU*
crw-rw----. 1 root dialout 511, 0 Dec 10 17:49 /dev/ttyQEMU0
crw-rw----. 1 root dialout 511, 1 Dec 10 17:49 /dev/ttyQEMU1
crw-rw----. 1 root dialout 511, 2 Dec 10 17:49 /dev/ttyQEMU2
crw-rw----. 1 root dialout 511, 3 Dec 10 17:49 /dev/ttyQEMU3
crw-rw----. 1 root dialout 511, 4 Dec 10 17:49 /dev/ttyQEMU4
crw-rw----. 1 root dialout 511, 5 Dec 10 17:49 /dev/ttyQEMU5
crw-rw----. 1 root dialout 511, 6 Dec 10 17:49 /dev/ttyQEMU6
crw-rw----. 1 root dialout 511, 7 Dec 10 17:49 /dev/ttyQEMU7
crw-rw----. 1 root dialout 511, 8 Dec 10 17:49 /dev/ttyQEMU8
crw-rw----. 1 root dialout 511, 9 Dec 10 17:49 /dev/ttyQEMU9
```

## 要らなくなったら
```
sudo rmmod simple_fifo
```
すれば良いが、
起動毎に読み込まれるようにはしていないので
わざわざ削除しなくても再起動するだけで消える

# ビルド

これを取得

https://github.com/tyano463/qemu/commit/b4cc9e2656ab9c0666db29b4250bdcd61d998c8e

```shell
git clone -b renesas/ra2l1-local-uart --depth=1 --recursive https://github.com/tyano463/qemu.git
mkdir build_qemu
cd build_qemu
../qemu/configure --target-list="arm-softmmu,aarch64-softmmu" --enable-debug --disable-docs
make
```

※aarch64-softmmuは不要だがついで

# 実行

## ルネサスサンプルコードのダウンロード
確認用にルネサスの[サンプルコード](https://www.renesas.com/en/document/scd/ek-ra2l1-example-project-bundle)を取得する(ルネサスへのユーザ登録が必要)

[ここから](https://www.renesas.com/en/products/microcontrollers-microprocessors/ra-cortex-m-mcus/ek-ra2l1-evaluation-kit-ra2l1-mcu-group)Applicationsタブを選んで`Sample Code`をクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786381/314d6923-0fd5-fe69-7b2d-d99e565e034e.png)

ダウンロード出来たら
```
unzip r20an0598eu0140-ek-ra2l1-exampleprojects.zip
```
などで解凍する。

## e2studioでサンプルコードのビルド

### ダウンロード
e2studioがない人は
e2studioとFSP(RA用)をダウンロードする必要がある。

#### e2studio
[ここから](https://www.renesas.com/en/software-tool/e-studio)e2studioをダウンロードする。

e2studioのインストールはFedoraだと[バグ](https://qiita.com/t-yano-tryhard/items/8f37d0614212c7bdb083)があるっぽいので
慣れてない人はubuntuのほうが良いかもしれない。

実行権限与えて実行して、あとは雰囲気で

#### FSP
FSPは[ここ](https://github.com/renesas/fsp)から右側のReleasesをクリック。
ちょっと下にスクロールして、Assetsと書いてあるのをクリックし、
`FSP_Packs_v5.6.0.zip`または最新版をダウンロードする。

解凍すると`internal`というディレクトリが出来るので
e2studioのインストール先に上書きする。
Fedoraでe2studioのインストールをデフォルトで行った場合は
`${HOME}/.local/share/renesas/e2_studio/internal/`になっていると思う。


### ビルド
#### `sci_uart`プロジェクトの読み込み
e2studioを起動し、ワークスペースを適当に決めて
File -> Open Projects from File Systems
ダイアログが開いたらImport source: の右のDirectoryをクリックして
解凍した場所のルネサスサンプルコードの`sci_uart`を選択する。
以下のように`sci_uart`ディレクトリではなく
その下の`e2studio`が指定対象なので注意

ek_ra2l1/sci_uart/sci_uart_ek_ra2l1_ep/e2studio/

`Finish`でプロジェクトが読み込まれる。

#### configuration.xml
configuration.xmlをダブルクリックして`BSP`タブを選び
FSP versionをダウンロードしたFSPのバージョンに合わせる。

BoardはEK-RA2L1、DeviceはR7FA2L1AB2DFPで良いと思います。

右上の`Generate Project Content`を実行

`Properties`ウィンドウを開いて`Stacks`タブのg_uart UARTを選ぶと

`Module g_uart UART(r_sci_uart) -> General -> Channel`で
使用チャンネルがわかる。多分2になっているはず。
というか2以外だと動きません。

#### ビルド
Project -> Cleanでビルド出来るはず。

#### その他

e2studioのデバッグは(ルネサス改造gdb用に特殊コマンドを使用するので)使えません。

## QEMUと対向UARTデバイス(もどき)の起動

### QEMUの起動
qemuを作成した`build_qemu`ディレクトリへ行ってサンプルコードを指定して実行
```shell
cd /path/to/build_qemu
sudo ./qemu-system-arm -machine ra2l1 -cpu cortex-m33 -monitor stdio -kernel /path/to/ek_ra2l1/sci_uart/sci_uart_ek_ra2l1_ep/e2studio/Debug/sci_uart_ek_ra2l1_ep.elf

```
### 対向UARTデバイス(もどき)の起動

仮想ターミナルエミュレータならなんでも良いですが、
ここではminicomを使用するものとして説明します。

無ければscreenでも良いし、`sudo yum install minicom`でインストール出来ます。

```
sudo minicom -b 115200 -D /dev/ttyQEMU2
```

速度は雰囲気で入れてます。もちろん意味はないし指定する必要はありません。
キャラクタデバイスの番号は
e2studioで確認出来る使用チャンネルに合わせる必要があります。

実行イメージ
```

Welcome to minicom 2.8

OPTIONS: I18n 
Compiled on Jan 25 2024, 00:00:00.
Port /dev/ttyQEMU2, 17:49:20

Press CTRL-A Z for help on special keys

123

Invalid input. Input range is from 1 - 100
22

```

ルネサスのサンプルコードは改行が`\r`で判定しているのでうまく動かない場合は
ターミナルエミュレータの改行コードの設定を確認してください。

minicomはCRは必ず送信されるはずなので設定は(多分)不要です。

## 確認

上の画面にもあるように範囲外の数値を入れると
`Invalid input. Input range is from 1 - 100`
と表示されます。

範囲内の数値を入れるとqemuがabortするはずです。が
これは正常動作です。

### abortの説明
`uart_ep_demo`内のコード

```c
            if(b_valid_data)
            {
                /* Change intensity of LED */
                err = set_intensity(intensity, TIMER_PIN);
                if (FSP_SUCCESS != err)
                {
                    APP_ERR_PRINT ("\r\n** GPT failed while changing intensity ** \r\n");
                    return err;
                }
                /* Resetting the temporary buffer */
                memset(g_temp_buffer, RESET_VALUE, DATA_LENGTH);
                b_valid_data = false;

                err = uart_print_user_msg((uint8_t *)"\r\nSet next value\r\n");
                if (FSP_SUCCESS != err)
                {
                    return err;
                }
            }
        } 
    }
}
```
`set_intensity`の引数intensityは入力した数字、TIMER_PINは1です。


```c
fsp_err_t set_intensity(uint32_t raw_count, uint8_t pin)
{
    fsp_err_t err = FSP_SUCCESS;
    raw_count *= STEP;
#if defined(BOARD_RA4W1_EK) || defined (BOARD_RA6T1_RSSK)
    raw_count = (MAX_DUTY_CYCLE - raw_count);
#endif
    /* Set GPT timer's DutyCycle as per user input */
    err = R_GPT_DutyCycleSet (&g_timer_ctrl, raw_count, pin);
    if (FSP_SUCCESS != err)
    {
        APP_ERR_PRINT ("\r\n ** R_GPT_DutyCycleSet API failed **\r\n");
    }
    return err;
}
```
`R_GPT_DutyCycleSet`へ行きます。

以下のdefineはデフォルトだとどちらも有効になっています。
```c
fsp_err_t R_GPT_DutyCycleSet (timer_ctrl_t * const p_ctrl, uint32_t const duty_cycle_counts, uint32_t const pin)
{
#if GPT_CFG_OUTPUT_SUPPORT_ENABLE
    uint32_t              tmp_pin         = pin & 3U;
    gpt_instance_ctrl_t * p_instance_ctrl = (gpt_instance_ctrl_t *) p_ctrl;
 #if GPT_CFG_PARAM_CHECKING_ENABLE
    FSP_ASSERT(NULL != p_instance_ctrl);
    FSP_ASSERT(tmp_pin <= GPT_IO_PIN_GTIOCA_AND_GTIOCB);
    bool pwm_mode3_pin = 0 != (pin & (GPT_IO_PIN_CREST | GPT_IO_PIN_TROUGH));
    if (TIMER_MODE_TRIANGLE_WAVE_ASYMMETRIC_PWM_MODE3 == p_instance_ctrl->p_cfg->mode)
    {
        /* In TIMER_MODE_TRIANGLE_WAVE_ASYMMETRIC_PWM_MODE3, the duty cycle must be for either a trough or crest. */
        FSP_ERROR_RETURN(pwm_mode3_pin, FSP_ERR_INVALID_MODE);
    }
    else
    {
        FSP_ERROR_RETURN((!pwm_mode3_pin) || (TIMER_MODE_ONE_SHOT_PULSE == p_instance_ctrl->p_cfg->mode),
                         FSP_ERR_INVALID_MODE);
    }
```

`pin`は1で固定なので`pwm_mode3_pin`もfalse固定になります。
`p_instance_ctrl->p_cfg->mode`はTIMER_MODE_PWM(3)なので
else文に入ります。

`pwm_mode3_pin`がfalseなのでエラーで返ります。

`uart_ep_demo`がエラーで返ります。

`hal_entry`の中身(抜粋)
```c
    err = uart_ep_demo();
    if (FSP_SUCCESS != err)
    {
        APP_PRINT ("\r\n ** UART EP Demo FAILED ** \r\n");
        timer_gpt_deinit();
        deinit_uart();
        APP_ERR_TRAP(err)
    }
}
```

`uart_ep_demo`がエラーで返ると`APP_ERR_TRAP`が実行されます。
`APP_ERR_TRAP`の中身は以下です。
```c
#define APP_ERR_TRAP(err)        if(err) {\
        SEGGER_RTT_printf(SEGGER_INDEX, "\r\nReturned Error Code: 0x%x  \r\n", err);\
        __asm("BKPT #0\n");} /* trap upon the error  */
```

`__asm("BKPT #0\n");`でAbortが発生します。
[BKPTの説明](https://developer.arm.com/documentation/dui0489/c/arm-and-thumb-instructions/miscellaneous-instructions/bkpt)

# アプリケーションデバッグ
qemuの実行を以下に変更して(`-s -S`をつけるだけ)
```
sudo ./qemu-system-arm -machine ra2l1 -cpu cortex-m33 -monitor stdio -kernel /path/to/ek_ra2l1/sci_uart/sci_uart_ek_ra2l1_ep/e2studio/Debug/sci_uart_ek_ra2l1_ep.elf -s -S
```

別のターミナルからgdbでアタッチしてください。
`arm-none-eabi-gdb`がない人は[ここから](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads/13-2-rel1)ダウンロードしてください。
右のDownloadアイコンは罠です。

**x86_64 Linux hosted cross toolchains** の
`arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz`
で良いと思います。
`tar xf arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz`などで解凍して
中のbinにパスを通してください。
以下はgdbのインストールと設定例(ダウンロードディレクトリに移動してから)
```shell
sudo chmod -R 777 /opt/
mv arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz /opt/
cd /opt
tar xf arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz
mv arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi toolchain
echo "export PATH=\$PATH:/opt/toolchain/bin" >> ~/.bashrc
```


以下はgdbでの接続例(arm-none-eabi-gdbはpython3.8でないと動かないっぽい)
```shell
$ cd /path/to/sci_uart/sci_uart_ek_ra2l1_ep/e2studio
$ cat <<EOF > .app-gdbinit
set pagination off
set logging on
target remote localhost:1234
define es
 stepi
 disas \$pc,+10
end
b hal_entry
command 1
 c
end
c
EOF
$
$ PYTHONHOME=/home/tyano/.pyenv/versions/3.8.19 PYTHONPATH=/home/tyano/.pyenv/versions/3.8.19/bin/python3.8 arm-none-eabi-gdb Debug/sci_uart_ek_ra2l1_ep.elf -x .app-gdbinit
```



# 疑問点など
- ルネサスがデフォルトでabortするサンプルコードを載せているのは謎
設定変更が必要なのか？実機だとabortしない？
- `APP_PRINT`や`APP_ERR_PRINT`は
`_SEGGER_RTT`変数にmemcpyするだけになっていて
UARTに接続されておらず、UART用には別に`uart_print_user_msg`が用意されている。
エミュレータだと対応してないので表示されないが
これは実機だと表示されるのだろうか？
- 一応テスト用に[GPIOの入出力をモニタするもの](https://github.com/tyano463/gpio_gui_lib)をつけてみた
- GPTはエラー回避に枠だけ作ったので動きません
- チャンネルは2固定で実装しています
チャンネルを変えたらターミナルエミュレータ側のキャラクタデバイスの番号を変更するだけではだめで
qemuの割り込み番号をvector_data.cの`g_vector_table`に合わせて指定してやる必要があります
```c
124     struct
125     {
126         int txi;
127         int tei;
128         int rxi;
129         // } irqnum[] = {{1, 2, 0}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {5, 6, 4}};
130     } irqnum[] = {{1, 2, 0}, {-1}, {5, 6, 4}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}};
```
ついでにこの部分も変更が必要です。
hw/arm/ra2l1.cの`ra2l1_soc_realize`
```c
149     ret = local_uart_init(system_memory, s, dev_soc, (intptr_t)R_SCI0, 0);
150     ret |= local_uart_init(system_memory, s, dev_soc, (intptr_t)R_SCI2, 2);
151     ret |= local_uart_init(system_memory, s, dev_soc, (intptr_t)R_SCI9, 9);
```
- Windowsでは(ドライバが対応していないので)動きません
- 詳細は`hw/arm/renesas_uart.c`を見てください

# 動作イメージ

対向のdummyプログラムを作成してやるとこんな感じに動かすことも出来ます。
（対向のdummyプログラムはキャラクタデバイスを`read`して`write`するだけです。）

<details><summary>ルネサスのサンプルコード用では無いですが、以下デモで使用した対向dummyのコードです</summary>

```c
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "dlog.h"

#define UPPER_UART "/dev/ttyQEMU0"

#define ASCII_CODE_CR '\r'
#define ASCII_CODE_LF '\n'

#define CRLF "\r\n"

#define MSG_DEVICE_FOUND "12:34:56:78:9a:bc -90 WestLK90-1613 11,22,33,44,55,66,77,88,99"
#define MSG_SWPUSH "SWPUSH"
#define MSG_SWREL "SWREL"

typedef enum {
    U_CMD_RDDN,
    U_CMD_STSC,
    U_CMD_SLDP,
    U_CMD_SWPUSH,
    U_CMD_SWREL,
    U_CMD_STAD,
    U_CMD_SPAD,
    U_CMD_MAX
} U_CMD;

typedef struct {
    U_CMD kind;
    const char *cmd;
} upper_command_t;

static void sig_handler(int signum);

volatile bool running;

typedef struct {
    int fd;
    const char *msg;
} send_msg_t;

static void *delay_send_impl(void *arg) {
    send_msg_t *p = (send_msg_t *)arg;
    sleep(1);
    write(p->fd, p->msg, strlen(p->msg));
    printf("Tx: %s", p->msg);

    free(arg);
    return NULL;
}

static uint8_t c2b(char c) {
    if ('0' <= c && c <= '9') {
        return c - '0';
    } else if ('A' <= c && c <= 'F') {
        return 0xa + c - 'A';
    } else if ('a' <= c && c <= 'f') {
        return 0xa + c - 'a';
    }
    return 0;
}

static bool is_led_on(const char *s, size_t len) {
    const char *sldp = "SLDP";
    size_t sldppos = 0;
    bool sldpchk = false;

    uint8_t upper, lower;
    uint8_t red = 0, blue = 0;
    size_t byte_pos = 0;

    for (size_t i = 0; i < len; i++) {
        if (!sldpchk) {
            if (sldp[sldppos] == s[i]) {
                sldppos++;
            }
            if (sldppos == strlen(sldp)) {
                sldpchk = true;
            }
        } else {
            if ((s[i] == ' ') || (s[i] == ','))
                continue;
            if (byte_pos % 2) {
                lower = c2b(s[i]);
                if (byte_pos / 2) {
                    blue = ((upper & 0xf) << 4) | (lower & 0xf);
                } else {
                    red = ((upper & 0xf) << 4) | (lower & 0xf);
                }
            } else {
                upper = c2b(s[i]);
            }
            byte_pos++;
        }
    }

    return ((red != 0) || (blue != 0));
}

static void delay_send(int fd, U_CMD cmd, const char *s, size_t len) {
    send_msg_t *msg;
    pthread_t th, th2;

    switch (cmd) {
    case U_CMD_STSC:
        msg = malloc(sizeof(send_msg_t));
        msg->fd = fd;
        msg->msg = MSG_DEVICE_FOUND CRLF;
        pthread_create(&th, NULL, delay_send_impl, msg);
        break;
    case U_CMD_SLDP:
        if (is_led_on(s, len)) {
            msg = malloc(sizeof(send_msg_t));
            msg->fd = fd;
            msg->msg = MSG_SWPUSH CRLF;
            sleep(2);
            pthread_create(&th, NULL, delay_send_impl, msg);
            usleep(500000);
            msg = malloc(sizeof(send_msg_t));
            msg->fd = fd;
            msg->msg = MSG_SWREL CRLF;
            pthread_create(&th2, NULL, delay_send_impl, msg);
        } else {
            // do nothing.
        }
        break;
    }
}

void response(int fd, const char *s, size_t len) {
    const char *devname = "IMBLE3-21948576";
    const upper_command_t commands[] = {
        {U_CMD_RDDN, "RDDN"},   {U_CMD_STSC, "STSC"},
        {U_CMD_SLDP, "SLDP"},   {U_CMD_SWPUSH, "SWPUSH"},
        {U_CMD_SWREL, "SWREL"}, {U_CMD_SPAD, "RDDN"},
        {U_CMD_STAD, "RDDN"},   {U_CMD_MAX, (const char *)NULL},
    };
    U_CMD cmd = U_CMD_MAX;
    for (int i = 0; commands[i].cmd; i++) {
        if (strncmp(s, commands[i].cmd, strlen(commands[i].cmd)) == 0) {
            cmd = commands[i].kind;
            break;
        }
    }

    ERR_RET(cmd == U_CMD_MAX, "command error %s", s);

    printf("Rx: %.*s\n", len, s);
    switch (cmd) {
    case U_CMD_RDDN:
        write(fd, devname, strlen(devname));
        write(fd, "\r\n", 2);
        printf("Tx: %s\n", devname);
        break;
    case U_CMD_STSC:
    case U_CMD_SLDP:
        write(fd, "OK\r\n", 4);
        printf("Tx: OK\n");
        delay_send(fd, cmd, s, len);
        break;
    default:
        write(fd, "OK\r\n", 4);
        printf("Tx: OK\n");
        break;
    }
error_return:
    return;
}

int main(void) {
    running = true;
    char buf[256];
    char rx_buf[256];
    int rx_pos = 0;

    int fd;

    fd = open(UPPER_UART, O_RDWR);
    ERR_RET(fd < 0, UPPER_UART " file open failed");

    signal(SIGINT, sig_handler);

    while (running) {
        ssize_t len = read(fd, buf, sizeof(buf) - 1);
        ERR_RET(len < 0, "read error");

        if (!len)
            continue;

        // buf[len] = '\0';
        // dlog("received %s", buf);
        memcpy(&rx_buf[rx_pos], buf, len);
        rx_pos += len;
        rx_buf[rx_pos] = '\0';

        size_t start = 0;
        for (size_t i = 0; i < rx_pos; i++) {
            if ((rx_buf[i] == ASCII_CODE_CR) || (rx_buf[i] == ASCII_CODE_LF)) {
                if (i > start) {
                    response(fd, rx_buf + start, i - start);
                }
                while ((i + 1 < rx_pos) &&
                       ((rx_buf[i + 1] == ASCII_CODE_CR) || (rx_buf[i + 1] == ASCII_CODE_LF))) {
                    i++;
                }
                start = i + 1;
            }
        }

        if (start < rx_pos) {
            memmove(rx_buf, rx_buf + start, rx_pos - start);
            rx_buf[rx_pos - start] = '\0';
            rx_pos -= start;
        } else {
            rx_buf[0] = '\0';
            rx_pos = 0;
        }
    }

error_return:
    if (fd >= 0)
        close(fd);
    return 0;
}

static void sig_handler(int signum) {
    running = false;
}

```


dlog.h
```c
#ifndef __DLOG_H__
#define __DLOG_H__

#include <string.h>

#define ERR_RETn(c)            \
    {                          \
        if (c)                 \
            goto error_return; \
    }

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

#define ERR_RET(c, s, ...)                                                                \
    {                                                                                     \
        if (c)                                                                            \
        {                                                                                 \
            printf("%s(%d) %s " s "\n", __FILENAME__, __LINE__, __func__, ##__VA_ARGS__); \
            goto error_return;                                                            \
        }                                                                                 \
    }

#define dlog(s, ...) \
    printf("%s(%d) %s " s "\n", __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif

#endif /* dlog_h */

```
</details>


![Peek 2024-12-16 12-57.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786381/f2be226d-b94a-0295-38d4-cd72840a8929.gif)