2 * uqmi -- tiny QMI support implementation
4 * Copyright (C) 2014-2015 Felix Fietkau <nbd@openwrt.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA.
22 #include "qmi-message.h"
24 #define MIN(a,b) (((a)<(b))?(a):(b))
25 #define CEILDIV(x,y) (((x) + (y) - 1) / (y))
27 static struct qmi_wms_list_messages_request lmreq
= {
28 QMI_INIT(storage_type
, QMI_WMS_STORAGE_TYPE_UIM
),
29 QMI_INIT(message_tag
, QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ
),
32 static struct qmi_wms_delete_request dmreq
= {
33 QMI_INIT(memory_storage
, QMI_WMS_STORAGE_TYPE_UIM
),
34 QMI_INIT(message_mode
, QMI_WMS_MESSAGE_MODE_GSM_WCDMA
),
37 static struct qmi_wms_raw_read_request gmreq
= {
38 QMI_INIT_SEQUENCE(message_memory_storage_id
,
39 .storage_type
= QMI_WMS_STORAGE_TYPE_UIM
,
41 QMI_INIT(message_mode
, QMI_WMS_MESSAGE_MODE_GSM_WCDMA
),
45 #define cmd_wms_storage_cb no_cb
46 static enum qmi_cmd_result
47 cmd_wms_storage_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
49 if (strcmp(arg
, "sim") == 0) {
50 } else if (strcmp(arg
, "me") == 0) {
51 qmi_set_ptr(&lmreq
, storage_type
, QMI_WMS_STORAGE_TYPE_NV
);
52 qmi_set_ptr(&dmreq
, memory_storage
, QMI_WMS_STORAGE_TYPE_NV
);
53 qmi_set_ptr(&gmreq
, message_memory_storage_id
.storage_type
, QMI_WMS_STORAGE_TYPE_NV
);
55 uqmi_add_error("Invalid value (sim or me)");
61 static void cmd_wms_list_messages_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
63 struct qmi_wms_list_messages_response res
;
67 qmi_parse_wms_list_messages_response(msg
, &res
);
68 c
= blobmsg_open_array(&status
, NULL
);
69 for (i
= 0; i
< res
.data
.message_list_n
; i
++)
70 blobmsg_add_u32(&status
, NULL
, res
.data
.message_list
[i
].memory_index
);
72 blobmsg_close_array(&status
, c
);
75 static enum qmi_cmd_result
76 cmd_wms_list_messages_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
78 qmi_set_wms_list_messages_request(msg
, &lmreq
);
80 return QMI_CMD_REQUEST
;
84 put_unicode_char(char *dest
, uint16_t c
)
89 } else if (c
< 0x800) {
90 *(dest
++) = 0xc0 | ((c
>> 6) & 0x1f);
91 *dest
= 0x80 | (c
& 0x3f);
94 *(dest
++) = 0xe0 | ((c
>> 12) & 0xf);
95 *(dest
++) = 0x80 | ((c
>> 6) & 0x3f);
96 *dest
= 0x80 | (c
& 0x3f);
103 pdu_decode_7bit_char(char *dest
, int len
, unsigned char c
, bool *escape
)
105 uint16_t conv_0x20
[] = {
106 0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC,
107 0x00F2, 0x00E7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5,
108 0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8,
109 0x03A3, 0x0398, 0x039E, 0x00A0, 0x00C6, 0x00E6, 0x00DF, 0x00C9,
111 uint16_t conv_0x5b
[] = {
112 0x00C4, 0x00D6, 0x00D1, 0x00DC, 0x00A7, 0x00BF,
114 uint16_t conv_0x7b
[] = {
115 0x00E4, 0x00F6, 0x00F1, 0x00FC, 0x00E0
120 fprintf(stderr
, " %02x", c
);
159 *(dest
++) = conv_0x20
[0x1B];
172 outc
= conv_0x20
[(int) c
];
175 else if (c
>= 0x5b && c
<= 0x60)
176 outc
= conv_0x5b
[c
- 0x5b];
177 else if (c
>= 0x7b && c
<= 0x7f)
178 outc
= conv_0x7b
[c
- 0x7b];
183 return cur_len
+ put_unicode_char(dest
, outc
);
187 pdu_decode_7bit_str(char *dest
, const unsigned char *data
, int data_len
, int bit_offset
)
193 fprintf(stderr
, "Raw text:");
194 for (i
= 0; i
< data_len
; i
++) {
195 int pos
= (i
+ bit_offset
) % 7;
198 len
+= pdu_decode_7bit_char(dest
, len
, data
[i
] & 0x7f, &escape
);
201 len
+= pdu_decode_7bit_char(dest
, len
,
202 (data
[i
- 1] >> (7 + 1 - pos
)) |
203 ((data
[i
] << pos
) & 0x7f), &escape
);
206 len
+= pdu_decode_7bit_char(dest
, len
, (data
[i
] >> 1) & 0x7f,
211 fprintf(stderr
, "\n");
215 static int decode_udh(const unsigned char *data
)
217 const unsigned char *end
;
218 unsigned int type
, len
, udh_len
;
221 end
= data
+ udh_len
;
223 const unsigned char *val
;
234 blobmsg_add_u32(&status
, "concat_ref", (uint32_t) val
[0]);
235 blobmsg_add_u32(&status
, "concat_part", (uint32_t) val
[2]);
236 blobmsg_add_u32(&status
, "concat_parts", (uint32_t) val
[1]);
239 blobmsg_add_u32(&status
, "concat_ref", (uint32_t) (val
[0] << 8 | val
[1]));
240 blobmsg_add_u32(&status
, "concat_part", (uint32_t) val
[3]);
241 blobmsg_add_u32(&status
, "concat_parts", (uint32_t) val
[2]);
251 static void decode_7bit_field(char *name
, const unsigned char *data
, int data_len
, int bit_offset
)
253 char *dest
= blobmsg_alloc_string_buffer(&status
, name
, 3 * data_len
+ 2);
254 int out_len
= pdu_decode_7bit_str(dest
, data
, CEILDIV(data_len
* 7, 8), bit_offset
);
256 blobmsg_add_string_buffer(&status
);
259 static char *pdu_add_semioctet(char *str
, char val
)
261 *str
= '0' + (val
& 0xf);
265 *str
= '0' + ((val
>> 4) & 0xf);
273 pdu_decode_address(char *str
, unsigned char *data
, int len
)
278 switch (toa
& 0x70) {
280 pdu_decode_7bit_str(str
, data
, len
, 0);
287 str
= pdu_add_semioctet(str
, *data
);
295 static void wms_decode_address(char *name
, unsigned char *data
, int len
)
297 char *str
= blobmsg_alloc_string_buffer(&status
, name
, len
* 2 + 2);
298 pdu_decode_address(str
, data
, len
);
299 blobmsg_add_string_buffer(&status
);
302 static void blobmsg_add_hex(struct blob_buf
*buf
, const char *name
, unsigned const char *data
, int len
)
304 char* str
= blobmsg_alloc_string_buffer(buf
, name
, len
* 2 + 1);
305 for (int i
= 0; i
< len
; i
++) {
306 str
+= sprintf(str
, "%02x", data
[i
]);
308 blobmsg_add_string_buffer(buf
);
311 #define cmd_wms_delete_message_cb no_cb
312 static enum qmi_cmd_result
313 cmd_wms_delete_message_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
318 id
= strtoul(arg
, &err
, 10);
320 uqmi_add_error("Invalid message ID");
324 dmreq
.set
.memory_index
= 1;
325 dmreq
.data
.memory_index
= id
;
327 qmi_set_wms_delete_request(msg
, &dmreq
);
329 return QMI_CMD_REQUEST
;
333 static void cmd_wms_get_message_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
335 struct qmi_wms_raw_read_response res
;
336 unsigned char *data
, *end
;
340 unsigned char first
, dcs
;
343 qmi_parse_wms_raw_read_response(msg
, &res
);
344 c
= blobmsg_open_table(&status
, NULL
);
345 data
= (unsigned char *) res
.data
.raw_message_data
.raw_data
;
346 end
= data
+ res
.data
.raw_message_data
.raw_data_n
;
349 if (data
+ cur_len
>= end
)
353 wms_decode_address("smsc", data
, cur_len
- 1);
361 sent
= (first
& 0x3) == 1;
366 if (data
+ cur_len
>= end
)
370 cur_len
= (cur_len
+ 1) / 2;
371 wms_decode_address(sent
? "receiver" : "sender", data
, cur_len
);
386 blobmsg_add_u32(&status
, "class", (dcs
& 3));
389 /* Message validity */
395 str
= blobmsg_alloc_string_buffer(&status
, "timestamp", 32);
400 str
= pdu_add_semioctet(str
, data
[0]);
403 str
= pdu_add_semioctet(str
, data
[1]);
406 str
= pdu_add_semioctet(str
, data
[2]);
410 str
= pdu_add_semioctet(str
, data
[3]);
413 str
= pdu_add_semioctet(str
, data
[4]);
416 str
= pdu_add_semioctet(str
, data
[5]);
419 blobmsg_add_string_buffer(&status
);
424 int message_len
= *(data
++);
428 /* User Data Header */
430 udh_len
= decode_udh(data
);
432 bit_offset
= udh_len
% 7;
440 /* 7 bit GSM alphabet */
441 message_len
= message_len
- CEILDIV(udh_len
* 8, 7);
442 message_len
= MIN(message_len
, CEILDIV((end
- data
) * 8, 7));
443 decode_7bit_field("text", data
, message_len
, bit_offset
);
447 message_len
= MIN(message_len
- udh_len
, end
- data
);
448 blobmsg_add_hex(&status
, "data", data
, message_len
);
451 /* 16 bit UCS-2 string */
452 message_len
= MIN(message_len
- udh_len
, end
- data
);
453 blobmsg_add_hex(&status
, "ucs-2", data
, message_len
);
459 blobmsg_close_table(&status
, c
);
463 blobmsg_close_table(&status
, c
);
464 fprintf(stderr
, "There was an error reading message.\n");
467 static enum qmi_cmd_result
468 cmd_wms_get_message_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
473 id
= strtoul(arg
, &err
, 10);
475 uqmi_add_error("Invalid message ID");
479 gmreq
.data
.message_memory_storage_id
.memory_index
= id
;
480 qmi_set_wms_raw_read_request(msg
, &gmreq
);
482 return QMI_CMD_REQUEST
;
486 static void cmd_wms_get_raw_message_cb(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
)
488 struct qmi_wms_raw_read_response res
;
493 qmi_parse_wms_raw_read_response(msg
, &res
);
494 data
= (unsigned char *) res
.data
.raw_message_data
.raw_data
;
495 str
= blobmsg_alloc_string_buffer(&status
, NULL
, res
.data
.raw_message_data
.raw_data_n
* 3);
496 for (i
= 0; i
< res
.data
.raw_message_data
.raw_data_n
; i
++) {
497 str
+= sprintf(str
, &" %02x"[i
? 0 : 1], data
[i
]);
499 blobmsg_add_string_buffer(&status
);
502 #define cmd_wms_get_raw_message_prepare cmd_wms_get_message_prepare
512 #define cmd_wms_send_message_smsc_cb no_cb
513 static enum qmi_cmd_result
514 cmd_wms_send_message_smsc_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
520 #define cmd_wms_send_message_target_cb no_cb
521 static enum qmi_cmd_result
522 cmd_wms_send_message_target_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
528 #define cmd_wms_send_message_flash_cb no_cb
529 static enum qmi_cmd_result
530 cmd_wms_send_message_flash_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
537 pdu_encode_semioctet(unsigned char *dest
, const char *str
)
543 char digit
= *str
- '0';
546 dest
[len
] = 0xf0 | digit
;
548 dest
[len
++] &= (digit
<< 4) | 0xf;
554 return lower
? len
: (len
+ 1);
558 pdu_encode_7bit_str(unsigned char *data
, const char *str
)
574 data
[len
++] |= c
<< (8 - ofs
);
575 data
[len
] = c
>> ofs
;
586 pdu_encode_number(unsigned char *dest
, const char *str
, bool smsc
)
588 unsigned char format
;
601 for (i
= 0; str
[i
]; i
++) {
602 if (str
[i
] >= '0' && str
[i
] <= '9')
612 dest
[len
++] = format
;
614 len
+= pdu_encode_semioctet(&dest
[len
], str
);
616 len
+= pdu_encode_7bit_str(&dest
[len
], str
);
621 dest
[0] = strlen(str
);
627 pdu_encode_data(unsigned char *dest
, const char *str
)
632 len
+= pdu_encode_7bit_str(&dest
[len
], str
);
633 dest
[0] = strlen(str
);
638 #define cmd_wms_send_message_cb no_cb
639 static enum qmi_cmd_result
640 cmd_wms_send_message_prepare(struct qmi_dev
*qmi
, struct qmi_request
*req
, struct qmi_msg
*msg
, char *arg
)
642 static unsigned char buf
[512];
643 static struct qmi_wms_raw_send_request mreq
= {
644 QMI_INIT_SEQUENCE(raw_message_data
,
645 .format
= QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT
,
649 unsigned char *cur
= buf
;
650 unsigned char first_octet
= 0x11;
651 unsigned char protocol_id
= 0x00;
652 unsigned char dcs
= 0x00;
654 if (!_send
.target
|| !*_send
.target
) {
655 uqmi_add_error("Missing argument");
659 if ((_send
.smsc
&& strlen(_send
.smsc
) > 16) || strlen(_send
.target
) > 16 || strlen(arg
) > 160) {
660 uqmi_add_error("Argument too long");
667 if (!_send
.smsc
|| !*_send
.smsc
)
670 cur
+= pdu_encode_number(cur
, _send
.smsc
, true);
672 *(cur
++) = first_octet
;
673 *(cur
++) = 0; /* reference */
675 cur
+= pdu_encode_number(cur
, _send
.target
, false);
676 *(cur
++) = protocol_id
;
679 *(cur
++) = 0xff; /* validity */
680 cur
+= pdu_encode_data(cur
, arg
);
682 mreq
.data
.raw_message_data
.raw_data_n
= cur
- buf
;
683 qmi_set_wms_raw_send_request(msg
, &mreq
);
685 return QMI_CMD_REQUEST
;