TAI deep dive (1)

Wataru Ishida
nttlabs
Published in
15 min readJun 10, 2019

こんにちは、NTTの石田です。

今回も前回前々回に引き続きTAIの解説記事です。

前回までは導入編でしたが、今回からは何回かに分けて、実際にTAIのソースコードを見ながら、より詳細にTAIの内部構造を紹介します。

まず今回は、TAIリポジトリに含まれるサンプルコードを参考に、TAIライブラリの初期化、API取得、TAI moduleの作成までを見ていきます。伝送装置に直接関係するものは一切出てきませんが、次回以降のための準備としてお付き合いください。

TAIではTAI APIを実装するライブラリのことをTAI adapter, TAI adapterを利用するアプリケーションのことをTAI adapter hostと呼びます。少々分かりづらい呼び方ですが、これはTAIがベースとしているSAIからとっています。本記事でもこの用語を使っていきます。

1. TAI adapterの初期化

それでは早速サンプルコードのmain関数から見ていきましょう。

tai_service_method_table_t g_service_table = {
.module_presence = module_event
};
int main() {
tai_status_t status;
int i;
status = tai_api_initialize(0, &g_service_table);
if ( status != TAI_STATUS_SUCCESS ) {
printf("failed to initialize TAI\n");
return 1;
}

まずadapterを初期化するためtai_api_initializeを呼びます。第1引数は現在は利用されていないフラグなので無視して大丈夫です。第2引数でtai_service_method_table_tを介して渡しているmodule_presenceコールバックは、adapterがadapter hostにmoduleの存在を知らせる際に使われます。module_presenceコールバックの関数シグネチャは

typedef void (*tai_module_presence_event_fn)(
_In_ bool present,
_In_ char * module_location);

で、第1引数はモジュールの有無を表すフラグ、第2引数はモジュールオブジェクト作成前にモジュールを識別するための文字列が渡されます。 TAIではSAI同様全てのオブジェクトにOID(Object ID)が付与されるので、モジュールの作成後はOIDを識別子として利用します。

サンプルプログラムでのmodule_presenceの定義は

void module_event(bool present, char * module_location)
{
int next_head = (g_module_location_head + 1) % (TAI_MAX_MODULES+1);
printf("module_event: module %s is %s\n",
module_location, present ? "present" : "absent");
if (present && (next_head != g_module_location_tail)) {
strcpy(g_module_locations[g_module_location_head], module_location);
g_module_location_head = next_head;
}
return;
}

となっており、presentフラグが立っていれば、グローバル変数g_module_locationsを更新するようになってます。

2. TAI APIの取得

status = tai_api_query(TAI_API_MODULE, (void**)&module_api);
if ( status != TAI_STATUS_SUCCESS ) {
printf("no api for TAI_API_MODULE\n");
return 1;
}
printf("module_api: %p\n", module_api); status = tai_api_query(TAI_API_NETWORKIF, (void**)&network_interface_api);
if ( status != TAI_STATUS_SUCCESS ) {
printf("no api for TAI_API_NETWORKIF\n");
return 1;
}
printf("network_interface_api: %p\n", network_interface_api); status = tai_api_query(TAI_API_HOSTIF, (void**)&host_interface_api);
if ( status != TAI_STATUS_SUCCESS ) {
printf("no api for TAI_API_HOSTIF\n");
return 1;
}
printf("host_interface_api: %p\n", host_interface_api);

次にtai_api_queryを使って、module, network interface, host interfaceのAPIの関数ポインタを格納する構造体( tai_module_api_t, tai_network_interface_t, tai_host_interface_t )を取得します。

// taimodule.h
typedef struct _tai_module_api_t
{
tai_create_module_fn create_module;
tai_remove_module_fn remove_module;
tai_set_module_attribute_fn set_module_attribute;
tai_set_module_attributes_fn set_module_attributes;
tai_get_module_attribute_fn get_module_attribute;
tai_get_module_attributes_fn get_module_attributes;
} tai_module_api_t;// tainetworkif.h
typedef struct _tai_network_interface_api_t
{
tai_create_network_interface_fn create_network_interface;
tai_remove_network_interface_fn remove_network_interface;
tai_set_network_interface_attribute_fn set_network_interface_attribute;
tai_set_network_interface_attributes_fn set_network_interface_attributes;
tai_get_network_interface_attribute_fn get_network_interface_attribute;
tai_get_network_interface_attributes_fn get_network_interface_attributes;
} tai_network_interface_api_t;// taihostif.h
typedef struct _tai_host_interface_api_t
{
tai_create_host_interface_fn create_host_interface;
tai_remove_host_interface_fn remove_host_interface;
tai_set_host_interface_attribute_fn set_host_interface_attribute;
tai_set_host_interface_attributes_fn set_host_interface_attributes;
tai_get_host_interface_attribute_fn get_host_interface_attribute;
tai_get_host_interface_attributes_fn get_host_interface_attributes;
} tai_host_interface_api_t;

それぞれのオブジェクトにはcreate, remove, set_attribute, get_attributeのAPIが定義されており、createはオブジェクト作成、removeはオブジェクト破棄、get_attributeはオブジェクトのアトリビュート取得, set_attributeはオブジェクトのアトリビュート設定を行います。

3. TAI moduleの作成

TAIを初期化し、APIを取得したところでTAI 次にTAI moduleを作成します。

moduleの作成は、tai_module_api_tcreate_moduleメソッドを利用します。

// taimodule.h
typedef tai_status_t (*tai_create_module_fn)(
_Out_ tai_object_id_t *module_id,
_In_ uint32_t attr_count,
_In_ const tai_attribute_t *attr_list);

第1引数に渡すtai_object_id_t*には作成が成功した場合、有効なOIDが割り当てられます。module作成以後は、このOIDを識別子にTAI APIを利用します。

第2引数, 第3引数にはアトリビュートの配列と要素数を渡します。tai_attribute_ttaitypes.hで次のように定義されており、unionのtai_attribute_value_tを利用することで様々の型のアトリビュートを扱えるようになっています。

/**
* @brief Data Type
*
* To use enum values as attribute value is tai_int32_t s32
*/
typedef union _tai_attribute_value_t
{
bool booldata;
char chardata[32];
tai_uint8_t u8;
tai_int8_t s8;
tai_uint16_t u16;
tai_int16_t s16;
tai_uint32_t u32;
tai_int32_t s32;
tai_uint64_t u64;
tai_int64_t s64;
tai_float_t flt;
tai_pointer_t ptr;
tai_object_id_t oid;
tai_object_list_t objlist;
tai_char_list_t charlist;
tai_u8_list_t u8list;
tai_s8_list_t s8list;
tai_u16_list_t u16list;
tai_s16_list_t s16list;
tai_u32_list_t u32list;
tai_s32_list_t s32list;
tai_float_list_t floatlist;
tai_u32_range_t u32range;
tai_s32_range_t s32range;
tai_object_map_list_t objmaplist;
tai_attr_value_list_t attrlist;
tai_notification_handler_t notification;
} tai_attribute_value_t;
typedef struct _tai_attribute_t
{
tai_attr_id_t id;
tai_attribute_value_t value;
} tai_attribute_t;

tai_attr_id_t idには各オブジェクトの定義ファイル(moduleならtaimodule.h)で定義されているアトリビュートのIDを識別子として代入します。

moduleのアトリビュートIDの一部を抜粋します。

typedef enum _tai_module_attr_t
{
/**
* @brief Start of attributes
*/
TAI_MODULE_ATTR_START,
/**
* @brief The location of the module
*
* Used (and required) in the tai_create_module_fn call. This allows the
* adapter to uniquely identify the module. This could be a PCI address,
* slot identifier, or other value that allows the adapter to determine
* which optical module is being initialized.
*
* @type #tai_char_list_t
* @flags MANDATORY_ON_CREATE | CREATE_ONLY
*/
TAI_MODULE_ATTR_LOCATION = TAI_MODULE_ATTR_START,
/**
* @brief The module vendor's name
* @type #tai_char_list_t
* @flags READ_ONLY
*/
TAI_MODULE_ATTR_VENDOR_NAME,
/**
* @brief The module vendor's part number
* @type #tai_char_list_t
* @flags READ_ONLY
*/
TAI_MODULE_ATTR_VENDOR_PART_NUMBER,
/**
* @brief The module vendor's serial number
* @type #tai_char_list_t
* @flags READ_ONLY
*/
TAI_MODULE_ATTR_VENDOR_SERIAL_NUMBER,

各アトリビュートIDにはアトリビュートの型(tai_attribute_value_t内の型のいずれかと一致している必要があります)とフラグがコメントとして書かれています。

フラグには TAI_ATTR_FLAGS_MANDATORY_ON_CREATE, TAI_ATTR_FLAGS_CREATE_ONLY, TAI_ATTR_FLAGS_CREATE_AND_SET, TAI_ATTR_FLAGS_READ_ONLY があり、TAI_ATTR_FLAGS_MANDATORY_ON_CREATEのフラグがついたアトリビュートはオブジェクト作成時に指定が必須のアトリビュートとなっています。

TAI moduleではTAI_MODULE_ATTR_LOCATIONTAI_ATTR_FLAGS_MANDATORY_ON_CREATEフラグがついており、これをcreate_moduleで渡さないとmoduleが作成できません。

TAI_MODULE_ATTR_LOCATIONの型はtai_char_list_tで、文字列を値にとります。この文字列にはmodule_presenceコールバックの第2引数で渡されたmodule_locationを利用します。

サンプルコードでは以下のように、TAI_MODULE_ATTR_LOCATION以外にTAI_MODULE_ATTR_MODULE_SHUTDOWN_REQUEST_NOTIFYTAI_MODULE_ATTR_MODULE_STATE_CHANGE_NOTIFYを指定してmoduleの作成を行っています。

tai_status_t create_modules() {
tai_status_t status;
tai_attribute_t attr[4];
int network_ifs;
int host_ifs;
int i;
while (g_module_location_head != g_module_location_tail) {
attr[0].id = TAI_MODULE_ATTR_LOCATION;
attr[0].value.charlist.count = strlen(g_module_locations[g_module_location_tail]);
attr[0].value.charlist.list = g_module_locations[g_module_location_tail];
attr[1].id = TAI_MODULE_ATTR_MODULE_SHUTDOWN_REQUEST_NOTIFY;
attr[1].value.ptr = module_shutdown;
attr[2].id = TAI_MODULE_ATTR_MODULE_STATE_CHANGE_NOTIFY;
attr[2].value.ptr = module_state_change;
status = module_api->create_module(&g_module_ids[g_module_location_tail], 3, attr);
if ( status != TAI_STATUS_SUCCESS ) {
return status;
}

おわりに

長くなってきたので、今回はここまでにします。次回は今回作成したTAI moduleに続いて、TAI network interface, TAI host interfaceを作成してどんなアトリビュートが定義されて、どんな風に使えるのか紹介していく予定です。

--

--