libqmiにQMIのメッセージを追加する

# APN接続以前の問題
[以前](http://qiita.com/air-gh/items/6add3c0b19e8fe9469aa#ppp%E6%8E%A5%E7%B6%9A)にL-03DをRaspberry Piに接続した時は、そもそも`LTE service`の`Status`が`available`にならなかった。この状態では、いくら`--wds-start-network`したところで、APNに接続されない。と思われる。
何が足らないのか。

# 解析
## L-03Dの動きを見る
[前回](http://qiita.com/air-gh/items/e60338f8c3ea33540c6d)、動いているUSBモデムのQMIメッセージを確認するシステムが構築できたので、これをL-03Dで使ってみる。
実際確認してみると…QMIのメッセージ、いっぱい流れすぎですよ。どれが効果あるか、わかりませんですよ。
ただ、qmicliのコマンドラインオプションを変えて色々試してみたところでは、全く効果がなかったので、libqmiで対応していないメッセージの可能性がある。APN接続のwdsの前に送るメッセージだろうから、ネットワークのnasか。
具体的には、`service = "nas"`で、`message     =`に説明が入っていないもの、を探す。そのようなメッセージは3種類:

### 0x003a
```
QMUX:
  length  = 27
  flags   = 0x80
  service = "nas"
  client  = 1
QMI:
  flags       = "indication"
  transaction = 0
  tlv_length  = 15
  message     = (0x003a)
TLV:
  type   = 0x10
  length = 12
  value  = XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
```

### 0x0026
```
QMUX:
  length  = 12
  flags   = 0x00
  service = "nas"
  client  = 1
QMI:
  flags       = "none"
  transaction = 29
  tlv_length  = 0
  message     = (0x0026)
```

### 0x0023
```
QMUX:
  length  = 16
  flags   = 0x00
  service = "nas"
  client  = 2
QMI:
  flags       = "none"
  transaction = 27
  tlv_length  = 4
  message     = (0x0023)
TLV:
  type   = 0x10
  length = 1
  value  = 01
```

## 正解は
- 0x003aは、indicationとのことで、ホストからの命令でなさそうなので、除外
- 0x0026は、responseに長い情報が入っており、get系の命令か?
- 0x0023は、これを送った後に、

```
QMUX:
  length  = 67
  flags   = 0x80
  service = "nas"
  client  = 1
QMI:
  flags       = "indication"
  transaction = 0
  tlv_length  = 55
  message     = "Serving System" (0x0024)
```

で通知される内容が、

```
TLV:
  type       = "Serving System" (0x01)
  length     = 6
  value      = 00:02:02:02:01:08
  translated = [ registration_state = 'not-registered' cs_attach_state = 'detached' ps_attach_state = 'detached' selected_network = '3gpp' radio_interfaces = '{ [0] = 'lte '}' ]
```

から、最終的に

```
TLV:
  type       = "Serving System" (0x01)
  length     = 6
  value      = 01:02:01:02:01:08
  translated = [ registration_state = 'registered' cs_attach_state = 'detached' ps_attach_state = 'attached' selected_network = '3gpp' radio_interfaces = '{ [0] = 'lte '}' ]
```

に、変化する。故に、0x0023が本命。
後で追加するのは面倒なので、0x0026も同時に追加しておくか。

# QMIメッセージの追加実装
## メッセージフォーマット
libqmiではQMIのメッセージフォーマットはjsonで記述されている。

```diff:data/qmi-service-nas.json
--- a/data/qmi-service-nas.json
+++ b/data/qmi-service-nas.json
@@ -582,6 +582,19 @@
      "output"  : [  { "common-ref" : "Operation Result" } ] },

   // *********************************************************************************
+  {  "name"    : "X0023",
+     "type"    : "Message",
+     "service" : "NAS",
+     "id"      : "0x0023",
+     "version" : "1.0",
+     "input"   : [  { "name"          : "X10",
+                      "id"            : "0x10",
+                      "mandatory"     : "no",
+                      "type"          : "TLV",
+                      "format"        : "guint8" } ],
+     "output"  : [  { "common-ref" : "Operation Result" } ] },
+
+  // *********************************************************************************
   {  "name"    : "Get Serving System",
      "type"    : "Message",
      "service" : "NAS",
@@ -1099,6 +1112,19 @@
                                         "public-format" : "gboolean" } ] } ] },

   // *********************************************************************************
+  {  "name"    : "X0026",
+     "type"    : "Message",
+     "service" : "NAS",
+     "id"      : "0x0026",
+     "version" : "1.0",
+     "output"  : [  { "common-ref" : "Operation Result" },
+                    { "name"      : "X11",
+                      "id"        : "0x11",
+                      "mandatory" : "no",
+                      "type"      : "TLV",
+                      "format"    : "string" } ] },
+
+  // *********************************************************************************
+  
   {  "name"    : "Set Technology Preference",
      "type"    : "Message",
      "service" : "NAS",
```

`input`には、requestで渡すパラメーターを、`output`には、responseで受け取るパラメーターを記載できる。
responseのid=0x02は、Operation Resultとして、あらかじめdata/qmi-common.jsonで定義されている。

# 処理の追加
## コマンドラインオプションの追加
```diff:src/qmicli/qmicli-nas.c
--- a/src/qmicli/qmicli-nas.c
+++ b/src/qmicli/qmicli-nas.c
@@ -46,6 +46,8 @@ static gboolean get_signal_strength_flag;
 static gboolean get_signal_info_flag;
 static gchar *get_tx_rx_info_str;
 static gboolean get_home_network_flag;
+static gboolean x0023_flag;
+static gboolean x0026_flag;
 static gboolean get_serving_system_flag;
 static gboolean get_system_info_flag;
 static gboolean get_technology_preference_flag;
```

これらは、コマンドラインオプションを格納しておく変数。

- `_flag`は、パラメーター無しオプションが設定された時に立つフラグ
- `_str`は、パラメーター付きオプションのパラメーター文字列

```diff:src/qmicli/qmicli-nas.c
@@ -75,6 +77,14 @@ static GOptionEntry entries[] = {
       "Get home network",
       NULL
     },
+    { "nas-0x0023", 0, 0, G_OPTION_ARG_NONE, &x0023_flag,
+      "Message ID 0x0023",
+      NULL
+    },
+    { "nas-0x0026", 0, 0, G_OPTION_ARG_NONE, &x0023_flag,
+      "Message ID 0x0026",
+      NULL
+    },
     { "nas-get-serving-system", 0, 0, G_OPTION_ARG_NONE, &get_serving_system_flag,
       "Get serving system",
       NULL
```

ここで、コマンドラインオプションの文字列, その種類, オプションを格納する変数, ヘルプ文字列を定義。

```diff:src/qmicli/qmicli-nas.c
@@ -150,6 +160,8 @@ qmicli_nas_options_enabled (void)
                  get_signal_info_flag +
                  !!get_tx_rx_info_str +
                  get_home_network_flag +
+                 x0023_flag +
+                 x0026_flag +
                  get_serving_system_flag +
                  get_system_info_flag +
                  get_technology_preference_flag +
```

ここでコマンドラインオプションが設定されたか、チェックしているらしい。
ここへの追加を忘れたら、いくらオプションを設定しても、反映されなかった。

```diff:src/qmicli/qmicli-nas.c
@@ -488,6 +500,87 @@ get_signal_strength_ready (QmiClientNas *client,
     operation_shutdown (TRUE);
 }

## メッセージ本体の作成

+static QmiMessageNasX0023Input *
+x0023_input_create (void)
+{
+    GError *error = NULL;
+    QmiMessageNasX0023Input *input;
+
+    input = qmi_message_nas_x0023_input_new ();
+    if (!qmi_message_nas_x0023_input_set_x10 (
+            input,
+            0x01,
+            &error)) {
+        g_printerr ("error: couldn't create input data bundle: '%s'\n",
+                    error->message);
+        g_error_free (error);
+        qmi_message_nas_x0023_input_unref (input);
+        input = NULL;
+    }
+
+    return input;
+}
+
```

0x0023のrequestは、TLVが1つ付いている。この場合は、`_create()`でメッセージ作成関数を作り、この中で、`_new()`と`_set()`を行い、`input`で指されるTLV付きrequestメッセージオブジェクトを作成する。
不要になったオブジェクトは、`_unref()`で解放。

```diff:src/qmicli/qmicli-nas.c
+static void
+x0023_ready (QmiClientNas *client,
+             GAsyncResult *res)
+{
+    QmiMessageNasX0023Output *output;
+    GError *error = NULL;
+
+    output = qmi_client_nas_x0023_finish (client, res, &error);
+    if (!output) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        operation_shutdown (FALSE);
+        return;
+    }
+
+    if (!qmi_message_nas_x0023_output_get_result (output, &error)) {
+        g_printerr ("error: couldn't message ID 0x0023: %s\n", error->message);
+        g_error_free (error);
+        qmi_message_nas_x0023_output_unref (output);
+        operation_shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Successfully message ID 0x0023\n",
+             qmi_device_get_path_display (ctx->device));
+
+    qmi_message_nas_x0023_output_unref (output);
+    operation_shutdown (TRUE);
+}
+
```

responseを受け取った時のコールバック関数は、`_ready()`で定義。`_finish()`でresponse全体を取得、`_get_result()`で中身の結果を取得。

```diff:src/qmicli/qmicli-nas.c
+static void
+x0026_ready (QmiClientNas *client,
+             GAsyncResult *res)
+{
+    QmiMessageNasX0026Output *output;
+    GError *error = NULL;
+
+    output = qmi_client_nas_x0026_finish (client, res, &error);
+    if (!output) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        operation_shutdown (FALSE);
+        return;
+    }
+
+    if (!qmi_message_nas_x0026_output_get_result (output, &error)) {
+        g_printerr ("error: couldn't message ID 0x0026: %s\n", error->message);
+        g_error_free (error);
+        qmi_message_nas_x0026_output_unref (output);
+        operation_shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Successfully message ID 0x0023\n",
+             qmi_device_get_path_display (ctx->device));
+
+    qmi_message_nas_x0026_output_unref (output);
+    operation_shutdown (TRUE);
+}
+
 static void
 get_tx_rx_info_ready (QmiClientNas *client,
                       GAsyncResult *res,
```

0x0026のほうは、requestにTLVが無いので、`_ready()`のコールバックのみ定義。

```diff:src/qmicli/qmicli-nas.c
@@ -3005,6 +3098,40 @@ qmicli_nas_run (QmiDevice *device,
         return;
     }

+    /* Request message ID 0x0023? */
+    if (x0023_flag) {
+        QmiMessageNasX0023Input *input;
+
+        input = x0023_input_create ();
+
+        if (!input) {
+            operation_shutdown (FALSE);
+            return;
+        }
+
+        g_debug ("Asynchronously message ID 0x0023...");
+        qmi_client_nas_x0023 (ctx->client,
+                              input,
+                              10,
+                              ctx->cancellable,
+                              (GAsyncReadyCallback)x0023_ready,
+                              NULL);
+        qmi_message_nas_x0023_input_unref (input);
+        return;
+    }
+
+    /* Request message ID 0x0026? */
+    if (x0026_flag) {
+        g_debug ("Asynchronously message ID 0x0026...");
+        qmi_client_nas_x0026 (ctx->client,
+                              NULL,
+                              10,
+                              ctx->cancellable,
+                              (GAsyncReadyCallback)x0026_ready,
+                              NULL);
+        return;
+    }
+
     /* Request to get serving system? */
     if (get_serving_system_flag) {
         g_debug ("Asynchronously getting serving system...");
```

コマンドラインオプションに応じ、メッセージの作成し、`qmi_client_nas_()`など、jsonから自動生成されたlibqmiのAPIを呼び出す。

# 結果
```shell-session
# qmicli -d /dev/cdc-wdm0 --nas-get-system-info
[/dev/cdc-wdm0] Successfully got system info:
	GSM service:
		Status: 'none'
		True Status: 'none'
		Preferred data path: 'no'
	WCDMA service:
		Status: 'none'
		True Status: 'none'
		Preferred data path: 'no'
	LTE service:
		Status: 'limited'
		True Status: 'limited-regional'
		Preferred data path: 'no'
		Domain: 'unknown'
		Service capability: 'cs-ps'
		Roaming status: 'off'
		Forbidden: 'no'
		Location Area Code: 'xx'
		Cell ID: 'xxxxxxxx'
		MCC: '440'
		MNC: '10'
		Tracking Area Code: '0'
		Voice support: 'yes'
# qmicli -d /dev/cdc-wdm0 --nas-0x0023
[/dev/cdc-wdm0] Successfully message ID 0x0023
# qmicli -d /dev/cdc-wdm0 --nas-get-system-info
[/dev/cdc-wdm0] Successfully got system info:
	GSM service:
		Status: 'none'
		True Status: 'none'
		Preferred data path: 'no'
	WCDMA service:
		Status: 'none'
		True Status: 'none'
		Preferred data path: 'no'
	LTE service:
		Status: 'available'
		True Status: 'none'
		Preferred data path: 'no'
		Domain: 'ps'
		Service capability: 'ps'
		Roaming status: 'off'
		Forbidden: 'no'
		Location Area Code: 'xxxxx'
		Cell ID: 'xxxxxxxx'
		MCC: '440'
		MNC: '10'
		Tracking Area Code: 'xxxx'
		Voice support: 'yes'
```

はい、LTEがlimitedからavailableに。

# 次回
最終章、Raspberry PiとUSBモデムでLTEのIPv4/IPv6デュアルスタックの世界。
IoTのご時世なのに、遠すぎだよこの世界…。