[PATCH v8 4/8] thunderbolt: Networking state machine

From: Amir Levy
Date: Wed Sep 28 2016 - 10:49:52 EST


This patch builds the peer to peer communication path.
Communication is established by a negotiation process whereby messages are
sent back and forth between the peers until a connection is established.
This includes the Thunderbolt Network driver communication with the second
peer via Intel Connection Manager(ICM) firmware.
+--------------------+ +--------------------+
|Host 1 | |Host 2 |
| | | |
| +-----------+ | | +-----------+ |
| |Thunderbolt| | | |Thunderbolt| |
| |Networking | | | |Networking | |
| |Driver | | | |Driver | |
| +-----------+ | | +-----------+ |
| ^ | | ^ |
| | | | | |
| +------------+---+ | | +------------+---+ |
| |Thunderbolt | | | | |Thunderbolt | | |
| |Controller v | | | |Controller v | |
| | +---+ | | | | +---+ | |
| | |ICM|<-+-+------------+-+-------->|ICM| | |
| | +---+ | | | | +---+ | |
| +----------------+ | | +----------------+ |
+--------------------+ +--------------------+
Note that this patch only establishes the link between the two hosts and
not Network Packet handling - this is dealt with in the next patch.

Signed-off-by: Amir Levy <amir.jer.levy@xxxxxxxxx>
---
drivers/thunderbolt/icm/Makefile | 2 +-
drivers/thunderbolt/icm/icm_nhi.c | 262 ++++++++++++-
drivers/thunderbolt/icm/net.c | 783 ++++++++++++++++++++++++++++++++++++++
drivers/thunderbolt/icm/net.h | 70 ++++
4 files changed, 1109 insertions(+), 8 deletions(-)
create mode 100644 drivers/thunderbolt/icm/net.c

diff --git a/drivers/thunderbolt/icm/Makefile b/drivers/thunderbolt/icm/Makefile
index f0d0fbb..94a2797 100644
--- a/drivers/thunderbolt/icm/Makefile
+++ b/drivers/thunderbolt/icm/Makefile
@@ -1,2 +1,2 @@
obj-${CONFIG_THUNDERBOLT_ICM} += thunderbolt-icm.o
-thunderbolt-icm-objs := icm_nhi.o
+thunderbolt-icm-objs := icm_nhi.o net.o
diff --git a/drivers/thunderbolt/icm/icm_nhi.c b/drivers/thunderbolt/icm/icm_nhi.c
index 23047d3..a849698 100644
--- a/drivers/thunderbolt/icm/icm_nhi.c
+++ b/drivers/thunderbolt/icm/icm_nhi.c
@@ -64,6 +64,13 @@ static const struct nla_policy nhi_genl_policy[NHI_ATTR_MAX + 1] = {
.len = TBT_ICM_RING_MAX_FRAME_SIZE },
[NHI_ATTR_MSG_FROM_ICM] = { .type = NLA_BINARY,
.len = TBT_ICM_RING_MAX_FRAME_SIZE },
+ [NHI_ATTR_LOCAL_ROUTE_STRING] = {
+ .len = sizeof(struct route_string) },
+ [NHI_ATTR_LOCAL_UUID] = { .len = sizeof(uuid_be) },
+ [NHI_ATTR_REMOTE_UUID] = { .len = sizeof(uuid_be) },
+ [NHI_ATTR_LOCAL_DEPTH] = { .type = NLA_U8, },
+ [NHI_ATTR_ENABLE_FULL_E2E] = { .type = NLA_FLAG, },
+ [NHI_ATTR_MATCH_FRAME_ID] = { .type = NLA_FLAG, },
};

/* NHI genetlink family */
@@ -480,6 +487,29 @@ int nhi_mailbox(struct tbt_nhi_ctxt *nhi_ctxt, u32 cmd, u32 data, bool deinit)
return 0;
}

+static inline bool nhi_is_path_disconnected(u32 cmd, u8 num_ports)
+{
+ return (cmd >= DISCONNECT_PORT_A_INTER_DOMAIN_PATH &&
+ cmd < (DISCONNECT_PORT_A_INTER_DOMAIN_PATH + num_ports));
+}
+
+static int nhi_mailbox_disconn_path(struct tbt_nhi_ctxt *nhi_ctxt, u32 cmd)
+ __releases(&controllers_list_mutex)
+{
+ struct port_net_dev *port;
+ u32 port_num = cmd - DISCONNECT_PORT_A_INTER_DOMAIN_PATH;
+
+ port = &(nhi_ctxt->net_devices[port_num]);
+ mutex_lock(&port->state_mutex);
+
+ mutex_unlock(&controllers_list_mutex);
+ port->medium_sts = MEDIUM_READY_FOR_APPROVAL;
+ if (port->net_dev)
+ negotiation_events(port->net_dev, MEDIUM_DISCONNECTED);
+ mutex_unlock(&port->state_mutex);
+ return 0;
+}
+
static int nhi_mailbox_generic(struct tbt_nhi_ctxt *nhi_ctxt, u32 mb_cmd)
__releases(&controllers_list_mutex)
{
@@ -526,13 +556,90 @@ static int nhi_genl_mailbox(__always_unused struct sk_buff *u_skb,
return -ERESTART;

nhi_ctxt = nhi_search_ctxt(*(u32 *)info->userhdr);
- if (nhi_ctxt && !nhi_ctxt->d0_exit)
- return nhi_mailbox_generic(nhi_ctxt, mb_cmd);
+ if (nhi_ctxt && !nhi_ctxt->d0_exit) {
+
+ /* rwsem is released later by the below functions */
+ if (nhi_is_path_disconnected(cmd, nhi_ctxt->num_ports))
+ return nhi_mailbox_disconn_path(nhi_ctxt, cmd);
+ else
+ return nhi_mailbox_generic(nhi_ctxt, mb_cmd);
+
+ }

mutex_unlock(&controllers_list_mutex);
return -ENODEV;
}

+static int nhi_genl_approve_networking(__always_unused struct sk_buff *u_skb,
+ struct genl_info *info)
+{
+ struct tbt_nhi_ctxt *nhi_ctxt;
+ struct route_string *route_str;
+ int res = -ENODEV;
+ u8 port_num;
+
+ if (!info || !info->userhdr || !info->attrs ||
+ !info->attrs[NHI_ATTR_LOCAL_ROUTE_STRING] ||
+ !info->attrs[NHI_ATTR_LOCAL_UUID] ||
+ !info->attrs[NHI_ATTR_REMOTE_UUID] ||
+ !info->attrs[NHI_ATTR_LOCAL_DEPTH])
+ return -EINVAL;
+
+ /*
+ * route_str is an unique topological address
+ * used for approving remote controller
+ */
+ route_str = nla_data(info->attrs[NHI_ATTR_LOCAL_ROUTE_STRING]);
+ /* extracts the port we're connected to */
+ port_num = PORT_NUM_FROM_LINK(L0_PORT_NUM(route_str->lo));
+
+ if (mutex_lock_interruptible(&controllers_list_mutex))
+ return -ERESTART;
+
+ nhi_ctxt = nhi_search_ctxt(*(u32 *)info->userhdr);
+ if (nhi_ctxt && !nhi_ctxt->d0_exit) {
+ struct port_net_dev *port;
+
+ if (port_num >= nhi_ctxt->num_ports) {
+ res = -EINVAL;
+ goto free_ctl_list;
+ }
+
+ port = &(nhi_ctxt->net_devices[port_num]);
+
+ mutex_lock(&port->state_mutex);
+ mutex_unlock(&controllers_list_mutex);
+
+ if (port->medium_sts != MEDIUM_READY_FOR_APPROVAL)
+ goto unlock;
+
+ port->medium_sts = MEDIUM_READY_FOR_CONNECTION;
+
+ if (!port->net_dev) {
+ port->net_dev = nhi_alloc_etherdev(nhi_ctxt, port_num,
+ info);
+ if (!port->net_dev) {
+ mutex_unlock(&port->state_mutex);
+ return -ENOMEM;
+ }
+ } else {
+ nhi_update_etherdev(nhi_ctxt, port->net_dev, info);
+
+ negotiation_events(port->net_dev,
+ MEDIUM_READY_FOR_CONNECTION);
+ }
+
+unlock:
+ mutex_unlock(&port->state_mutex);
+
+ return 0;
+ }
+
+free_ctl_list:
+ mutex_unlock(&controllers_list_mutex);
+
+ return res;
+}

static int nhi_genl_send_msg(struct tbt_nhi_ctxt *nhi_ctxt, enum pdf_value pdf,
const u8 *msg, u32 msg_len)
@@ -579,17 +686,127 @@ genl_put_reply_failure:
return res;
}

