diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | CMakeLists.txt | 32 | ||||
-rw-r--r-- | Readme.md | 452 | ||||
-rwxr-xr-x | buildtest.sh | 12 | ||||
-rw-r--r-- | include/ipc.h | 32 | ||||
-rw-r--r-- | include/ipc_protocol.h | 148 | ||||
-rw-r--r-- | ipc.pc.in | 10 | ||||
-rw-r--r-- | ipc_unit_test/CMakeLists.txt | 20 | ||||
-rw-r--r-- | ipc_unit_test/ipc_unit_test_client.c | 153 | ||||
-rw-r--r-- | ipc_unit_test/ipc_unit_test_common.c | 83 | ||||
-rw-r--r-- | ipc_unit_test/ipc_unit_test_common.h | 17 | ||||
-rw-r--r-- | ipc_unit_test/ipc_unit_test_server.c | 201 | ||||
-rw-r--r-- | src/CMakeLists.txt | 35 | ||||
-rw-r--r-- | src/ipc_client.c | 582 | ||||
-rw-r--r-- | src/ipc_internal.c | 57 | ||||
-rw-r--r-- | src/ipc_internal.h | 48 | ||||
-rw-r--r-- | src/ipc_server.c | 543 | ||||
-rw-r--r-- | src/ipc_usage_info_table.c | 66 |
18 files changed, 2492 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..501bf48 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +project(ipc C) + +cmake_minimum_required(VERSION 2.8) + +# set gcc flags +set(CMAKE_C_FLAGS "-Wall -O2") + +# Build option for DEBUG +option(DEBUG_BUILD "This is debug build." OFF) +if(DEBUG_BUILD) + add_definitions(-DDEBUG) + set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/installdir/usr CACHE PATH "..." FORCE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") +endif() + +include(GNUInstallDirs) +set(IPC_LIBRARY_VERSION "1.0.0") + +# Name of library API +set(TARGET_NAME ipc) + +# Name of IC-service server API +#set(SERVER_API_NAME cluster_server_api) + +# Subdirectories +add_subdirectory(src) +add_subdirectory(ipc_unit_test) + +configure_file(ipc.pc.in ipc.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ipc.pc + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..904c027 --- /dev/null +++ b/Readme.md @@ -0,0 +1,452 @@ +# IPC流用可能部 + +## 概要 + +* ServerとClientの通信処理(IPC)部分を汎用的に実装したものです。 +* 大きく以下で構成されています。 + * IPCライブラリ実装ソース: src, include + * IPC単体テスト用プログラム: ipc_unit_test + +# ビルド方法 + +* 以下の手順でビルド可能です。 + ```bash + $ mkdir build + $ cd build + $ cmake .. + $ make + ``` + * 上記のコマンドは以下のスクリプトでも実行可能です。 + ```bash + $ ./buildtest.sh + ``` + +後述するインストールでは、ホストPC(/usr/local/)にインストールされる。インストール先を変更するためには、cmakeに渡すオプションを変更する。 + +``` +例 +$ cmake -DCMAKE_INSTALL_PREFIX=./install .. +``` + +# インストール方法 + + ```bash + $ cd build + $ sudo make install + ``` + +成功すると、下記のログが出力される。 + +``` +[100%] Built target ipc +Install the project... +-- Install configuration: "" +-- Installing: /usr/local/lib/pkgconfig/ipc.pc +-- Installing: /usr/local/lib/libipc.so.1.0.0 +-- Installing: /usr/local/lib/libipc.so.1 +-- Installing: /usr/local/lib/libipc.so +-- Installing: /usr/local/include/ipc.h +-- Installing: /usr/local/include/ipc_protocol.h +``` + +# ビルド生成物 + +* ビルドにより、最終的には以下が生成されます。 + * \<installdir\>/include/ 以下 + 外部公開向けヘッダファイル + ```bash + ipc.h + ipc_protocol.h + ``` + * \<installdir\>/lib/ 以下 + 共有ライブラリファイル + ```bash + libipc.so (シンボリックリンク) + libipc.so.1 (シンボリックリンク) + libipc.so.1.0.0 + ``` + * build/ipc_unit_test/ 以下 + テストプログラム実行ファイル + ```bash + ipc_unit_test_client + ipc_unit_test_server + ``` +<br> + +# 使用方法 + +* 本ライブラリにはServer用とClient用の機能が入っており、それぞれ使用方法の異なる部分があります。 + +## Server/Client共通 + +* 使用者は以下のライブラリとリンクする必要があります。 + * `libipc.so` +* 本ライブラリ使用者は以下をincludeします。 + * #include <ipc.h> + * 後述のipc_protocol.hはipc.h内からincludeされるため、記載不要です。 +* 使用するヘッダファイルは以下の通りです。 + * ipc.h + * 使用可能なAPI関数一覧が宣言されています。 + * ipc_protocol.h + * IPCの用途種別と、各用途に応じたデータ構造体が定義されています。 +* 本ライブラリでは、Unix Domain Socketを用いてServerとClientが通信します。 + * 用途種別ごとに異なる通信用ファイルを生成します。 + * デフォルトではServerの実行階層に通信用ファイルを生成します。 + * 環境変数 "IPC_DOMAIN_PATH" を設定しておくことで、通信用ファイルの生成場所を変更することができます。 + ``` + 例) + $ export IPC_DOMAIN_PATH="/tmp" + →/tmp以下にUnix Domain Socket通信用ファイルが生成されるようになる。 + ``` + +## IC-Service向け + +* 本ライブラリをIC-Service向けに使用する場合、以下の値・構造体を用います(ipc_protocol.h参照)。 + * 用途種別usageType:IPC_USAGE_TYPE_IC_SERVICE + * 送信データ構造体:IPC_DATA_IC_SERVICE_S + * 変化種別コールバック通知用enum:IPC_KIND_IC_SERVICE_E + * Unix Domain Socket通信用ファイル名:IpcIcService +* IC-Serviceの場合、Cluster APIライブラリ(libcluster_api.so)がIPC Clientとなっています。 + * 後述のClient用 APIは、libcluster_api.so内から呼び出されます。 + +## Server用 API + +* libipc.soを用いるServerは以下のAPIを使用できます。 + * ipcServerStart(IPC_USAGE_TYPE_E usageType); + * 指定した用途種別usageType用のIPC Serverを起動します。 + * ipcSendMessage(IPC_USAGE_TYPE_E usageType, const void* pData, signed int size); + * 指定した用途種別usageType用に、IPC Clientへのデータ送信を行います。 + * 送信データのアドレスとサイズを引数pData, sizeで指定します。 + * 送信データは、IPC Client側で用意しているData Poolに格納されます。 + * ipcServerStop(IPC_USAGE_TYPE_E usageType); + * 指定した用途種別usageType用のIPC Serverを終了します。 + +## Client用 API + +* libipc.soを用いるClientは以下のAPIを使用できます。 + * ipcClientStart(IPC_USAGE_TYPE_E usageType); + * 指定した用途種別usageType用のIPC Clientを起動します。 + * 同じusageType用のIPC Serverと接続します。 + * ipcReadDataPool(IPC_USAGE_TYPE_E usageType, void* pData, signed int* pSize); + * 指定したusageType用のData Poolの全データを読み込みます。 + * 読み込みデータ格納先のアドレスはpDataに、格納可能なサイズはpSizeに指定します。 + * Data Poolの内容はpDataに出力され、実際に読み込めたサイズはpSizeに出力されます。 + * ipcRegisterCallback(IPC_USAGE_TYPE_E usageType, IPC_CHANGE_NOTIFY_CB changeNotifyCb); + * IPC Serverからデータを受信した時、どのデータが何に変化したかの通知を受けるためのコールバック関数を、指定したusageType用に登録します。 + * ipcClientStop(IPC_USAGE_TYPE_E usageType); + * 指定した用途種別usageType用のIPC Clientを終了します。 + +# 単体テスト実行方法 + +* 制限 + * 2020/12/25現在、このテストプログラムは必ずIC-Service用としてServerとClientを接続します。 + * unix domain socket通信用ファイルは /tmp/ 以下に ipcIcService というファイル名で生成されます。 + +* ipc_unit_test_server と ipc_unit_test_client 間でプロセス間通信を行いますので、それぞれを別々のターミナルで起動します。 + 操作は手動ですが、以下、テストの一例です。 + + 1. **Server, Clientの順に起動します。** + ```bash + (Terminal 1) + $ ./ipc_unit_test_server + command (h=help, q=quit): + ``` + ```bash + (Terminal 2) + $ ./ipc_unit_test_client + command (h=help, q=quit): + ``` + この時点でServerとClientはIC-Service用として接続済みです。 + (ipcServerStart()とipcClientStart()が実行されています) + + 2. **Server側の送信データを適当に編集して送信してみます。** + ```bash + (Terminal 1) + command (h=help, q=quit):w ←★ w を入力 + write command (h=help q=goto main menu):2 1 ←★2 1を入力 + write command (h=help q=goto main menu):70 50 ←★70 50を入力 + write command (h=help q=goto main menu):l ←★l を入力 + ★送信データ一覧が表示されるが、以下、入力内容が反映されていること。 + 2: brake(4) = 1 ←★write command 2 1 の結果 + 70: oTempUnitVal(4) = 50 ←write command 70 50 の結果 + write command (h=help q=goto main menu):q ←★q を入力 + command (h=help, q=quit):s ←★s を入力(ipcSendMessage()の実行) + ipcSendMessage return:0 + command (h=help, q=quit): + ``` + Client側にコールバック関数の反応が出ているはず。 + ```bash + (Terminal 2) + command (h=help, q=quit):Enter changeNotifyCb ←★コールバック + kind = 2, size = 4, data=1 ←★brakeの値が1に変わったことを通知 + Leave changeNotifyCb + ``` + ★oTempUnitValの変化についてはIC-Serviceとしては監視対象でないのでコールバック無し。 + + 3. **Client側で受信できているか確認します。** + ```bash + (Terminal 2) + command (h=help, q=quit):r ←★r を入力 + ★送信データ一覧が表示されるが、以下、送信されたデータが入っていること。 + 2: brake(4) = 1 + 70: oTempUnitVal(4) = 50 + ``` + + 4. **Client, Serverの順に終了します。** + ```bash + (Terminal 2) + command (h=help, q=quit):q + bye... + $ + ``` + ```bash + (Terminal 1) + command (h=help, q=quit):q + bye... + $ + ``` + +# IPC用途種別の追加・変更方法 + +* まずはIC-Service向けにのみ実装しましたが、別の用途向けのデータを容易に追加することが可能なように構成しています。 +* 各用途種別向けの情報は、以下のファイルにて管理しています。 + * include/ipc_protocol.h (外部公開ヘッダ) + * src/ipc_usage_info_table.c (IPC内部向けソース) +* 新規用途の情報追加、もしくは既存用途への情報変更は上記2つのファイルに対してのみ行うだけで良いようにしています。 + * ipc内の他の.cファイルや.hファイルに対しては変更不要です。 + * ただし、その用途でIPCを用いるアプリやテストプログラムに対しては、ipc_protocol.hへの定義追加・変更に合わせた対応が別途必要になります。 +* 理想ではツール等でコードを自動生成できることが理想ですが、今回はそこまでの実装を考慮しておりません。 + +## 用途種別の追加・変更に関するサンプルコード(差分) + +まずは2つのファイルに対してどのように追加・変更する際のサンプルコードを示します。 + +### 例1:新規の用途種別を追加する場合 + +仮にNEW_SERVICEという用途種別を追加する場合の差分例を示します。 +IPC内における、新規の用途種別追加による影響範囲となります。 + +```patch +diff --git a/include/ipc_protocol.h b/include/ipc_protocol.h +index c0ad861..2bc1115 100644 +--- a/include/ipc_protocol.h ++++ b/include/ipc_protocol.h +@@ -6,6 +6,7 @@ + typedef enum { + IPC_USAGE_TYPE_IC_SERVICE = 0, + IPC_USAGE_TYPE_FOR_TEST, ++ IPC_USAGE_TYPE_NEW_SERVICE, // 用途種別追加 + IPC_USAGE_TYPE_MAX + } IPC_USAGE_TYPE_E; + +@@ -145,4 +146,17 @@ typedef struct { + signed int test; + } IPC_DATA_FOR_TEST_S; + ++// for IPC_USAGE_TYPE_NEW_SERVICE ++typedef enum { // データ変化を監視・通知したい種別のみ用意 ++ IPC_KIND_NS_PARAM1 = 0, ++ IPC_KIND_NS_PARAM2 ++} IPC_KIND_NEW_SERVICE_E; ++ ++typedef struct { // この用途で送受信する全データ ++ int param1; ++ int param2; ++ int param3; ++ int param4; ++} IPC_DATA_NEW_SERVICE_S; ++ + #endif // IPC_PROTOCOL_H +diff --git a/src/ipc_usage_info_table.c b/src/ipc_usage_info_table.c +index 976cc73..51264c6 100644 +--- a/src/ipc_usage_info_table.c ++++ b/src/ipc_usage_info_table.c +@@ -51,16 +51,24 @@ static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeForTest[] = { + DEFINE_OFFSET_SIZE(IPC_DATA_FOR_TEST_S, test, IPC_KIND_TEST_TEST) + }; + ++// for IPC_USAGE_TYPE_FOR_TEST ++static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeNewService[] = { // データ変化を監視・通知したい種別のみ記載 ++ DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param1, IPC_KIND_NS_PARAM1), ++ DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param2, IPC_KIND_NS_PARAM2) ++}; // この例では、param3, param4のデータ変化については監視・通知しない。 ++ + // == usage info table == + // index of [] is IPC_USAGE_TYPE_E + IPC_DOMAIN_INFO_S g_ipcDomainInfoList[] = + { + {sizeof(IPC_DATA_IC_SERVICE_S), "ipcIcService"}, +- {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"} ++ {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"}, ++ {sizeof(IPC_DATA_NEW_SERVICE_S), "ipcNewService"} // 新規用途用の送受信サイズ情報追加 + }; + + IPC_CHECK_CHANGE_INFO_TABLE_S g_ipcCheckChangeInfoTbl[] = { + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeIcService), +- DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest) ++ DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest), ++ DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeNewService) // 新規用途用 データ変化監視テーブルを登録 + }; + +``` + +### 例2:既存の用途種別のデータの一部を削除する場合 + +既存の用途IC-Serviceの送受信データから、メンバ変数brakeを削除する場合の差分例を示します。 +IPC内における、メンバ変数を削除した場合の影響範囲となります。 + +```patch +diff --git a/include/ipc_protocol.h b/include/ipc_protocol.h +index c0ad861..7fed8bf 100644 +--- a/include/ipc_protocol.h ++++ b/include/ipc_protocol.h +@@ -13,7 +13,6 @@ typedef enum { + typedef enum { + IPC_KIND_ICS_TURN_R = 0, + IPC_KIND_ICS_TURN_L, +- IPC_KIND_ICS_BRAKE, + IPC_KIND_ICS_SEATBELT, + IPC_KIND_ICS_HIGHBEAM, + IPC_KIND_ICS_DOOR, +@@ -51,7 +50,6 @@ typedef struct { + // Telltale + signed int turnR; + signed int turnL; +- signed int brake; + signed int seatbelt; + signed int frontRightSeatbelt; + signed int frontCenterSeatbelt; +diff --git a/src/ipc_usage_info_table.c b/src/ipc_usage_info_table.c +index 976cc73..40ac8df 100644 +--- a/src/ipc_usage_info_table.c ++++ b/src/ipc_usage_info_table.c +@@ -12,7 +12,6 @@ + static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeIcService[] = { + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, turnR, IPC_KIND_ICS_TURN_R), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, turnL, IPC_KIND_ICS_TURN_L), +- DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, brake, IPC_KIND_ICS_BRAKE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, seatbelt, IPC_KIND_ICS_SEATBELT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, highbeam, IPC_KIND_ICS_HIGHBEAM), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, door, IPC_KIND_ICS_DOOR), +``` + +## 用途種別の新規追加に関する共通事項 + +* いくつか列挙体・構造体の新規追加、および既存の列挙体・構造体内への追記を伴いますが、いずれも名称については特に制約はありません。 + +## include/ipc_protocol.h へ追記する情報 +* 1つの用途種別に対し、以下の3つの情報を追記します。 + * 用途種別名の追記 + * 新規用途向けの変化通知種別用列挙体の定義 + * 新規用途向けの送受信データ構造体の定義 +* 用途種別名の追記 + * サンプルコードの以下の部分のことになります。 + ```patch + typedef enum { + IPC_USAGE_TYPE_IC_SERVICE = 0, + IPC_USAGE_TYPE_FOR_TEST, + + IPC_USAGE_TYPE_NEW_SERVICE, // 用途種別追加 + IPC_USAGE_TYPE_MAX + } IPC_USAGE_TYPE_E; + ``` + * enum IPC_USAGE_TYPE_E 内に用途種別となるメンバを追加します。 + * IPC_USAGE_TYPE_MAXの1つ手前に追加するようにしてください(既存の定義に影響を及ぼさないようにするため)。 + * ここで定義した値は、ipc.hで定義されているipcServerStart()などの引数usageTypeへの指定用に使用します。 +* 新規用途向けの変化通知種別用列挙体の定義 + * サンプルコードの以下の部分のことになります。 + ```patch + +typedef enum { // データ変化を監視したい種別のみ用意 + + IPC_KIND_NS_PARAM1 = 0, + + IPC_KIND_NS_PARAM2 + +} IPC_KIND_NEW_SERVICE_E; + ``` + * データ変化通知の種別用列挙体を追加します。後述の送受信データ構造体と関連があります。 + * この値は、ipcRegisterCallback()で登録されたコールバック関数の第3引数kindへの指定に使用します。 + * 列挙体名、メンバ名について、特に名称の制約はありません。 +* 新規用途向けのデータ構造体の定義 + * サンプルコードの以下の部分のことになります。 + ```patch + +typedef struct { // この用途で送受信する全データ + + int param1; + + int param2; + + int param3; + + int param4; + +} IPC_DATA_NEW_SERVICE_S; + ``` + * 新規用途で送受信するデータ構造体を追加します。 + * IPC ServerからIPC Clientへは、ここで定義した構造体のデータ全てを送信することになります。 + +## src/ipc_usage_info_table.c に対する追記 +* 1つの用途種別に対し、以下の3つの情報を追記します。 + * データ変化通知用の種別対応テーブルの追加 + * 通信用ドメイン情報追記(通信サイズ、ドメインファイル名) + * 用途と変化種別対応テーブルとの関係追記 +* データ変化通知用の種別対応テーブルの追加 + * サンプルコードの以下の部分のことになります。 + ``` + +// for IPC_USAGE_TYPE_FOR_TEST + +static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeNewService[] = { // データ変化を監視・通知したい種別のみ記載 + + DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param1, IPC_KIND_NS_PARAM1), + + DEFINE_OFFSET_SIZE(IPC_DATA_NEW_SERVICE_S, param2, IPC_KIND_NS_PARAM2) + +}; // この例では、param3, param4のデータ変化については監視・通知しない。 + ``` + * 新規用途向けに、IPC_CHECK_CHANGE_INFO_Sの構造体配列を追加します。 + * ipc_protocol.hで定義した変化通知種別用列挙体の定義と、通信するデータ構造体メンバを対応付けるテーブルを記載します。 + * このテーブルは、IPC ClientがIPC Serverからデータを受信する時に、前回受信時と変化しているデータ種別をコールバック通知する際に使用します。 + * 構造体配列内には、以下のようなマクロを複数個記載して対応を定義します。 + ```c + DEFINE_OFFSET_SIZE(<データ構造体名>, <構造体メンバ名>, 変化通知列挙体メンバ名), + ``` + * 上記サンプルコード、g_ipcCheckChangeNewService[]の場合は以下のようになります。 + * param1が前回受信時と値が異なる場合、変化種別 IPC_KIND_NS_PARAM1 としてIPC Clientへコールバック通知する。 + * param2が前回受信時と値が異なる場合、変化種別 IPC_KIND_NS_PARAM2 としてIPC Clientへコールバック通知する。 + * 記載していないparam3, param4については、前回受信時と値が異なっていてもコールバック通知はしない。 + +* 通信用ドメイン情報追記(通信サイズ、ドメインファイル名) + * サンプルコードの以下の部分のことになります。 + ```patch + IPC_DOMAIN_INFO_S g_ipcDomainInfoList[] = + { + {sizeof(IPC_DATA_IC_SERVICE_S), "ipcIcService"}, + - {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"} + + {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"}, + + {sizeof(IPC_DATA_NEW_SERVICE_S), "ipcNewService"} // 新規用途用の送受信サイズ情報追加 + }; + ``` + * 構造体配列 g_ipcDomainInfoList[] に、新規用途向けのドメイン情報を追記します。 + * この追記により、新規追加した用途種別で用いる送受信データサイズと、Unix Domain Socket通信で用いるドメインファイル名が決まります。 + * ipc_protocol.hのenum IPC_USAGE_TYPE_Eの定義順と一致させる必要があるので、必ず末尾に追加してください。 + * 以下のように、通信するデータ構造体のサイズと、ドメインファイル名の情報を、g_ipcDomainInfoList[] の末尾に追記します。 + ```c + {sizeof(<通信するデータ構造体名>), "ドメインファイル名"}, + ``` +* 用途と変化種別対応テーブルとの関係追記 + * サンプルコードの以下の部分のことになります。 + ```patch + IPC_CHECK_CHANGE_INFO_TABLE_S g_ipcCheckChangeInfoTbl[] = { + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeIcService), + - DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest) + + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest), + + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeNewService) // 新規用途用 データ変化監視テーブルを登録 + }; + ``` + * 構造体配列 g_ipcCheckChangeInfoTbl[] に、新規用途向けの変化通知種別対応テーブルに関する情報を追記します。 + * ipc_protocol.hのenum IPC_USAGE_TYPE_Eの定義順と一致させる必要があるので、必ず末尾に追加してください。 + * 前述の「変化通知種別対応テーブル構造体」を、以下のようなマクロに記載し、g_ipcCheckChangeInfoTbl[]の末尾に追記します。 + ```c + DEFINE_CHANGE_INFO_TABLE(<変化通知種別対応テーブル構造体名>), + ``` + +## 既存用途向けの送信データを一部変更する場合 +* ipc_protocol.h内の既存の送信データ構造体内のメンバ変数の削除、もしくは名称変更する場合 + * ipc部分、およびipcをその用途で用いるアプリをそれぞれビルドしてみて、コンパイルエラーとなった部分を修正します。 + +* ipc_protocol.h内の既存の送信データ構想体へメンバ変数を追加する場合 + * [IPC用途種別の追加・変更方法](#IPC用途種別の追加・変更方法) を参考に、include/ipc_protocol.hとsrc/ipc_usage_info_table.cへの追記を行います。 + +## 補足 +* src/ipc_usage_info_table.cにて、DEFINE_OFFSET_SIZE()マクロにて情報を記載しているが、これはoffsetof()とsizeof()を使うことで、メンバ変数に関する構造体先頭からオフセットとサイズを取得しています。 + * 用途種別の追加を容易に行えるようにするため、IPC処理内部ではデータ構造体内の変数名を直接指定しないような実装を行っています。 + * 各用途に対して、データ構造体のオフセットテーブルを用意することで、送信されたデータの何バイト目に何の変数があるかがわかるようになります。 + * この仕組みにより、IPC処理内部でメンバ変数名を直接指定しなくとも、データ変化の確認が可能となります。 + * [IPC用途種別の追加・変更方法](#IPC用途種別の追加・変更方法) に従って用途種別を追加することで、IPC内部処理は新たな用途に対する処理ができるようになります。 diff --git a/buildtest.sh b/buildtest.sh new file mode 100755 index 0000000..4c4e2c3 --- /dev/null +++ b/buildtest.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +rm -rf build/ +mkdir build +cd build +cmake .. +make +# make install +#echo "**************** Test for cluster_api ****************" +#./test/cluster_api_test +#echo "*************** Test for cluster_server **************" +#./server_test/cluster_server_test diff --git a/include/ipc.h b/include/ipc.h new file mode 100644 index 0000000..d1bc90d --- /dev/null +++ b/include/ipc.h @@ -0,0 +1,32 @@ +#ifndef IPC_H +#define IPC_H + +#include <ipc_protocol.h> + +// Environment Variable for unix-domain-socket file path +#define IPC_ENV_DOMAIN_SOCKET_PATH "IPC_DOMAIN_PATH" + +// return value for API +typedef enum { + IPC_RET_OK = 0, + IPC_ERR_PARAM, + IPC_ERR_SEQUENCE, + IPC_ERR_NO_RESOURCE, + IPC_ERR_OTHER +} IPC_RET_E; + +// format of callback function +typedef void (*IPC_CHANGE_NOTIFY_CB)(void* pData, signed int size, int kind); + +// for Server Function +IPC_RET_E ipcServerStart(IPC_USAGE_TYPE_E usageType); +IPC_RET_E ipcSendMessage(IPC_USAGE_TYPE_E usageType, const void* pData, signed int size); +IPC_RET_E ipcServerStop(IPC_USAGE_TYPE_E usageType); + +// for Client Function +IPC_RET_E ipcClientStart(IPC_USAGE_TYPE_E usageType); +IPC_RET_E ipcReadDataPool(IPC_USAGE_TYPE_E usageType, void* pData, signed int* pSize); +IPC_RET_E ipcRegisterCallback(IPC_USAGE_TYPE_E usageType, IPC_CHANGE_NOTIFY_CB changeNotifyCb); +IPC_RET_E ipcClientStop(IPC_USAGE_TYPE_E usageType); + +#endif // IPC_H diff --git a/include/ipc_protocol.h b/include/ipc_protocol.h new file mode 100644 index 0000000..c0ad861 --- /dev/null +++ b/include/ipc_protocol.h @@ -0,0 +1,148 @@ +#ifndef IPC_PROTOCOL_H +#define IPC_PROTOCOL_H + +// TODO: Write something + +typedef enum { + IPC_USAGE_TYPE_IC_SERVICE = 0, + IPC_USAGE_TYPE_FOR_TEST, + IPC_USAGE_TYPE_MAX +} IPC_USAGE_TYPE_E; + +// for IPC_USAGE_TYPE_IC_SERVICE +typedef enum { + IPC_KIND_ICS_TURN_R = 0, + IPC_KIND_ICS_TURN_L, + IPC_KIND_ICS_BRAKE, + IPC_KIND_ICS_SEATBELT, + IPC_KIND_ICS_HIGHBEAM, + IPC_KIND_ICS_DOOR, + IPC_KIND_ICS_EPS, + IPC_KIND_ICS_SRS_AIRBAG, + IPC_KIND_ICS_ABS, + IPC_KIND_ICS_LOW_BATTERY, + IPC_KIND_ICS_OIL_PRESS, + IPC_KIND_ICS_ENGINE, + IPC_KIND_ICS_FUEL, + IPC_KIND_ICS_IMMOBI, + IPC_KIND_ICS_TM_FAIL, + IPC_KIND_ICS_ESP_ACT, + IPC_KIND_ICS_ESP_OFF, + IPC_KIND_ICS_ADAPTING_LIGHTING, + IPC_KIND_ICS_AUTO_STOP, + IPC_KIND_ICS_AUTO_STOP_FAIL, + IPC_KIND_ICS_PARKING_LIGHTS, + IPC_KIND_ICS_FRONT_FOG, + IPC_KIND_ICS_EXTERIOR_LIGHT_FAULT, + IPC_KIND_ICS_ACC_FAIL, + IPC_KIND_ICS_LDW_OFF, + IPC_KIND_ICS_HILL_DESCENT, + IPC_KIND_ICS_AUTO_HI_BEAM_GREEN, + IPC_KIND_ICS_AUTO_HI_BEAM_AMBER, + IPC_KIND_ICS_LDW_OPERATE, + IPC_KIND_ICS_GENERAL_WARN, + IPC_KIND_ICS_SPORTS_MODE, + IPC_KIND_ICS_DRIVING_POWER_MODE, + IPC_KIND_ICS_HOT_TEMP, + IPC_KIND_ICS_LOW_TEMP +} IPC_KIND_IC_SERVICE_E; + +typedef struct { + // Telltale + signed int turnR; + signed int turnL; + signed int brake; + signed int seatbelt; + signed int frontRightSeatbelt; + signed int frontCenterSeatbelt; + signed int frontLeftSeatbelt; + signed int mid1RightSeatbelt; + signed int mid1CenterSeatbelt; + signed int mid1LeftSeatbelt; + signed int mid2RightSeatbelt; + signed int mid2CenterSeatbelt; + signed int mid2LeftSeatbelt; + signed int rearRightSeatbelt; + signed int rearCenterSeatbelt; + signed int rearLeftSeatbelt; + signed int highbeam; + signed int door; + signed int frontRightDoor; + signed int frontLeftDoor; + signed int rearRightDoor; + signed int rearLeftDoor; + signed int trunkDoor; + signed int hoodDoor; + signed int eps; + signed int srsAirbag; + signed int abs; + signed int lowBattery; + signed int oilPress; + signed int engine; + signed int fuel; + signed int immobi; + signed int tmFail; + signed int espAct; + signed int espOff; + signed int adaptingLighting; + signed int autoStop; + signed int autoStopFail; + signed int parkingLights; + signed int frontFog; + signed int exteriorLightFault; + signed int accFail; + signed int ldwOff; + signed int hillDescent; + signed int autoHiBeamGreen; + signed int autoHiBeamAmber; + signed int sportsMode; + signed int ldwOperate; + signed int generalWarn; + signed int drivingPowerMode; + signed int hotTemp; + signed int lowTemp; + + // ShiftPosition + signed int gearAtVal; + signed int gearMtVal; + + // Speed + unsigned long spAnalogVal; + signed int spAnaDigUnitVal; + + // Tacho + unsigned long taAnalogVal; + + // TripComputer + unsigned long trcomTripAVal; + unsigned long trcomTripBVal; + unsigned long trcomOdoVal; + signed int trcomUnitVal; + unsigned short avgSpeedAVal; + unsigned short avgSpeedBVal; + unsigned short hourAVal; + unsigned short hourBVal; + unsigned char minuteAVal; + unsigned char minuteBVal; + unsigned char secondAVal; + unsigned char secondBVal; + signed short oTempVal; + signed int oTempUnitVal; + unsigned short cruRangeVal; + unsigned short avgFuelAVal; + unsigned short avgFuelBVal; + unsigned short insFuelAVal; + unsigned short insFuelBVal; + signed int fuelEconomyUnitVal; +} IPC_DATA_IC_SERVICE_S; + +// for IPC_USAGE_TYPE_FOR_TEST +typedef enum { + IPC_KIND_TEST_TEST = 0 +} IPC_KIND_FOR_TEST_E; + +typedef struct { + signed int test; +} IPC_DATA_FOR_TEST_S; + +#endif // IPC_PROTOCOL_H diff --git a/ipc.pc.in b/ipc.pc.in new file mode 100644 index 0000000..55c1157 --- /dev/null +++ b/ipc.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ + +Name: @TARGET_NAME@ +Description: client and server ipc library by domain socket with defined shared data protocol +Version: @IPC_LIBRARY_VERSION@ + +Cflags: -I${includedir} +Libs: -L${libdir} -lipc
\ No newline at end of file diff --git a/ipc_unit_test/CMakeLists.txt b/ipc_unit_test/CMakeLists.txt new file mode 100644 index 0000000..32d2118 --- /dev/null +++ b/ipc_unit_test/CMakeLists.txt @@ -0,0 +1,20 @@ + +# Define project Targets +set(TEST_CLIENT_NAME ipc_unit_test_client) +set(TEST_SERVER_NAME ipc_unit_test_server) + +add_executable(${TEST_CLIENT_NAME} ipc_unit_test_client.c ipc_unit_test_common.c) +target_link_libraries(${TEST_CLIENT_NAME} ${TARGET_NAME}) +target_include_directories(${TEST_CLIENT_NAME} PRIVATE + ./ + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> +) + +add_executable(${TEST_SERVER_NAME} ipc_unit_test_server.c ipc_unit_test_common.c) +target_link_libraries(${TEST_SERVER_NAME} ${TARGET_NAME}) +target_include_directories(${TEST_SERVER_NAME} PRIVATE + ./ + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> +) + + diff --git a/ipc_unit_test/ipc_unit_test_client.c b/ipc_unit_test/ipc_unit_test_client.c new file mode 100644 index 0000000..4a49b22 --- /dev/null +++ b/ipc_unit_test/ipc_unit_test_client.c @@ -0,0 +1,153 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <unistd.h> +#include <ipc.h> + +#include "ipc_unit_test_common.h" + +IPC_DATA_IC_SERVICE_S g_dataIcService; + +static void changeNotifyCb(void* pData, signed int size, int kind); +static void helpPrint(void); +static void listPrint(void); + +int main(void) +{ + IPC_RET_E ret; + char command[10]; + char dummy[10]; + bool isRunning = true; + signed int size; + char *pRetStr; + + memset(&g_dataIcService, 0, sizeof(g_dataIcService)); + + ret = ipcClientStart(IPC_USAGE_TYPE_IC_SERVICE); + if (ret != IPC_RET_OK) { + printf("ipcClientStart Error:%d\n", ret); + goto end; + } + + ret = ipcRegisterCallback(IPC_USAGE_TYPE_IC_SERVICE, changeNotifyCb); + if (ret != IPC_RET_OK) { + printf("ipcRegisterCallback Error:%d\n", ret); + goto end; + } + + while(isRunning) { + printf("command (h=help, q=quit):"); + pRetStr = fgets(command, 10, stdin); + if (pRetStr == NULL) { + continue; + } + memcpy(dummy, command, 10); + + while(strlen(dummy) == 9 && dummy[8] != '\n') { + pRetStr = fgets(dummy, 10, stdin); + if (pRetStr == NULL) { + break; + } + } + + command[strlen(command)-1] = '\0'; + + switch(command[0]) { + case 'h': + helpPrint(); + break; + case 'q': + isRunning = false; + break; + case 'r': + size = sizeof(g_dataIcService); + ret = ipcReadDataPool(IPC_USAGE_TYPE_IC_SERVICE, &g_dataIcService, &size); + printf("ipcReadDataPool return:%d\n", ret); + listPrint(); + break; + default: + break; + } + } + +end: + ipcClientStop(IPC_USAGE_TYPE_IC_SERVICE); + printf("bye...\n"); + sleep(1); + + return 0; +} + +static void changeNotifyCb(void* pData, signed int size, int kind) +{ + signed long longVal; + signed int intVal; + signed short shortVal; + signed char charVal; + + printf("Enter %s\n", __func__); + + switch(size) { + case 8: + longVal = *((signed long*)pData); + printf("kind = %d, size = %d, data=%ld\n", kind, size, longVal); + break; + case 4: + intVal = *((signed int*)pData); + printf("kind = %d, size = %d, data=%d\n", kind, size, intVal); + break; + case 2: + shortVal = *((signed short *)pData); + printf("kind = %d, size = %d, data=%d\n", kind, size, shortVal); + break; + default: + charVal = *((signed char *)pData); + printf("kind = %d, size = %d, data=%d\n", kind, size, charVal); + break; + } + printf("Leave %s\n", __func__); + return; +} + +static void helpPrint(void) +{ + printf("\n-----------\n"); + printf("'h' : help\n"); + printf("'q' : quit\n"); + printf("'r' : read from Client data pool\n"); + printf("-----------\n\n"); +} + +static void listPrint(void) +{ + int i; + signed long longVal; + signed int intVal; + signed short shortVal; + signed char charVal; + void *pValue; + + for (i = 0; i < IC_SERVICE_LIST_NUM; i++) { + pValue = (void *)&g_dataIcService + IcServiceList[i].offset; + switch(IcServiceList[i].size) { + case 8: + longVal = *((signed long*)pValue); + printf("%3d: %s(%d) = %ld\n", i, IcServiceList[i].name, IcServiceList[i].size, longVal); + break; + case 4: + intVal = *((signed int*)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, intVal); + break; + case 2: + shortVal = *((signed short *)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, shortVal); + break; + default: + charVal = *((signed char *)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, charVal); + break; + } + } +} + diff --git a/ipc_unit_test/ipc_unit_test_common.c b/ipc_unit_test/ipc_unit_test_common.c new file mode 100644 index 0000000..02416c2 --- /dev/null +++ b/ipc_unit_test/ipc_unit_test_common.c @@ -0,0 +1,83 @@ +#include <ipc.h> +#include "ipc_unit_test_common.h" + +IPC_UNIT_TEST_DATA_LIST IcServiceList[] = { + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, turnR), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, turnL), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, brake), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, seatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontRightSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontCenterSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontLeftSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid1RightSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid1CenterSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid1LeftSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid2RightSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid2CenterSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, mid2LeftSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, rearRightSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, rearCenterSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, rearLeftSeatbelt), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, highbeam), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, door), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontRightDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontLeftDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, rearRightDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, rearLeftDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, trunkDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, hoodDoor), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, eps), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, srsAirbag), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, abs), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, lowBattery), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, oilPress), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, engine), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, fuel), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, immobi), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, tmFail), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, espAct), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, espOff), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, adaptingLighting), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, autoStop), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, autoStopFail), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, parkingLights), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, frontFog), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, exteriorLightFault), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, accFail), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, ldwOff), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, hillDescent), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, autoHiBeamGreen), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, autoHiBeamAmber), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, sportsMode), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, ldwOperate), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, generalWarn), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, drivingPowerMode), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, hotTemp), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, lowTemp), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, gearAtVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, gearMtVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, spAnalogVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, spAnaDigUnitVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, taAnalogVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, trcomTripAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, trcomTripBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, trcomOdoVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, trcomUnitVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, avgSpeedAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, avgSpeedBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, hourAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, hourBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, minuteAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, minuteBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, secondAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, secondBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, oTempVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, oTempUnitVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, cruRangeVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, avgFuelAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, avgFuelBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, insFuelAVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, insFuelBVal), + DEFINE_STRUCT_DATA(IPC_DATA_IC_SERVICE_S, fuelEconomyUnitVal) +}; + diff --git a/ipc_unit_test/ipc_unit_test_common.h b/ipc_unit_test/ipc_unit_test_common.h new file mode 100644 index 0000000..e4ad196 --- /dev/null +++ b/ipc_unit_test/ipc_unit_test_common.h @@ -0,0 +1,17 @@ +#ifndef IPC_UNIT_TEST_COMMON_H +#define IPC_UNIT_TEST_COMMON_H +#include <stddef.h> + +#define DEFINE_STRUCT_DATA(struct_name, member) \ + {#member, offsetof(struct_name, member), sizeof(((struct_name *)0)->member)} +#define IC_SERVICE_LIST_NUM (77) + +typedef struct { + const char *name; + int offset; + int size; +} IPC_UNIT_TEST_DATA_LIST; + +extern IPC_UNIT_TEST_DATA_LIST IcServiceList[]; + +#endif // IPC_UNIT_TEST_COMMON_H diff --git a/ipc_unit_test/ipc_unit_test_server.c b/ipc_unit_test/ipc_unit_test_server.c new file mode 100644 index 0000000..374cda6 --- /dev/null +++ b/ipc_unit_test/ipc_unit_test_server.c @@ -0,0 +1,201 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdbool.h> + +#include <ipc.h> + +#include "ipc_unit_test_common.h" + +static void helpPrint(void); +static void helpWritePrint(void); +static void listPrint(void); +static void writeData(void); +static void writeDataToStruct(int id, long value); + +IPC_DATA_IC_SERVICE_S g_dataIcService; + +int main(void) +{ + IPC_RET_E ret; + char command[10]; + char dummy[10]; + bool isRunning = true; + char *pRetStr; + + memset(&g_dataIcService, 0, sizeof(g_dataIcService)); + + ret = ipcServerStart(IPC_USAGE_TYPE_IC_SERVICE); + if (ret != IPC_RET_OK) { + printf("ipcServerStart Error:%d\n", ret); + goto end; + } + + while(isRunning) { + printf("command (h=help, q=quit):"); + pRetStr = fgets(command, 10, stdin); + if (pRetStr == NULL) { + continue; + } + memcpy(dummy, command, 10); + + while(strlen(dummy) == 9 && dummy[8] != '\n') { + pRetStr = fgets(dummy, 10, stdin); + if (pRetStr == NULL) { + break; + } + } + + command[strlen(command)-1] = '\0'; + + switch(command[0]) { + case 'h': + helpPrint(); + break; + case 'q': + isRunning = false; + break; + case 'l': + listPrint(); + break; + case 'w': + writeData(); + break; + case 's': + ret = ipcSendMessage(IPC_USAGE_TYPE_IC_SERVICE, (const void*)&g_dataIcService, sizeof(g_dataIcService)); + printf("ipcSendMessage return:%d\n", ret); + break; + default: + break; + } + } + +end: + ipcServerStop(IPC_USAGE_TYPE_IC_SERVICE); + printf("bye...\n"); + sleep(1); + + return 0; +} + +static void helpPrint(void) +{ + printf("\n-----------\n"); + printf("'h' : help\n"); + printf("'q' : quit\n"); + printf("'l' : list of Server data\n"); + printf("'w' : write to Server data\n"); + printf("'s' : send data to Client\n"); + printf("-----------\n\n"); +} + +static void listPrint(void) +{ + int i; + signed long longVal; + signed int intVal; + signed short shortVal; + signed char charVal; + void *pValue; + + for (i = 0; i < IC_SERVICE_LIST_NUM; i++) { + pValue = (void *)&g_dataIcService + IcServiceList[i].offset; + switch(IcServiceList[i].size) { + case 8: + longVal = *((signed long*)pValue); + printf("%3d: %s(%d) = %ld\n", i, IcServiceList[i].name, IcServiceList[i].size, longVal); + break; + case 4: + intVal = *((signed int*)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, intVal); + break; + case 2: + shortVal = *((signed short *)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, shortVal); + break; + default: + charVal = *((signed char *)pValue); + printf("%3d: %s(%d) = %d\n", i, IcServiceList[i].name, IcServiceList[i].size, charVal); + break; + } + } +} + +static void writeData(void) +{ + char command[40]; + char dummy[40]; + bool isRunning = true; + int i; + + int id; + long value; + + char *pRetStr; + + while(isRunning) { + printf("write command (h=help q=goto main menu):"); + pRetStr = fgets(command, 40, stdin); + if (pRetStr == NULL) { + continue; + } + memcpy(dummy, command, 40); + + while(strlen(dummy) == 39 && dummy[38] != '\n') { + pRetStr = fgets(dummy, 40, stdin); + if (pRetStr == NULL) { + break; + } + } + + command[strlen(command)-1] = '\0'; + switch(command[0]) { + case 'h': + helpWritePrint(); + break; + case 'q': + isRunning = false; + break; + case 'l': + listPrint(); + break; + default: + // write data + id = (int)strtol(command, NULL, 0); + value = 0; + for (i = 0; i < strlen(command); i++) { + if (command[i] == ' ') { + value = (int)strtol(&command[i], NULL, 0); + } + } + writeDataToStruct(id, value); + } + } +} + +static void helpWritePrint(void) +{ + printf("\n-----------\n"); + printf("'h' : help\n"); + printf("'q' : goto main menu\n"); + printf("'l' : list of Server data\n"); + printf("<ID> <value>: write data\n"); + printf(" ex)\n"); + printf(" write command 2 4\n"); + printf(" -> 2: brake = 4\n"); + printf("-----------\n\n"); +} + +static void writeDataToStruct(int id, long value) +{ + void *pValue; + + if (id < 0 || IC_SERVICE_LIST_NUM < id) { + return; + } + + pValue = (void *)&g_dataIcService + IcServiceList[id].offset; + memcpy(pValue, (void *)&value, IcServiceList[id].size); +} + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..a2443d1 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,35 @@ + +# Define project Targets +add_library(${TARGET_NAME} SHARED + ipc_client.c + ipc_server.c + ipc_internal.c + ipc_usage_info_table.c +) + +# Include directories +target_include_directories(${TARGET_NAME} PRIVATE + ./ + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> + $<INSTALL_INTERFACE:/include> +) + +# find thread library +find_package(Threads REQUIRED) +target_link_libraries(${TARGET_NAME} INTERFACE Threads::Threads) +#target_link_libraries(${TARGET_NAME} INTERFACE ${SERVER_API_NAME}) + +set_target_properties(${TARGET_NAME} + PROPERTIES + VERSION ${IPC_LIBRARY_VERSION} + SOVERSION 1 +) + +# make install +# Generate config file +install( + TARGETS ${TARGET_NAME} + LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} +# PUBLIC_HEADER DESTINATION include +) +install(FILES ../include/ipc.h ../include/ipc_protocol.h DESTINATION include) diff --git a/src/ipc_client.c b/src/ipc_client.c new file mode 100644 index 0000000..2bfd576 --- /dev/null +++ b/src/ipc_client.c @@ -0,0 +1,582 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> +#include <pthread.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/epoll.h> +#include <errno.h> + +#include <ipc.h> +#include "ipc_internal.h" + +#define IPC_CLIENT_USAGE_MAX_NUM (4) +#define IPC_CLIENT_EPOLL_WAIT_NUM (IPC_CLIENT_USAGE_MAX_NUM + 1) +#define IPC_CLIENT_CONNECT_CHECK_TIME (500) // msec + +// == Internal global values == +static bool g_initedFlag = false; +static pthread_t g_clientThread; +static bool g_threadRunning = false; +static int g_threadCtlPipeFd[2] = {-1, -1}; +static int g_epollFd = -1; + +typedef struct { + IPC_USAGE_TYPE_E usage; + int serverFd; + void *pDataPool; + int poolSize; + IPC_CHANGE_NOTIFY_CB changeNotifyCb; +} IPC_CLIENT_INFO_S; +static IPC_CLIENT_INFO_S g_clientInfo[IPC_CLIENT_USAGE_MAX_NUM]; + +static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; + +// == Prototype declaration +static void *ipcClientThread(void *arg); +static int ipcClientInit(void); +static int ipcClientDeinit(void); +static void ipcClientInfoClear(int index); +static int ipcGetClientInfoIndex(IPC_USAGE_TYPE_E usageType); +static int ipcClientCreateSocket(IPC_USAGE_TYPE_E usageType); +static void ipcCloseConnectFromServer(int eventFd); +static void ipcReceiveDataFromServer(int eventFd, int *pIndex, void *pLocalDataPool); +static void ipcCheckChangeAndCallback(int index, void *pLocalDataPool); +static void ipcWriteToDataPool(int index, void *pLocalDataPool); +static int ipcAddClient(IPC_USAGE_TYPE_E usageType); +static int ipcRemoveClient(IPC_USAGE_TYPE_E usageType); +static int ipcCountClient(void); + +// == Thread function == +static void *ipcClientThread(void *arg) +{ + int fdNum; + struct epoll_event epEvents[IPC_CLIENT_USAGE_MAX_NUM]; + int i; + int index; + IPC_ALL_USAGE_DATA_POOL_U localDataPool; + char dummy; + int rc; + + while(g_threadRunning != false) { + fdNum = epoll_wait(g_epollFd, epEvents, IPC_CLIENT_USAGE_MAX_NUM, -1); + if (g_threadRunning == false) { + break; + } + + pthread_mutex_lock(&g_mutex); + for (i = 0; i < fdNum; i++) { + if (epEvents[i].data.fd == g_threadCtlPipeFd[0]) { + // dummy notify from API function. + rc = read(g_threadCtlPipeFd[0], &dummy, 1); + if (rc < 0) { + printf("[##ERROR##] %s:%s:%d (%s) is false. (%s=%d)\n", __FILE__, __func__, __LINE__, "rc >= 0", "rc", (int)rc); + continue; + } + } + else { + if (epEvents[i].events & EPOLLRDHUP) { + ipcCloseConnectFromServer(epEvents[i].data.fd); + } + else if (epEvents[i].events & EPOLLIN) { + ipcReceiveDataFromServer(epEvents[i].data.fd, &index, (void *)&localDataPool); + if (index >= 0) { + ipcCheckChangeAndCallback(index, &localDataPool); + ipcWriteToDataPool(index, &localDataPool); + } + } + } + } + pthread_mutex_unlock(&g_mutex); + } + + pthread_exit(NULL); + return NULL; +} + +// == Internal function == +static int ipcClientInit(void) +{ + int ret = -1; + int rc; + int i; + struct epoll_event epollEv; + + if (g_initedFlag == false) { + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + ipcClientInfoClear(i); + } + g_threadRunning = false; + rc = pipe(g_threadCtlPipeFd); + IPC_E_CHECK(rc == 0, rc, end); + + g_epollFd = epoll_create(IPC_CLIENT_EPOLL_WAIT_NUM); + IPC_E_CHECK(g_epollFd >= 0, g_epollFd, end); + + epollEv.events = EPOLLIN; + epollEv.data.fd = g_threadCtlPipeFd[0]; + epoll_ctl(g_epollFd, EPOLL_CTL_ADD, epollEv.data.fd, &epollEv); + + g_initedFlag = true; + } + + ret = 0; + +end: + if (ret == -1) { + for (i = 0; i < 2; i++) { + if (g_threadCtlPipeFd[i] >= 0) { + close(g_threadCtlPipeFd[i]); + g_threadCtlPipeFd[i] = -1; + } + } + } + return ret; +} + +static int ipcClientDeinit(void) +{ + int i; + + if (g_initedFlag == true) { + if (g_threadRunning == true) { + g_threadRunning = false; + pthread_cancel(g_clientThread); + pthread_join(g_clientThread, NULL); + } + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + ipcClientInfoClear(i); + } + for (i = 0; i < 2; i++) { + if (g_threadCtlPipeFd[i] >= 0) { + close(g_threadCtlPipeFd[i]); + g_threadCtlPipeFd[i] = -1; + } + } + close(g_epollFd); + g_epollFd = -1; + + g_initedFlag = false; + } + + return 0; +} + +static void ipcClientInfoClear(int index) +{ + IPC_E_CHECK(0 <= index && index < IPC_CLIENT_USAGE_MAX_NUM, index, end); + + g_clientInfo[index].usage = IPC_USAGE_TYPE_MAX; + g_clientInfo[index].serverFd = -1; + g_clientInfo[index].pDataPool = NULL; + g_clientInfo[index].poolSize = 0; + g_clientInfo[index].changeNotifyCb = NULL; + +end: + return; +} + +static int ipcGetClientInfoIndex(IPC_USAGE_TYPE_E usageType) +{ + int index = -1; + int i; + + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + if (g_clientInfo[i].usage == usageType) { + index = i; + break; + } + } + + return index; +} + +static int ipcClientCreateSocket(IPC_USAGE_TYPE_E usageType) +{ + int rc; + int fd = -1; + struct sockaddr_un unixAddr; + int len; + char domainName[IPC_DOMAIN_PATH_MAX] = ""; + int domainLen = IPC_DOMAIN_PATH_MAX; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, err); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + IPC_E_CHECK(fd >= 0, fd, err); + + rc = ipcCreateDomainName(usageType, domainName, &domainLen); + IPC_E_CHECK(rc == 0, rc, err); + + rc = ipcCreateUnixDomainAddr(domainName, &unixAddr, &len); + IPC_E_CHECK(rc == 0, rc, err); + + rc = connect(fd, (struct sockaddr *)&unixAddr, len); + IPC_E_CHECK(rc == 0, rc, err); + + return fd; + +err: + if (fd >= 0) { + shutdown(fd, SHUT_RDWR); + close(fd); + } + return -1; +} + +static void ipcCloseConnectFromServer(int eventFd) +{ + int index; + IPC_CLIENT_INFO_S *pInfo; + struct epoll_event epollEv; + + for (index = 0; index < IPC_CLIENT_USAGE_MAX_NUM; index++) { + pInfo = &(g_clientInfo[index]); + if (pInfo->usage == IPC_USAGE_TYPE_MAX) { + continue; + } + + if (pInfo->serverFd == eventFd) { + shutdown(pInfo->serverFd, SHUT_RDWR); + close(pInfo->serverFd); + if (pInfo->pDataPool != NULL) { + free(pInfo->pDataPool); + pInfo->pDataPool = NULL; + } + memset(&epollEv, 0, sizeof(epollEv)); + epoll_ctl(g_epollFd, EPOLL_CTL_DEL, pInfo->serverFd, &epollEv); + ipcClientInfoClear(index); + } + } +} + +static void ipcReceiveDataFromServer(int eventFd, int *pIndex, void *pLocalDataPool) +{ + int rc; + int i; + IPC_CLIENT_INFO_S *pInfo = NULL; + + *pIndex = -1; + + // check fd + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + if (g_clientInfo[i].serverFd == eventFd) { + pInfo = &(g_clientInfo[i]); + break; + } + } + + IPC_E_CHECK(pInfo != NULL, eventFd, end); + IPC_E_CHECK(pInfo->pDataPool != NULL, i, end); + IPC_E_CHECK(pInfo->poolSize > 0, i, end); + + // receive from server and write to data pool. + rc = recv(eventFd, pLocalDataPool, pInfo->poolSize, 0); + if ((rc == 0) + || (rc >= 0 && errno == ECONNREFUSED)) { + ipcCloseConnectFromServer(eventFd); + } + IPC_E_CHECK(rc >= 0, errno, end); + + *pIndex = i; + +end: + return; +} + +static void ipcCheckChangeAndCallback(int index, void *pLocalDataPool) +{ + IPC_CLIENT_INFO_S *pInfo = NULL; + IPC_CHECK_CHANGE_INFO_TABLE_S *pChangeInfoTbl = NULL; + IPC_CHECK_CHANGE_INFO_S *pChangeInfo = NULL; + int i; + void *pMemCmpData, *pMemCmpLocal; + + pInfo = &(g_clientInfo[index]); + if (pInfo->changeNotifyCb == NULL) { + goto end; + } + + pChangeInfoTbl = &(g_ipcCheckChangeInfoTbl[pInfo->usage]); + + // Check for changes in the data pool. + for (i = 0; i < pChangeInfoTbl->num; i++) { + pChangeInfo = &(pChangeInfoTbl->pInfo[i]); + pMemCmpData = pInfo->pDataPool + pChangeInfo->offset; + pMemCmpLocal = pLocalDataPool + pChangeInfo->offset; + + if (0 != memcmp(pMemCmpData, pMemCmpLocal, pChangeInfo->size)) { + pInfo->changeNotifyCb(pMemCmpLocal, pChangeInfo->size, pChangeInfo->kind); + } + } + +end: + return; +} + +static void ipcWriteToDataPool(int index, void *pLocalDataPool) +{ + IPC_CLIENT_INFO_S *pInfo = NULL; + + pInfo = &(g_clientInfo[index]); + + memcpy(pInfo->pDataPool, pLocalDataPool, pInfo->poolSize); + + return; +} + +static int ipcAddClient(IPC_USAGE_TYPE_E usageType) +{ + int ret = -1; + int index = -1; + int i; + IPC_CLIENT_INFO_S *pInfo; + int fd; + void *pDataPool = NULL; + int dataPoolSize; + struct epoll_event epollEv; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + // check if the usageType is already used + index = ipcGetClientInfoIndex(usageType); + IPC_E_CHECK(index == -1, usageType, end); + + // find empty index + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + pInfo = &(g_clientInfo[i]); + if (pInfo->usage == IPC_USAGE_TYPE_MAX) { + index = i; + break; + } + } + + IPC_E_CHECK(index >= 0, i, end); + pInfo = &(g_clientInfo[index]); + + dataPoolSize = g_ipcDomainInfoList[usageType].size; + pDataPool = malloc(dataPoolSize); + IPC_E_CHECK(pDataPool != NULL, 0, end); + memset(pDataPool, 0, dataPoolSize); + + fd = ipcClientCreateSocket(usageType); + + IPC_E_CHECK(fd >= 0, usageType, end); + + pInfo->usage = usageType; + pInfo->serverFd = fd; + pInfo->pDataPool = pDataPool; + pInfo->poolSize = dataPoolSize; + + memset(&epollEv, 0, sizeof(epollEv)); + epollEv.events = EPOLLIN | EPOLLRDHUP; + epollEv.data.fd = fd; + epoll_ctl(g_epollFd, EPOLL_CTL_ADD, epollEv.data.fd, &epollEv); + + ret = 0; +end: + if (ret != 0 && pDataPool != NULL) { + free(pDataPool); + } + return ret; +} + +static int ipcRemoveClient(IPC_USAGE_TYPE_E usageType) +{ + int ret = -1; + int index; + IPC_CLIENT_INFO_S *pInfo; + struct epoll_event epollEv; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + index = ipcGetClientInfoIndex(usageType); + IPC_E_CHECK(index >= 0, usageType, end); + + pInfo = &(g_clientInfo[index]); + + shutdown(pInfo->serverFd, SHUT_RDWR); + close(pInfo->serverFd); + if (pInfo->pDataPool != NULL) { + free(pInfo->pDataPool); + pInfo->pDataPool = NULL; + } + + memset(&epollEv, 0, sizeof(epollEv)); + epoll_ctl(g_epollFd, EPOLL_CTL_DEL, pInfo->serverFd, &epollEv); + + ipcClientInfoClear(index); + + ret = 0; + +end: + return ret; +} + +static int ipcCountClient(void) +{ + int count = 0; + int i; + + for (i = 0; i < IPC_CLIENT_USAGE_MAX_NUM; i++) { + if (g_clientInfo[i].usage != IPC_USAGE_TYPE_MAX) { + count++; + } + } + + return count; +} + +// == API function for client == +IPC_RET_E ipcClientStart(IPC_USAGE_TYPE_E usageType) +{ + IPC_RET_E ret; + int rc; + char dummy = 's'; + int index; + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + ret = IPC_ERR_OTHER; + rc = ipcClientInit(); + IPC_E_CHECK(rc == 0, rc, end); + + pthread_mutex_lock(&g_mutex); + rc = ipcAddClient(usageType); + pthread_mutex_unlock(&g_mutex); + + ret = IPC_ERR_NO_RESOURCE; + IPC_E_CHECK(rc == 0, rc, end); + + if (g_threadRunning == false) { + rc = pthread_create(&g_clientThread, NULL, ipcClientThread, NULL); + IPC_E_CHECK(rc == 0, rc, end); + + g_threadRunning = true; + } + + rc = write(g_threadCtlPipeFd[1], &dummy, 1); // for wakeup epoll_wait + IPC_E_CHECK(rc >= 0, rc, end); + + // Check to if the connection is rejected. + usleep(IPC_CLIENT_EPOLL_WAIT_NUM * 1000); + pthread_mutex_lock(&g_mutex); + index = ipcGetClientInfoIndex(usageType); + pthread_mutex_unlock(&g_mutex); + ret = IPC_ERR_NO_RESOURCE; + IPC_E_CHECK(index >= 0, usageType, end); + + ret = IPC_RET_OK; + +end: + if (ret != IPC_RET_OK && g_threadRunning == true) { + pthread_mutex_lock(&g_mutex); + if (ipcCountClient() == 0) { + pthread_mutex_unlock(&g_mutex); + ipcClientDeinit(); + } + else { + pthread_mutex_unlock(&g_mutex); + } + } + return ret; +} + +IPC_RET_E ipcReadDataPool(IPC_USAGE_TYPE_E usageType, void* pData, signed int* pSize) +{ + IPC_RET_E ret; + int index = -1; + IPC_CLIENT_INFO_S *pInfo; + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + IPC_E_CHECK(pData != NULL, 0, end); + IPC_E_CHECK(pSize != NULL, 0, end); + + pthread_mutex_lock(&g_mutex); + index = ipcGetClientInfoIndex(usageType); + + ret = IPC_ERR_SEQUENCE; + IPC_E_CHECK(index >= 0, usageType, end_with_unlock); + pInfo = &(g_clientInfo[index]); + IPC_E_CHECK(pInfo->pDataPool != NULL, usageType, end_with_unlock); + IPC_E_CHECK(*pSize >= pInfo->poolSize, *pSize, end_with_unlock); + + memcpy(pData, pInfo->pDataPool, pInfo->poolSize); + + ret = IPC_RET_OK; + +end_with_unlock: + pthread_mutex_unlock(&g_mutex); + +end: + return ret; +} + +IPC_RET_E ipcRegisterCallback(IPC_USAGE_TYPE_E usageType, IPC_CHANGE_NOTIFY_CB changeNotifyCb) +{ + IPC_RET_E ret; + int index = -1; + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + IPC_E_CHECK(changeNotifyCb != NULL, 0, end); + + pthread_mutex_lock(&g_mutex); + index = ipcGetClientInfoIndex(usageType); + + ret = IPC_ERR_SEQUENCE; + IPC_E_CHECK(index >= 0, usageType, end_with_unlock); + + g_clientInfo[index].changeNotifyCb = changeNotifyCb; + + ret = IPC_RET_OK; + +end_with_unlock: + pthread_mutex_unlock(&g_mutex); + +end: + return ret; +} + +IPC_RET_E ipcClientStop(IPC_USAGE_TYPE_E usageType) +{ + IPC_RET_E ret; + int rc; + char dummy = 'e'; + + ret = IPC_ERR_SEQUENCE; + IPC_E_CHECK(g_initedFlag != false, g_initedFlag, end); + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + pthread_mutex_lock(&g_mutex); + rc = ipcRemoveClient(usageType); + ret = IPC_ERR_PARAM; + IPC_E_CHECK(rc == 0, rc, end_with_unlock); + + rc = write(g_threadCtlPipeFd[1], &dummy, 1); // for wakeup epoll_wait + IPC_E_CHECK(rc >= 0, rc, end_with_unlock); + + if (ipcCountClient() == 0) { + pthread_mutex_unlock(&g_mutex); + ipcClientDeinit(); + } + else { + pthread_mutex_unlock(&g_mutex); + } + + ret = IPC_RET_OK; + +end: + return ret; + +end_with_unlock: + pthread_mutex_unlock(&g_mutex); + return ret; +} + diff --git a/src/ipc_internal.c b/src/ipc_internal.c new file mode 100644 index 0000000..c42e2a4 --- /dev/null +++ b/src/ipc_internal.c @@ -0,0 +1,57 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/socket.h> + +#include <ipc.h> +#include "ipc_internal.h" + +int ipcCreateDomainName(IPC_USAGE_TYPE_E usageType, char *pOutName, int *pSize) +{ + int ret = -1; + int len; + char *ipcDomainPath; // from getenv + char *domainName; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + IPC_E_CHECK(pSize != NULL, 0, end); + IPC_E_CHECK(pOutName != NULL, 0, end); + + domainName = g_ipcDomainInfoList[usageType].domainName; + len = strlen(domainName); + ipcDomainPath = getenv(IPC_ENV_DOMAIN_SOCKET_PATH); + if (ipcDomainPath != NULL) { + len += strlen(ipcDomainPath); + } + + IPC_E_CHECK(*pSize > len, len, end); + + strcpy(pOutName, ""); + if (ipcDomainPath != NULL) { + sprintf(pOutName, "%s/", ipcDomainPath); + } + strcat(pOutName, domainName); + + *pSize = strlen(pOutName) + 1; + + ret = 0; +end: + return ret; +} + +int ipcCreateUnixDomainAddr(const char *domainName, struct sockaddr_un *pOutUnixAddr, int *pOutLen) +{ + int ret = -1; + + IPC_E_CHECK(pOutLen != NULL, 0, end); + IPC_E_CHECK(pOutUnixAddr != NULL, 0, end); + + pOutUnixAddr->sun_family = AF_UNIX; + strcpy(pOutUnixAddr->sun_path, domainName); + *pOutLen = sizeof(pOutUnixAddr->sun_family)+strlen(pOutUnixAddr->sun_path); + + ret = 0; +end: + return ret; +} + diff --git a/src/ipc_internal.h b/src/ipc_internal.h new file mode 100644 index 0000000..972a58b --- /dev/null +++ b/src/ipc_internal.h @@ -0,0 +1,48 @@ +#ifndef IPC_INTERNAL_H +#define IPC_INTERNAL_H + +#include <ipc_protocol.h> +#include <stddef.h> +#include <sys/un.h> + +#define IPC_E_CHECK(condition, value, label) \ + do { \ + if (!(condition)) { \ + printf("[##ERROR##] %s:%s:%d (%s) is false. (%s=%d)\n", __FILE__, __func__, __LINE__, #condition, #value, (int)value); \ + goto label; \ + } \ + } while(0) + +#define CHECK_VALID_USAGE(usageType) \ + (0 <= usageType && usageType < IPC_USAGE_TYPE_MAX) + +#define IPC_DOMAIN_PATH_MAX (108) // defined in sun_path[] of sys/un.h + +typedef struct { + signed long size; + char *domainName; +} IPC_DOMAIN_INFO_S; + +typedef struct { + int kind; + int offset; + int size; +} IPC_CHECK_CHANGE_INFO_S; + +typedef struct { + IPC_CHECK_CHANGE_INFO_S* pInfo; + int num; +} IPC_CHECK_CHANGE_INFO_TABLE_S; + +// the union to know the maximum size of the data pool. +typedef union { + IPC_DATA_IC_SERVICE_S icService; +} IPC_ALL_USAGE_DATA_POOL_U; + +extern IPC_DOMAIN_INFO_S g_ipcDomainInfoList[]; +extern IPC_CHECK_CHANGE_INFO_TABLE_S g_ipcCheckChangeInfoTbl[]; + +int ipcCreateDomainName(IPC_USAGE_TYPE_E usageType, char *pOutName, int *pSize); +int ipcCreateUnixDomainAddr(const char *domainName, struct sockaddr_un *pOutUnixAddr, int *pOutLen); + +#endif // IPC_INTERNAL_H diff --git a/src/ipc_server.c b/src/ipc_server.c new file mode 100644 index 0000000..809a644 --- /dev/null +++ b/src/ipc_server.c @@ -0,0 +1,543 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/epoll.h> +#include <pthread.h> + +#include <ipc.h> + +#include "ipc_internal.h" + +#define IPC_SERVER_USAGE_MAX_NUM (1) +#define IPC_LISTEN_CLIENT_NUM (4) +#define IPC_SERVER_EPOLL_WAIT_NUM (IPC_SERVER_USAGE_MAX_NUM * IPC_LISTEN_CLIENT_NUM + 1) + +// == Internal global values == +static bool g_initedFlag = false; +static pthread_t g_serverThread; +static bool g_threadRunning = false; +static int g_threadCtlPipeFd[2] = {-1, -1}; +static int g_epollFd = -1; + +typedef struct { + IPC_USAGE_TYPE_E usage; + int fd; + int clientFd[IPC_LISTEN_CLIENT_NUM]; +} IPC_SERVER_INFO_S; +static IPC_SERVER_INFO_S g_serverInfo[IPC_SERVER_USAGE_MAX_NUM]; + +static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; + +// == Prototype declaration +static void *ipcServerThread(void *arg); +static int ipcServerInit(void); +static int ipcServerDeinit(void); +static void ipcServerInfoClear(int index); +static int ipcGetServerInfoIndex(IPC_USAGE_TYPE_E usageType); +static int ipcServerCreateSocket(IPC_USAGE_TYPE_E usageType); +static void ipcAcceptClient(int eventFd); +static void ipcCloseClient(int eventFd); +static int ipcAddServer(IPC_USAGE_TYPE_E usageType); +static int ipcAddConnectClient(int index, int clientFd); +static int ipcRemoveServer(IPC_USAGE_TYPE_E usageType); +static int ipcCountServer(void); + +// == Thread function == +static void *ipcServerThread(void *arg) +{ + int fdNum; + struct epoll_event epEvents[IPC_SERVER_EPOLL_WAIT_NUM]; + int i; + char dummy; + int rc; + + while(g_threadRunning != false) { + fdNum = epoll_wait(g_epollFd, epEvents, IPC_SERVER_USAGE_MAX_NUM, -1); + if (g_threadRunning == false) { + break; + } + + pthread_mutex_lock(&g_mutex); + for (i = 0; i < fdNum; i++) { + if (epEvents[i].data.fd == g_threadCtlPipeFd[0]) { + // dummy notify from API function. + rc = read(g_threadCtlPipeFd[0], &dummy, 1); + if (rc < 0) { + printf("[##ERROR##] %s:%s:%d (%s) is false. (%s=%d)\n", __FILE__, __func__, __LINE__, "rc < 0", "rc", (int)rc); + continue; + } + } + else { + if (epEvents[i].events & EPOLLRDHUP) { + ipcCloseClient(epEvents[i].data.fd); + } + else if (epEvents[i].events & EPOLLIN) { + ipcAcceptClient(epEvents[i].data.fd); + } + } + } + pthread_mutex_unlock(&g_mutex); + } + + pthread_exit(NULL); + return NULL; +} + +// == Internal function == +static int ipcServerInit(void) +{ + int ret = -1; + int rc; + int i; + struct epoll_event epollEv; + + if (g_initedFlag == false) { + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + ipcServerInfoClear(i); + } + g_threadRunning = false; + rc = pipe(g_threadCtlPipeFd); + IPC_E_CHECK(rc == 0, rc, end); + + g_epollFd = epoll_create(IPC_SERVER_EPOLL_WAIT_NUM); + IPC_E_CHECK(g_epollFd >= 0, g_epollFd, end); + + epollEv.events = EPOLLIN; + epollEv.data.fd = g_threadCtlPipeFd[0]; + epoll_ctl(g_epollFd, EPOLL_CTL_ADD, epollEv.data.fd, &epollEv); + + g_initedFlag = true; + } + + ret = 0; + +end: + if (ret == -1) { + for (i = 0; i < 2; i++) { + if (g_threadCtlPipeFd[i] >= 0) { + close(g_threadCtlPipeFd[i]); + g_threadCtlPipeFd[i] = -1; + } + } + } + return ret; +} + +static int ipcServerDeinit(void) +{ + int i; + + if (g_initedFlag == true) { + if (g_threadRunning == true) { + g_threadRunning = false; + pthread_cancel(g_serverThread); + pthread_join(g_serverThread, NULL); + } + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + ipcServerInfoClear(i); + } + for (i = 0; i < 2; i++) { + if (g_threadCtlPipeFd[i] >= 0) { + close(g_threadCtlPipeFd[i]); + g_threadCtlPipeFd[i] = -1; + } + } + close(g_epollFd); + g_epollFd = -1; + + g_initedFlag = false; + } + + return 0; +} + +static void ipcServerInfoClear(int index) +{ + int i; + + IPC_E_CHECK(0 <= index && index < IPC_SERVER_USAGE_MAX_NUM, index, end); + + g_serverInfo[index].usage = IPC_USAGE_TYPE_MAX; + g_serverInfo[index].fd = -1; + for (i = 0; i < IPC_LISTEN_CLIENT_NUM; i++) { + g_serverInfo[index].clientFd[i] = -1; + } + +end: + return; +} + +static int ipcGetServerInfoIndex(IPC_USAGE_TYPE_E usageType) +{ + int index = -1; + int i; + + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + if (g_serverInfo[i].usage == usageType) { + index = i; + break; + } + } + + return index; +} + +static int ipcServerCreateSocket(IPC_USAGE_TYPE_E usageType) +{ + int rc; + int fd = -1; + struct sockaddr_un unixAddr; + int len; + char domainName[IPC_DOMAIN_PATH_MAX] = ""; + int domainLen = IPC_DOMAIN_PATH_MAX; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, err); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + IPC_E_CHECK(fd >= 0, fd, err); + + rc = ipcCreateDomainName(usageType, domainName, &domainLen); + IPC_E_CHECK(rc == 0, rc, err); + + rc = ipcCreateUnixDomainAddr(domainName, &unixAddr, &len); + IPC_E_CHECK(rc == 0, rc, err); + + rc = bind(fd, (struct sockaddr *)&unixAddr, len); + IPC_E_CHECK(rc == 0, rc, err); + + rc = listen(fd, IPC_LISTEN_CLIENT_NUM); + IPC_E_CHECK(rc == 0, rc, err); + + return fd; + +err: + if (fd >= 0) { + shutdown(fd, SHUT_RDWR); + close(fd); + unlink(domainName); + } + return -1; +} + +static void ipcAcceptClient(int eventFd) +{ + int rc; + int clientFd; + char domainName[IPC_DOMAIN_PATH_MAX] = ""; + int domainLen = IPC_DOMAIN_PATH_MAX; + struct sockaddr_un unixAddr; + int len; + IPC_SERVER_INFO_S *pInfo; + int index = -1; + int i; + struct epoll_event epollEv; + + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + if (eventFd == g_serverInfo[i].fd) { + index = i; + break; + } + } + IPC_E_CHECK(0 <= index && index < IPC_SERVER_USAGE_MAX_NUM, eventFd, end); + pInfo = &(g_serverInfo[index]); + + rc = ipcCreateDomainName(pInfo->usage, domainName, &domainLen); + IPC_E_CHECK(rc == 0, rc, end); + + rc = ipcCreateUnixDomainAddr(domainName, &unixAddr, &len); + IPC_E_CHECK(rc == 0, rc, end); + + // check connect client + clientFd = accept(pInfo->fd, (struct sockaddr*)&unixAddr, (socklen_t *)&len); + if (clientFd >= 0) { + rc = ipcAddConnectClient(index, clientFd); + if (rc == 0) { + memset(&epollEv, 0, sizeof(epollEv)); + epollEv.events = EPOLLRDHUP; + epollEv.data.fd = clientFd; + epoll_ctl(g_epollFd, EPOLL_CTL_ADD, clientFd, &epollEv); + } + else { // The number of connections is already limited. + shutdown(clientFd, SHUT_RDWR); + close(clientFd); + } + } + +end: + return; +} + +static void ipcCloseClient(int eventFd) +{ + int i; + int index; + IPC_SERVER_INFO_S *pInfo; + struct epoll_event epollEv; + + for (index = 0; index < IPC_SERVER_USAGE_MAX_NUM; index++) { + pInfo = &(g_serverInfo[index]); + if (pInfo->usage == IPC_USAGE_TYPE_MAX) { + continue; + } + + for (i = 0; i < IPC_LISTEN_CLIENT_NUM; i++) { + if (pInfo->clientFd[i] == eventFd) { + pInfo->clientFd[i] = -1; + + shutdown(eventFd, SHUT_RDWR); + close(eventFd); + + memset(&epollEv, 0, sizeof(epollEv)); + epoll_ctl(g_epollFd, EPOLL_CTL_DEL, eventFd, &epollEv); + + break; + } + } + } +} + +static int ipcAddServer(IPC_USAGE_TYPE_E usageType) +{ + int ret = -1; + int index = -1; + int i; + int fd; + IPC_SERVER_INFO_S *pInfo; + struct epoll_event epollEv; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + // check if the usageType is used + index = ipcGetServerInfoIndex(usageType); + IPC_E_CHECK(index == -1, usageType, end); + + // find empty index + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + pInfo = &(g_serverInfo[i]); + if (pInfo->usage == IPC_USAGE_TYPE_MAX) { + pInfo->usage = usageType; + index = i; + break; + } + } + IPC_E_CHECK(index >= 0, i, end); + pInfo = &(g_serverInfo[index]); + + fd = ipcServerCreateSocket(usageType); + + IPC_E_CHECK(fd >= 0, usageType, end); + + pInfo->fd = fd; + memset(&epollEv, 0, sizeof(epollEv)); + epollEv.events = EPOLLIN; + epollEv.data.fd = fd; + epoll_ctl(g_epollFd, EPOLL_CTL_ADD, epollEv.data.fd, &epollEv); + + ret = 0; + +end: + if (ret == -1 && index >= 0) { + ipcServerInfoClear(index); + } + return ret; +} + +static int ipcAddConnectClient(int index, int clientFd) +{ + int ret = -1; + int i; + IPC_SERVER_INFO_S *pInfo; + + if (clientFd < 0) { + // do nothing + goto end; + } + + pInfo = &(g_serverInfo[index]); + + // find empty index + for (i = 0; i < IPC_LISTEN_CLIENT_NUM; i++) { + if (pInfo->clientFd[i] == -1) { + pInfo->clientFd[i] = clientFd; + ret = 0; + break; + } + } + +end: + return ret; +} + +static int ipcRemoveServer(IPC_USAGE_TYPE_E usageType) +{ + int ret = -1; + int rc; + int index; + char domainName[IPC_DOMAIN_PATH_MAX] = ""; + int domainLen = IPC_DOMAIN_PATH_MAX; + IPC_SERVER_INFO_S *pInfo; + struct epoll_event epollEv; + + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + index = ipcGetServerInfoIndex(usageType); + IPC_E_CHECK(index >= 0, usageType, end); + + pInfo = &(g_serverInfo[index]); + + rc = ipcCreateDomainName(pInfo->usage, domainName, &domainLen); + IPC_E_CHECK(rc == 0, rc, end); + + shutdown(pInfo->fd, SHUT_RDWR); + close(pInfo->fd); + unlink(domainName); + + memset(&epollEv, 0, sizeof(epollEv)); + epoll_ctl(g_epollFd, EPOLL_CTL_DEL, pInfo->fd, &epollEv); + + ipcServerInfoClear(index); + + ret = 0; + +end: + return ret; +} + +static int ipcCountServer(void) +{ + int count = 0; + int i; + + for (i = 0; i < IPC_SERVER_USAGE_MAX_NUM; i++) { + if (g_serverInfo[i].usage != IPC_USAGE_TYPE_MAX) { + count++; + } + } + + return count; +} + +// == API function for server == +IPC_RET_E ipcServerStart(IPC_USAGE_TYPE_E usageType) +{ + IPC_RET_E ret; + int rc; + char dummy = 's'; + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + ret = IPC_ERR_OTHER; + rc = ipcServerInit(); + IPC_E_CHECK(rc == 0, rc, end); + + pthread_mutex_lock(&g_mutex); + rc = ipcAddServer(usageType); + pthread_mutex_unlock(&g_mutex); + + ret = IPC_ERR_NO_RESOURCE; + IPC_E_CHECK(rc == 0, rc, end); + + if (g_threadRunning == false) { + rc = pthread_create(&g_serverThread, NULL, ipcServerThread, NULL); + IPC_E_CHECK(rc == 0, rc, end); + + g_threadRunning = true; + } + + rc = write(g_threadCtlPipeFd[1], &dummy, 1); // for wakeup epoll_wait + IPC_E_CHECK(rc >= 0, rc, end); + ret = IPC_RET_OK; + +end: + return ret; +} + +IPC_RET_E ipcSendMessage(IPC_USAGE_TYPE_E usageType, const void* pData, signed int size) +{ + IPC_RET_E ret; + int rc; + int index; + IPC_SERVER_INFO_S *pInfo = NULL; + int i; + int clientFd; + + ret = IPC_ERR_SEQUENCE; + IPC_E_CHECK(g_initedFlag != false, g_initedFlag, end); + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + IPC_E_CHECK(pData != NULL, 0, end); + IPC_E_CHECK(g_ipcDomainInfoList[usageType].size >= size, size, end); + + pthread_mutex_lock(&g_mutex); + index = ipcGetServerInfoIndex(usageType); + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(index >= 0, usageType, end_with_unlock); + pInfo = &(g_serverInfo[index]); + + IPC_E_CHECK(pInfo->fd >= 0, usageType, end_with_unlock); + + // Send to All Client + for (i = 0; i < IPC_LISTEN_CLIENT_NUM; i++) { + clientFd = pInfo->clientFd[i]; + if (clientFd == -1) { + continue; + } + rc = write(clientFd, pData, size); + IPC_E_CHECK(rc >= 0, rc, end_with_unlock); + } + + ret = IPC_RET_OK; +end_with_unlock: + pthread_mutex_unlock(&g_mutex); + +end: + return ret; +} + +IPC_RET_E ipcServerStop(IPC_USAGE_TYPE_E usageType) +{ + IPC_RET_E ret; + int rc; + char dummy = 'e'; + + ret = IPC_ERR_SEQUENCE; + IPC_E_CHECK(g_initedFlag != false, g_initedFlag, end); + + ret = IPC_ERR_PARAM; + IPC_E_CHECK(CHECK_VALID_USAGE(usageType), usageType, end); + + pthread_mutex_lock(&g_mutex); + rc = ipcRemoveServer(usageType); + ret = IPC_ERR_PARAM; + IPC_E_CHECK(rc == 0, rc, end_with_unlock); + + rc = write(g_threadCtlPipeFd[1], &dummy, 1); // for wakeup epoll_wait + IPC_E_CHECK(rc >= 0, rc, end_with_unlock); + + if (ipcCountServer() == 0) { + pthread_mutex_unlock(&g_mutex); + ipcServerDeinit(); + } + else { + pthread_mutex_unlock(&g_mutex); + } + + ret = IPC_RET_OK; + +end: + return ret; + +end_with_unlock: + pthread_mutex_unlock(&g_mutex); + return ret; +} + diff --git a/src/ipc_usage_info_table.c b/src/ipc_usage_info_table.c new file mode 100644 index 0000000..976cc73 --- /dev/null +++ b/src/ipc_usage_info_table.c @@ -0,0 +1,66 @@ +#include <ipc.h> +#include "ipc_internal.h" + +#define DEFINE_OFFSET_SIZE(struct_name, member, kind) \ + {kind, offsetof(struct_name, member), sizeof(((struct_name *)0)->member)} + +#define DEFINE_CHANGE_INFO_TABLE(changeInfoName) \ + {changeInfoName, sizeof(changeInfoName) / sizeof(changeInfoName[0])} + +// == check change table == +// for IPC_USAGE_TYPE_IC_SERVICE +static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeIcService[] = { + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, turnR, IPC_KIND_ICS_TURN_R), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, turnL, IPC_KIND_ICS_TURN_L), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, brake, IPC_KIND_ICS_BRAKE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, seatbelt, IPC_KIND_ICS_SEATBELT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, highbeam, IPC_KIND_ICS_HIGHBEAM), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, door, IPC_KIND_ICS_DOOR), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, eps, IPC_KIND_ICS_EPS), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, srsAirbag, IPC_KIND_ICS_SRS_AIRBAG), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, abs, IPC_KIND_ICS_ABS), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, lowBattery, IPC_KIND_ICS_LOW_BATTERY), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, oilPress, IPC_KIND_ICS_OIL_PRESS), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, engine, IPC_KIND_ICS_ENGINE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, fuel, IPC_KIND_ICS_FUEL), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, immobi, IPC_KIND_ICS_IMMOBI), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, tmFail, IPC_KIND_ICS_TM_FAIL), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, espAct, IPC_KIND_ICS_ESP_ACT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, espOff, IPC_KIND_ICS_ESP_OFF), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, adaptingLighting, IPC_KIND_ICS_ADAPTING_LIGHTING), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, autoStop, IPC_KIND_ICS_AUTO_STOP), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, autoStopFail, IPC_KIND_ICS_AUTO_STOP_FAIL), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, parkingLights, IPC_KIND_ICS_PARKING_LIGHTS), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, frontFog, IPC_KIND_ICS_FRONT_FOG), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, exteriorLightFault, IPC_KIND_ICS_EXTERIOR_LIGHT_FAULT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, accFail, IPC_KIND_ICS_ACC_FAIL), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, ldwOff, IPC_KIND_ICS_LDW_OFF), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, hillDescent, IPC_KIND_ICS_HILL_DESCENT), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, autoHiBeamGreen, IPC_KIND_ICS_AUTO_HI_BEAM_GREEN), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, autoHiBeamAmber, IPC_KIND_ICS_AUTO_HI_BEAM_AMBER), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, ldwOperate, IPC_KIND_ICS_LDW_OPERATE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, generalWarn, IPC_KIND_ICS_GENERAL_WARN), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, sportsMode, IPC_KIND_ICS_SPORTS_MODE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, drivingPowerMode, IPC_KIND_ICS_DRIVING_POWER_MODE), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, hotTemp, IPC_KIND_ICS_HOT_TEMP), + DEFINE_OFFSET_SIZE(IPC_DATA_IC_SERVICE_S, lowTemp, IPC_KIND_ICS_LOW_TEMP) +}; + +// for IPC_USAGE_TYPE_FOR_TEST +static IPC_CHECK_CHANGE_INFO_S g_ipcCheckChangeForTest[] = { + DEFINE_OFFSET_SIZE(IPC_DATA_FOR_TEST_S, test, IPC_KIND_TEST_TEST) +}; + +// == usage info table == +// index of [] is IPC_USAGE_TYPE_E +IPC_DOMAIN_INFO_S g_ipcDomainInfoList[] = +{ + {sizeof(IPC_DATA_IC_SERVICE_S), "ipcIcService"}, + {sizeof(IPC_DATA_FOR_TEST_S), "ipcForTest"} +}; + +IPC_CHECK_CHANGE_INFO_TABLE_S g_ipcCheckChangeInfoTbl[] = { + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeIcService), + DEFINE_CHANGE_INFO_TABLE(g_ipcCheckChangeForTest) +}; + |