1/*
2 * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
3 * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses.  You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directory of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 *     Redistribution and use in source and binary forms, with or
12 *     without modification, are permitted provided that the following
13 *     conditions are met:
14 *
15 *      - Redistributions of source code must retain the above
16 *        copyright notice, this list of conditions and the following
17 *        disclaimer.
18 *
19 *      - Redistributions in binary form must reproduce the above
20 *        copyright notice, this list of conditions and the following
21 *        disclaimer in the documentation and/or other materials
22 *        provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 */
33
34#include <linux/errno.h>
35#include <linux/string.h>
36#include <linux/export.h>
37#include <linux/if_ether.h>
38
39#include <rdma/ib_pack.h>
40
41#define STRUCT_FIELD(header, field) \
42	.struct_offset_bytes = offsetof(struct ib_unpacked_ ## header, field),      \
43	.struct_size_bytes   = sizeof ((struct ib_unpacked_ ## header *) 0)->field, \
44	.field_name          = #header ":" #field
45
46static const struct ib_field lrh_table[]  = {
47	{ STRUCT_FIELD(lrh, virtual_lane),
48	  .offset_words = 0,
49	  .offset_bits  = 0,
50	  .size_bits    = 4 },
51	{ STRUCT_FIELD(lrh, link_version),
52	  .offset_words = 0,
53	  .offset_bits  = 4,
54	  .size_bits    = 4 },
55	{ STRUCT_FIELD(lrh, service_level),
56	  .offset_words = 0,
57	  .offset_bits  = 8,
58	  .size_bits    = 4 },
59	{ RESERVED,
60	  .offset_words = 0,
61	  .offset_bits  = 12,
62	  .size_bits    = 2 },
63	{ STRUCT_FIELD(lrh, link_next_header),
64	  .offset_words = 0,
65	  .offset_bits  = 14,
66	  .size_bits    = 2 },
67	{ STRUCT_FIELD(lrh, destination_lid),
68	  .offset_words = 0,
69	  .offset_bits  = 16,
70	  .size_bits    = 16 },
71	{ RESERVED,
72	  .offset_words = 1,
73	  .offset_bits  = 0,
74	  .size_bits    = 5 },
75	{ STRUCT_FIELD(lrh, packet_length),
76	  .offset_words = 1,
77	  .offset_bits  = 5,
78	  .size_bits    = 11 },
79	{ STRUCT_FIELD(lrh, source_lid),
80	  .offset_words = 1,
81	  .offset_bits  = 16,
82	  .size_bits    = 16 }
83};
84
85static const struct ib_field eth_table[]  = {
86	{ STRUCT_FIELD(eth, dmac_h),
87	  .offset_words = 0,
88	  .offset_bits  = 0,
89	  .size_bits    = 32 },
90	{ STRUCT_FIELD(eth, dmac_l),
91	  .offset_words = 1,
92	  .offset_bits  = 0,
93	  .size_bits    = 16 },
94	{ STRUCT_FIELD(eth, smac_h),
95	  .offset_words = 1,
96	  .offset_bits  = 16,
97	  .size_bits    = 16 },
98	{ STRUCT_FIELD(eth, smac_l),
99	  .offset_words = 2,
100	  .offset_bits  = 0,
101	  .size_bits    = 32 },
102	{ STRUCT_FIELD(eth, type),
103	  .offset_words = 3,
104	  .offset_bits  = 0,
105	  .size_bits    = 16 }
106};
107
108static const struct ib_field vlan_table[]  = {
109	{ STRUCT_FIELD(vlan, tag),
110	  .offset_words = 0,
111	  .offset_bits  = 0,
112	  .size_bits    = 16 },
113	{ STRUCT_FIELD(vlan, type),
114	  .offset_words = 0,
115	  .offset_bits  = 16,
116	  .size_bits    = 16 }
117};
118
119static const struct ib_field grh_table[]  = {
120	{ STRUCT_FIELD(grh, ip_version),
121	  .offset_words = 0,
122	  .offset_bits  = 0,
123	  .size_bits    = 4 },
124	{ STRUCT_FIELD(grh, traffic_class),
125	  .offset_words = 0,
126	  .offset_bits  = 4,
127	  .size_bits    = 8 },
128	{ STRUCT_FIELD(grh, flow_label),
129	  .offset_words = 0,
130	  .offset_bits  = 12,
131	  .size_bits    = 20 },
132	{ STRUCT_FIELD(grh, payload_length),
133	  .offset_words = 1,
134	  .offset_bits  = 0,
135	  .size_bits    = 16 },
136	{ STRUCT_FIELD(grh, next_header),
137	  .offset_words = 1,
138	  .offset_bits  = 16,
139	  .size_bits    = 8 },
140	{ STRUCT_FIELD(grh, hop_limit),
141	  .offset_words = 1,
142	  .offset_bits  = 24,
143	  .size_bits    = 8 },
144	{ STRUCT_FIELD(grh, source_gid),
145	  .offset_words = 2,
146	  .offset_bits  = 0,
147	  .size_bits    = 128 },
148	{ STRUCT_FIELD(grh, destination_gid),
149	  .offset_words = 6,
150	  .offset_bits  = 0,
151	  .size_bits    = 128 }
152};
153
154static const struct ib_field bth_table[]  = {
155	{ STRUCT_FIELD(bth, opcode),
156	  .offset_words = 0,
157	  .offset_bits  = 0,
158	  .size_bits    = 8 },
159	{ STRUCT_FIELD(bth, solicited_event),
160	  .offset_words = 0,
161	  .offset_bits  = 8,
162	  .size_bits    = 1 },
163	{ STRUCT_FIELD(bth, mig_req),
164	  .offset_words = 0,
165	  .offset_bits  = 9,
166	  .size_bits    = 1 },
167	{ STRUCT_FIELD(bth, pad_count),
168	  .offset_words = 0,
169	  .offset_bits  = 10,
170	  .size_bits    = 2 },
171	{ STRUCT_FIELD(bth, transport_header_version),
172	  .offset_words = 0,
173	  .offset_bits  = 12,
174	  .size_bits    = 4 },
175	{ STRUCT_FIELD(bth, pkey),
176	  .offset_words = 0,
177	  .offset_bits  = 16,
178	  .size_bits    = 16 },
179	{ RESERVED,
180	  .offset_words = 1,
181	  .offset_bits  = 0,
182	  .size_bits    = 8 },
183	{ STRUCT_FIELD(bth, destination_qpn),
184	  .offset_words = 1,
185	  .offset_bits  = 8,
186	  .size_bits    = 24 },
187	{ STRUCT_FIELD(bth, ack_req),
188	  .offset_words = 2,
189	  .offset_bits  = 0,
190	  .size_bits    = 1 },
191	{ RESERVED,
192	  .offset_words = 2,
193	  .offset_bits  = 1,
194	  .size_bits    = 7 },
195	{ STRUCT_FIELD(bth, psn),
196	  .offset_words = 2,
197	  .offset_bits  = 8,
198	  .size_bits    = 24 }
199};
200
201static const struct ib_field deth_table[] = {
202	{ STRUCT_FIELD(deth, qkey),
203	  .offset_words = 0,
204	  .offset_bits  = 0,
205	  .size_bits    = 32 },
206	{ RESERVED,
207	  .offset_words = 1,
208	  .offset_bits  = 0,
209	  .size_bits    = 8 },
210	{ STRUCT_FIELD(deth, source_qpn),
211	  .offset_words = 1,
212	  .offset_bits  = 8,
213	  .size_bits    = 24 }
214};
215
216/**
217 * ib_ud_header_init - Initialize UD header structure
218 * @payload_bytes:Length of packet payload
219 * @lrh_present: specify if LRH is present
220 * @eth_present: specify if Eth header is present
221 * @vlan_present: packet is tagged vlan
222 * @grh_present:GRH flag (if non-zero, GRH will be included)
223 * @immediate_present: specify if immediate data is present
224 * @header:Structure to initialize
225 */
226void ib_ud_header_init(int     		    payload_bytes,
227		       int		    lrh_present,
228		       int		    eth_present,
229		       int		    vlan_present,
230		       int    		    grh_present,
231		       int		    immediate_present,
232		       struct ib_ud_header *header)
233{
234	memset(header, 0, sizeof *header);
235
236	if (lrh_present) {
237		u16 packet_length;
238
239		header->lrh.link_version     = 0;
240		header->lrh.link_next_header =
241			grh_present ? IB_LNH_IBA_GLOBAL : IB_LNH_IBA_LOCAL;
242		packet_length = (IB_LRH_BYTES	+
243				 IB_BTH_BYTES	+
244				 IB_DETH_BYTES	+
245				 (grh_present ? IB_GRH_BYTES : 0) +
246				 payload_bytes	+
247				 4		+ /* ICRC     */
248				 3) / 4;	  /* round up */
249		header->lrh.packet_length = cpu_to_be16(packet_length);
250	}
251
252	if (vlan_present)
253		header->eth.type = cpu_to_be16(ETH_P_8021Q);
254
255	if (grh_present) {
256		header->grh.ip_version      = 6;
257		header->grh.payload_length  =
258			cpu_to_be16((IB_BTH_BYTES     +
259				     IB_DETH_BYTES    +
260				     payload_bytes    +
261				     4                + /* ICRC     */
262				     3) & ~3);          /* round up */
263		header->grh.next_header     = 0x1b;
264	}
265
266	if (immediate_present)
267		header->bth.opcode           = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE;
268	else
269		header->bth.opcode           = IB_OPCODE_UD_SEND_ONLY;
270	header->bth.pad_count                = (4 - payload_bytes) & 3;
271	header->bth.transport_header_version = 0;
272
273	header->lrh_present = lrh_present;
274	header->eth_present = eth_present;
275	header->vlan_present = vlan_present;
276	header->grh_present = grh_present;
277	header->immediate_present = immediate_present;
278}
279EXPORT_SYMBOL(ib_ud_header_init);
280
281/**
282 * ib_ud_header_pack - Pack UD header struct into wire format
283 * @header:UD header struct
284 * @buf:Buffer to pack into
285 *
286 * ib_ud_header_pack() packs the UD header structure @header into wire
287 * format in the buffer @buf.
288 */
289int ib_ud_header_pack(struct ib_ud_header *header,
290		      void                *buf)
291{
292	int len = 0;
293
294	if (header->lrh_present) {
295		ib_pack(lrh_table, ARRAY_SIZE(lrh_table),
296			&header->lrh, buf + len);
297		len += IB_LRH_BYTES;
298	}
299	if (header->eth_present) {
300		ib_pack(eth_table, ARRAY_SIZE(eth_table),
301			&header->eth, buf + len);
302		len += IB_ETH_BYTES;
303	}
304	if (header->vlan_present) {
305		ib_pack(vlan_table, ARRAY_SIZE(vlan_table),
306			&header->vlan, buf + len);
307		len += IB_VLAN_BYTES;
308	}
309	if (header->grh_present) {
310		ib_pack(grh_table, ARRAY_SIZE(grh_table),
311			&header->grh, buf + len);
312		len += IB_GRH_BYTES;
313	}
314
315	ib_pack(bth_table, ARRAY_SIZE(bth_table),
316		&header->bth, buf + len);
317	len += IB_BTH_BYTES;
318
319	ib_pack(deth_table, ARRAY_SIZE(deth_table),
320		&header->deth, buf + len);
321	len += IB_DETH_BYTES;
322
323	if (header->immediate_present) {
324		memcpy(buf + len, &header->immediate_data, sizeof header->immediate_data);
325		len += sizeof header->immediate_data;
326	}
327
328	return len;
329}
330EXPORT_SYMBOL(ib_ud_header_pack);
331
332/**
333 * ib_ud_header_unpack - Unpack UD header struct from wire format
334 * @header:UD header struct
335 * @buf:Buffer to pack into
336 *
337 * ib_ud_header_pack() unpacks the UD header structure @header from wire
338 * format in the buffer @buf.
339 */
340int ib_ud_header_unpack(void                *buf,
341			struct ib_ud_header *header)
342{
343	ib_unpack(lrh_table, ARRAY_SIZE(lrh_table),
344		  buf, &header->lrh);
345	buf += IB_LRH_BYTES;
346
347	if (header->lrh.link_version != 0) {
348		printk(KERN_WARNING "Invalid LRH.link_version %d\n",
349		       header->lrh.link_version);
350		return -EINVAL;
351	}
352
353	switch (header->lrh.link_next_header) {
354	case IB_LNH_IBA_LOCAL:
355		header->grh_present = 0;
356		break;
357
358	case IB_LNH_IBA_GLOBAL:
359		header->grh_present = 1;
360		ib_unpack(grh_table, ARRAY_SIZE(grh_table),
361			  buf, &header->grh);
362		buf += IB_GRH_BYTES;
363
364		if (header->grh.ip_version != 6) {
365			printk(KERN_WARNING "Invalid GRH.ip_version %d\n",
366			       header->grh.ip_version);
367			return -EINVAL;
368		}
369		if (header->grh.next_header != 0x1b) {
370			printk(KERN_WARNING "Invalid GRH.next_header 0x%02x\n",
371			       header->grh.next_header);
372			return -EINVAL;
373		}
374		break;
375
376	default:
377		printk(KERN_WARNING "Invalid LRH.link_next_header %d\n",
378		       header->lrh.link_next_header);
379		return -EINVAL;
380	}
381
382	ib_unpack(bth_table, ARRAY_SIZE(bth_table),
383		  buf, &header->bth);
384	buf += IB_BTH_BYTES;
385
386	switch (header->bth.opcode) {
387	case IB_OPCODE_UD_SEND_ONLY:
388		header->immediate_present = 0;
389		break;
390	case IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE:
391		header->immediate_present = 1;
392		break;
393	default:
394		printk(KERN_WARNING "Invalid BTH.opcode 0x%02x\n",
395		       header->bth.opcode);
396		return -EINVAL;
397	}
398
399	if (header->bth.transport_header_version != 0) {
400		printk(KERN_WARNING "Invalid BTH.transport_header_version %d\n",
401		       header->bth.transport_header_version);
402		return -EINVAL;
403	}
404
405	ib_unpack(deth_table, ARRAY_SIZE(deth_table),
406		  buf, &header->deth);
407	buf += IB_DETH_BYTES;
408
409	if (header->immediate_present)
410		memcpy(&header->immediate_data, buf, sizeof header->immediate_data);
411
412	return 0;
413}
414EXPORT_SYMBOL(ib_ud_header_unpack);
415