+static bool nhi_handle_inter_domain_msg(struct tbt_nhi_ctxt *nhi_ctxt,
+ struct thunderbolt_ip_header *hdr)
+{
+ struct port_net_dev *port;
+ u8 port_num;
+
+ const uuid_be proto_uuid = APPLE_THUNDERBOLT_IP_PROTOCOL_UUID;
+
+ if (uuid_be_cmp(proto_uuid, hdr->apple_tbt_ip_proto_uuid) != 0)
+ return true;
+
+ port_num = PORT_NUM_FROM_LINK(
+ L0_PORT_NUM(be32_to_cpu(hdr->route_str.lo)));
+
+ if (unlikely(port_num >= nhi_ctxt->num_ports))
+ return false;
+
+ port = &(nhi_ctxt->net_devices[port_num]);
+ mutex_lock(&port->state_mutex);
+ if (port->net_dev != NULL)
+ negotiation_messages(port->net_dev, hdr);
+ mutex_unlock(&port->state_mutex);
+
+ return false;
+}
+
+static void nhi_handle_notification_msg(struct tbt_nhi_ctxt *nhi_ctxt,
+ const u8 *msg)
+{
+ struct port_net_dev *port;
+ u8 port_num;
+
+#define INTER_DOMAIN_LINK_SHIFT 0
+#define INTER_DOMAIN_LINK_MASK GENMASK(2, INTER_DOMAIN_LINK_SHIFT)
+ switch (msg[3]) {
+
+ case NC_INTER_DOMAIN_CONNECTED:
+ port_num = PORT_NUM_FROM_MSG(msg[5]);
+#define INTER_DOMAIN_APPROVED BIT(3)
+ if (port_num < nhi_ctxt->num_ports &&
+ !(msg[5] & INTER_DOMAIN_APPROVED))
+ nhi_ctxt->net_devices[port_num].medium_sts =
+ MEDIUM_READY_FOR_APPROVAL;
+ break;
+
+ case NC_INTER_DOMAIN_DISCONNECTED:
+ port_num = PORT_NUM_FROM_MSG(msg[5]);
+
+ if (unlikely(port_num >= nhi_ctxt->num_ports))
+ break;
+
+ port = &(nhi_ctxt->net_devices[port_num]);
+ mutex_lock(&port->state_mutex);
+ port->medium_sts = MEDIUM_DISCONNECTED;
+
+ if (port->net_dev != NULL)
+ negotiation_events(port->net_dev,
+ MEDIUM_DISCONNECTED);
+ mutex_unlock(&port->state_mutex);
+ break;
+ }
+}
+
+static bool nhi_handle_icm_response_msg(struct tbt_nhi_ctxt *nhi_ctxt,
+ const u8 *msg)
+{
+ struct port_net_dev *port;
+ bool send_event = true;
+ u8 port_num;
+
+ if (nhi_ctxt->ignore_icm_resp &&
+ msg[3] == RC_INTER_DOMAIN_PKT_SENT) {
+ nhi_ctxt->ignore_icm_resp = false;
+ send_event = false;
+ }
+ if (nhi_ctxt->wait_for_icm_resp) {
+ nhi_ctxt->wait_for_icm_resp = false;
+ up(&nhi_ctxt->send_sem);
+ }
+
+ if (msg[3] == RC_APPROVE_INTER_DOMAIN_CONNECTION) {
+#define APPROVE_INTER_DOMAIN_ERROR BIT(0)
+ if (unlikely(msg[2] & APPROVE_INTER_DOMAIN_ERROR))
+ return send_event;
+
+ port_num = PORT_NUM_FROM_LINK((msg[5]&INTER_DOMAIN_LINK_MASK)>>
+ INTER_DOMAIN_LINK_SHIFT);
+
+ if (unlikely(port_num >= nhi_ctxt->num_ports))
+ return send_event;
+
+ port = &(nhi_ctxt->net_devices[port_num]);
+ mutex_lock(&port->state_mutex);
+ port->medium_sts = MEDIUM_CONNECTED;
+
+ if (port->net_dev != NULL)
+ negotiation_events(port->net_dev, MEDIUM_CONNECTED);
+ mutex_unlock(&port->state_mutex);
+ }
+
+ return send_event;
+}
+
static bool nhi_msg_from_icm_analysis(struct tbt_nhi_ctxt *nhi_ctxt,
enum pdf_value pdf,
const u8 *msg, u32 msg_len)
{
- /*
- * preparation for messages that won't be sent,
- * currently unused in this patch.
- */
bool send_event = true;

switch (pdf) {
+ case PDF_INTER_DOMAIN_REQUEST:
+ case PDF_INTER_DOMAIN_RESPONSE:
+ send_event = nhi_handle_inter_domain_msg(
+ nhi_ctxt,
+ (struct thunderbolt_ip_header *)msg);
+ break;
+
+ case PDF_FW_TO_SW_NOTIFICATION:
+ nhi_handle_notification_msg(nhi_ctxt, msg);
+ break;
+
case PDF_ERROR_NOTIFICATION:
/* fallthrough */
case PDF_WRITE_CONFIGURATION_REGISTERS:
@@ -599,7 +816,12 @@ static bool nhi_msg_from_icm_analysis(struct tbt_nhi_ctxt *nhi_ctxt,
nhi_ctxt->wait_for_icm_resp = false;
up(&nhi_ctxt->send_sem);
}
- /* fallthrough */
+ break;
+
+ case PDF_FW_TO_SW_RESPONSE:
+ send_event = nhi_handle_icm_response_msg(nhi_ctxt, msg);
+ break;
+
default:
break;
}
@@ -788,6 +1010,12 @@ static const struct genl_ops nhi_ops[] = {
.doit = nhi_genl_mailbox,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NHI_CMD_APPROVE_TBT_NETWORKING,
+ .policy = nhi_genl_policy,
+ .doit = nhi_genl_approve_networking,
+ .flags = GENL_ADMIN_PERM,
+ },
};

