aboutsummaryrefslogtreecommitdiff
path: root/libwmc/src
diff options
context:
space:
mode:
Diffstat (limited to 'libwmc/src')
-rw-r--r--libwmc/src/commands.c150
-rw-r--r--libwmc/src/commands.h48
-rw-r--r--libwmc/src/protocol.h136
3 files changed, 299 insertions, 35 deletions
diff --git a/libwmc/src/commands.c b/libwmc/src/commands.c
index 241d0fa2..2f28a600 100644
--- a/libwmc/src/commands.c
+++ b/libwmc/src/commands.c
@@ -213,51 +213,171 @@ wmc_cmd_network_info_new (char *buf, size_t buflen)
return sizeof (*cmd);
}
+static wmcbool
+is_gsm_service (u_int8_t service)
+{
+ return (service == WMC_SERVICE_GSM || service == WMC_SERVICE_GPRS || service == WMC_SERVICE_EDGE);
+}
+
+static wmcbool
+is_umts_service (u_int8_t service)
+{
+ return (service == WMC_SERVICE_UMTS || service == WMC_SERVICE_HSDPA
+ || service == WMC_SERVICE_HSUPA || service == WMC_SERVICE_HSPA);
+}
+
+static wmcbool
+is_cdma_service (u_int8_t service)
+{
+ return (service == WMC_SERVICE_IS95A || service == WMC_SERVICE_IS95B || service == WMC_SERVICE_1XRTT);
+}
+
+static wmcbool
+is_evdo_service (u_int8_t service)
+{
+ return (service == WMC_SERVICE_EVDO_0 || service == WMC_SERVICE_EVDO_A);
+}
+
+static wmcbool
+is_lte_service (u_int8_t service)
+{
+ return (service == WMC_SERVICE_LTE);
+}
+
static u_int8_t
-sanitize_dbm (u_int8_t in_dbm)
+sanitize_dbm (u_int8_t in_dbm, u_int8_t service)
{
- /* 0x7D (-125 dBm) really means no signal */
- return in_dbm >= 0x7D ? 0 : in_dbm;
+ u_int8_t cutoff;
+
+ /* 0x6A (-106 dBm) = no signal for GSM/GPRS/EDGE */
+ /* 0x7D (-125 dBm) = no signal for everything else */
+ cutoff = is_gsm_service (service) ? 0x6A : 0x7D;
+
+ return in_dbm >= cutoff ? 0 : in_dbm;
}
+
WmcResult *
wmc_cmd_network_info_result (const char *buf, size_t buflen)
{
WmcResult *r = NULL;
WmcCmdNetworkInfoRsp *rsp = (WmcCmdNetworkInfoRsp *) buf;
WmcCmdNetworkInfo2Rsp *rsp2 = (WmcCmdNetworkInfo2Rsp *) buf;
+ WmcCmdNetworkInfo3Rsp *rsp3 = (WmcCmdNetworkInfo3Rsp *) buf;
char tmp[65];
+ int err;
+ u_int32_t mccmnc = 0, mcc, mnc;
wmc_return_val_if_fail (buf != NULL, NULL);
- if (check_command (buf, buflen, WMC_CMD_NET_INFO, sizeof (WmcCmdNetworkInfo2Rsp)) < 0) {
- rsp2 = NULL;
- if (check_command (buf, buflen, WMC_CMD_NET_INFO, sizeof (WmcCmdNetworkInfoRsp)) < 0)
+ err = check_command (buf, buflen, WMC_CMD_NET_INFO, sizeof (WmcCmdNetworkInfo3Rsp));
+ if (err != WMC_SUCCESS) {
+ if (err != -WMC_ERROR_RESPONSE_BAD_LENGTH)
return NULL;
+ rsp3 = NULL;
+
+ err = check_command (buf, buflen, WMC_CMD_NET_INFO, sizeof (WmcCmdNetworkInfo2Rsp));
+ if (err != WMC_SUCCESS) {
+ if (err != -WMC_ERROR_RESPONSE_BAD_LENGTH)
+ return NULL;
+ rsp2 = NULL;
+
+ err = check_command (buf, buflen, WMC_CMD_NET_INFO, sizeof (WmcCmdNetworkInfoRsp));
+ if (err != WMC_SUCCESS)
+ return NULL;
+ }
}
r = wmc_result_new ();
- wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_CDMA_DBM, sanitize_dbm (rsp->cdma1x_dbm));
+ wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_SERVICE, rsp->service);
if (rsp2) {
- wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_HDR_DBM, sanitize_dbm (rsp2->hdr_dbm));
- wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_LTE_DBM, sanitize_dbm (rsp2->lte_dbm));
+ wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_2G_DBM, sanitize_dbm (rsp2->two_g_dbm, rsp->service));
+ wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_3G_DBM, sanitize_dbm (rsp2->three_g_dbm, WMC_SERVICE_NONE));
memset (tmp, 0, sizeof (tmp));
- if (sanitize_dbm (rsp2->lte_dbm)) {
- /* LTE operator name */
- wmc_assert (sizeof (rsp2->lte_opname) <= sizeof (tmp));
- memcpy (tmp, rsp2->lte_opname, sizeof (rsp2->lte_opname));
- wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_OPNAME, tmp);
- } else if (sanitize_dbm (rsp2->hdr_dbm) || sanitize_dbm (rsp2->cdma1x_dbm)) {
+ if ( (is_cdma_service (rsp->service) && sanitize_dbm (rsp2->two_g_dbm, rsp->service))
+ || (is_evdo_service (rsp->service) && sanitize_dbm (rsp2->three_g_dbm, rsp->service))) {
/* CDMA2000 operator name */
wmc_assert (sizeof (rsp2->cdma_opname) <= sizeof (tmp));
memcpy (tmp, rsp2->cdma_opname, sizeof (rsp2->cdma_opname));
wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_OPNAME, tmp);
+ } else {
+ if ( (is_gsm_service (rsp->service) && sanitize_dbm (rsp2->two_g_dbm, rsp->service))
+ || (is_umts_service (rsp->service) && sanitize_dbm (rsp2->three_g_dbm, rsp->service))) {
+ /* GSM/UMTS operator name */
+ wmc_assert (sizeof (rsp2->tgpp_opname) <= sizeof (tmp));
+ memcpy (tmp, rsp2->tgpp_opname, sizeof (rsp2->tgpp_opname));
+ wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_OPNAME, tmp);
+ }
+ }
+
+ /* MCC/MNC */
+ mccmnc = le32toh (rsp2->mcc_mnc);
+ if (mccmnc < 100000)
+ mccmnc *= 10; /* account for possible 2-digit MNC */
+ mcc = mccmnc / 1000;
+ mnc = mccmnc - (mcc * 1000);
+
+ if (mcc > 100) {
+ memset (tmp, 0, sizeof (tmp));
+ snprintf (tmp, sizeof (tmp), "%u", mccmnc / 1000);
+ wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_MCC, tmp);
+
+ memset (tmp, 0, sizeof (tmp));
+ snprintf (tmp, sizeof (tmp), "%03u", mnc);
+ wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_MNC, tmp);
}
+ } else {
+ /* old format */
+ wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_2G_DBM, sanitize_dbm (rsp->two_g_dbm, rsp->service));
}
+ if (rsp3) {
+ wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_LTE_DBM, sanitize_dbm (rsp3->lte_dbm, WMC_SERVICE_NONE));
+
+ memset (tmp, 0, sizeof (tmp));
+ if (is_lte_service (rsp->service) && sanitize_dbm (rsp3->lte_dbm, rsp->service)) {
+ /* LTE operator name */
+ wmc_assert (sizeof (rsp2->tgpp_opname) <= sizeof (tmp));
+ memcpy (tmp, rsp2->tgpp_opname, sizeof (rsp2->tgpp_opname));
+ wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_OPNAME, tmp);
+ }
+ }
+
+ return r;
+}
+
+/**********************************************************************/
+
+size_t
+wmc_cmd_get_global_mode_new (char *buf, size_t buflen)
+{
+ WmcCmdHeader *cmd = (WmcCmdHeader *) buf;
+
+ wmc_return_val_if_fail (buf != NULL, 0);
+ wmc_return_val_if_fail (buflen >= sizeof (*cmd), 0);
+
+ memset (cmd, 0, sizeof (*cmd));
+ cmd->marker = WMC_CMD_MARKER;
+ cmd->cmd = WMC_CMD_GET_GLOBAL_MODE;
+ return sizeof (*cmd);
+}
+
+WmcResult *
+wmc_cmd_get_global_mode_result (const char *buf, size_t buflen)
+{
+ WmcResult *r = NULL;
+ WmcCmdGetGlobalModeRsp *rsp = (WmcCmdGetGlobalModeRsp *) buf;
+
+ wmc_return_val_if_fail (buf != NULL, NULL);
+
+ if (check_command (buf, buflen, WMC_CMD_GET_GLOBAL_MODE, sizeof (WmcCmdGetGlobalModeRsp)) < 0)
+ return NULL;
+
+ r = wmc_result_new ();
+ wmc_result_add_u8 (r, WMC_CMD_GET_GLOBAL_MODE_ITEM_MODE, rsp->mode);
return r;
}
diff --git a/libwmc/src/commands.h b/libwmc/src/commands.h
index 07b0ec63..4cb19ced 100644
--- a/libwmc/src/commands.h
+++ b/libwmc/src/commands.h
@@ -47,10 +47,33 @@ WmcResult * wmc_cmd_device_info_result (const char *buf, size_t len);
/**********************************************************************/
-#define WMC_CMD_NETWORK_INFO_ITEM_CDMA_DBM "cdma-dbm"
-#define WMC_CMD_NETWORK_INFO_ITEM_HDR_DBM "hdr-dbm"
-#define WMC_CMD_NETWORK_INFO_ITEM_LTE_DBM "lte-dbm"
-#define WMC_CMD_NETWORK_INFO_ITEM_OPNAME "opname"
+enum {
+ WMC_NETWORK_SERVICE_NONE = 0,
+ WMC_NETWORK_SERVICE_AMPS = 1,
+ WMC_NETWORK_SERVICE_IS95A = 2,
+ WMC_NETWORK_SERVICE_IS95B = 3,
+ WMC_NETWORK_SERVICE_GSM = 4,
+ WMC_NETWORK_SERVICE_GPRS = 5,
+ WMC_NETWORK_SERVICE_1XRTT = 6,
+ WMC_NETWORK_SERVICE_EVDO_0 = 7,
+ WMC_NETWORK_SERVICE_UMTS = 8,
+ WMC_NETWORK_SERVICE_EVDO_A = 9,
+ WMC_NETWORK_SERVICE_EDGE = 10,
+ WMC_NETWORK_SERVICE_HSDPA = 11,
+ WMC_NETWORK_SERVICE_HSUPA = 12,
+ WMC_NETWORK_SERVICE_HSPA = 13,
+ WMC_NETWORK_SERVICE_LTE = 14
+};
+
+/* One of WMC_NETWORK_SERVICE_* */
+#define WMC_CMD_NETWORK_INFO_ITEM_SERVICE "service"
+
+#define WMC_CMD_NETWORK_INFO_ITEM_2G_DBM "2g-dbm"
+#define WMC_CMD_NETWORK_INFO_ITEM_3G_DBM "3g-dbm"
+#define WMC_CMD_NETWORK_INFO_ITEM_LTE_DBM "lte-dbm"
+#define WMC_CMD_NETWORK_INFO_ITEM_OPNAME "opname"
+#define WMC_CMD_NETWORK_INFO_ITEM_MCC "mcc"
+#define WMC_CMD_NETWORK_INFO_ITEM_MNC "mnc"
size_t wmc_cmd_network_info_new (char *buf, size_t buflen);
@@ -58,4 +81,21 @@ WmcResult * wmc_cmd_network_info_result (const char *buf, size_t len);
/**********************************************************************/
+enum {
+ WMC_NETWORK_MODE_CDMA = 0x00,
+ WMC_NETWORK_MODE_GSM_AUTO = 0x0A,
+ WMC_NETWORK_MODE_GPRS_ONLY = 0x0B,
+ WMC_NETWORK_MODE_UMTS_ONLY = 0x0C,
+ WMC_NETWORK_MODE_AUTO = 0x14,
+};
+
+/* One of WMC_NETWORK_MODE_* */
+#define WMC_CMD_GET_GLOBAL_MODE_ITEM_MODE "mode"
+
+size_t wmc_cmd_get_global_mode_new (char *buf, size_t buflen);
+
+WmcResult * wmc_cmd_get_global_mode_result (const char *buf, size_t len);
+
+/**********************************************************************/
+
#endif /* LIBWMC_COMMANDS_H */
diff --git a/libwmc/src/protocol.h b/libwmc/src/protocol.h
index 2ee803aa..ab23d4c1 100644
--- a/libwmc/src/protocol.h
+++ b/libwmc/src/protocol.h
@@ -27,9 +27,26 @@ enum {
WMC_CMD_IP_INFO = 0x0A,
WMC_CMD_NET_INFO = 0x0B,
WMC_CMD_INIT = 0x0D,
+ WMC_CMD_SET_OPERATOR = 0x33,
+ WMC_CMD_GET_FIRST_OPERATOR = 0x34,
+ WMC_CMD_GET_NEXT_OPERATOR = 0x35,
WMC_CMD_EPS_BEARER_INFO = 0x4D,
};
+/* MCC/MNC representation
+ *
+ * Various commands accept or return an MCC/MNC. When sending, convert
+ * the MCC/MNC into a number using eg. atoi() and store it as an LE 32-bit
+ * value. 3-digit MNCs appear to be sent as 3-digit only if the firmware
+ * reports them as 3-digit. For example:
+ *
+ * T-Mobile US: 310-260
+ * mcc_mnc = 0x00007932 = 31026 (note the 2-digit-only MNC)
+ *
+ * AT&T: 310-410
+ * mcc_mnc = 0x0004bc8a = 310410
+ */
+
/* Generic WMC command header */
struct WmcCmdHeader {
@@ -105,9 +122,9 @@ enum {
WMC_SERVICE_GSM = 4,
WMC_SERVICE_GPRS = 5,
WMC_SERVICE_1XRTT = 6,
- WMC_SERVICE_1XEVDO_0 = 7,
+ WMC_SERVICE_EVDO_0 = 7,
WMC_SERVICE_UMTS = 8,
- WMC_SERVICE_1XEVDO_A = 9,
+ WMC_SERVICE_EVDO_A = 9,
WMC_SERVICE_EDGE = 10,
WMC_SERVICE_HSDPA = 11,
WMC_SERVICE_HSUPA = 12,
@@ -115,7 +132,7 @@ enum {
WMC_SERVICE_LTE = 14
};
-/* Shorter response used by earlier devices like PC5740 */
+/* PC5740 response */
struct WmcCmdNetworkInfoRsp {
WmcCmdHeader hdr;
u_int8_t _unknown1;
@@ -127,12 +144,12 @@ struct WmcCmdNetworkInfoRsp {
u_int8_t _unknown5;
u_int8_t _unknown6;
u_int8_t _unknown7[3]; /* Always 0xFE 0xFF 0xFF */
- u_int8_t cdma1x_dbm;
+ u_int8_t two_g_dbm; /* 0x7D = no signal */
u_int8_t _unknown8[37]; /* Always zero */
} __attribute__ ((packed));
typedef struct WmcCmdNetworkInfoRsp WmcCmdNetworkInfoRsp;
-/* Long-format response used on newer devices like the UML290 */
+/* UML190 response */
struct WmcCmdNetworkInfo2Rsp {
WmcCmdHeader hdr;
u_int8_t _unknown1; /* 0x00 on LTE, 0x07 or 0x1F on CDMA */
@@ -144,23 +161,57 @@ struct WmcCmdNetworkInfo2Rsp {
u_int16_t counter2; /* Time since firmware start? */
u_int8_t _unknown5; /* 0x00 on LTE, various values (0xD4, 0x5C) on CDMA */
u_int8_t _unknown6[3]; /* always zero on LTE, 0xFE 0xFF 0xFF on CDMA */
- u_int8_t cdma1x_dbm; /* 0x7D = no signal */
+ u_int8_t two_g_dbm; /* 0x7D = no CDMA signal, 0x6a = no GSM signal */
u_int8_t _unknown7[3]; /* Always zero */
u_int8_t cdma_opname[16]; /* Zero terminated? */
u_int8_t _unknown8[18]; /* Always zero */
- u_int8_t hdr_dbm; /* 0x7D = no signal */
- u_int8_t _unknown9[3]; /* Always zero */
+ u_int8_t three_g_dbm; /* 0x7D = no signal */
+ u_int8_t _unknown9[3]; /* Always zero */
u_int8_t _unknown10; /* 0x01 on LTE, 0x40 on CDMA */
u_int8_t _unknown11[3]; /* Always zero */
u_int8_t _unknown12; /* Always 0x01 */
- u_int8_t lte_opname[8]; /* Zero terminated? Sometimes "MCC MNC" too */
- u_int8_t _unknown13[60]; /* Always zero */
- u_int8_t lte_dbm; /* 0x00 if not in LTE mode */
- u_int8_t _unknown14[3]; /* Always zero */
- u_int8_t _unknown15[4];
+ u_int8_t tgpp_opname[8]; /* 3GPP operator name (Zero terminated? Sometimes "MCC MNC" too */
+ u_int8_t _unknown13[4]; /* Always zero */
+ u_int32_t _unknown14; /* 43 75 3a 00 on GSM/WCDMA mode, zero on others */
+ u_int32_t _unknown15; /* 49 7d 3a 00 on GSM/WCDMA mode, zero on others */
+ u_int8_t _unknown16[44]; /* Always zero */
+ u_int32_t mcc_mnc; /* GSM/WCDMA only, see MCC/MNC format note */
} __attribute__ ((packed));
typedef struct WmcCmdNetworkInfo2Rsp WmcCmdNetworkInfo2Rsp;
+/* UML290 response */
+struct WmcCmdNetworkInfo3Rsp {
+ WmcCmdHeader hdr;
+ u_int8_t _unknown1; /* 0x00 on LTE, 0x07 or 0x1F on CDMA */
+ u_int8_t _unknown2[3]; /* Always zero */
+ u_int8_t service; /* One of WMC_SERVICE_* */
+ u_int8_t _unknown4;
+ u_int8_t magic[10]; /* Whatever was passed in WMC_CMD_INIT with some changes */
+ u_int16_t counter1; /* A timestamp/counter? */
+ u_int16_t counter2; /* Time since firmware start? */
+ u_int8_t _unknown5; /* 0x00 on LTE, various values (0xD4, 0x5C) on CDMA */
+ u_int8_t _unknown6[3]; /* always zero on LTE, 0xFE 0xFF 0xFF on CDMA */
+ u_int8_t two_g_dbm; /* 0x7D = no CDMA signal, 0x6a = no GSM signal */
+ u_int8_t _unknown7[3]; /* Always zero */
+ u_int8_t cdma_opname[16]; /* Zero terminated? */
+ u_int8_t _unknown8[18]; /* Always zero */
+ u_int8_t three_g_dbm; /* 0x7D = no signal */
+ u_int8_t _unknown9[3]; /* Always zero */
+ u_int8_t _unknown10; /* 0x01 on LTE, 0x40 on CDMA */
+ u_int8_t _unknown11[3]; /* Always zero */
+ u_int8_t _unknown12; /* Always 0x01 */
+ u_int8_t tgpp_opname[8]; /* Zero terminated? Sometimes "MCC MNC" too */
+ u_int8_t _unknown13[4]; /* Always zero */
+ u_int32_t _unknown14; /* 43 75 3a 00 on GSM/WCDMA mode, zero on others */
+ u_int32_t _unknown15; /* 49 7d 3a 00 on GSM/WCDMA mode, zero on others */
+ u_int8_t _unknown16[44]; /* Always zero */
+ u_int32_t mcc_mnc; /* GSM/WCDMA only, see MCC/MNC format note */
+ u_int8_t lte_dbm; /* 0x00 if not in LTE mode */
+ u_int8_t _unknown17[3]; /* Always zero */
+ u_int8_t _unknown18[4];
+} __attribute__ ((packed));
+typedef struct WmcCmdNetworkInfo3Rsp WmcCmdNetworkInfo3Rsp;
+
/*****************************************************/
struct WmcCmdConnectionInfoRsp {
@@ -179,9 +230,11 @@ typedef struct WmcCmdConnectionInfoRsp WmcCmdConnectionInfoRsp;
/*****************************************************/
enum {
- WMC_GLOBAL_MODE_CDMA = 0x00,
- WMC_GLOBAL_MODE_GSM = 0x0A,
- WMC_GLOBAL_MODE_AUTO = 0x14,
+ WMC_GLOBAL_MODE_CDMA = 0x00,
+ WMC_GLOBAL_MODE_GSM_AUTO = 0x0A,
+ WMC_GLOBAL_MODE_GPRS_ONLY = 0x0B,
+ WMC_GLOBAL_MODE_UMTS_ONLY = 0x0C,
+ WMC_GLOBAL_MODE_AUTO = 0x14,
};
struct WmcCmdGetGlobalMode {
@@ -219,4 +272,55 @@ typedef struct WmcCmdSetGlobalModeRsp WmcCmdSetGlobalModeRsp;
/*****************************************************/
+struct WmcCmdSetOperator {
+ WmcCmdHeader hdr;
+ u_int8_t automatic; /* 0x00 = manual, 0x01 = auto */
+ u_int8_t _unknown1; /* always 0x50 */
+ u_int8_t _unknown2[3]; /* always zero */
+ u_int32_t mcc_mnc; /* MCC/MNC for manual reg (see format note), zero for auto */
+ u_int8_t _unknown3[56]; /* always zero */
+} __attribute__ ((packed));
+typedef struct WmcCmdSetOperator WmcCmdSetOperator;
+
+enum {
+ WMC_SET_OPERATOR_STATUS_OK = 0,
+ WMC_SET_OPERATOR_STATUS_REGISTERING = 0x63,
+ WMC_SET_OPERATOR_STATUS_FAILED = 0x68,
+};
+
+struct WmcCmdSetOperatorRsp {
+ WmcCmdHeader hdr;
+ u_int8_t status; /* one of WMC_SET_OPERATOR_STATUS_* */
+ u_int8_t _unknown1[3]; /* always zero */
+} __attribute__ ((packed));
+typedef struct WmcCmdSetOperatorRsp WmcCmdSetOperatorRsp;
+
+/*****************************************************/
+
+enum {
+ WMC_OPERATOR_SERVICE_UNKNOWN = 0,
+ WMC_OPERATOR_SERVICE_GSM = 1,
+ WMC_OPERATOR_SERVICE_UMTS = 2,
+};
+
+/* Response for both GET_FIRST_OPERATOR and GET_NEXT_OPERATOR */
+struct WmcCmdGetOperatorRsp {
+ WmcCmdHeader hdr;
+ u_int8_t _unknown1; /* Usually 0x50, sometimes 0x00 */
+ u_int8_t _unknown2[3]; /* always zero */
+ u_int32_t mcc_mnc; /* see format note */
+ u_int8_t opname[8];
+ u_int8_t _unknown3[56]; /* always zero */
+ u_int8_t stat; /* follows 3GPP TS27.007 +COPS <stat> ? */
+ u_int8_t _unknown4[3]; /* always zero */
+ u_int8_t service; /* one of WMC_OPERATOR_SERVICE_* */
+ u_int8_t _unknown5[3]; /* always zero */
+ u_int8_t _unknown6; /* 0x63 (GET_FIRST_OP) might mean "wait" */
+ u_int8_t _unknown7; /* 0x00 or 0x01 */
+ u_int8_t _unknown8[2]; /* always zero */
+} __attribute__ ((packed));
+typedef struct WmcCmdSetOperatorRsp WmcCmdSetOperatorRsp;
+
+/*****************************************************/
+
#endif /* LIBWMC_PROTOCOL_H */