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