Blame view

net/caif/cfrfml.c 6.6 KB
b482cd205   Sjur Braendeland   net-caif: add CAI...
1
2
3
4
5
  /*
   * Copyright (C) ST-Ericsson AB 2010
   * Author:	Sjur Brendeland/sjur.brandeland@stericsson.com
   * License terms: GNU General Public License (GPL) version 2
   */
b31fa5bad   Joe Perches   net/caif: Use pr_fmt
6
  #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
b482cd205   Sjur Braendeland   net-caif: add CAI...
7
8
9
  #include <linux/stddef.h>
  #include <linux/spinlock.h>
  #include <linux/slab.h>
7e368739e   Jeff Mahoney   net/caif/cfrfml.c...
10
  #include <asm/unaligned.h>
b482cd205   Sjur Braendeland   net-caif: add CAI...
11
12
13
  #include <net/caif/caif_layer.h>
  #include <net/caif/cfsrvl.h>
  #include <net/caif/cfpkt.h>
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
14
  #define container_obj(layr) container_of(layr, struct cfrfml, serv.layer)
b482cd205   Sjur Braendeland   net-caif: add CAI...
15
  #define RFM_SEGMENTATION_BIT 0x01
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
16
  #define RFM_HEAD_SIZE 7
b482cd205   Sjur Braendeland   net-caif: add CAI...
17
18
19
  
  static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt);
  static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt);
b482cd205   Sjur Braendeland   net-caif: add CAI...
20

a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
21
22
23
24
25
26
27
28
29
  struct cfrfml {
  	struct cfsrvl serv;
  	struct cfpkt *incomplete_frm;
  	int fragment_size;
  	u8  seghead[6];
  	u16 pdu_size;
  	/* Protects serialized processing of packets */
  	spinlock_t sync;
  };
43e369210   sjur.brandeland@stericsson.com   caif: Move refcou...
30
  static void cfrfml_release(struct cflayer *layer)
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
31
  {
43e369210   sjur.brandeland@stericsson.com   caif: Move refcou...
32
  	struct cfsrvl *srvl = container_of(layer, struct cfsrvl, layer);
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
33
34
35
36
37
38
39
40
41
42
  	struct cfrfml *rfml = container_obj(&srvl->layer);
  
  	if (rfml->incomplete_frm)
  		cfpkt_destroy(rfml->incomplete_frm);
  
  	kfree(srvl);
  }
  
  struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info,
  					int mtu_size)
