summaryrefslogtreecommitdiffstats
path: root/src/clms.c
blob: d2ba857aafa489d35297565909820ea887d099d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/*
 * DECT Connetionless Message Service (CLMS)
 *
 * Copyright (c) 2010 Patrick McHardy <kaber@trash.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

/**
 * @defgroup clms ConnectionLess Message Service
 * @{
 */

#include <stdint.h>
#include <linux/byteorder/little_endian.h>

#include <libdect.h>
#include <clms.h>
#include <lce.h>

static DECT_SFMT_MSG_DESC(clms_variable,
	DECT_SFMT_IE(DECT_IE_PORTABLE_IDENTITY,		IE_MANDATORY, IE_MANDATORY, 0),
	DECT_SFMT_IE(DECT_IE_MMS_GENERIC_HEADER,	IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_MMS_OBJECT_HEADER,		IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_REPEAT_INDICATOR,		IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_MMS_EXTENDED_HEADER,	IE_OPTIONAL,  IE_OPTIONAL,  DECT_SFMT_IE_REPEAT),
	DECT_SFMT_IE(DECT_IE_REPEAT_INDICATOR,		IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_TIME_DATE,			IE_OPTIONAL,  IE_OPTIONAL,  DECT_SFMT_IE_REPEAT),
	DECT_SFMT_IE(DECT_IE_REPEAT_INDICATOR,		IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_CALLING_PARTY_NUMBER,	IE_OPTIONAL,  IE_OPTIONAL,  DECT_SFMT_IE_REPEAT),
	DECT_SFMT_IE(DECT_IE_CALLING_PARTY_NAME,	IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_REPEAT_INDICATOR,		IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_CALLED_PARTY_NUMBER,	IE_OPTIONAL,  IE_OPTIONAL,  DECT_SFMT_IE_REPEAT),
	DECT_SFMT_IE(DECT_IE_CALLED_PARTY_SUBADDR,	IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_SEGMENTED_INFO,		IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_ALPHANUMERIC,		IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_IWU_TO_IWU,		IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_IWU_PACKET,		IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE(DECT_IE_ESCAPE_TO_PROPRIETARY,	IE_OPTIONAL,  IE_OPTIONAL,  0),
	DECT_SFMT_IE_END_MSG
);

#define __clms_debug(pfx, fmt, args...) \
	dect_debug(DECT_DEBUG_CLMS, "%sCLMS: " fmt "\n", pfx, ## args)

#define clms_debug(fmt, args...) \
	__clms_debug("", fmt, ##args)
#define clms_debug_entry(fmt, args...) \
	__clms_debug("\n", fmt, ##args)

static const struct dect_trans_tbl clms_header_codings[] = {
	TRANS_TBL(DECT_CLMS_HDR_STANDARD_ONE_SECTION,		"One section/Standard"),
	TRANS_TBL(DECT_CLMS_HDR_STANDARD_MULTI_SECTION,		"Multi-section/Standard"),
	TRANS_TBL(DECT_CLMS_HDR_BITSTREAM_ONE_SECTION,		"One section/Bit stream"),
	TRANS_TBL(DECT_CLMS_HDR_BITSTREAM_MULTI_SECTION,	"Multi-section/Bit stream"),
	TRANS_TBL(DECT_CLMS_HDR_ALPHANUMERIC_ONE_SECTION,	"One section/Alphanumeric"),
	TRANS_TBL(DECT_CLMS_HDR_ALPHANUMERIC_MULTI_SECTION,	"Multi-section/Alphanumeric"),
};

void dect_clms_rcv_fixed(struct dect_handle *dh, struct dect_msg_buf *mb)
{
	DECT_DEFINE_MSG_BUF_ONSTACK(_mb), *mbr = &_mb;
	struct dect_clms_fixed_addr_section *as;
	struct dect_clms_fixed_data_section *ds;
	unsigned int n, len, section, sections;
	char buf[128];

	clms_debug("parse {CLMS-FIXED} message");
	dect_assert(mb->len % 5 == 0);

	as = (void *)mb->data;
	if ((as->hdr & DECT_CLMS_SECTION_TYPE_MASK) != DECT_CLMS_SECTION_ADDR)
		return;

	dect_debug(DECT_DEBUG_CLMS, "  address section:\n");
	dect_debug(DECT_DEBUG_CLMS, "\tHeader: %s\n",
		   dect_val2str(clms_header_codings, buf, as->hdr & DECT_CLMS_HDR_MASK));
	dect_debug(DECT_DEBUG_CLMS, "\tAddress: %04x\n", __be16_to_cpu(as->addr));
	dect_debug(DECT_DEBUG_CLMS, "\tProtocol Discriminator: %02x\n", as->pd);
	dect_debug(DECT_DEBUG_CLMS, "\tLength Indicator: %02x\n", as->li);

	if (as->pd != DECT_CLMS_PD_DECT_IE_CODING && as->pd != 0x6)
		return;

	switch (as->hdr & DECT_CLMS_HDR_MASK) {
	case DECT_CLMS_HDR_STANDARD_ONE_SECTION:
	case DECT_CLMS_HDR_BITSTREAM_ONE_SECTION:
	case DECT_CLMS_HDR_ALPHANUMERIC_ONE_SECTION:
		memcpy(dect_mbuf_put(mbr, 1), &as->li, 1);
		goto deliver;
	case DECT_CLMS_HDR_STANDARD_MULTI_SECTION:
	case DECT_CLMS_HDR_BITSTREAM_MULTI_SECTION:
	case DECT_CLMS_HDR_ALPHANUMERIC_MULTI_SECTION:
		if (as->li == 0 || as->li % 8)
			return;
		len = as->li / 8;
		break;
	default:
		return;
	}

	dect_mbuf_pull(mb, sizeof(*as));

	sections = 0;
	while (mb->len > 0) {
		ds = (void *)mb->data;
		if ((ds->hdr & DECT_CLMS_SECTION_TYPE_MASK) !=
		    DECT_CLMS_SECTION_DATA)
			return;

		section = ds->hdr & DECT_CLMS_SECTION_NUM_MASK;
		dect_debug(DECT_DEBUG_CLMS, "  data section %u:\n", section);
		dect_hexdump(DECT_DEBUG_CLMS, "\tData", ds->data, 4);

		if (section >= 5)
			return;
		if (section != sections)
			return;
		sections++;

		n = min(len, DECT_CLMS_DATA_SIZE);
		memcpy(dect_mbuf_put(mbr, n), ds->data, n);
		len -= n;

		dect_mbuf_pull(mb, sizeof(*ds));
	}

	if (len > 0)
		return;
deliver:
	dect_mbuf_dump(DECT_DEBUG_CLMS, mbr, "CLMS");

	clms_debug("MNCL_UNITDATA-ind: type: %u", DECT_CLMS_FIXED);
	dh->ops->clms_ops->mncl_unitdata_ind(dh, DECT_CLMS_FIXED, NULL, mbr);
}

static void dect_clms_send_fixed(struct dect_handle *dh,
				 const void *data, unsigned int len)
{
	DECT_DEFINE_MSG_BUF_ONSTACK(_mb), *mb = &_mb;
	struct dect_clms_fixed_addr_section *as;
	struct dect_clms_fixed_data_section *ds;
	unsigned int n, section;

	as = dect_mbuf_put(mb, sizeof(*as));
	as->hdr   = DECT_CLMS_SECTION_ADDR;
	as->addr  = __cpu_to_be16(DECT_TPUI_CBI);
	as->pd    = DECT_CLMS_PD_DECT_IE_CODING;
	if (len > 1) {
		as->hdr |= DECT_CLMS_HDR_STANDARD_MULTI_SECTION;
		as->li   = len * 8;
	} else {
		as->hdr |= DECT_CLMS_HDR_STANDARD_ONE_SECTION;
		as->li   = *(uint8_t *)data;
		goto deliver;
	}

	section = 0;
	while (len > 0) {
		ds = dect_mbuf_put(mb, sizeof(*ds));
		ds->hdr  = DECT_CLMS_SECTION_DATA;
		ds->hdr |= section;

		n = min(len, DECT_CLMS_DATA_SIZE);
		memcpy(ds->data, data, n);
		if (n < DECT_CLMS_DATA_SIZE)
			memset(ds->data + n, 0, DECT_CLMS_DATA_SIZE - n);
		data += n;
		len  -= n;

		section++;
	}
deliver:
	dect_lce_broadcast(dh, mb, true, false);
}

/**
 * DECT_MNCL_UNITDATA-req primitive
 *
 * @param dh		libdect DECT handle
 * @param type		message type (fixed/variable) to use
 * @param param		unitdata parameters
 * @param mb		message buffer for {CLMS-FIXED} messages
 */
void dect_mncl_unitdata_req(struct dect_handle *dh,
			    enum dect_clms_message_types type,
			    const struct dect_mncl_unitdata_param *param,
			    const struct dect_msg_buf *mb)
{
	clms_debug_entry("MNCL_UNITDATA-req");
	dect_clms_send_fixed(dh, mb->data, mb->len);
}
EXPORT_SYMBOL(dect_mncl_unitdata_req);

static void dect_clms_rcv_variable(struct dect_handle *dh,
				   struct dect_transaction *ta,
				   struct dect_msg_buf *mb)
{
	struct dect_clms_variable_msg msg;

	clms_debug("CLMS-VARIABLE");
	if (dect_parse_sfmt_msg(dh, &clms_variable_msg_desc,
				&msg.common, mb) < 0)
		return;

	if (msg.portable_identity->type != DECT_PORTABLE_ID_TYPE_IPUI)
		goto out;
	if (dect_ddl_set_ipui(dh, ta->link, &msg.portable_identity->ipui) < 0)
		goto out;
out:
	dect_msg_free(dh, &clms_variable_msg_desc, &msg.common);
}

static void dect_clms_open(struct dect_handle *dh,
			   struct dect_transaction *req,
			   struct dect_msg_buf *mb)
{
	clms_debug("msg type: %x", mb->type);

	switch (mb->type) {
	case CLMS_VARIABLE:
		return dect_clms_rcv_variable(dh, req, mb);
	default:
		return;
	}
}

const struct dect_nwk_protocol dect_clms_protocol = {
	.name			= "ConnectionLess Message Service",
	.pd			= DECT_PD_CLMS,
	.max_transactions	= 1,
	.open			= dect_clms_open,
};

/** @} */