1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
3219820Sjeff * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
4219820Sjeff *
5219820Sjeff * This software is available to you under a choice of one of two
6219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
7219820Sjeff * General Public License (GPL) Version 2, available from the file
8219820Sjeff * COPYING in the main directory of this source tree, or the
9219820Sjeff * OpenIB.org BSD license below:
10219820Sjeff *
11219820Sjeff *     Redistribution and use in source and binary forms, with or
12219820Sjeff *     without modification, are permitted provided that the following
13219820Sjeff *     conditions are met:
14219820Sjeff *
15219820Sjeff *      - Redistributions of source code must retain the above
16219820Sjeff *        copyright notice, this list of conditions and the following
17219820Sjeff *        disclaimer.
18219820Sjeff *
19219820Sjeff *      - Redistributions in binary form must reproduce the above
20219820Sjeff *        copyright notice, this list of conditions and the following
21219820Sjeff *        disclaimer in the documentation and/or other materials
22219820Sjeff *        provided with the distribution.
23219820Sjeff *
24219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31219820Sjeff * SOFTWARE.
32219820Sjeff */
33219820Sjeff
34219820Sjeff#include <linux/errno.h>
35219820Sjeff#include <linux/string.h>
36219820Sjeff#include <linux/if_ether.h>
37219820Sjeff
38219820Sjeff#include <rdma/ib_pack.h>
39219820Sjeff
40219820Sjeff#define STRUCT_FIELD(header, field) \
41219820Sjeff	.struct_offset_bytes = offsetof(struct ib_unpacked_ ## header, field),      \
42219820Sjeff	.struct_size_bytes   = sizeof ((struct ib_unpacked_ ## header *) 0)->field, \
43219820Sjeff	.field_name          = #header ":" #field
44219820Sjeff
45219820Sjeffstatic const struct ib_field lrh_table[]  = {
46219820Sjeff	{ STRUCT_FIELD(lrh, virtual_lane),
47219820Sjeff	  .offset_words = 0,
48219820Sjeff	  .offset_bits  = 0,
49219820Sjeff	  .size_bits    = 4 },
50219820Sjeff	{ STRUCT_FIELD(lrh, link_version),
51219820Sjeff	  .offset_words = 0,
52219820Sjeff	  .offset_bits  = 4,
53219820Sjeff	  .size_bits    = 4 },
54219820Sjeff	{ STRUCT_FIELD(lrh, service_level),
55219820Sjeff	  .offset_words = 0,
56219820Sjeff	  .offset_bits  = 8,
57219820Sjeff	  .size_bits    = 4 },
58219820Sjeff	{ RESERVED,
59219820Sjeff	  .offset_words = 0,
60219820Sjeff	  .offset_bits  = 12,
61219820Sjeff	  .size_bits    = 2 },
62219820Sjeff	{ STRUCT_FIELD(lrh, link_next_header),
63219820Sjeff	  .offset_words = 0,
64219820Sjeff	  .offset_bits  = 14,
65219820Sjeff	  .size_bits    = 2 },
66219820Sjeff	{ STRUCT_FIELD(lrh, destination_lid),
67219820Sjeff	  .offset_words = 0,
68219820Sjeff	  .offset_bits  = 16,
69219820Sjeff	  .size_bits    = 16 },
70219820Sjeff	{ RESERVED,
71219820Sjeff	  .offset_words = 1,
72219820Sjeff	  .offset_bits  = 0,
73219820Sjeff	  .size_bits    = 5 },
74219820Sjeff	{ STRUCT_FIELD(lrh, packet_length),
75219820Sjeff	  .offset_words = 1,
76219820Sjeff	  .offset_bits  = 5,
77219820Sjeff	  .size_bits    = 11 },
78219820Sjeff	{ STRUCT_FIELD(lrh, source_lid),
79219820Sjeff	  .offset_words = 1,
80219820Sjeff	  .offset_bits  = 16,
81219820Sjeff	  .size_bits    = 16 }
82219820Sjeff};
83219820Sjeff
84219820Sjeffstatic const struct ib_field eth_table[]  = {
85219820Sjeff	{ STRUCT_FIELD(eth, dmac_h),
86219820Sjeff	  .offset_words = 0,
87219820Sjeff	  .offset_bits  = 0,
88219820Sjeff	  .size_bits    = 32 },
89219820Sjeff	{ STRUCT_FIELD(eth, dmac_l),
90219820Sjeff	  .offset_words = 1,
91219820Sjeff	  .offset_bits  = 0,
92219820Sjeff	  .size_bits    = 16 },
93219820Sjeff	{ STRUCT_FIELD(eth, smac_h),
94219820Sjeff	  .offset_words = 1,
95219820Sjeff	  .offset_bits  = 16,
96219820Sjeff	  .size_bits    = 16 },
97219820Sjeff	{ STRUCT_FIELD(eth, smac_l),
98219820Sjeff	  .offset_words = 2,
99219820Sjeff	  .offset_bits  = 0,
100219820Sjeff	  .size_bits    = 32 },
101219820Sjeff	{ STRUCT_FIELD(eth, type),
102219820Sjeff	  .offset_words = 3,
103219820Sjeff	  .offset_bits  = 0,
104219820Sjeff	  .size_bits    = 16 }
105219820Sjeff};
106219820Sjeff
107219820Sjeffstatic const struct ib_field vlan_table[]  = {
108219820Sjeff	{ STRUCT_FIELD(vlan, tag),
109219820Sjeff	  .offset_words = 0,
110219820Sjeff	  .offset_bits  = 0,
111219820Sjeff	  .size_bits    = 16 },
112219820Sjeff	{ STRUCT_FIELD(vlan, type),
113219820Sjeff	  .offset_words = 0,
114219820Sjeff	  .offset_bits  = 16,
115219820Sjeff	  .size_bits    = 16 }
116219820Sjeff};
117219820Sjeff
118219820Sjeffstatic const struct ib_field grh_table[]  = {
119219820Sjeff	{ STRUCT_FIELD(grh, ip_version),
120219820Sjeff	  .offset_words = 0,
121219820Sjeff	  .offset_bits  = 0,
122219820Sjeff	  .size_bits    = 4 },
123219820Sjeff	{ STRUCT_FIELD(grh, traffic_class),
124219820Sjeff	  .offset_words = 0,
125219820Sjeff	  .offset_bits  = 4,
126219820Sjeff	  .size_bits    = 8 },
127219820Sjeff	{ STRUCT_FIELD(grh, flow_label),
128219820Sjeff	  .offset_words = 0,
129219820Sjeff	  .offset_bits  = 12,
130219820Sjeff	  .size_bits    = 20 },
131219820Sjeff	{ STRUCT_FIELD(grh, payload_length),
132219820Sjeff	  .offset_words = 1,
133219820Sjeff	  .offset_bits  = 0,
134219820Sjeff	  .size_bits    = 16 },
135219820Sjeff	{ STRUCT_FIELD(grh, next_header),
136219820Sjeff	  .offset_words = 1,
137219820Sjeff	  .offset_bits  = 16,
138219820Sjeff	  .size_bits    = 8 },
139219820Sjeff	{ STRUCT_FIELD(grh, hop_limit),
140219820Sjeff	  .offset_words = 1,
141219820Sjeff	  .offset_bits  = 24,
142219820Sjeff	  .size_bits    = 8 },
143219820Sjeff	{ STRUCT_FIELD(grh, source_gid),
144219820Sjeff	  .offset_words = 2,
145219820Sjeff	  .offset_bits  = 0,
146219820Sjeff	  .size_bits    = 128 },
147219820Sjeff	{ STRUCT_FIELD(grh, destination_gid),
148219820Sjeff	  .offset_words = 6,
149219820Sjeff	  .offset_bits  = 0,
150219820Sjeff	  .size_bits    = 128 }
151219820Sjeff};
152219820Sjeff
153219820Sjeffstatic const struct ib_field bth_table[]  = {
154219820Sjeff	{ STRUCT_FIELD(bth, opcode),
155219820Sjeff	  .offset_words = 0,
156219820Sjeff	  .offset_bits  = 0,
157219820Sjeff	  .size_bits    = 8 },
158219820Sjeff	{ STRUCT_FIELD(bth, solicited_event),
159219820Sjeff	  .offset_words = 0,
160219820Sjeff	  .offset_bits  = 8,
161219820Sjeff	  .size_bits    = 1 },
162219820Sjeff	{ STRUCT_FIELD(bth, mig_req),
163219820Sjeff	  .offset_words = 0,
164219820Sjeff	  .offset_bits  = 9,
165219820Sjeff	  .size_bits    = 1 },
166219820Sjeff	{ STRUCT_FIELD(bth, pad_count),
167219820Sjeff	  .offset_words = 0,
168219820Sjeff	  .offset_bits  = 10,
169219820Sjeff	  .size_bits    = 2 },
170219820Sjeff	{ STRUCT_FIELD(bth, transport_header_version),
171219820Sjeff	  .offset_words = 0,
172219820Sjeff	  .offset_bits  = 12,
173219820Sjeff	  .size_bits    = 4 },
174219820Sjeff	{ STRUCT_FIELD(bth, pkey),
175219820Sjeff	  .offset_words = 0,
176219820Sjeff	  .offset_bits  = 16,
177219820Sjeff	  .size_bits    = 16 },
178219820Sjeff	{ RESERVED,
179219820Sjeff	  .offset_words = 1,
180219820Sjeff	  .offset_bits  = 0,
181219820Sjeff	  .size_bits    = 8 },
182219820Sjeff	{ STRUCT_FIELD(bth, destination_qpn),
183219820Sjeff	  .offset_words = 1,
184219820Sjeff	  .offset_bits  = 8,
185219820Sjeff	  .size_bits    = 24 },
186219820Sjeff	{ STRUCT_FIELD(bth, ack_req),
187219820Sjeff	  .offset_words = 2,
188219820Sjeff	  .offset_bits  = 0,
189219820Sjeff	  .size_bits    = 1 },
190219820Sjeff	{ RESERVED,
191219820Sjeff	  .offset_words = 2,
192219820Sjeff	  .offset_bits  = 1,
193219820Sjeff	  .size_bits    = 7 },
194219820Sjeff	{ STRUCT_FIELD(bth, psn),
195219820Sjeff	  .offset_words = 2,
196219820Sjeff	  .offset_bits  = 8,
197219820Sjeff	  .size_bits    = 24 }
198219820Sjeff};
199219820Sjeff
200219820Sjeffstatic const struct ib_field deth_table[] = {
201219820Sjeff	{ STRUCT_FIELD(deth, qkey),
202219820Sjeff	  .offset_words = 0,
203219820Sjeff	  .offset_bits  = 0,
204219820Sjeff	  .size_bits    = 32 },
205219820Sjeff	{ RESERVED,
206219820Sjeff	  .offset_words = 1,
207219820Sjeff	  .offset_bits  = 0,
208219820Sjeff	  .size_bits    = 8 },
209219820Sjeff	{ STRUCT_FIELD(deth, source_qpn),
210219820Sjeff	  .offset_words = 1,
211219820Sjeff	  .offset_bits  = 8,
212219820Sjeff	  .size_bits    = 24 }
213219820Sjeff};
214219820Sjeff
215219820Sjeff/**
216219820Sjeff * ib_ud_header_init - Initialize UD header structure
217219820Sjeff * @payload_bytes:Length of packet payload
218219820Sjeff * @lrh_present: specify if LRH is present
219219820Sjeff * @eth_present: specify if Eth header is present
220219820Sjeff * @vlan_present: packet is tagged vlan
221219820Sjeff * @grh_present:GRH flag (if non-zero, GRH will be included)
222219820Sjeff * @immediate_present: specify if immediate data is present
223219820Sjeff * @header:Structure to initialize
224219820Sjeff */
225219820Sjeffvoid ib_ud_header_init(int     		    payload_bytes,
226219820Sjeff		       int		    lrh_present,
227219820Sjeff		       int		    eth_present,
228219820Sjeff		       int		    vlan_present,
229219820Sjeff		       int    		    grh_present,
230219820Sjeff		       int		    immediate_present,
231219820Sjeff		       struct ib_ud_header *header)
232219820Sjeff{
233242933Sdim	u16 packet_length = 0;
234219820Sjeff
235219820Sjeff	memset(header, 0, sizeof *header);
236219820Sjeff
237219820Sjeff	if (lrh_present) {
238219820Sjeff		header->lrh.link_version     = 0;
239219820Sjeff		header->lrh.link_next_header =
240219820Sjeff			grh_present ? IB_LNH_IBA_GLOBAL : IB_LNH_IBA_LOCAL;
241219820Sjeff		packet_length = IB_LRH_BYTES;
242219820Sjeff	}
243219820Sjeff
244219820Sjeff	if (eth_present) {
245219820Sjeff		if (vlan_present) {
246219820Sjeff			header->eth.type = cpu_to_be16(ETH_P_8021Q);
247219820Sjeff			packet_length += IB_VLAN_BYTES;
248219820Sjeff
249219820Sjeff		}
250219820Sjeff		packet_length += IB_ETH_BYTES;
251219820Sjeff	}
252219820Sjeff
253219820Sjeff	packet_length += IB_BTH_BYTES + IB_DETH_BYTES + payload_bytes +
254219820Sjeff		4       + /* ICRC     */
255219820Sjeff		3;        /* round up */
256219820Sjeff	packet_length /= 4;
257219820Sjeff	if (grh_present) {
258219820Sjeff		packet_length += IB_GRH_BYTES / 4;
259219820Sjeff		header->grh.ip_version = 6;
260219820Sjeff		header->grh.payload_length =
261219820Sjeff			cpu_to_be16((IB_BTH_BYTES  +
262219820Sjeff				     IB_DETH_BYTES +
263219820Sjeff				     payload_bytes +
264219820Sjeff				     4             + /* ICRC     */
265219820Sjeff				     3) & ~3);       /* round up */
266219820Sjeff		header->grh.next_header     = 0x1b;
267219820Sjeff	}
268219820Sjeff
269219820Sjeff	if (lrh_present)
270219820Sjeff		header->lrh.packet_length = cpu_to_be16(packet_length);
271219820Sjeff
272219820Sjeff	if (immediate_present)
273219820Sjeff		header->bth.opcode           = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE;
274219820Sjeff	else
275219820Sjeff		header->bth.opcode           = IB_OPCODE_UD_SEND_ONLY;
276219820Sjeff	header->bth.pad_count                = (4 - payload_bytes) & 3;
277219820Sjeff	header->bth.transport_header_version = 0;
278219820Sjeff
279219820Sjeff	header->lrh_present = lrh_present;
280219820Sjeff	header->eth_present = eth_present;
281219820Sjeff	header->vlan_present = vlan_present;
282219820Sjeff	header->grh_present = grh_present;
283219820Sjeff	header->immediate_present = immediate_present;
284219820Sjeff}
285219820SjeffEXPORT_SYMBOL(ib_ud_header_init);
286219820Sjeff
287219820Sjeff/**
288219820Sjeff * ib_lrh_header_pack - Pack LRH header struct into wire format
289219820Sjeff * @lrh:unpacked LRH header struct
290219820Sjeff * @buf:Buffer to pack into
291219820Sjeff *
292219820Sjeff * ib_lrh_header_pack() packs the LRH header structure @lrh into
293219820Sjeff * wire format in the buffer @buf.
294219820Sjeff */
295219820Sjeffint ib_lrh_header_pack(struct ib_unpacked_lrh *lrh, void *buf)
296219820Sjeff{
297219820Sjeff	ib_pack(lrh_table, ARRAY_SIZE(lrh_table), lrh, buf);
298219820Sjeff	return 0;
299219820Sjeff}
300219820SjeffEXPORT_SYMBOL(ib_lrh_header_pack);
301219820Sjeff
302219820Sjeff/**
303219820Sjeff * ib_lrh_header_unpack - Unpack LRH structure from wire format
304219820Sjeff * @lrh:unpacked LRH header struct
305219820Sjeff * @buf:Buffer to pack into
306219820Sjeff *
307219820Sjeff * ib_lrh_header_unpack() unpacks the LRH header structure from
308219820Sjeff * wire format (in buf) into @lrh.
309219820Sjeff */
310219820Sjeffint ib_lrh_header_unpack(void *buf, struct ib_unpacked_lrh *lrh)
311219820Sjeff{
312219820Sjeff	ib_unpack(lrh_table, ARRAY_SIZE(lrh_table), buf, lrh);
313219820Sjeff	return 0;
314219820Sjeff}
315219820SjeffEXPORT_SYMBOL(ib_lrh_header_unpack);
316219820Sjeff
317219820Sjeff/**
318219820Sjeff * ib_ud_header_pack - Pack UD header struct into wire format
319219820Sjeff * @header:UD header struct
320219820Sjeff * @buf:Buffer to pack into
321219820Sjeff *
322219820Sjeff * ib_ud_header_pack() packs the UD header structure @header into wire
323219820Sjeff * format in the buffer @buf.
324219820Sjeff */
325219820Sjeffint ib_ud_header_pack(struct ib_ud_header *header,
326219820Sjeff		      void                *buf)
327219820Sjeff{
328219820Sjeff	int len = 0;
329219820Sjeff
330219820Sjeff	if (header->lrh_present) {
331219820Sjeff		ib_pack(lrh_table, ARRAY_SIZE(lrh_table),
332219820Sjeff			&header->lrh, buf + len);
333219820Sjeff		len += IB_LRH_BYTES;
334219820Sjeff	}
335219820Sjeff	if (header->eth_present) {
336219820Sjeff		ib_pack(eth_table, ARRAY_SIZE(eth_table),
337219820Sjeff			&header->eth, buf + len);
338219820Sjeff		len += IB_ETH_BYTES;
339219820Sjeff	}
340219820Sjeff
341219820Sjeff
342219820Sjeff	if (header->vlan_present) {
343219820Sjeff		ib_pack(vlan_table, ARRAY_SIZE(vlan_table),
344219820Sjeff			&header->vlan, buf + len);
345219820Sjeff		len += IB_VLAN_BYTES;
346219820Sjeff	}
347219820Sjeff
348219820Sjeff	if (header->grh_present) {
349219820Sjeff		ib_pack(grh_table, ARRAY_SIZE(grh_table),
350219820Sjeff			&header->grh, buf + len);
351219820Sjeff		len += IB_GRH_BYTES;
352219820Sjeff	}
353219820Sjeff
354219820Sjeff	ib_pack(bth_table, ARRAY_SIZE(bth_table),
355219820Sjeff		&header->bth, buf + len);
356219820Sjeff	len += IB_BTH_BYTES;
357219820Sjeff
358219820Sjeff	ib_pack(deth_table, ARRAY_SIZE(deth_table),
359219820Sjeff		&header->deth, buf + len);
360219820Sjeff	len += IB_DETH_BYTES;
361219820Sjeff
362219820Sjeff	if (header->immediate_present) {
363219820Sjeff		memcpy(buf + len, &header->immediate_data, sizeof header->immediate_data);
364219820Sjeff		len += sizeof header->immediate_data;
365219820Sjeff	}
366219820Sjeff
367219820Sjeff	return len;
368219820Sjeff}
369219820SjeffEXPORT_SYMBOL(ib_ud_header_pack);
370219820Sjeff
371219820Sjeff/**
372219820Sjeff * ib_ud_header_unpack - Unpack UD header struct from wire format
373219820Sjeff * @header:UD header struct
374219820Sjeff * @buf:Buffer to pack into
375219820Sjeff *
376219820Sjeff * ib_ud_header_pack() unpacks the UD header structure @header from wire
377219820Sjeff * format in the buffer @buf.
378219820Sjeff */
379219820Sjeffint ib_ud_header_unpack(void                *buf,
380219820Sjeff			struct ib_ud_header *header)
381219820Sjeff{
382219820Sjeff	ib_unpack(lrh_table, ARRAY_SIZE(lrh_table),
383219820Sjeff		  buf, &header->lrh);
384219820Sjeff	buf += IB_LRH_BYTES;
385219820Sjeff
386219820Sjeff	if (header->lrh.link_version != 0) {
387219820Sjeff		printk(KERN_WARNING "Invalid LRH.link_version %d\n",
388219820Sjeff		       header->lrh.link_version);
389219820Sjeff		return -EINVAL;
390219820Sjeff	}
391219820Sjeff
392219820Sjeff	switch (header->lrh.link_next_header) {
393219820Sjeff	case IB_LNH_IBA_LOCAL:
394219820Sjeff		header->grh_present = 0;
395219820Sjeff		break;
396219820Sjeff
397219820Sjeff	case IB_LNH_IBA_GLOBAL:
398219820Sjeff		header->grh_present = 1;
399219820Sjeff		ib_unpack(grh_table, ARRAY_SIZE(grh_table),
400219820Sjeff			  buf, &header->grh);
401219820Sjeff		buf += IB_GRH_BYTES;
402219820Sjeff
403219820Sjeff		if (header->grh.ip_version != 6) {
404219820Sjeff			printk(KERN_WARNING "Invalid GRH.ip_version %d\n",
405219820Sjeff			       header->grh.ip_version);
406219820Sjeff			return -EINVAL;
407219820Sjeff		}
408219820Sjeff		if (header->grh.next_header != 0x1b) {
409219820Sjeff			printk(KERN_WARNING "Invalid GRH.next_header 0x%02x\n",
410219820Sjeff			       header->grh.next_header);
411219820Sjeff			return -EINVAL;
412219820Sjeff		}
413219820Sjeff		break;
414219820Sjeff
415219820Sjeff	default:
416219820Sjeff		printk(KERN_WARNING "Invalid LRH.link_next_header %d\n",
417219820Sjeff		       header->lrh.link_next_header);
418219820Sjeff		return -EINVAL;
419219820Sjeff	}
420219820Sjeff
421219820Sjeff	ib_unpack(bth_table, ARRAY_SIZE(bth_table),
422219820Sjeff		  buf, &header->bth);
423219820Sjeff	buf += IB_BTH_BYTES;
424219820Sjeff
425219820Sjeff	switch (header->bth.opcode) {
426219820Sjeff	case IB_OPCODE_UD_SEND_ONLY:
427219820Sjeff		header->immediate_present = 0;
428219820Sjeff		break;
429219820Sjeff	case IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE:
430219820Sjeff		header->immediate_present = 1;
431219820Sjeff		break;
432219820Sjeff	default:
433219820Sjeff		printk(KERN_WARNING "Invalid BTH.opcode 0x%02x\n",
434219820Sjeff		       header->bth.opcode);
435219820Sjeff		return -EINVAL;
436219820Sjeff	}
437219820Sjeff
438219820Sjeff	if (header->bth.transport_header_version != 0) {
439219820Sjeff		printk(KERN_WARNING "Invalid BTH.transport_header_version %d\n",
440219820Sjeff		       header->bth.transport_header_version);
441219820Sjeff		return -EINVAL;
442219820Sjeff	}
443219820Sjeff
444219820Sjeff	ib_unpack(deth_table, ARRAY_SIZE(deth_table),
445219820Sjeff		  buf, &header->deth);
446219820Sjeff	buf += IB_DETH_BYTES;
447219820Sjeff
448219820Sjeff	if (header->immediate_present)
449219820Sjeff		memcpy(&header->immediate_data, buf, sizeof header->immediate_data);
450219820Sjeff
451219820Sjeff	return 0;
452219820Sjeff}
453219820SjeffEXPORT_SYMBOL(ib_ud_header_unpack);
454