Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1ff7024 authored by Mika Westerberg's avatar Mika Westerberg Committed by David S. Miller
Browse files

thunderbolt: Add support for XDomain discovery protocol



When two hosts are connected over a Thunderbolt cable, there is a
protocol they can use to communicate capabilities supported by the host.
The discovery protocol uses automatically configured control channel
(ring 0) and is build on top of request/response transactions using
special XDomain primitives provided by the Thunderbolt base protocol.

The capabilities consists of a root directory block of basic properties
used for identification of the host, and then there can be zero or more
directories each describing a Thunderbolt service and its capabilities.

Once both sides have discovered what is supported the two hosts can
setup high-speed DMA paths and transfer data to the other side using
whatever protocol was agreed based on the properties. The software
protocol used to communicate which DMA paths to enable is service
specific.

This patch adds support for the XDomain discovery protocol to the
Thunderbolt bus. We model each remote host connection as a Linux XDomain
device. For each Thunderbolt service found supported on the XDomain
device, we create Linux Thunderbolt service device which Thunderbolt
service drivers can then bind to based on the protocol identification
information retrieved from the property directory describing the
service.

This code is based on the work done by Amir Levy and Michael Jamet.

Signed-off-by: default avatarMichael Jamet <michael.jamet@intel.com>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: default avatarYehezkel Bernat <yehezkel.bernat@intel.com>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e69b71f8
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -110,3 +110,51 @@ Description: When new NVM image is written to the non-active NVM
		is directly the status value from the DMA configuration
		based mailbox before the device is power cycled. Writing
		0 here clears the status.

What:		/sys/bus/thunderbolt/devices/<xdomain>.<service>/key
Date:		Jan 2018
KernelVersion:	4.15
Contact:	thunderbolt-software@lists.01.org
Description:	This contains name of the property directory the XDomain
		service exposes. This entry describes the protocol in
		question. Following directories are already reserved by
		the Apple XDomain specification:

		network:  IP/ethernet over Thunderbolt
		targetdm: Target disk mode protocol over Thunderbolt
		extdisp:  External display mode protocol over Thunderbolt

What:		/sys/bus/thunderbolt/devices/<xdomain>.<service>/modalias
Date:		Jan 2018
KernelVersion:	4.15
Contact:	thunderbolt-software@lists.01.org
Description:	Stores the same MODALIAS value emitted by uevent for
		the XDomain service. Format: tbtsvc:kSpNvNrN

What:		/sys/bus/thunderbolt/devices/<xdomain>.<service>/prtcid
Date:		Jan 2018
KernelVersion:	4.15
Contact:	thunderbolt-software@lists.01.org
Description:	This contains XDomain protocol identifier the XDomain
		service supports.

What:		/sys/bus/thunderbolt/devices/<xdomain>.<service>/prtcvers
Date:		Jan 2018
KernelVersion:	4.15
Contact:	thunderbolt-software@lists.01.org
Description:	This contains XDomain protocol version the XDomain
		service supports.

What:		/sys/bus/thunderbolt/devices/<xdomain>.<service>/prtcrevs
Date:		Jan 2018
KernelVersion:	4.15
Contact:	thunderbolt-software@lists.01.org
Description:	This contains XDomain software version the XDomain
		service supports.

What:		/sys/bus/thunderbolt/devices/<xdomain>.<service>/prtcstns
Date:		Jan 2018
KernelVersion:	4.15
Contact:	thunderbolt-software@lists.01.org
Description:	This contains XDomain service specific settings as
		bitmask. Format: %x
+1 −1
Original line number Diff line number Diff line
obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o
thunderbolt-objs += domain.o dma_port.o icm.o property.o
thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o
+7 −4
Original line number Diff line number Diff line
@@ -368,10 +368,10 @@ static int tb_ctl_tx(struct tb_ctl *ctl, const void *data, size_t len,
/**
 * tb_ctl_handle_event() - acknowledge a plug event, invoke ctl->callback
 */
static void tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type,
static bool tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type,
				struct ctl_pkg *pkg, size_t size)
{
	ctl->callback(ctl->callback_data, type, pkg->buffer, size);
	return ctl->callback(ctl->callback_data, type, pkg->buffer, size);
}