static int nhi_suspend(struct device *dev) __releases(&nhi_ctxt->send_sem)
@@ -795,6 +1023,17 @@ static int nhi_suspend(struct device *dev) __releases(&nhi_ctxt->send_sem)
struct tbt_nhi_ctxt *nhi_ctxt = pci_get_drvdata(to_pci_dev(dev));
void __iomem *rx_reg, *tx_reg;
u32 rx_reg_val, tx_reg_val;
+ int i;
+
+ for (i = 0; i < nhi_ctxt->num_ports; i++) {
+ struct port_net_dev *port = &nhi_ctxt->net_devices[i];
+
+ mutex_lock(&port->state_mutex);
+ port->medium_sts = MEDIUM_DISCONNECTED;
+ if (port->net_dev)
+ negotiation_events(port->net_dev, MEDIUM_DISCONNECTED);
+ mutex_unlock(&port->state_mutex);
+ }

/* must be after negotiation_events, since messages might be sent */
nhi_ctxt->d0_exit = true;
@@ -954,6 +1193,15 @@ static void icm_nhi_remove(struct pci_dev *pdev)

nhi_suspend(&pdev->dev);

+ for (i = 0; i < nhi_ctxt->num_ports; i++) {
+ mutex_lock(&nhi_ctxt->net_devices[i].state_mutex);
+ if (nhi_ctxt->net_devices[i].net_dev) {
+ nhi_dealloc_etherdev(nhi_ctxt->net_devices[i].net_dev);
+ nhi_ctxt->net_devices[i].net_dev = NULL;
+ }
+ mutex_unlock(&nhi_ctxt->net_devices[i].state_mutex);
+ }
+
if (nhi_ctxt->net_workqueue)
destroy_workqueue(nhi_ctxt->net_workqueue);