b482cd205   Sjur Braendeland   net-caif: add CAI...
43
  {
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
44
45
46
  	int tmp;
  	struct cfrfml *this =
  		kzalloc(sizeof(struct cfrfml), GFP_ATOMIC);
b1c74247b   Sjur Braendeland   caif: Bugfix not ...
47

a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
48
  	if (!this) {
b31fa5bad   Joe Perches   net/caif: Use pr_fmt
49
50
  		pr_warn("Out of memory
  ");
b482cd205   Sjur Braendeland   net-caif: add CAI...
51
52
  		return NULL;
  	}
b1c74247b   Sjur Braendeland   caif: Bugfix not ...
53

a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
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
  	cfsrvl_init(&this->serv, channel_id, dev_info, false);
  	this->serv.release = cfrfml_release;
  	this->serv.layer.receive = cfrfml_receive;
  	this->serv.layer.transmit = cfrfml_transmit;
  
  	/* Round down to closest multiple of 16 */
  	tmp = (mtu_size - RFM_HEAD_SIZE - 6) / 16;
  	tmp *= 16;
  
  	this->fragment_size = tmp;
  	spin_lock_init(&this->sync);
  	snprintf(this->serv.layer.name, CAIF_LAYER_NAME_SZ,
  		"rfm%d", channel_id);
  
  	return &this->serv.layer;
  }
  
  static struct cfpkt *rfm_append(struct cfrfml *rfml, char *seghead,
  			struct cfpkt *pkt, int *err)
  {
  	struct cfpkt *tmppkt;
  	*err = -EPROTO;
  	/* n-th but not last segment */
  
  	if (cfpkt_extr_head(pkt, seghead, 6) < 0)
  		return NULL;
  
  	/* Verify correct header */
  	if (memcmp(seghead, rfml->seghead, 6) != 0)
  		return NULL;
  
  	tmppkt = cfpkt_append(rfml->incomplete_frm, pkt,
  			rfml->pdu_size + RFM_HEAD_SIZE);
  
  	/* If cfpkt_append failes input pkts are not freed */
  	*err = -ENOMEM;
  	if (tmppkt == NULL)
  		return NULL;
b1c74247b   Sjur Braendeland   caif: Bugfix not ...
92

a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
93
94
  	*err = 0;
  	return tmppkt;
b482cd205   Sjur Braendeland   net-caif: add CAI...
95
  }
b482cd205   Sjur Braendeland   net-caif: add CAI...
96
97
98
99
  static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt)
  {
  	u8 tmp;
  	bool segmented;
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
100
101
102
103
  	int err;
  	u8 seghead[6];
  	struct cfrfml *rfml;
  	struct cfpkt *tmppkt = NULL;
b482cd205   Sjur Braendeland   net-caif: add CAI...
104
105
  	caif_assert(layr->up != NULL);
  	caif_assert(layr->receive != NULL);
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
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
  	rfml = container_obj(layr);
  	spin_lock(&rfml->sync);
  
  	err = -EPROTO;
  	if (cfpkt_extr_head(pkt, &tmp, 1) < 0)
  		goto out;
  	segmented = tmp & RFM_SEGMENTATION_BIT;
  
  	if (segmented) {
  		if (rfml->incomplete_frm == NULL) {
  			/* Initial Segment */
  			if (cfpkt_peek_head(pkt, rfml->seghead, 6) < 0)
  				goto out;
  
  			rfml->pdu_size = get_unaligned_le16(rfml->seghead+4);
  
  			if (cfpkt_erroneous(pkt))
  				goto out;
  			rfml->incomplete_frm = pkt;
  			pkt = NULL;
  		} else {
  
  			tmppkt = rfm_append(rfml, seghead, pkt, &err);
  			if (tmppkt == NULL)
  				goto out;
  
  			if (cfpkt_erroneous(tmppkt))
  				goto out;
  
  			rfml->incomplete_frm = tmppkt;
  
  
  			if (cfpkt_erroneous(tmppkt))
  				goto out;
  		}
  		err = 0;
  		goto out;
  	}
  
  	if (rfml->incomplete_frm) {
  
  		/* Last Segment */
  		tmppkt = rfm_append(rfml, seghead, pkt, &err);
  		if (tmppkt == NULL)
  			goto out;
  
  		if (cfpkt_erroneous(tmppkt))
  			goto out;
  
  		rfml->incomplete_frm = NULL;
  		pkt = tmppkt;
  		tmppkt = NULL;
  
  		/* Verify that length is correct */
  		err = EPROTO;
  		if (rfml->pdu_size != cfpkt_getlen(pkt) - RFM_HEAD_SIZE + 1)
  			goto out;
  	}
  
  	err = rfml->serv.layer.up->receive(rfml->serv.layer.up, pkt);
  
  out:
  
  	if (err != 0) {
  		if (tmppkt)
  			cfpkt_destroy(tmppkt);
  		if (pkt)
  			cfpkt_destroy(pkt);
  		if (rfml->incomplete_frm)
  			cfpkt_destroy(rfml->incomplete_frm);
  		rfml->incomplete_frm = NULL;
b31fa5bad   Joe Perches   net/caif: Use pr_fmt
177
178
  		pr_info("Connection error %d triggered on RFM link
  ", err);
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
179
180
181
182
183
184
185
186
187
188
189
190
  
  		/* Trigger connection error upon failure.*/
  		layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND,
  					rfml->serv.dev_info.id);
  	}
  	spin_unlock(&rfml->sync);
  	return err;
  }
  
  
  static int cfrfml_transmit_segment(struct cfrfml *rfml, struct cfpkt *pkt)
  {
f2527ec43   AndrĂ© Carvalho de Matos   caif: Bugfix for ...
191
  	caif_assert(cfpkt_getlen(pkt) < rfml->fragment_size);
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
192
193
194
  
  	/* Add info for MUX-layer to route the packet out. */
  	cfpkt_info(pkt)->channel_id = rfml->serv.layer.id;
b482cd205   Sjur Braendeland   net-caif: add CAI...
195
196
  
  	/*
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
197
198
  	 * To optimize alignment, we add up the size of CAIF header before
  	 * payload.
b482cd205   Sjur Braendeland   net-caif: add CAI...
199
  	 */
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
200
201
  	cfpkt_info(pkt)->hdr_len = RFM_HEAD_SIZE;
  	cfpkt_info(pkt)->dev_info = &rfml->serv.dev_info;
b482cd205   Sjur Braendeland   net-caif: add CAI...
202

a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
203
  	return rfml->serv.layer.dn->transmit(rfml->serv.layer.dn, pkt);
b482cd205   Sjur Braendeland   net-caif: add CAI...
204
205
206
207
  }
  
  static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)
  {
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
208
209
210
211
212
213
  	int err;
  	u8 seg;
  	u8 head[6];
  	struct cfpkt *rearpkt = NULL;
  	struct cfpkt *frontpkt = pkt;
  	struct cfrfml *rfml = container_obj(layr);
b482cd205   Sjur Braendeland   net-caif: add CAI...
214
215
216
  
  	caif_assert(layr->dn != NULL);
  	caif_assert(layr->dn->transmit != NULL);
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  	if (!cfsrvl_ready(&rfml->serv, &err))
  		return err;
  
  	err = -EPROTO;
  	if (cfpkt_getlen(pkt) <= RFM_HEAD_SIZE-1)
  		goto out;
  
  	err = 0;
  	if (cfpkt_getlen(pkt) > rfml->fragment_size + RFM_HEAD_SIZE)
  		err = cfpkt_peek_head(pkt, head, 6);
  
  	if (err < 0)
  		goto out;
  
  	while (cfpkt_getlen(frontpkt) > rfml->fragment_size + RFM_HEAD_SIZE) {
  
  		seg = 1;
  		err = -EPROTO;
  
  		if (cfpkt_add_head(frontpkt, &seg, 1) < 0)
  			goto out;
  		/*
  		 * On OOM error cfpkt_split returns NULL.
  		 *
  		 * NOTE: Segmented pdu is not correctly aligned.
  		 * This has negative performance impact.
  		 */
  
  		rearpkt = cfpkt_split(frontpkt, rfml->fragment_size);
  		if (rearpkt == NULL)
  			goto out;
  
  		err = cfrfml_transmit_segment(rfml, frontpkt);
  
  		if (err != 0)
  			goto out;
  		frontpkt = rearpkt;
  		rearpkt = NULL;
  
  		err = -ENOMEM;
  		if (frontpkt == NULL)
  			goto out;
  		err = -EPROTO;
  		if (cfpkt_add_head(frontpkt, head, 6) < 0)
  			goto out;
b482cd205   Sjur Braendeland   net-caif: add CAI...
262

b482cd205   Sjur Braendeland   net-caif: add CAI...
263
  	}
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
264
265
266
267
268
269
270
271
272
273
274
275
276
  
  	seg = 0;
  	err = -EPROTO;
  
  	if (cfpkt_add_head(frontpkt, &seg, 1) < 0)
  		goto out;
  
  	err = cfrfml_transmit_segment(rfml, frontpkt);
  
  	frontpkt = NULL;
  out:
  
  	if (err != 0) {
b31fa5bad   Joe Perches   net/caif: Use pr_fmt
277
278
  		pr_info("Connection error %d triggered on RFM link
  ", err);
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  		/* Trigger connection error upon failure.*/
  
  		layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND,
  					rfml->serv.dev_info.id);
  
  		if (rearpkt)
  			cfpkt_destroy(rearpkt);
  
  		if (frontpkt && frontpkt != pkt) {
  
  			cfpkt_destroy(frontpkt);
  			/*
  			 * Socket layer will free the original packet,
  			 * but this packet may already be sent and
  			 * freed. So we have to return 0 in this case
  			 * to avoid socket layer to re-free this packet.
  			 * The return of shutdown indication will
  			 * cause connection to be invalidated anyhow.
  			 */
  			err = 0;
  		}
b482cd205   Sjur Braendeland   net-caif: add CAI...
300
  	}
a7da1f55a   Sjur Braendeland   caif: Bugfix - RF...
301
  	return err;
b482cd205   Sjur Braendeland   net-caif: add CAI...
302
  }