static void tb_ctl_rx_submit(struct ctl_pkg *pkg)
@@ -444,6 +444,8 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
		break;

	case TB_CFG_PKG_EVENT:
	case TB_CFG_PKG_XDOMAIN_RESP:
	case TB_CFG_PKG_XDOMAIN_REQ:
		if (*(__be32 *)(pkg->buffer + frame->size) != crc32) {
			tb_ctl_err(pkg->ctl,
				   "RX: checksum mismatch, dropping packet\n");
@@ -451,8 +453,9 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
		}
		/* Fall through */
	case TB_CFG_PKG_ICM_EVENT:
		tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size);
		if (tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size))
			goto rx;
		break;

	default:
		break;
+1 −1
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@
/* control channel */
struct tb_ctl;

typedef void (*event_cb)(void *data, enum tb_cfg_pkg_type type,
typedef bool (*event_cb)(void *data, enum tb_cfg_pkg_type type,
			 const void *buf, size_t size);

struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data);
+193 −4
Original line number Diff line number Diff line
@@ -20,6 +20,98 @@

static DEFINE_IDA(tb_domain_ida);

static bool match_service_id(const struct tb_service_id *id,
			     const struct tb_service *svc)
{
	if (id->match_flags & TBSVC_MATCH_PROTOCOL_KEY) {
		if (strcmp(id->protocol_key, svc->key))
			return false;
	}

	if (id->match_flags & TBSVC_MATCH_PROTOCOL_ID) {
		if (id->protocol_id != svc->prtcid)
			return false;
	}

	if (id->match_flags & TBSVC_MATCH_PROTOCOL_VERSION) {
		if (id->protocol_version != svc->prtcvers)
			return false;
	}

	if (id->match_flags & TBSVC_MATCH_PROTOCOL_VERSION) {
		if (id->protocol_revision != svc->prtcrevs)
			return false;
	}

	return true;
}

static const struct tb_service_id *__tb_service_match(struct device *dev,
						      struct device_driver *drv)
{
	struct tb_service_driver *driver;
	const struct tb_service_id *ids;
	struct tb_service *svc;

	svc = tb_to_service(dev);
	if (!svc)
		return NULL;

	driver = container_of(drv, struct tb_service_driver, driver);
	if (!driver->id_table)
		return NULL;

	for (ids = driver->id_table; ids->match_flags != 0; ids++) {
		if (match_service_id(ids, svc))
			return ids;
	}

	return NULL;
}

static int tb_service_match(struct device *dev, struct device_driver *drv)
{
	return !!__tb_service_match(dev, drv);
}

static int tb_service_probe(struct device *dev)
{
	struct tb_service *svc = tb_to_service(dev);
	struct tb_service_driver *driver;
	const struct tb_service_id *id;

	driver = container_of(dev->driver, struct tb_service_driver, driver);
	id = __tb_service_match(dev, &driver->driver);

	return driver->probe(svc, id);
}

static int tb_service_remove(struct device *dev)
{
	struct tb_service *svc = tb_to_service(dev);
	struct tb_service_driver *driver;

	driver = container_of(dev->driver, struct tb_service_driver, driver);
	if (driver->remove)
		driver->remove(svc);

	return 0;
}

static void tb_service_shutdown(struct device *dev)
{
	struct tb_service_driver *driver;
	struct tb_service *svc;

	svc = tb_to_service(dev);
	if (!svc || !dev->driver)
		return;

	driver = container_of(dev->driver, struct tb_service_driver, driver);
	if (driver->shutdown)
		driver->shutdown(svc);
}