diff --git a/drivers/thunderbolt/icm/net.c b/drivers/thunderbolt/icm/net.c
new file mode 100644
index 0000000..beeafb3
--- /dev/null
+++ b/drivers/thunderbolt/icm/net.c
@@ -0,0 +1,783 @@
+/*******************************************************************************
+ *
+ * Intel Thunderbolt(TM) driver
+ * Copyright(c) 2014 - 2016 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/prefetch.h>
+#include <linux/highmem.h>
+#include <linux/if_vlan.h>
+#include <linux/jhash.h>
+#include <linux/vmalloc.h>
+#include <net/ip6_checksum.h>
+#include "icm_nhi.h"
+#include "net.h"
+
+#define DEFAULT_MSG_ENABLE (NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_IFUP)
+static int debug = -1;
+module_param(debug, int, 0000);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+#define TBT_NET_RX_HDR_SIZE 256
+
+#define NUM_TX_LOGIN_RETRIES 60
+
+#define APPLE_THUNDERBOLT_IP_PROTOCOL_REVISION 1
+
+#define LOGIN_TX_PATH 0xf
+
+#define TBT_NET_MTU (64 * 1024)
+
+/* Number of Rx buffers we bundle into one write to the hardware */
+#define TBT_NET_RX_BUFFER_WRITE 16
+
+#define TBT_NET_MULTICAST_HASH_TABLE_SIZE 1024
+#define TBT_NET_ETHER_ADDR_HASH(addr) (((addr[4] >> 4) | (addr[5] << 4)) % \
+ TBT_NET_MULTICAST_HASH_TABLE_SIZE)
+
+#define BITS_PER_U32 (sizeof(u32) * BITS_PER_BYTE)
+
+#define TBT_NET_NUM_TX_BUFS 256
+#define TBT_NET_NUM_RX_BUFS 256
+#define TBT_NET_SIZE_TOTAL_DESCS ((TBT_NET_NUM_TX_BUFS + TBT_NET_NUM_RX_BUFS) \
+ * sizeof(struct tbt_buf_desc))
+
+
+#define TBT_NUM_FRAMES_PER_PAGE (PAGE_SIZE / TBT_RING_MAX_FRAME_SIZE)
+
+#define TBT_NUM_BUFS_BETWEEN(idx1, idx2, num_bufs) \
+ (((num_bufs) - 1) - \
+ ((((idx1) - (idx2)) + (num_bufs)) & ((num_bufs) - 1)))
+
+#define TX_WAKE_THRESHOLD (2 * DIV_ROUND_UP(TBT_NET_MTU, \
+ TBT_RING_MAX_FRM_DATA_SZ))
+
+#define TBT_NET_DESC_ATTR_SOF_EOF (((PDF_TBT_NET_START_OF_FRAME << \
+ DESC_ATTR_SOF_SHIFT) & \
+ DESC_ATTR_SOF_MASK) | \
+ ((PDF_TBT_NET_END_OF_FRAME << \
+ DESC_ATTR_EOF_SHIFT) & \
+ DESC_ATTR_EOF_MASK))
+
+/* E2E workaround */
+#define TBT_EXIST_BUT_UNUSED_HOPID 2
+
+enum tbt_net_frame_pdf {
+ PDF_TBT_NET_MIDDLE_FRAME,
+ PDF_TBT_NET_START_OF_FRAME,
+ PDF_TBT_NET_END_OF_FRAME,
+};
+
+struct thunderbolt_ip_login {
+ struct thunderbolt_ip_header header;
+ __be32 protocol_revision;
+ __be32 transmit_path;
+ __be32 reserved[4];
+ __be32 crc;
+};
+
+struct thunderbolt_ip_login_response {
+ struct thunderbolt_ip_header header;
+ __be32 status;
+ __be32 receiver_mac_address[2];
+ __be32 receiver_mac_address_length;
+ __be32 reserved[4];
+ __be32 crc;
+};
+
+struct thunderbolt_ip_logout {
+ struct thunderbolt_ip_header header;
+ __be32 crc;
+};
+
+struct thunderbolt_ip_status {
+ struct thunderbolt_ip_header header;
+ __be32 status;
+ __be32 crc;
+};
+
+struct approve_inter_domain_connection_cmd {
+ __be32 req_code;
+ __be32 attributes;
+#define AIDC_ATTR_LINK_SHIFT 16
+#define AIDC_ATTR_LINK_MASK GENMASK(18, AIDC_ATTR_LINK_SHIFT)
+#define AIDC_ATTR_DEPTH_SHIFT 20
+#define AIDC_ATTR_DEPTH_MASK GENMASK(23, AIDC_ATTR_DEPTH_SHIFT)
+ uuid_be remote_uuid;
+ __be16 transmit_ring_number;
+ __be16 transmit_path;
+ __be16 receive_ring_number;
+ __be16 receive_path;
+ __be32 crc;
+
+};
+
+enum neg_event {
+ RECEIVE_LOGOUT = NUM_MEDIUM_STATUSES,
+ RECEIVE_LOGIN_RESPONSE,
+ RECEIVE_LOGIN,
+ NUM_NEG_EVENTS
+};
+
+enum disconnect_path_stage {
+ STAGE_1 = BIT(0),
+ STAGE_2 = BIT(1)
+};
+
+/**
+ * struct tbt_port - the basic tbt_port structure
+ * @tbt_nhi_ctxt: context of the nhi controller.
+ * @net_dev: networking device object.
+ * @login_retry_work: work queue for sending login requests.
+ * @login_response_work: work queue for sending login responses.
+ * @work_struct logout_work: work queue for sending logout requests.
+ * @status_reply_work: work queue for sending logout replies.
+ * @approve_inter_domain_work: work queue for sending interdomain to icm.
+ * @route_str: allows to route the messages to destination.
+ * @interdomain_local_uuid: allows to route the messages from local source.
+ * @interdomain_remote_uuid: allows to route the messages to destination.
+ * @command_id a number that identifies the command.
+ * @negotiation_status: holds the network negotiation state.
+ * @msg_enable: used for debugging filters.
+ * @seq_num: a number that identifies the session.
+ * @login_retry_count: counts number of login retries sent.
+ * @local_depth: depth of the remote peer in the chain.
+ * @transmit_path: routing parameter for the icm.
+ * @frame_id: counting ID of frames.
+ * @num: port number.
+ * @local_path: routing parameter for the icm.
+ * @enable_full_e2e: whether to enable full E2E.
+ * @match_frame_id: whether to match frame id on incoming packets.
+ */
+struct tbt_port {
+ struct tbt_nhi_ctxt *nhi_ctxt;
+ struct net_device *net_dev;
+ struct delayed_work login_retry_work;
+ struct work_struct login_response_work;
+ struct work_struct logout_work;
+ struct work_struct status_reply_work;
+ struct work_struct approve_inter_domain_work;
+ struct route_string route_str;
+ uuid_be interdomain_local_uuid;
+ uuid_be interdomain_remote_uuid;
+ u32 command_id;
+ u16 negotiation_status;
+ u16 msg_enable;
+ u8 seq_num;
+ u8 login_retry_count;
+ u8 local_depth;
+ u8 transmit_path;
+ u16 frame_id;
+ u8 num;
+ u8 local_path;
+ bool enable_full_e2e : 1;
+ bool match_frame_id : 1;
+};
+
+static void disconnect_path(struct tbt_port *port,
+ enum disconnect_path_stage stage)
+{
+ u32 cmd = (DISCONNECT_PORT_A_INTER_DOMAIN_PATH + port->num);
+
+ cmd <<= REG_INMAIL_CMD_CMD_SHIFT;
+ cmd &= REG_INMAIL_CMD_CMD_MASK;
+ cmd |= REG_INMAIL_CMD_REQUEST;
+
+ mutex_lock(&port->nhi_ctxt->mailbox_mutex);
+ if (!mutex_trylock(&port->nhi_ctxt->d0_exit_mailbox_mutex)) {
+ netif_notice(port, link, port->net_dev, "controller id %#x is existing D0\n",
+ port->nhi_ctxt->id);
+ } else {
+ nhi_mailbox(port->nhi_ctxt, cmd, stage, false);
+
+ port->nhi_ctxt->net_devices[port->num].medium_sts =
+ MEDIUM_READY_FOR_CONNECTION;
+
+ mutex_unlock(&port->nhi_ctxt->d0_exit_mailbox_mutex);
+ }
+ mutex_unlock(&port->nhi_ctxt->mailbox_mutex);
+}
+
+static void tbt_net_tear_down(struct net_device *net_dev, bool send_logout)
+{
+ struct tbt_port *port = netdev_priv(net_dev);
+ void __iomem *iobase = port->nhi_ctxt->iobase;
+ void __iomem *tx_reg = NULL;
+ u32 tx_reg_val = 0;
+
+ netif_carrier_off(net_dev);
+ netif_stop_queue(net_dev);
+
+ if (port->negotiation_status & BIT(MEDIUM_CONNECTED)) {
+ void __iomem *rx_reg = iobase + REG_RX_OPTIONS_BASE +
+ (port->local_path * REG_OPTS_STEP);
+ u32 rx_reg_val = ioread32(rx_reg) & ~REG_OPTS_E2E_EN;
+
+ tx_reg = iobase + REG_TX_OPTIONS_BASE +
+ (port->local_path * REG_OPTS_STEP);
+ tx_reg_val = ioread32(tx_reg) & ~REG_OPTS_E2E_EN;
+
+ disconnect_path(port, STAGE_1);
+
+ /* disable RX flow control */
+ iowrite32(rx_reg_val, rx_reg);
+ /* disable TX flow control */
+ iowrite32(tx_reg_val, tx_reg);
+ /* disable RX ring */
+ iowrite32(rx_reg_val & ~REG_OPTS_VALID, rx_reg);
+
+ rx_reg = iobase + REG_RX_RING_BASE +
+ (port->local_path * REG_RING_STEP);
+ iowrite32(0, rx_reg + REG_RING_PHYS_LO_OFFSET);
+ iowrite32(0, rx_reg + REG_RING_PHYS_HI_OFFSET);
+ }
+
+ /* Stop login messages */
+ cancel_delayed_work_sync(&port->login_retry_work);
+
+ if (send_logout)
+ queue_work(port->nhi_ctxt->net_workqueue, &port->logout_work);
+
+ if (port->negotiation_status & BIT(MEDIUM_CONNECTED)) {
+ unsigned long flags;
+
+ /* wait for TX to finish */
+ usleep_range(5 * USEC_PER_MSEC, 7 * USEC_PER_MSEC);
+ /* disable TX ring */
+ iowrite32(tx_reg_val & ~REG_OPTS_VALID, tx_reg);
+
+ disconnect_path(port, STAGE_2);
+
+ spin_lock_irqsave(&port->nhi_ctxt->lock, flags);
+ /* disable RX and TX interrupts */
+ RING_INT_DISABLE_TX_RX(iobase, port->local_path,
+ port->nhi_ctxt->num_paths);
+ spin_unlock_irqrestore(&port->nhi_ctxt->lock, flags);
+ }
+}
+
+static inline int send_message(struct tbt_port *port, const char *func,
+ enum pdf_value pdf, u32 msg_len,
+ const void *msg)
+{
+ u32 crc_offset = msg_len - sizeof(__be32);
+ __be32 *crc = (__be32 *)((u8 *)msg + crc_offset);
+ bool is_intdom = (pdf == PDF_INTER_DOMAIN_RESPONSE);
+ int res;
+
+ *crc = cpu_to_be32(~__crc32c_le(~0, msg, crc_offset));
+ res = down_timeout(&port->nhi_ctxt->send_sem,
+ msecs_to_jiffies(3 * MSEC_PER_SEC));
+ if (res) {
+ netif_err(port, link, port->net_dev, "%s: controller id %#x timeout on send semaphore\n",
+ func, port->nhi_ctxt->id);
+ return res;
+ }
+
+ if (!mutex_trylock(&port->nhi_ctxt->d0_exit_send_mutex)) {
+ up(&port->nhi_ctxt->send_sem);
+ netif_notice(port, link, port->net_dev, "%s: controller id %#x is existing D0\n",
+ func, port->nhi_ctxt->id);
+ return -ENODEV;
+ }
+
+ res = nhi_send_message(port->nhi_ctxt, pdf, msg_len, msg, is_intdom);
+
+ mutex_unlock(&port->nhi_ctxt->d0_exit_send_mutex);
+ if (res)
+ up(&port->nhi_ctxt->send_sem);
+
+ return res;
+}
+
+static void approve_inter_domain(struct work_struct *work)
+{
+ struct tbt_port *port = container_of(work, typeof(*port),
+ approve_inter_domain_work);
+ struct approve_inter_domain_connection_cmd approve_msg = {
+ .req_code = cpu_to_be32(CC_APPROVE_INTER_DOMAIN_CONNECTION),
+ .transmit_path = cpu_to_be16(LOGIN_TX_PATH),
+ };
+ u32 aidc = (L0_PORT_NUM(port->route_str.lo) << AIDC_ATTR_LINK_SHIFT) &
+ AIDC_ATTR_LINK_MASK;
+
+ aidc |= (port->local_depth << AIDC_ATTR_DEPTH_SHIFT) &
+ AIDC_ATTR_DEPTH_MASK;
+
+ approve_msg.attributes = cpu_to_be32(aidc);
+
+ memcpy(&approve_msg.remote_uuid, &port->interdomain_remote_uuid,
+ sizeof(approve_msg.remote_uuid));
+ approve_msg.transmit_ring_number = cpu_to_be16(port->local_path);
+ approve_msg.receive_ring_number = cpu_to_be16(port->local_path);
+ approve_msg.receive_path = cpu_to_be16(port->transmit_path);
+
+ send_message(port, __func__, PDF_SW_TO_FW_COMMAND, sizeof(approve_msg),
+ &approve_msg);
+}
+
+static inline void prepare_header(struct thunderbolt_ip_header *header,
+ struct tbt_port *port,
+ enum thunderbolt_ip_packet_type packet_type,
+ u8 len_dwords)
+{
+ const uuid_be proto_uuid = APPLE_THUNDERBOLT_IP_PROTOCOL_UUID;
+
+ header->packet_type = cpu_to_be32(packet_type);
+ header->route_str.hi = cpu_to_be32(port->route_str.hi);
+ header->route_str.lo = cpu_to_be32(port->route_str.lo);
+ header->attributes = cpu_to_be32(
+ ((port->seq_num << HDR_ATTR_SEQ_NUM_SHIFT) &
+ HDR_ATTR_SEQ_NUM_MASK) |
+ ((len_dwords << HDR_ATTR_LEN_SHIFT) & HDR_ATTR_LEN_MASK));
+ memcpy(&header->apple_tbt_ip_proto_uuid, &proto_uuid,
+ sizeof(header->apple_tbt_ip_proto_uuid));
+ memcpy(&header->initiator_uuid, &port->interdomain_local_uuid,
+ sizeof(header->initiator_uuid));
+ memcpy(&header->target_uuid, &port->interdomain_remote_uuid,
+ sizeof(header->target_uuid));
+ header->command_id = cpu_to_be32(port->command_id);
+
+ port->command_id++;
+}
+
+static void status_reply(struct work_struct *work)
+{
+ struct tbt_port *port = container_of(work, typeof(*port),
+ status_reply_work);
+ struct thunderbolt_ip_status status_msg = {
+ .status = 0,
+ };
+
+ prepare_header(&status_msg.header, port,
+ THUNDERBOLT_IP_STATUS_TYPE,
+ (offsetof(struct thunderbolt_ip_status, crc) -
+ offsetof(struct thunderbolt_ip_status,
+ header.apple_tbt_ip_proto_uuid)) /
+ sizeof(u32));
+
+ send_message(port, __func__, PDF_INTER_DOMAIN_RESPONSE,
+ sizeof(status_msg), &status_msg);
+
+}
+
+static void logout(struct work_struct *work)
+{
+ struct tbt_port *port = container_of(work, typeof(*port),
+ logout_work);
+ struct thunderbolt_ip_logout logout_msg;
+
+ prepare_header(&logout_msg.header, port,
+ THUNDERBOLT_IP_LOGOUT_TYPE,
+ (offsetof(struct thunderbolt_ip_logout, crc) -
+ offsetof(struct thunderbolt_ip_logout,
+ header.apple_tbt_ip_proto_uuid)) / sizeof(u32));
+
+ send_message(port, __func__, PDF_INTER_DOMAIN_RESPONSE,
+ sizeof(logout_msg), &logout_msg);
+
+}
+
+static void login_response(struct work_struct *work)
+{
+ struct tbt_port *port = container_of(work, typeof(*port),
+ login_response_work);
+ struct thunderbolt_ip_login_response login_res_msg = {
+ .receiver_mac_address_length = cpu_to_be32(ETH_ALEN),
+ };
+
+ prepare_header(&login_res_msg.header, port,
+ THUNDERBOLT_IP_LOGIN_RESPONSE_TYPE,
+ (offsetof(struct thunderbolt_ip_login_response, crc) -
+ offsetof(struct thunderbolt_ip_login_response,
+ header.apple_tbt_ip_proto_uuid)) / sizeof(u32));
+
+ ether_addr_copy((u8 *)login_res_msg.receiver_mac_address,
+ port->net_dev->dev_addr);
+
+ send_message(port, __func__, PDF_INTER_DOMAIN_RESPONSE,
+ sizeof(login_res_msg), &login_res_msg);
+
+}
+
+static void login_retry(struct work_struct *work)
+{
+ struct tbt_port *port = container_of(work, typeof(*port),
+ login_retry_work.work);
+ struct thunderbolt_ip_login login_msg = {
+ .protocol_revision = cpu_to_be32(
+ APPLE_THUNDERBOLT_IP_PROTOCOL_REVISION),
+ .transmit_path = cpu_to_be32(LOGIN_TX_PATH),
+ };
+
+
+ if (port->nhi_ctxt->d0_exit)
+ return;
+
+ port->login_retry_count++;
+
+ prepare_header(&login_msg.header, port,
+ THUNDERBOLT_IP_LOGIN_TYPE,
+ (offsetof(struct thunderbolt_ip_login, crc) -
+ offsetof(struct thunderbolt_ip_login,
+ header.apple_tbt_ip_proto_uuid)) / sizeof(u32));
+
+ if (send_message(port, __func__, PDF_INTER_DOMAIN_RESPONSE,
+ sizeof(login_msg), &login_msg) == -ENODEV)
+ return;
+
+ if (likely(port->login_retry_count < NUM_TX_LOGIN_RETRIES))
+ queue_delayed_work(port->nhi_ctxt->net_workqueue,
+ &port->login_retry_work,
+ msecs_to_jiffies(5 * MSEC_PER_SEC));
+ else
+ netif_notice(port, link, port->net_dev, "port %u (%#x) login timeout after %u retries\n",
+ port->num, port->negotiation_status,
+ port->login_retry_count);
+}
+
+void negotiation_events(struct net_device *net_dev,
+ enum medium_status medium_sts)
+{
+ struct tbt_port *port = netdev_priv(net_dev);
+ void __iomem *iobase = port->nhi_ctxt->iobase;
+ u32 sof_eof_en, tx_ring_conf, rx_ring_conf, e2e_en;
+ void __iomem *reg;
+ unsigned long flags;
+ u16 hop_id;
+ bool send_logout;
+
+ if (!netif_running(net_dev)) {
+ netif_dbg(port, link, net_dev, "port %u (%#x) is down\n",
+ port->num, port->negotiation_status);
+ return;
+ }
+
+ netif_dbg(port, link, net_dev, "port %u (%#x) receive event %u\n",
+ port->num, port->negotiation_status, medium_sts);
+
+ switch (medium_sts) {
+ case MEDIUM_DISCONNECTED:
+ send_logout = (port->negotiation_status
+ & (BIT(MEDIUM_CONNECTED)
+ | BIT(MEDIUM_READY_FOR_CONNECTION)));
+ send_logout = send_logout && !(port->negotiation_status &
+ BIT(RECEIVE_LOGOUT));
+
+ tbt_net_tear_down(net_dev, send_logout);
+ port->negotiation_status = BIT(MEDIUM_DISCONNECTED);
+ break;
+
+ case MEDIUM_CONNECTED:
+ /*
+ * check if meanwhile other side sent logout
+ * if yes, just don't allow connection to take place
+ * and disconnect path
+ */
+ if (port->negotiation_status & BIT(RECEIVE_LOGOUT)) {
+ disconnect_path(port, STAGE_1 | STAGE_2);
+ break;
+ }
+
+ port->negotiation_status = BIT(MEDIUM_CONNECTED);
+
+ /* configure TX ring */
+ reg = iobase + REG_TX_RING_BASE +
+ (port->local_path * REG_RING_STEP);
+
+ tx_ring_conf = (TBT_NET_NUM_TX_BUFS << REG_RING_SIZE_SHIFT) &
+ REG_RING_SIZE_MASK;
+
+ iowrite32(tx_ring_conf, reg + REG_RING_SIZE_OFFSET);
+
+ /* enable the rings */
+ reg = iobase + REG_TX_OPTIONS_BASE +
+ (port->local_path * REG_OPTS_STEP);
+ if (port->enable_full_e2e) {
+ iowrite32(REG_OPTS_VALID | REG_OPTS_E2E_EN, reg);
+ hop_id = port->local_path;
+ } else {
+ iowrite32(REG_OPTS_VALID, reg);
+ hop_id = TBT_EXIST_BUT_UNUSED_HOPID;
+ }
+
+ reg = iobase + REG_RX_OPTIONS_BASE +
+ (port->local_path * REG_OPTS_STEP);
+
+ sof_eof_en = (BIT(PDF_TBT_NET_START_OF_FRAME) <<
+ REG_RX_OPTS_MASK_SOF_SHIFT) &
+ REG_RX_OPTS_MASK_SOF_MASK;
+
+ sof_eof_en |= (BIT(PDF_TBT_NET_END_OF_FRAME) <<
+ REG_RX_OPTS_MASK_EOF_SHIFT) &
+ REG_RX_OPTS_MASK_EOF_MASK;
+
+ iowrite32(sof_eof_en, reg + REG_RX_OPTS_MASK_OFFSET);
+
+ e2e_en = REG_OPTS_VALID | REG_OPTS_E2E_EN;
+ e2e_en |= (hop_id << REG_RX_OPTS_TX_E2E_HOP_ID_SHIFT) &
+ REG_RX_OPTS_TX_E2E_HOP_ID_MASK;
+
+ iowrite32(e2e_en, reg);
+
+ /*
+ * Configure RX ring
+ * must be after enable ring for E2E to work
+ */
+ reg = iobase + REG_RX_RING_BASE +
+ (port->local_path * REG_RING_STEP);
+
+ rx_ring_conf = (TBT_NET_NUM_RX_BUFS << REG_RING_SIZE_SHIFT) &
+ REG_RING_SIZE_MASK;
+
+ rx_ring_conf |= (TBT_RING_MAX_FRAME_SIZE <<
+ REG_RING_BUF_SIZE_SHIFT) &
+ REG_RING_BUF_SIZE_MASK;
+
+ iowrite32(rx_ring_conf, reg + REG_RING_SIZE_OFFSET);
+
+ spin_lock_irqsave(&port->nhi_ctxt->lock, flags);
+ /* enable RX interrupt */
+ iowrite32(ioread32(iobase + REG_RING_INTERRUPT_BASE) |
+ REG_RING_INT_RX_PROCESSED(port->local_path,
+ port->nhi_ctxt->num_paths),
+ iobase + REG_RING_INTERRUPT_BASE);
+ spin_unlock_irqrestore(&port->nhi_ctxt->lock, flags);
+
+ netif_info(port, link, net_dev, "Thunderbolt(TM) Networking port %u - ready\n",
+ port->num);
+
+ netif_carrier_on(net_dev);
+ netif_start_queue(net_dev);
+ break;
+
+ case MEDIUM_READY_FOR_CONNECTION:
+ /*
+ * If medium is connected, no reason to go back,
+ * keep it 'connected'.
+ * If received login response, don't need to trigger login
+ * retries again.
+ */
+ if (unlikely(port->negotiation_status &
+ (BIT(MEDIUM_CONNECTED) |
+ BIT(RECEIVE_LOGIN_RESPONSE))))
+ break;
+
+ port->negotiation_status = BIT(MEDIUM_READY_FOR_CONNECTION);
+ port->login_retry_count = 0;
+ queue_delayed_work(port->nhi_ctxt->net_workqueue,
+ &port->login_retry_work, 0);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void negotiation_messages(struct net_device *net_dev,
+ struct thunderbolt_ip_header *hdr)
+{
+ struct tbt_port *port = netdev_priv(net_dev);
+ __be32 status;
+
+ if (!netif_running(net_dev)) {
+ netif_dbg(port, link, net_dev, "port %u (%#x) is down\n",
+ port->num, port->negotiation_status);
+ return;
+ }
+
+ switch (hdr->packet_type) {
+ case cpu_to_be32(THUNDERBOLT_IP_LOGIN_TYPE):
+ port->transmit_path = be32_to_cpu(
+ ((struct thunderbolt_ip_login *)hdr)->transmit_path);
+ netif_dbg(port, link, net_dev, "port %u (%#x) receive ThunderboltIP login message with transmit path %u\n",
+ port->num, port->negotiation_status,
+ port->transmit_path);
+
+ if (unlikely(port->negotiation_status &
+ BIT(MEDIUM_DISCONNECTED)))
+ break;
+
+ queue_work(port->nhi_ctxt->net_workqueue,
+ &port->login_response_work);
+
+ if (unlikely(port->negotiation_status & BIT(MEDIUM_CONNECTED)))
+ break;
+
+ /*
+ * In case a login response received from other peer
+ * on my login and acked their login for the first time,
+ * so just approve the inter-domain now
+ */
+ if (port->negotiation_status & BIT(RECEIVE_LOGIN_RESPONSE)) {
+ if (!(port->negotiation_status & BIT(RECEIVE_LOGIN)))
+ queue_work(port->nhi_ctxt->net_workqueue,
+ &port->approve_inter_domain_work);
+ /*
+ * if we reached the number of max retries or previous
+ * logout, schedule another round of login retries
+ */
+ } else if ((port->login_retry_count >= NUM_TX_LOGIN_RETRIES) ||
+ (port->negotiation_status & BIT(RECEIVE_LOGOUT))) {
+ port->negotiation_status &= ~(BIT(RECEIVE_LOGOUT));
+ port->login_retry_count = 0;
+ queue_delayed_work(port->nhi_ctxt->net_workqueue,
+ &port->login_retry_work, 0);
+ }
+
+ port->negotiation_status |= BIT(RECEIVE_LOGIN);
+
+ break;
+
+ case cpu_to_be32(THUNDERBOLT_IP_LOGIN_RESPONSE_TYPE):
+ status = ((struct thunderbolt_ip_login_response *)hdr)->status;
+ if (likely(status == 0)) {
+ netif_dbg(port, link, net_dev, "port %u (%#x) receive ThunderboltIP login response message\n",
+ port->num,
+ port->negotiation_status);
+
+ if (unlikely(port->negotiation_status &
+ (BIT(MEDIUM_DISCONNECTED) |
+ BIT(MEDIUM_CONNECTED) |
+ BIT(RECEIVE_LOGIN_RESPONSE))))
+ break;
+
+ port->negotiation_status |=
+ BIT(RECEIVE_LOGIN_RESPONSE);
+ cancel_delayed_work_sync(&port->login_retry_work);
+ /*
+ * login was received from other peer and now response
+ * on our login so approve the inter-domain
+ */
+ if (port->negotiation_status & BIT(RECEIVE_LOGIN))
+ queue_work(port->nhi_ctxt->net_workqueue,
+ &port->approve_inter_domain_work);
+ else
+ port->negotiation_status &=
+ ~BIT(RECEIVE_LOGOUT);
+ } else {
+ netif_notice(port, link, net_dev, "port %u (%#x) receive ThunderboltIP login response message with status %u\n",
+ port->num,
+ port->negotiation_status,
+ be32_to_cpu(status));
+ }
+ break;
+
+ case cpu_to_be32(THUNDERBOLT_IP_LOGOUT_TYPE):
+ netif_dbg(port, link, net_dev, "port %u (%#x) receive ThunderboltIP logout message\n",
+ port->num, port->negotiation_status);
+
+ queue_work(port->nhi_ctxt->net_workqueue,
+ &port->status_reply_work);
+ port->negotiation_status &= ~(BIT(RECEIVE_LOGIN) |
+ BIT(RECEIVE_LOGIN_RESPONSE));
+ port->negotiation_status |= BIT(RECEIVE_LOGOUT);
+
+ if (!(port->negotiation_status & BIT(MEDIUM_CONNECTED))) {
+ tbt_net_tear_down(net_dev, false);
+ break;
+ }
+
+ tbt_net_tear_down(net_dev, true);
+
+ port->negotiation_status |= BIT(MEDIUM_READY_FOR_CONNECTION);
+ port->negotiation_status &= ~(BIT(MEDIUM_CONNECTED));
+ break;
+
+ case cpu_to_be32(THUNDERBOLT_IP_STATUS_TYPE):
+ netif_dbg(port, link, net_dev, "port %u (%#x) receive ThunderboltIP status message with status %u\n",
+ port->num, port->negotiation_status,
+ be32_to_cpu(
+ ((struct thunderbolt_ip_status *)hdr)->status));
+ break;
+ }
+}
+
+void nhi_dealloc_etherdev(struct net_device *net_dev)
+{
+ unregister_netdev(net_dev);
+ free_netdev(net_dev);
+}
+
+void nhi_update_etherdev(struct tbt_nhi_ctxt *nhi_ctxt,
+ struct net_device *net_dev, struct genl_info *info)
+{
+ struct tbt_port *port = netdev_priv(net_dev);
+
+ nla_memcpy(&(port->route_str),
+ info->attrs[NHI_ATTR_LOCAL_ROUTE_STRING],
+ sizeof(port->route_str));
+ nla_memcpy(&port->interdomain_remote_uuid,
+ info->attrs[NHI_ATTR_REMOTE_UUID],
+ sizeof(port->interdomain_remote_uuid));
+ port->local_depth = nla_get_u8(info->attrs[NHI_ATTR_LOCAL_DEPTH]);
+ port->enable_full_e2e = nhi_ctxt->support_full_e2e ?
+ nla_get_flag(info->attrs[NHI_ATTR_ENABLE_FULL_E2E]) : false;
+ port->match_frame_id =
+ nla_get_flag(info->attrs[NHI_ATTR_MATCH_FRAME_ID]);
+ port->frame_id = 0;
+}
+
+struct net_device *nhi_alloc_etherdev(struct tbt_nhi_ctxt *nhi_ctxt,
+ u8 port_num, struct genl_info *info)
+{
+ struct tbt_port *port;
+ struct net_device *net_dev = alloc_etherdev(sizeof(struct tbt_port));
+ u32 hash;
+
+ if (!net_dev)
+ return NULL;
+
+ SET_NETDEV_DEV(net_dev, &nhi_ctxt->pdev->dev);
+
+ port = netdev_priv(net_dev);
+ port->nhi_ctxt = nhi_ctxt;
+ port->net_dev = net_dev;
+ nla_memcpy(&port->interdomain_local_uuid,
+ info->attrs[NHI_ATTR_LOCAL_UUID],
+ sizeof(port->interdomain_local_uuid));
+ nhi_update_etherdev(nhi_ctxt, net_dev, info);
+ port->num = port_num;
+ port->local_path = PATH_FROM_PORT(nhi_ctxt->num_paths, port_num);
+
+ port->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
+
+ net_dev->addr_assign_type = NET_ADDR_PERM;
+ /* unicast and locally administred MAC */
+ net_dev->dev_addr[0] = (port_num << 4) | 0x02;
+ hash = jhash2((u32 *)&port->interdomain_local_uuid,
+ sizeof(port->interdomain_local_uuid)/sizeof(u32), 0);
+
+ memcpy(net_dev->dev_addr + 1, &hash, sizeof(hash));
+ hash = jhash2((u32 *)&port->interdomain_local_uuid,
+ sizeof(port->interdomain_local_uuid)/sizeof(u32), hash);
+
+ net_dev->dev_addr[5] = hash & 0xff;
+
+ scnprintf(net_dev->name, sizeof(net_dev->name), "tbtnet%%dp%hhu",
+ port_num);
+
+ INIT_DELAYED_WORK(&port->login_retry_work, login_retry);
+ INIT_WORK(&port->login_response_work, login_response);
+ INIT_WORK(&port->logout_work, logout);
+ INIT_WORK(&port->status_reply_work, status_reply);
+ INIT_WORK(&port->approve_inter_domain_work, approve_inter_domain);
+
+ netif_info(port, probe, net_dev,
+ "Thunderbolt(TM) Networking port %u - MAC Address: %pM\n",
+ port_num, net_dev->dev_addr);
+
+ return net_dev;
+}
diff --git a/drivers/thunderbolt/icm/net.h b/drivers/thunderbolt/icm/net.h
index 0281201..1cb6701 100644
--- a/drivers/thunderbolt/icm/net.h
+++ b/drivers/thunderbolt/icm/net.h
@@ -23,6 +23,10 @@
#include <linux/semaphore.h>
#include <net/genetlink.h>

+#define APPLE_THUNDERBOLT_IP_PROTOCOL_UUID \
+ UUID_BE(0x9E588F79, 0x478A, 0x1636, \
+ 0x64, 0x56, 0xC6, 0x97, 0xDD, 0xC8, 0x20, 0xA9)
+
/*
* Each physical port contains 2 channels.
* Devices are exposed to user based on physical ports.
@@ -33,6 +37,9 @@
* host channel/link which starts from 1.
*/
#define PORT_NUM_FROM_LINK(link) (((link) - 1) / CHANNELS_PER_PORT_NUM)
+#define PORT_NUM_FROM_MSG(msg) PORT_NUM_FROM_LINK(((msg) & \
+ INTER_DOMAIN_LINK_MASK) >> \
+ INTER_DOMAIN_LINK_SHIFT)