static const char * const tb_security_names[] = {
	[TB_SECURITY_NONE] = "none",
	[TB_SECURITY_USER] = "user",
@@ -52,6 +144,10 @@ static const struct attribute_group *domain_attr_groups[] = {

struct bus_type tb_bus_type = {
	.name = "thunderbolt",
	.match = tb_service_match,
	.probe = tb_service_probe,
	.remove = tb_service_remove,
	.shutdown = tb_service_shutdown,
};

static void tb_domain_release(struct device *dev)
@@ -128,19 +224,28 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize)
	return NULL;
}

static void tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type,
static bool tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type,
			       const void *buf, size_t size)
{
	struct tb *tb = data;

	if (!tb->cm_ops->handle_event) {
		tb_warn(tb, "domain does not have event handler\n");
		return;
		return true;
	}

	switch (type) {
	case TB_CFG_PKG_XDOMAIN_REQ:
	case TB_CFG_PKG_XDOMAIN_RESP:
		return tb_xdomain_handle_request(tb, type, buf, size);

	default:
		tb->cm_ops->handle_event(tb, type, buf, size);
	}

	return true;
}

/**
 * tb_domain_add() - Add domain to the system
 * @tb: Domain to add
@@ -443,9 +548,92 @@ int tb_domain_disconnect_pcie_paths(struct tb *tb)
	return tb->cm_ops->disconnect_pcie_paths(tb);
}

/**
 * tb_domain_approve_xdomain_paths() - Enable DMA paths for XDomain
 * @tb: Domain enabling the DMA paths
 * @xd: XDomain DMA paths are created to
 *
 * Calls connection manager specific method to enable DMA paths to the
 * XDomain in question.
 *
 * Return: 0% in case of success and negative errno otherwise. In
 * particular returns %-ENOTSUPP if the connection manager
 * implementation does not support XDomains.
 */
int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
{
	if (!tb->cm_ops->approve_xdomain_paths)
		return -ENOTSUPP;

	return tb->cm_ops->approve_xdomain_paths(tb, xd);
}

/**
 * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain
 * @tb: Domain disabling the DMA paths
 * @xd: XDomain whose DMA paths are disconnected
 *
 * Calls connection manager specific method to disconnect DMA paths to
 * the XDomain in question.
 *
 * Return: 0% in case of success and negative errno otherwise. In
 * particular returns %-ENOTSUPP if the connection manager
 * implementation does not support XDomains.
 */
int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
{
	if (!tb->cm_ops->disconnect_xdomain_paths)
		return -ENOTSUPP;

	return tb->cm_ops->disconnect_xdomain_paths(tb, xd);
}

static int disconnect_xdomain(struct device *dev, void *data)
{
	struct tb_xdomain *xd;
	struct tb *tb = data;
	int ret = 0;

	xd = tb_to_xdomain(dev);
	if (xd && xd->tb == tb)
		ret = tb_xdomain_disable_paths(xd);

	return ret;
}

/**
 * tb_domain_disconnect_all_paths() - Disconnect all paths for the domain
 * @tb: Domain whose paths are disconnected
 *
 * This function can be used to disconnect all paths (PCIe, XDomain) for
 * example in preparation for host NVM firmware upgrade. After this is
 * called the paths cannot be established without resetting the switch.
 *
 * Return: %0 in case of success and negative errno otherwise.
 */
int tb_domain_disconnect_all_paths(struct tb *tb)
{
	int ret;

	ret = tb_domain_disconnect_pcie_paths(tb);
	if (ret)
		return ret;

	return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain);
}

int tb_domain_init(void)
{
	return bus_register(&tb_bus_type);
	int ret;

	ret = tb_xdomain_init();
	if (ret)
		return ret;
	ret = bus_register(&tb_bus_type);
	if (ret)
		tb_xdomain_exit();

	return ret;
}

void tb_domain_exit(void)
@@ -453,4 +641,5 @@ void tb_domain_exit(void)
	bus_unregister(&tb_bus_type);
	ida_destroy(&tb_domain_ida);
	tb_switch_exit();
	tb_xdomain_exit();
}
Loading