#define TBT_TX_RING_FULL(prod, cons, size) ((((prod) + 1) % (size)) == (cons))
#define TBT_TX_RING_EMPTY(prod, cons) ((prod) == (cons))
@@ -125,6 +132,17 @@ enum {
CC_SET_FW_MODE_FDA_DA_ALL
};

+struct route_string {
+ u32 hi;
+ u32 lo;
+};
+
+struct route_string_be {
+ __be32 hi;
+ __be32 lo;
+};
+
+#define L0_PORT_NUM(cpu_route_str_lo) ((cpu_route_str_lo) & GENMASK(5, 0))

/* NHI genetlink attributes */
enum {
@@ -138,12 +156,53 @@ enum {
NHI_ATTR_PDF,
NHI_ATTR_MSG_TO_ICM,
NHI_ATTR_MSG_FROM_ICM,
+ NHI_ATTR_LOCAL_ROUTE_STRING,
+ NHI_ATTR_LOCAL_UUID,
+ NHI_ATTR_REMOTE_UUID,
+ NHI_ATTR_LOCAL_DEPTH,
+ NHI_ATTR_ENABLE_FULL_E2E,
+ NHI_ATTR_MATCH_FRAME_ID,
__NHI_ATTR_MAX,
};
#define NHI_ATTR_MAX (__NHI_ATTR_MAX - 1)

+/* ThunderboltIP Packet Types */
+enum thunderbolt_ip_packet_type {
+ THUNDERBOLT_IP_LOGIN_TYPE,
+ THUNDERBOLT_IP_LOGIN_RESPONSE_TYPE,
+ THUNDERBOLT_IP_LOGOUT_TYPE,
+ THUNDERBOLT_IP_STATUS_TYPE
+};
+
+struct thunderbolt_ip_header {
+ struct route_string_be route_str;
+ __be32 attributes;
+#define HDR_ATTR_LEN_SHIFT 0
+#define HDR_ATTR_LEN_MASK GENMASK(5, HDR_ATTR_LEN_SHIFT)
+#define HDR_ATTR_SEQ_NUM_SHIFT 27
+#define HDR_ATTR_SEQ_NUM_MASK GENMASK(28, HDR_ATTR_SEQ_NUM_SHIFT)
+ uuid_be apple_tbt_ip_proto_uuid;
+ uuid_be initiator_uuid;
+ uuid_be target_uuid;
+ __be32 packet_type;
+ __be32 command_id;
+};
+
+enum medium_status {
+ /* Handle cable disconnection or peer down */
+ MEDIUM_DISCONNECTED,
+ /* Connection is fully established */
+ MEDIUM_CONNECTED,
+ /* Awaiting for being approved by user-space module */
+ MEDIUM_READY_FOR_APPROVAL,
+ /* Approved by user-space, awaiting for establishment flow to finish */
+ MEDIUM_READY_FOR_CONNECTION,
+ NUM_MEDIUM_STATUSES
+};
+
struct port_net_dev {
struct net_device *net_dev;
+ enum medium_status medium_sts;
struct mutex state_mutex;
};

@@ -213,5 +272,16 @@ struct tbt_nhi_ctxt {
int nhi_send_message(struct tbt_nhi_ctxt *nhi_ctxt, enum pdf_value pdf,
u32 msg_len, const void *msg, bool ignore_icm_resp);
int nhi_mailbox(struct tbt_nhi_ctxt *nhi_ctxt, u32 cmd, u32 data, bool deinit);
+struct net_device *nhi_alloc_etherdev(struct tbt_nhi_ctxt *nhi_ctxt,
+ u8 port_num, struct genl_info *info);
+void nhi_update_etherdev(struct tbt_nhi_ctxt *nhi_ctxt,
+ struct net_device *net_dev, struct genl_info *info);
+void nhi_dealloc_etherdev(struct net_device *net_dev);
+void negotiation_events(struct net_device *net_dev,
+ enum medium_status medium_sts);
+void negotiation_messages(struct net_device *net_dev,
+ struct thunderbolt_ip_header *hdr);
+void tbt_net_rx_msi(struct net_device *net_dev);
+void tbt_net_tx_msi(struct net_device *net_dev);

#endif
--
2.7.4