On Mon, 2017-09-18 at 11:12 -0500, Dan Williams wrote: > On Mon, 2017-09-18 at 18:30 +0300, Mika Westerberg wrote: > > 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: Michael Jamet <michael.ja...@intel.com> > > Signed-off-by: Mika Westerberg <mika.westerb...@linux.intel.com> > > Reviewed-by: Yehezkel Bernat <yehezkel.ber...@intel.com> > > --- > > Documentation/ABI/testing/sysfs-bus-thunderbolt | 48 + > > drivers/thunderbolt/Makefile | 2 +- > > drivers/thunderbolt/ctl.c | 11 +- > > drivers/thunderbolt/ctl.h | 2 +- > > drivers/thunderbolt/domain.c | 197 ++- > > drivers/thunderbolt/icm.c | 218 +++- > > drivers/thunderbolt/nhi.h | 2 + > > drivers/thunderbolt/switch.c | 7 +- > > drivers/thunderbolt/tb.h | 39 +- > > drivers/thunderbolt/tb_msgs.h | 123 ++ > > drivers/thunderbolt/xdomain.c | 1576 > > +++++++++++++++++++++++ > > include/linux/mod_devicetable.h | 26 + > > include/linux/thunderbolt.h | 242 ++++ > > scripts/mod/devicetable-offsets.c | 7 + > > scripts/mod/file2alias.c | 25 + > > 15 files changed, 2507 insertions(+), 18 deletions(-) > > create mode 100644 drivers/thunderbolt/xdomain.c > > > > diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt > > b/Documentation/ABI/testing/sysfs-bus-thunderbolt > > index 392bef5bd399..cb48850bd79b 100644 > > --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt > > +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt > > @@ -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>.<servi > > ce > > > /key > > > > +Date: Dec 2017 > > +KernelVersion: 4.14 > > +Contact: thunderbolt-softw...@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>.<servi > > ce > > > /modalias > > > > +Date: Dec 2017 > > +KernelVersion: 4.14 > > +Contact: thunderbolt-softw...@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>.<servi > > ce > > > /prtcid > > > > +Date: Dec 2017 > > +KernelVersion: 4.14 > > +Contact: thunderbolt-softw...@lists.01.org > > +Description: This contains XDomain protocol identifier the > > XDomain > > + service supports. > > + > > +What: /sys/bus/thunderbolt/devices/<xdomain>.<servi > > ce > > > /prtcvers > > > > +Date: Dec 2017 > > +KernelVersion: 4.14 > > +Contact: thunderbolt-softw...@lists.01.org > > +Description: This contains XDomain protocol version the > > XDomain > > + service supports. > > + > > +What: /sys/bus/thunderbolt/devices/<xdomain>.<servi > > ce > > > /prtcrevs > > > > +Date: Dec 2017 > > +KernelVersion: 4.14 > > +Contact: thunderbolt-softw...@lists.01.org > > +Description: This contains XDomain software version the > > XDomain > > + service supports. > > + > > +What: /sys/bus/thunderbolt/devices/<xdomain>.<servi > > ce > > > /prtcstns > > > > +Date: Dec 2017 > > +KernelVersion: 4.14 > > +Contact: thunderbolt-softw...@lists.01.org > > +Description: This contains XDomain service specific > > settings > > as > > + bitmask. Format: %x > > diff --git a/drivers/thunderbolt/Makefile > > b/drivers/thunderbolt/Makefile > > index 7afd21f5383a..f2f0de27252b 100644 > > --- a/drivers/thunderbolt/Makefile > > +++ b/drivers/thunderbolt/Makefile > > @@ -1,3 +1,3 @@ > > 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 > > diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c > > index e6a4c9458c76..46e393c5fd1d 100644 > > --- a/drivers/thunderbolt/ctl.c > > +++ b/drivers/thunderbolt/ctl.c > > @@ -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); > > - goto rx; > > + if (tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, > > frame->size)) > > + goto rx; > > + break; > > > > default: > > break; > > diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h > > index d0f21e1e0b8b..85c49dd301ea 100644 > > --- a/drivers/thunderbolt/ctl.h > > +++ b/drivers/thunderbolt/ctl.h > > @@ -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); > > diff --git a/drivers/thunderbolt/domain.c > > b/drivers/thunderbolt/domain.c > > index 9f2dcd48974d..29d6436ec8ce 100644 > > --- a/drivers/thunderbolt/domain.c > > +++ b/drivers/thunderbolt/domain.c > > @@ -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); > > Could you pass 'dev' to the probe function so that things like the > network sub-driver can sensibly link the netdev to the parent > hardware > in sysfs with SET_NETDEV_DEV()?
Nevermind, I'm blind, you've handled that already in patch #16. Ignore me. Dan > Dan > > > +} > > + > > +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,17 +224,26 @@ 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; > > } > > > > - tb->cm_ops->handle_event(tb, type, buf, size); > > + 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; > > } > > > > /** > > @@ -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 reseting 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(); > > } > > diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c > > index 8c22b91ed040..ab02d13f40b7 100644 > > --- a/drivers/thunderbolt/icm.c > > +++ b/drivers/thunderbolt/icm.c > > @@ -60,6 +60,8 @@ > > * @get_route: Find a route string for given switch > > * @device_connected: Handle device connected ICM message > > * @device_disconnected: Handle device disconnected ICM message > > + * @xdomain_connected - Handle XDomain connected ICM message > > + * @xdomain_disconnected - Handle XDomain disconnected ICM message > > */ > > struct icm { > > struct mutex request_lock; > > @@ -74,6 +76,10 @@ struct icm { > > const struct icm_pkg_header > > *hdr); > > void (*device_disconnected)(struct tb *tb, > > const struct icm_pkg_header > > *hdr); > > + void (*xdomain_connected)(struct tb *tb, > > + const struct icm_pkg_header > > *hdr); > > + void (*xdomain_disconnected)(struct tb *tb, > > + const struct icm_pkg_header > > *hdr); > > }; > > > > struct icm_notification { > > @@ -89,7 +95,10 @@ static inline struct tb *icm_to_tb(struct icm > > *icm) > > > > static inline u8 phy_port_from_route(u64 route, u8 depth) > > { > > - return tb_phy_port_from_link(route >> ((depth - 1) * 8)); > > + u8 link; > > + > > + link = depth ? route >> ((depth - 1) * 8) : route; > > + return tb_phy_port_from_link(link); > > } > > > > static inline u8 dual_link_from_link(u8 link) > > @@ -320,6 +329,51 @@ static int icm_fr_challenge_switch_key(struct > > tb > > *tb, struct tb_switch *sw, > > return 0; > > } > > > > +static int icm_fr_approve_xdomain_paths(struct tb *tb, struct > > tb_xdomain *xd) > > +{ > > + struct icm_fr_pkg_approve_xdomain_response reply; > > + struct icm_fr_pkg_approve_xdomain request; > > + int ret; > > + > > + memset(&request, 0, sizeof(request)); > > + request.hdr.code = ICM_APPROVE_XDOMAIN; > > + request.link_info = xd->depth << ICM_LINK_INFO_DEPTH_SHIFT > > | > > xd->link; > > + memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd- > > > remote_uuid)); > > > > + > > + request.transmit_path = xd->transmit_path; > > + request.transmit_ring = xd->transmit_ring; > > + request.receive_path = xd->receive_path; > > + request.receive_ring = xd->receive_ring; > > + > > + memset(&reply, 0, sizeof(reply)); > > + ret = icm_request(tb, &request, sizeof(request), &reply, > > sizeof(reply), > > + 1, ICM_TIMEOUT); > > + if (ret) > > + return ret; > > + > > + if (reply.hdr.flags & ICM_FLAGS_ERROR) > > + return -EIO; > > + > > + return 0; > > +} > > + > > +static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct > > tb_xdomain *xd) > > +{ > > + u8 phy_port; > > + u8 cmd; > > + > > + phy_port = tb_phy_port_from_link(xd->link); > > + if (phy_port == 0) > > + cmd = NHI_MAILBOX_DISCONNECT_PA; > > + else > > + cmd = NHI_MAILBOX_DISCONNECT_PB; > > + > > + nhi_mailbox_cmd(tb->nhi, cmd, 1); > > + usleep_range(10, 50); > > + nhi_mailbox_cmd(tb->nhi, cmd, 2); > > + return 0; > > +} > > + > > static void remove_switch(struct tb_switch *sw) > > { > > struct tb_switch *parent_sw; > > @@ -475,6 +529,141 @@ icm_fr_device_disconnected(struct tb *tb, > > const > > struct icm_pkg_header *hdr) > > tb_switch_put(sw); > > } > > > > +static void remove_xdomain(struct tb_xdomain *xd) > > +{ > > + struct tb_switch *sw; > > + > > + sw = tb_to_switch(xd->dev.parent); > > + tb_port_at(xd->route, sw)->xdomain = NULL; > > + tb_xdomain_remove(xd); > > +} > > + > > +static void > > +icm_fr_xdomain_connected(struct tb *tb, const struct > > icm_pkg_header > > *hdr) > > +{ > > + const struct icm_fr_event_xdomain_connected *pkg = > > + (const struct icm_fr_event_xdomain_connected > > *)hdr; > > + struct tb_xdomain *xd; > > + struct tb_switch *sw; > > + u8 link, depth; > > + bool approved; > > + u64 route; > > + > > + /* > > + * After NVM upgrade adding root switch device fails > > because > > we > > + * initiated reset. During that time ICM might still send > > + * XDomain connected message which we ignore here. > > + */ > > + if (!tb->root_switch) > > + return; > > + > > + link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; > > + depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> > > + ICM_LINK_INFO_DEPTH_SHIFT; > > + approved = pkg->link_info & ICM_LINK_INFO_APPROVED; > > + > > + if (link > ICM_MAX_LINK || depth > ICM_MAX_DEPTH) { > > + tb_warn(tb, "invalid topology %u.%u, ignoring\n", > > link, depth); > > + return; > > + } > > + > > + route = get_route(pkg->local_route_hi, pkg- > > >local_route_lo); > > + > > + xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); > > + if (xd) { > > + u8 xd_phy_port, phy_port; > > + > > + xd_phy_port = phy_port_from_route(xd->route, xd- > > > depth); > > > > + phy_port = phy_port_from_route(route, depth); > > + > > + if (xd->depth == depth && xd_phy_port == phy_port) > > { > > + xd->link = link; > > + xd->route = route; > > + xd->is_unplugged = false; > > + tb_xdomain_put(xd); > > + return; > > + } > > + > > + /* > > + * If we find an existing XDomain connection > > remove > > it > > + * now. We need to go through login handshake and > > + * everything anyway to be able to re-establish > > the > > + * connection. > > + */ > > + remove_xdomain(xd); > > + tb_xdomain_put(xd); > > + } > > + > > + /* > > + * Look if there already exists an XDomain in the same > > place > > + * than the new one and in that case remove it because it > > is > > + * most likely another host that got disconnected. > > + */ > > + xd = tb_xdomain_find_by_link_depth(tb, link, depth); > > + if (!xd) { > > + u8 dual_link; > > + > > + dual_link = dual_link_from_link(link); > > + if (dual_link) > > + xd = tb_xdomain_find_by_link_depth(tb, > > dual_link, > > + depth); > > + } > > + if (xd) { > > + remove_xdomain(xd); > > + tb_xdomain_put(xd); > > + } > > + > > + /* > > + * If the user disconnected a switch during suspend and > > + * connected another host to the same port, remove the > > switch > > + * first. > > + */ > > + sw = get_switch_at_route(tb->root_switch, route); > > + if (sw) > > + remove_switch(sw); > > + > > + sw = tb_switch_find_by_link_depth(tb, link, depth); > > + if (!sw) { > > + tb_warn(tb, "no switch exists at %u.%u, > > ignoring\n", > > link, > > + depth); > > + return; > > + } > > + > > + xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, > > + &pkg->local_uuid, &pkg- > > >remote_uuid); > > + if (!xd) { > > + tb_switch_put(sw); > > + return; > > + } > > + > > + xd->link = link; > > + xd->depth = depth; > > + > > + tb_port_at(route, sw)->xdomain = xd; > > + > > + tb_xdomain_add(xd); > > + tb_switch_put(sw); > > +} > > + > > +static void > > +icm_fr_xdomain_disconnected(struct tb *tb, const struct > > icm_pkg_header *hdr) > > +{ > > + const struct icm_fr_event_xdomain_disconnected *pkg = > > + (const struct icm_fr_event_xdomain_disconnected > > *)hdr; > > + struct tb_xdomain *xd; > > + > > + /* > > + * If the connection is through one or multiple devices, > > the > > + * XDomain device is removed along with them so it is fine > > if we > > + * cannot find it here. > > + */ > > + xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); > > + if (xd) { > > + remove_xdomain(xd); > > + tb_xdomain_put(xd); > > + } > > +} > > + > > static struct pci_dev *get_upstream_port(struct pci_dev *pdev) > > { > > struct pci_dev *parent; > > @@ -594,6 +783,12 @@ static void icm_handle_notification(struct > > work_struct *work) > > case ICM_EVENT_DEVICE_DISCONNECTED: > > icm->device_disconnected(tb, n->pkg); > > break; > > + case ICM_EVENT_XDOMAIN_CONNECTED: > > + icm->xdomain_connected(tb, n->pkg); > > + break; > > + case ICM_EVENT_XDOMAIN_DISCONNECTED: > > + icm->xdomain_disconnected(tb, n->pkg); > > + break; > > } > > > > mutex_unlock(&tb->lock); > > @@ -927,6 +1122,10 @@ static void icm_unplug_children(struct > > tb_switch *sw) > > > > if (tb_is_upstream_port(port)) > > continue; > > + if (port->xdomain) { > > + port->xdomain->is_unplugged = true; > > + continue; > > + } > > if (!port->remote) > > continue; > > > > @@ -943,6 +1142,13 @@ static void > > icm_free_unplugged_children(struct > > tb_switch *sw) > > > > if (tb_is_upstream_port(port)) > > continue; > > + > > + if (port->xdomain && port->xdomain->is_unplugged) > > { > > + tb_xdomain_remove(port->xdomain); > > + port->xdomain = NULL; > > + continue; > > + } > > + > > if (!port->remote) > > continue; > > > > @@ -1009,8 +1215,10 @@ static int icm_start(struct tb *tb) > > tb->root_switch->no_nvm_upgrade = x86_apple_machine; > > > > ret = tb_switch_add(tb->root_switch); > > - if (ret) > > + if (ret) { > > tb_switch_put(tb->root_switch); > > + tb->root_switch = NULL; > > + } > > > > return ret; > > } > > @@ -1042,6 +1250,8 @@ static const struct tb_cm_ops icm_fr_ops = { > > .add_switch_key = icm_fr_add_switch_key, > > .challenge_switch_key = icm_fr_challenge_switch_key, > > .disconnect_pcie_paths = icm_disconnect_pcie_paths, > > + .approve_xdomain_paths = icm_fr_approve_xdomain_paths, > > + .disconnect_xdomain_paths = > > icm_fr_disconnect_xdomain_paths, > > }; > > > > struct tb *icm_probe(struct tb_nhi *nhi) > > @@ -1064,6 +1274,8 @@ struct tb *icm_probe(struct tb_nhi *nhi) > > icm->get_route = icm_fr_get_route; > > icm->device_connected = icm_fr_device_connected; > > icm->device_disconnected = > > icm_fr_device_disconnected; > > + icm->xdomain_connected = icm_fr_xdomain_connected; > > + icm->xdomain_disconnected = > > icm_fr_xdomain_disconnected; > > tb->cm_ops = &icm_fr_ops; > > break; > > > > @@ -1077,6 +1289,8 @@ struct tb *icm_probe(struct tb_nhi *nhi) > > icm->get_route = icm_ar_get_route; > > icm->device_connected = icm_fr_device_connected; > > icm->device_disconnected = > > icm_fr_device_disconnected; > > + icm->xdomain_connected = icm_fr_xdomain_connected; > > + icm->xdomain_disconnected = > > icm_fr_xdomain_disconnected; > > tb->cm_ops = &icm_fr_ops; > > break; > > } > > diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h > > index 5b5bb2c436be..0e05828983db 100644 > > --- a/drivers/thunderbolt/nhi.h > > +++ b/drivers/thunderbolt/nhi.h > > @@ -157,6 +157,8 @@ enum nhi_mailbox_cmd { > > NHI_MAILBOX_SAVE_DEVS = 0x05, > > NHI_MAILBOX_DISCONNECT_PCIE_PATHS = 0x06, > > NHI_MAILBOX_DRV_UNLOADS = 0x07, > > + NHI_MAILBOX_DISCONNECT_PA = 0x10, > > + NHI_MAILBOX_DISCONNECT_PB = 0x11, > > NHI_MAILBOX_ALLOW_ALL_DEVS = 0x23, > > }; > > > > diff --git a/drivers/thunderbolt/switch.c > > b/drivers/thunderbolt/switch.c > > index 53f40c57df59..dfc357d33e1e 100644 > > --- a/drivers/thunderbolt/switch.c > > +++ b/drivers/thunderbolt/switch.c > > @@ -171,11 +171,11 @@ static int nvm_authenticate_host(struct > > tb_switch *sw) > > > > /* > > * Root switch NVM upgrade requires that we disconnect the > > - * existing PCIe paths first (in case it is not in safe > > mode > > + * existing paths first (in case it is not in safe mode > > * already). > > */ > > if (!sw->safe_mode) { > > - ret = tb_domain_disconnect_pcie_paths(sw->tb); > > + ret = tb_domain_disconnect_all_paths(sw->tb); > > if (ret) > > return ret; > > /* > > @@ -1363,6 +1363,9 @@ void tb_switch_remove(struct tb_switch *sw) > > if (sw->ports[i].remote) > > tb_switch_remove(sw->ports[i].remote->sw); > > sw->ports[i].remote = NULL; > > + if (sw->ports[i].xdomain) > > + tb_xdomain_remove(sw->ports[i].xdomain); > > + sw->ports[i].xdomain = NULL; > > } > > > > if (!sw->is_unplugged) > > diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h > > index ea21d927bd09..74af9d4929ab 100644 > > --- a/drivers/thunderbolt/tb.h > > +++ b/drivers/thunderbolt/tb.h > > @@ -9,6 +9,7 @@ > > > > #include <linux/nvmem-provider.h> > > #include <linux/pci.h> > > +#include <linux/thunderbolt.h> > > #include <linux/uuid.h> > > > > #include "tb_regs.h" > > @@ -109,14 +110,25 @@ struct tb_switch { > > > > /** > > * struct tb_port - a thunderbolt port, part of a tb_switch > > + * @config: Cached port configuration read from registers > > + * @sw: Switch the port belongs to > > + * @remote: Remote port (%NULL if not connected) > > + * @xdomain: Remote host (%NULL if not connected) > > + * @cap_phy: Offset, zero if not found > > + * @port: Port number on switch > > + * @disabled: Disabled by eeprom > > + * @dual_link_port: If the switch is connected using two ports, > > points > > + * to the other port. > > + * @link_nr: Is this primary or secondary port on the dual_link. > > */ > > struct tb_port { > > struct tb_regs_port_header config; > > struct tb_switch *sw; > > - struct tb_port *remote; /* remote port, NULL if not > > connected */ > > - int cap_phy; /* offset, zero if not found */ > > - u8 port; /* port number on switch */ > > - bool disabled; /* disabled by eeprom */ > > + struct tb_port *remote; > > + struct tb_xdomain *xdomain; > > + int cap_phy; > > + u8 port; > > + bool disabled; > > struct tb_port *dual_link_port; > > u8 link_nr:1; > > }; > > @@ -189,6 +201,8 @@ struct tb_path { > > * @add_switch_key: Add key to switch > > * @challenge_switch_key: Challenge switch using key > > * @disconnect_pcie_paths: Disconnects PCIe paths before NVM > > update > > + * @approve_xdomain_paths: Approve (establish) XDomain DMA paths > > + * @disconnect_xdomain_paths: Disconnect XDomain DMA paths > > */ > > struct tb_cm_ops { > > int (*driver_ready)(struct tb *tb); > > @@ -205,6 +219,8 @@ struct tb_cm_ops { > > int (*challenge_switch_key)(struct tb *tb, struct > > tb_switch > > *sw, > > const u8 *challenge, u8 > > *response); > > int (*disconnect_pcie_paths)(struct tb *tb); > > + int (*approve_xdomain_paths)(struct tb *tb, struct > > tb_xdomain *xd); > > + int (*disconnect_xdomain_paths)(struct tb *tb, struct > > tb_xdomain *xd); > > }; > > > > static inline void *tb_priv(struct tb *tb) > > @@ -331,6 +347,8 @@ extern struct device_type tb_switch_type; > > int tb_domain_init(void); > > void tb_domain_exit(void); > > void tb_switch_exit(void); > > +int tb_xdomain_init(void); > > +void tb_xdomain_exit(void); > > > > struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize); > > int tb_domain_add(struct tb *tb); > > @@ -343,6 +361,9 @@ int tb_domain_approve_switch(struct tb *tb, > > struct tb_switch *sw); > > int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch > > *sw); > > int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch > > *sw); > > int tb_domain_disconnect_pcie_paths(struct tb *tb); > > +int tb_domain_approve_xdomain_paths(struct tb *tb, struct > > tb_xdomain > > *xd); > > +int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct > > tb_xdomain *xd); > > +int tb_domain_disconnect_all_paths(struct tb *tb); > > > > static inline void tb_domain_put(struct tb *tb) > > { > > @@ -422,4 +443,14 @@ static inline u64 tb_downstream_route(struct > > tb_port *port) > > | ((u64) port->port << (port->sw->config.depth * > > 8)); > > } > > > > +bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type > > type, > > + const void *buf, size_t size); > > +struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device > > *parent, > > + u64 route, const uuid_t > > *local_uuid, > > + const uuid_t *remote_uuid); > > +void tb_xdomain_add(struct tb_xdomain *xd); > > +void tb_xdomain_remove(struct tb_xdomain *xd); > > +struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 > > link, > > + u8 depth); > > + > > #endif > > diff --git a/drivers/thunderbolt/tb_msgs.h > > b/drivers/thunderbolt/tb_msgs.h > > index fe3039b05da6..2a76908537a6 100644 > > --- a/drivers/thunderbolt/tb_msgs.h > > +++ b/drivers/thunderbolt/tb_msgs.h > > @@ -101,11 +101,14 @@ enum icm_pkg_code { > > ICM_CHALLENGE_DEVICE = 0x5, > > ICM_ADD_DEVICE_KEY = 0x6, > > ICM_GET_ROUTE = 0xa, > > + ICM_APPROVE_XDOMAIN = 0x10, > > }; > > > > enum icm_event_code { > > ICM_EVENT_DEVICE_CONNECTED = 3, > > ICM_EVENT_DEVICE_DISCONNECTED = 4, > > + ICM_EVENT_XDOMAIN_CONNECTED = 6, > > + ICM_EVENT_XDOMAIN_DISCONNECTED = 7, > > }; > > > > struct icm_pkg_header { > > @@ -188,6 +191,25 @@ struct icm_fr_event_device_disconnected { > > u16 link_info; > > } __packed; > > > > +struct icm_fr_event_xdomain_connected { > > + struct icm_pkg_header hdr; > > + u16 reserved; > > + u16 link_info; > > + uuid_t remote_uuid; > > + uuid_t local_uuid; > > + u32 local_route_hi; > > + u32 local_route_lo; > > + u32 remote_route_hi; > > + u32 remote_route_lo; > > +} __packed; > > + > > +struct icm_fr_event_xdomain_disconnected { > > + struct icm_pkg_header hdr; > > + u16 reserved; > > + u16 link_info; > > + uuid_t remote_uuid; > > +} __packed; > > + > > struct icm_fr_pkg_add_device_key { > > struct icm_pkg_header hdr; > > uuid_t ep_uuid; > > @@ -224,6 +246,28 @@ struct icm_fr_pkg_challenge_device_response { > > u32 response[8]; > > } __packed; > > > > +struct icm_fr_pkg_approve_xdomain { > > + struct icm_pkg_header hdr; > > + u16 reserved; > > + u16 link_info; > > + uuid_t remote_uuid; > > + u16 transmit_path; > > + u16 transmit_ring; > > + u16 receive_path; > > + u16 receive_ring; > > +} __packed; > > + > > +struct icm_fr_pkg_approve_xdomain_response { > > + struct icm_pkg_header hdr; > > + u16 reserved; > > + u16 link_info; > > + uuid_t remote_uuid; > > + u16 transmit_path; > > + u16 transmit_ring; > > + u16 receive_path; > > + u16 receive_ring; > > +} __packed; > > + > > /* Alpine Ridge only messages */ > > > > struct icm_ar_pkg_get_route { > > @@ -240,4 +284,83 @@ struct icm_ar_pkg_get_route_response { > > u32 route_lo; > > } __packed; > > > > +/* XDomain messages */ > > + > > +struct tb_xdomain_header { > > + u32 route_hi; > > + u32 route_lo; > > + u32 length_sn; > > +} __packed; > > + > > +#define TB_XDOMAIN_LENGTH_MASK GENMASK(5, 0) > > +#define TB_XDOMAIN_SN_MASK GENMASK(28, 27) > > +#define TB_XDOMAIN_SN_SHIFT 27 > > + > > +enum tb_xdp_type { > > + UUID_REQUEST_OLD = 1, > > + UUID_RESPONSE = 2, > > + PROPERTIES_REQUEST, > > + PROPERTIES_RESPONSE, > > + PROPERTIES_CHANGED_REQUEST, > > + PROPERTIES_CHANGED_RESPONSE, > > + ERROR_RESPONSE, > > + UUID_REQUEST = 12, > > +}; > > + > > +struct tb_xdp_header { > > + struct tb_xdomain_header xd_hdr; > > + uuid_t uuid; > > + u32 type; > > +} __packed; > > + > > +struct tb_xdp_properties { > > + struct tb_xdp_header hdr; > > + uuid_t src_uuid; > > + uuid_t dst_uuid; > > + u16 offset; > > + u16 reserved; > > +} __packed; > > + > > +struct tb_xdp_properties_response { > > + struct tb_xdp_header hdr; > > + uuid_t src_uuid; > > + uuid_t dst_uuid; > > + u16 offset; > > + u16 data_length; > > + u32 generation; > > + u32 data[0]; > > +} __packed; > > + > > +/* > > + * Max length of data array single XDomain property response is > > allowed > > + * to carry. > > + */ > > +#define TB_XDP_PROPERTIES_MAX_DATA_LENGTH \ > > + (((256 - 4 - sizeof(struct tb_xdp_properties_response))) / > > 4) > > + > > +/* Maximum size of the total property block in dwords we allow */ > > +#define TB_XDP_PROPERTIES_MAX_LENGTH 500 > > + > > +struct tb_xdp_properties_changed { > > + struct tb_xdp_header hdr; > > + uuid_t src_uuid; > > +} __packed; > > + > > +struct tb_xdp_properties_changed_response { > > + struct tb_xdp_header hdr; > > +} __packed; > > + > > +enum tb_xdp_error { > > + ERROR_SUCCESS, > > + ERROR_UNKNOWN_PACKET, > > + ERROR_UNKNOWN_DOMAIN, > > + ERROR_NOT_SUPPORTED, > > + ERROR_NOT_READY, > > +}; > > + > > +struct tb_xdp_error_response { > > + struct tb_xdp_header hdr; > > + u32 error; > > +} __packed; > > + > > #endif > > diff --git a/drivers/thunderbolt/xdomain.c > > b/drivers/thunderbolt/xdomain.c > > new file mode 100644 > > index 000000000000..1b929be8fdd6 > > --- /dev/null > > +++ b/drivers/thunderbolt/xdomain.c > > @@ -0,0 +1,1576 @@ > > +/* > > + * Thunderbolt XDomain discovery protocol support > > + * > > + * Copyright (C) 2017, Intel Corporation > > + * Authors: Michael Jamet <michael.ja...@intel.com> > > + * Mika Westerberg <mika.westerb...@linux.intel.com> > > + * > > + * This program is free software; you can redistribute it and/or > > modify > > + * it under the terms of the GNU General Public License version 2 > > as > > + * published by the Free Software Foundation. > > + */ > > + > > +#include <linux/device.h> > > +#include <linux/kmod.h> > > +#include <linux/module.h> > > +#include <linux/utsname.h> > > +#include <linux/uuid.h> > > +#include <linux/workqueue.h> > > + > > +#include "tb.h" > > + > > +#define XDOMAIN_DEFAULT_TIMEOUT 5000 /* ms > > */ > > +#define XDOMAIN_PROPERTIES_RETRIES 60 > > +#define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10 > > + > > +struct xdomain_request_work { > > + struct work_struct work; > > + struct tb_xdp_header *pkg; > > + struct tb *tb; > > +}; > > + > > +/* Serializes access to the properties and protocol handlers below > > */ > > +static DEFINE_MUTEX(xdomain_lock); > > + > > +/* Properties exposed to the remote domains */ > > +static struct tb_property_dir *xdomain_property_dir; > > +static u32 *xdomain_property_block; > > +static u32 xdomain_property_block_len; > > +static u32 xdomain_property_block_gen; > > + > > +/* Additional protocol handlers */ > > +static LIST_HEAD(protocol_handlers); > > + > > +/* UUID for XDomain discovery protocol */ > > +static const uuid_t tb_xdp_uuid = > > + UUID_INIT(0xb638d70e, 0x42ff, 0x40bb, > > + 0x97, 0xc2, 0x90, 0xe2, 0xc0, 0xb2, 0xff, 0x07); > > + > > +static bool tb_xdomain_match(const struct tb_cfg_request *req, > > + const struct ctl_pkg *pkg) > > +{ > > + switch (pkg->frame.eof) { > > + case TB_CFG_PKG_ERROR: > > + return true; > > + > > + case TB_CFG_PKG_XDOMAIN_RESP: { > > + const struct tb_xdp_header *res_hdr = pkg->buffer; > > + const struct tb_xdp_header *req_hdr = req- > > >request; > > + u8 req_seq, res_seq; > > + > > + if (pkg->frame.size < req->response_size / 4) > > + return false; > > + > > + /* Make sure route matches */ > > + if ((res_hdr->xd_hdr.route_hi & ~BIT(31)) != > > + req_hdr->xd_hdr.route_hi) > > + return false; > > + if ((res_hdr->xd_hdr.route_lo) != req_hdr- > > > xd_hdr.route_lo) > > > > + return false; > > + > > + /* Then check that the sequence number matches */ > > + res_seq = res_hdr->xd_hdr.length_sn & > > TB_XDOMAIN_SN_MASK; > > + res_seq >>= TB_XDOMAIN_SN_SHIFT; > > + req_seq = req_hdr->xd_hdr.length_sn & > > TB_XDOMAIN_SN_MASK; > > + req_seq >>= TB_XDOMAIN_SN_SHIFT; > > + if (res_seq != req_seq) > > + return false; > > + > > + /* Check that the XDomain protocol matches */ > > + if (!uuid_equal(&res_hdr->uuid, &req_hdr->uuid)) > > + return false; > > + > > + return true; > > + } > > + > > + default: > > + return false; > > + } > > +} > > + > > +static bool tb_xdomain_copy(struct tb_cfg_request *req, > > + const struct ctl_pkg *pkg) > > +{ > > + memcpy(req->response, pkg->buffer, req->response_size); > > + req->result.err = 0; > > + return true; > > +} > > + > > +static void response_ready(void *data) > > +{ > > + tb_cfg_request_put(data); > > +} > > + > > +static int __tb_xdomain_response(struct tb_ctl *ctl, const void > > *response, > > + size_t size, enum tb_cfg_pkg_type > > type) > > +{ > > + struct tb_cfg_request *req; > > + > > + req = tb_cfg_request_alloc(); > > + if (!req) > > + return -ENOMEM; > > + > > + req->match = tb_xdomain_match; > > + req->copy = tb_xdomain_copy; > > + req->request = response; > > + req->request_size = size; > > + req->request_type = type; > > + > > + return tb_cfg_request(ctl, req, response_ready, req); > > +} > > + > > +/** > > + * tb_xdomain_response() - Send a XDomain response message > > + * @xd: XDomain to send the message > > + * @response: Response to send > > + * @size: Size of the response > > + * @type: PDF type of the response > > + * > > + * This can be used to send a XDomain response message to the > > other > > + * domain. No response for the message is expected. > > + * > > + * Return: %0 in case of success and negative errno in case of > > failure > > + */ > > +int tb_xdomain_response(struct tb_xdomain *xd, const void > > *response, > > + size_t size, enum tb_cfg_pkg_type type) > > +{ > > + return __tb_xdomain_response(xd->tb->ctl, response, size, > > type); > > +} > > +EXPORT_SYMBOL_GPL(tb_xdomain_response); > > + > > +static int __tb_xdomain_request(struct tb_ctl *ctl, const void > > *request, > > + size_t request_size, enum tb_cfg_pkg_type request_type, > > void > > *response, > > + size_t response_size, enum tb_cfg_pkg_type response_type, > > + unsigned int timeout_msec) > > +{ > > + struct tb_cfg_request *req; > > + struct tb_cfg_result res; > > + > > + req = tb_cfg_request_alloc(); > > + if (!req) > > + return -ENOMEM; > > + > > + req->match = tb_xdomain_match; > > + req->copy = tb_xdomain_copy; > > + req->request = request; > > + req->request_size = request_size; > > + req->request_type = request_type; > > + req->response = response; > > + req->response_size = response_size; > > + req->response_type = response_type; > > + > > + res = tb_cfg_request_sync(ctl, req, timeout_msec); > > + > > + tb_cfg_request_put(req); > > + > > + return res.err == 1 ? -EIO : res.err; > > +} > > + > > +/** > > + * tb_xdomain_request() - Send a XDomain request > > + * @xd: XDomain to send the request > > + * @request: Request to send > > + * @request_size: Size of the request in bytes > > + * @request_type: PDF type of the request > > + * @response: Response is copied here > > + * @response_size: Expected size of the response in bytes > > + * @response_type: Expected PDF type of the response > > + * @timeout_msec: Timeout in milliseconds to wait for the response > > + * > > + * This function can be used to send XDomain control channel > > messages to > > + * the other domain. The function waits until the response is > > received > > + * or when timeout triggers. Whichever comes first. > > + * > > + * Return: %0 in case of success and negative errno in case of > > failure > > + */ > > +int tb_xdomain_request(struct tb_xdomain *xd, const void *request, > > + size_t request_size, enum tb_cfg_pkg_type request_type, > > + void *response, size_t response_size, > > + enum tb_cfg_pkg_type response_type, unsigned int > > timeout_msec) > > +{ > > + return __tb_xdomain_request(xd->tb->ctl, request, > > request_size, > > + request_type, response, > > response_size, > > + response_type, timeout_msec); > > +} > > +EXPORT_SYMBOL_GPL(tb_xdomain_request); > > + > > +static inline void tb_xdp_fill_header(struct tb_xdp_header *hdr, > > u64 > > route, > > + u8 sequence, enum tb_xdp_type type, size_t size) > > +{ > > + u32 length_sn; > > + > > + length_sn = (size - sizeof(hdr->xd_hdr)) / 4; > > + length_sn |= (sequence << TB_XDOMAIN_SN_SHIFT) & > > TB_XDOMAIN_SN_MASK; > > + > > + hdr->xd_hdr.route_hi = upper_32_bits(route); > > + hdr->xd_hdr.route_lo = lower_32_bits(route); > > + hdr->xd_hdr.length_sn = length_sn; > > + hdr->type = type; > > + memcpy(&hdr->uuid, &tb_xdp_uuid, sizeof(tb_xdp_uuid)); > > +} > > + > > +static int tb_xdp_handle_error(const struct tb_xdp_header *hdr) > > +{ > > + const struct tb_xdp_error_response *error; > > + > > + if (hdr->type != ERROR_RESPONSE) > > + return 0; > > + > > + error = (const struct tb_xdp_error_response *)hdr; > > + > > + switch (error->error) { > > + case ERROR_UNKNOWN_PACKET: > > + case ERROR_UNKNOWN_DOMAIN: > > + return -EIO; > > + case ERROR_NOT_SUPPORTED: > > + return -ENOTSUPP; > > + case ERROR_NOT_READY: > > + return -EAGAIN; > > + default: > > + break; > > + } > > + > > + return 0; > > +} > > + > > +static int tb_xdp_error_response(struct tb_ctl *ctl, u64 route, u8 > > sequence, > > + enum tb_xdp_error error) > > +{ > > + struct tb_xdp_error_response res; > > + > > + memset(&res, 0, sizeof(res)); > > + tb_xdp_fill_header(&res.hdr, route, sequence, > > ERROR_RESPONSE, > > + sizeof(res)); > > + res.error = error; > > + > > + return __tb_xdomain_response(ctl, &res, sizeof(res), > > + TB_CFG_PKG_XDOMAIN_RESP); > > +} > > + > > +static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 > > route, > > + const uuid_t *src_uuid, const uuid_t *dst_uuid, int retry, > > + u32 **block, u32 *generation) > > +{ > > + struct tb_xdp_properties_response *res; > > + struct tb_xdp_properties req; > > + u16 data_len, len; > > + size_t total_size; > > + u32 *data = NULL; > > + int ret; > > + > > + total_size = sizeof(*res) + > > TB_XDP_PROPERTIES_MAX_DATA_LENGTH * 4; > > + res = kzalloc(total_size, GFP_KERNEL); > > + if (!res) > > + return -ENOMEM; > > + > > + memset(&req, 0, sizeof(req)); > > + tb_xdp_fill_header(&req.hdr, route, retry % 4, > > PROPERTIES_REQUEST, > > + sizeof(req)); > > + memcpy(&req.src_uuid, src_uuid, sizeof(*src_uuid)); > > + memcpy(&req.dst_uuid, dst_uuid, sizeof(*dst_uuid)); > > + > > + len = 0; > > + data_len = 0; > > + > > + do { > > + ret = __tb_xdomain_request(ctl, &req, sizeof(req), > > + TB_CFG_PKG_XDOMAIN_REQ, > > res, > > + total_size, > > TB_CFG_PKG_XDOMAIN_RESP, > > + XDOMAIN_DEFAULT_TIMEOUT > > ); > > + if (ret) > > + goto err; > > + > > + ret = tb_xdp_handle_error(&res->hdr); > > + if (ret) > > + goto err; > > + > > + /* > > + * Package length includes the whole payload > > without > > the > > + * XDomain header. Validate first that the package > > is at > > + * least size of the response structure. > > + */ > > + len = res->hdr.xd_hdr.length_sn & > > TB_XDOMAIN_LENGTH_MASK; > > + if (len < sizeof(*res) / 4) { > > + ret = -EINVAL; > > + goto err; > > + } > > + > > + len += sizeof(res->hdr.xd_hdr) / 4; > > + len -= sizeof(*res) / 4; > > + > > + if (res->offset != req.offset) { > > + ret = -EINVAL; > > + goto err; > > + } > > + > > + /* > > + * First time allocate block that has enough space > > for > > + * the whole properties block. > > + */ > > + if (!data) { > > + data_len = res->data_length; > > + if (data_len > > > TB_XDP_PROPERTIES_MAX_LENGTH) > > { > > + ret = -E2BIG; > > + goto err; > > + } > > + > > + data = kcalloc(data_len, sizeof(u32), > > GFP_KERNEL); > > + if (!data) { > > + ret = -ENOMEM; > > + goto err; > > + } > > + } > > + > > + memcpy(data + req.offset, res->data, len * 4); > > + req.offset += len; > > + } while (!data_len || req.offset < data_len); > > + > > + *block = data; > > + *generation = res->generation; > > + > > + kfree(res); > > + > > + return data_len; > > + > > +err: > > + kfree(data); > > + kfree(res); > > + > > + return ret; > > +} > > + > > +static int tb_xdp_properties_response(struct tb *tb, struct tb_ctl > > *ctl, > > + u64 route, u8 sequence, const uuid_t *src_uuid, > > + const struct tb_xdp_properties *req) > > +{ > > + struct tb_xdp_properties_response *res; > > + size_t total_size; > > + u16 len; > > + int ret; > > + > > + /* > > + * Currently we expect all requests to be directed to us. > > The > > + * protocol supports forwarding, though which we might add > > + * support later on. > > + */ > > + if (!uuid_equal(src_uuid, &req->dst_uuid)) { > > + tb_xdp_error_response(ctl, route, sequence, > > + ERROR_UNKNOWN_DOMAIN); > > + return 0; > > + } > > + > > + mutex_lock(&xdomain_lock); > > + > > + if (req->offset >= xdomain_property_block_len) { > > + mutex_unlock(&xdomain_lock); > > + return -EINVAL; > > + } > > + > > + len = xdomain_property_block_len - req->offset; > > + len = min_t(u16, len, TB_XDP_PROPERTIES_MAX_DATA_LENGTH); > > + total_size = sizeof(*res) + len * 4; > > + > > + res = kzalloc(total_size, GFP_KERNEL); > > + if (!res) { > > + mutex_unlock(&xdomain_lock); > > + return -ENOMEM; > > + } > > + > > + tb_xdp_fill_header(&res->hdr, route, sequence, > > PROPERTIES_RESPONSE, > > + total_size); > > + res->generation = xdomain_property_block_gen; > > + res->data_length = xdomain_property_block_len; > > + res->offset = req->offset; > > + uuid_copy(&res->src_uuid, src_uuid); > > + uuid_copy(&res->dst_uuid, &req->src_uuid); > > + memcpy(res->data, &xdomain_property_block[req->offset], > > len > > * 4); > > + > > + mutex_unlock(&xdomain_lock); > > + > > + ret = __tb_xdomain_response(ctl, res, total_size, > > + TB_CFG_PKG_XDOMAIN_RESP); > > + > > + kfree(res); > > + return ret; > > +} > > + > > +static int tb_xdp_properties_changed_request(struct tb_ctl *ctl, > > u64 > > route, > > + int retry, const > > uuid_t > > *uuid) > > +{ > > + struct tb_xdp_properties_changed_response res; > > + struct tb_xdp_properties_changed req; > > + int ret; > > + > > + memset(&req, 0, sizeof(req)); > > + tb_xdp_fill_header(&req.hdr, route, retry % 4, > > + PROPERTIES_CHANGED_REQUEST, > > sizeof(req)); > > + uuid_copy(&req.src_uuid, uuid); > > + > > + memset(&res, 0, sizeof(res)); > > + ret = __tb_xdomain_request(ctl, &req, sizeof(req), > > + TB_CFG_PKG_XDOMAIN_REQ, &res, > > sizeof(res), > > + TB_CFG_PKG_XDOMAIN_RESP, > > + XDOMAIN_DEFAULT_TIMEOUT); > > + if (ret) > > + return ret; > > + > > + return tb_xdp_handle_error(&res.hdr); > > +} > > + > > +static int > > +tb_xdp_properties_changed_response(struct tb_ctl *ctl, u64 route, > > u8 > > sequence) > > +{ > > + struct tb_xdp_properties_changed_response res; > > + > > + memset(&res, 0, sizeof(res)); > > + tb_xdp_fill_header(&res.hdr, route, sequence, > > + PROPERTIES_CHANGED_RESPONSE, > > sizeof(res)); > > + return __tb_xdomain_response(ctl, &res, sizeof(res), > > + TB_CFG_PKG_XDOMAIN_RESP); > > +} > > + > > +/** > > + * tb_register_protocol_handler() - Register protocol handler > > + * @handler: Handler to register > > + * > > + * This allows XDomain service drivers to hook into incoming > > XDomain > > + * messages. After this function is called the service driver > > needs > > to > > + * be able to handle calls to callback whenever a package with the > > + * registered protocol is received. > > + */ > > +int tb_register_protocol_handler(struct tb_protocol_handler > > *handler) > > +{ > > + if (!handler->uuid || !handler->callback) > > + return -EINVAL; > > + if (uuid_equal(handler->uuid, &tb_xdp_uuid)) > > + return -EINVAL; > > + > > + mutex_lock(&xdomain_lock); > > + list_add_tail(&handler->list, &protocol_handlers); > > + mutex_unlock(&xdomain_lock); > > + > > + return 0; > > +} > > +EXPORT_SYMBOL_GPL(tb_register_protocol_handler); > > + > > +/** > > + * tb_unregister_protocol_handler() - Unregister protocol handler > > + * @handler: Handler to unregister > > + * > > + * Removes the previously registered protocol handler. > > + */ > > +void tb_unregister_protocol_handler(struct tb_protocol_handler > > *handler) > > +{ > > + mutex_lock(&xdomain_lock); > > + list_del_init(&handler->list); > > + mutex_unlock(&xdomain_lock); > > +} > > +EXPORT_SYMBOL_GPL(tb_unregister_protocol_handler); > > + > > +static void tb_xdp_handle_request(struct work_struct *work) > > +{ > > + struct xdomain_request_work *xw = container_of(work, > > typeof(*xw), work); > > + const struct tb_xdp_header *pkg = xw->pkg; > > + const struct tb_xdomain_header *xhdr = &pkg->xd_hdr; > > + struct tb *tb = xw->tb; > > + struct tb_ctl *ctl = tb->ctl; > > + const uuid_t *uuid; > > + int ret = 0; > > + u8 sequence; > > + u64 route; > > + > > + route = ((u64)xhdr->route_hi << 32 | xhdr->route_lo) & > > ~BIT_ULL(63); > > + sequence = xhdr->length_sn & TB_XDOMAIN_SN_MASK; > > + sequence >>= TB_XDOMAIN_SN_SHIFT; > > + > > + mutex_lock(&tb->lock); > > + if (tb->root_switch) > > + uuid = tb->root_switch->uuid; > > + else > > + uuid = NULL; > > + mutex_unlock(&tb->lock); > > + > > + if (!uuid) { > > + tb_xdp_error_response(ctl, route, sequence, > > ERROR_NOT_READY); > > + goto out; > > + } > > + > > + switch (pkg->type) { > > + case PROPERTIES_REQUEST: > > + ret = tb_xdp_properties_response(tb, ctl, route, > > sequence, uuid, > > + (const struct tb_xdp_properties *)pkg); > > + break; > > + > > + case PROPERTIES_CHANGED_REQUEST: { > > + const struct tb_xdp_properties_changed *xchg = > > + (const struct tb_xdp_properties_changed > > *)pkg; > > + struct tb_xdomain *xd; > > + > > + ret = tb_xdp_properties_changed_response(ctl, > > route, > > sequence); > > + > > + /* > > + * Since the properties have been changed, let's > > update > > + * the xdomain related to this connection as well > > in > > + * case there is a change in services it offers. > > + */ > > + xd = tb_xdomain_find_by_uuid_locked(tb, &xchg- > > > src_uuid); > > > > + if (xd) { > > + queue_delayed_work(tb->wq, &xd- > > > get_properties_work, > > > > + msecs_to_jiffies(50)); > > + tb_xdomain_put(xd); > > + } > > + > > + break; > > + } > > + > > + default: > > + break; > > + } > > + > > + if (ret) { > > + tb_warn(tb, "failed to send XDomain response for > > %#x\n", > > + pkg->type); > > + } > > + > > +out: > > + kfree(xw->pkg); > > + kfree(xw); > > +} > > + > > +static void > > +tb_xdp_schedule_request(struct tb *tb, const struct tb_xdp_header > > *hdr, > > + size_t size) > > +{ > > + struct xdomain_request_work *xw; > > + > > + xw = kmalloc(sizeof(*xw), GFP_KERNEL); > > + if (!xw) > > + return; > > + > > + INIT_WORK(&xw->work, tb_xdp_handle_request); > > + xw->pkg = kmemdup(hdr, size, GFP_KERNEL); > > + xw->tb = tb; > > + > > + queue_work(tb->wq, &xw->work); > > +} > > + > > +/** > > + * tb_register_service_driver() - Register XDomain service driver > > + * @drv: Driver to register > > + * > > + * Registers new service driver from @drv to the bus. > > + */ > > +int tb_register_service_driver(struct tb_service_driver *drv) > > +{ > > + drv->driver.bus = &tb_bus_type; > > + return driver_register(&drv->driver); > > +} > > +EXPORT_SYMBOL_GPL(tb_register_service_driver); > > + > > +/** > > + * tb_unregister_service_driver() - Unregister XDomain service > > driver > > + * @xdrv: Driver to unregister > > + * > > + * Unregisters XDomain service driver from the bus. > > + */ > > +void tb_unregister_service_driver(struct tb_service_driver *drv) > > +{ > > + driver_unregister(&drv->driver); > > +} > > +EXPORT_SYMBOL_GPL(tb_unregister_service_driver); > > + > > +static ssize_t key_show(struct device *dev, struct > > device_attribute > > *attr, > > + char *buf) > > +{ > > + struct tb_service *svc = container_of(dev, struct > > tb_service, dev); > > + > > + /* > > + * It should be null terminated but anything else is > > pretty > > much > > + * allowed. > > + */ > > + return sprintf(buf, "%*pEp\n", (int)strlen(svc->key), svc- > > > key); > > > > +} > > +static DEVICE_ATTR_RO(key); > > + > > +static int get_modalias(struct tb_service *svc, char *buf, size_t > > size) > > +{ > > + return snprintf(buf, size, "tbsvc:k%sp%08Xv%08Xr%08X", > > svc- > > > key, > > > > + svc->prtcid, svc->prtcvers, svc- > > >prtcrevs); > > +} > > + > > +static ssize_t modalias_show(struct device *dev, struct > > device_attribute *attr, > > + char *buf) > > +{ > > + struct tb_service *svc = container_of(dev, struct > > tb_service, dev); > > + > > + /* Full buffer size except new line and null termination > > */ > > + get_modalias(svc, buf, PAGE_SIZE - 2); > > + return sprintf(buf, "%s\n", buf); > > +} > > +static DEVICE_ATTR_RO(modalias); > > + > > +static ssize_t prtcid_show(struct device *dev, struct > > device_attribute *attr, > > + char *buf) > > +{ > > + struct tb_service *svc = container_of(dev, struct > > tb_service, dev); > > + > > + return sprintf(buf, "%u\n", svc->prtcid); > > +} > > +static DEVICE_ATTR_RO(prtcid); > > + > > +static ssize_t prtcvers_show(struct device *dev, struct > > device_attribute *attr, > > + char *buf) > > +{ > > + struct tb_service *svc = container_of(dev, struct > > tb_service, dev); > > + > > + return sprintf(buf, "%u\n", svc->prtcvers); > > +} > > +static DEVICE_ATTR_RO(prtcvers); > > + > > +static ssize_t prtcrevs_show(struct device *dev, struct > > device_attribute *attr, > > + char *buf) > > +{ > > + struct tb_service *svc = container_of(dev, struct > > tb_service, dev); > > + > > + return sprintf(buf, "%u\n", svc->prtcrevs); > > +} > > +static DEVICE_ATTR_RO(prtcrevs); > > + > > +static ssize_t prtcstns_show(struct device *dev, struct > > device_attribute *attr, > > + char *buf) > > +{ > > + struct tb_service *svc = container_of(dev, struct > > tb_service, dev); > > + > > + return sprintf(buf, "0x%08x\n", svc->prtcstns); > > +} > > +static DEVICE_ATTR_RO(prtcstns); > > + > > +static struct attribute *tb_service_attrs[] = { > > + &dev_attr_key.attr, > > + &dev_attr_modalias.attr, > > + &dev_attr_prtcid.attr, > > + &dev_attr_prtcvers.attr, > > + &dev_attr_prtcrevs.attr, > > + &dev_attr_prtcstns.attr, > > + NULL, > > +}; > > + > > +static struct attribute_group tb_service_attr_group = { > > + .attrs = tb_service_attrs, > > +}; > > + > > +static const struct attribute_group *tb_service_attr_groups[] = { > > + &tb_service_attr_group, > > + NULL, > > +}; > > + > > +static int tb_service_uevent(struct device *dev, struct > > kobj_uevent_env *env) > > +{ > > + struct tb_service *svc = container_of(dev, struct > > tb_service, dev); > > + char modalias[64]; > > + > > + get_modalias(svc, modalias, sizeof(modalias)); > > + return add_uevent_var(env, "MODALIAS=%s", modalias); > > +} > > + > > +static void tb_service_release(struct device *dev) > > +{ > > + struct tb_service *svc = container_of(dev, struct > > tb_service, dev); > > + struct tb_xdomain *xd = tb_service_parent(svc); > > + > > + ida_simple_remove(&xd->service_ids, svc->id); > > + kfree(svc->key); > > + kfree(svc); > > +} > > + > > +struct device_type tb_service_type = { > > + .name = "thunderbolt_service", > > + .groups = tb_service_attr_groups, > > + .uevent = tb_service_uevent, > > + .release = tb_service_release, > > +}; > > +EXPORT_SYMBOL_GPL(tb_service_type); > > + > > +static int remove_missing_service(struct device *dev, void *data) > > +{ > > + struct tb_xdomain *xd = data; > > + struct tb_service *svc; > > + > > + svc = tb_to_service(dev); > > + if (!svc) > > + return 0; > > + > > + if (!tb_property_find(xd->properties, svc->key, > > + TB_PROPERTY_TYPE_DIRECTORY)) > > + device_unregister(dev); > > + > > + return 0; > > +} > > + > > +static int find_service(struct device *dev, void *data) > > +{ > > + const struct tb_property *p = data; > > + struct tb_service *svc; > > + > > + svc = tb_to_service(dev); > > + if (!svc) > > + return 0; > > + > > + return !strcmp(svc->key, p->key); > > +} > > + > > +static int populate_service(struct tb_service *svc, > > + struct tb_property *property) > > +{ > > + struct tb_property_dir *dir = property->value.dir; > > + struct tb_property *p; > > + > > + /* Fill in standard properties */ > > + p = tb_property_find(dir, "prtcid", > > TB_PROPERTY_TYPE_VALUE); > > + if (p) > > + svc->prtcid = p->value.immediate; > > + p = tb_property_find(dir, "prtcvers", > > TB_PROPERTY_TYPE_VALUE); > > + if (p) > > + svc->prtcvers = p->value.immediate; > > + p = tb_property_find(dir, "prtcrevs", > > TB_PROPERTY_TYPE_VALUE); > > + if (p) > > + svc->prtcrevs = p->value.immediate; > > + p = tb_property_find(dir, "prtcstns", > > TB_PROPERTY_TYPE_VALUE); > > + if (p) > > + svc->prtcstns = p->value.immediate; > > + > > + svc->key = kstrdup(property->key, GFP_KERNEL); > > + if (!svc->key) > > + return -ENOMEM; > > + > > + return 0; > > +} > > + > > +static void enumerate_services(struct tb_xdomain *xd) > > +{ > > + struct tb_service *svc; > > + struct tb_property *p; > > + struct device *dev; > > + > > + /* > > + * First remove all services that are not available > > anymore > > in > > + * the updated property block. > > + */ > > + device_for_each_child_reverse(&xd->dev, xd, > > remove_missing_service); > > + > > + /* Then re-enumerate properties creating new services as > > we > > go */ > > + tb_property_for_each(xd->properties, p) { > > + if (p->type != TB_PROPERTY_TYPE_DIRECTORY) > > + continue; > > + > > + /* If the service exists already we are fine */ > > + dev = device_find_child(&xd->dev, p, > > find_service); > > + if (dev) { > > + put_device(dev); > > + continue; > > + } > > + > > + svc = kzalloc(sizeof(*svc), GFP_KERNEL); > > + if (!svc) > > + break; > > + > > + if (populate_service(svc, p)) { > > + kfree(svc); > > + break; > > + } > > + > > + svc->id = ida_simple_get(&xd->service_ids, 0, 0, > > GFP_KERNEL); > > + svc->dev.bus = &tb_bus_type; > > + svc->dev.type = &tb_service_type; > > + svc->dev.parent = &xd->dev; > > + dev_set_name(&svc->dev, "%s.%d", dev_name(&xd- > > >dev), > > svc->id); > > + > > + if (device_register(&svc->dev)) { > > + put_device(&svc->dev); > > + break; > > + } > > + } > > +} > > + > > +static int populate_properties(struct tb_xdomain *xd, > > + struct tb_property_dir *dir) > > +{ > > + const struct tb_property *p; > > + > > + /* Required properties */ > > + p = tb_property_find(dir, "deviceid", > > TB_PROPERTY_TYPE_VALUE); > > + if (!p) > > + return -EINVAL; > > + xd->device = p->value.immediate; > > + > > + p = tb_property_find(dir, "vendorid", > > TB_PROPERTY_TYPE_VALUE); > > + if (!p) > > + return -EINVAL; > > + xd->vendor = p->value.immediate; > > + > > + kfree(xd->device_name); > > + xd->device_name = NULL; > > + kfree(xd->vendor_name); > > + xd->vendor_name = NULL; > > + > > + /* Optional properties */ > > + p = tb_property_find(dir, "deviceid", > > TB_PROPERTY_TYPE_TEXT); > > + if (p) > > + xd->device_name = kstrdup(p->value.text, > > GFP_KERNEL); > > + p = tb_property_find(dir, "vendorid", > > TB_PROPERTY_TYPE_TEXT); > > + if (p) > > + xd->vendor_name = kstrdup(p->value.text, > > GFP_KERNEL); > > + > > + return 0; > > +} > > + > > +/* Called with @xd->lock held */ > > +static void tb_xdomain_restore_paths(struct tb_xdomain *xd) > > +{ > > + if (!xd->resume) > > + return; > > + > > + xd->resume = false; > > + if (xd->transmit_path) { > > + dev_dbg(&xd->dev, "re-establishing DMA path\n"); > > + tb_domain_approve_xdomain_paths(xd->tb, xd); > > + } > > +} > > + > > +static void tb_xdomain_get_properties(struct work_struct *work) > > +{ > > + struct tb_xdomain *xd = container_of(work, typeof(*xd), > > + get_properties_work.w > > or > > k); > > + struct tb_property_dir *dir; > > + struct tb *tb = xd->tb; > > + bool update = false; > > + u32 *block = NULL; > > + u32 gen = 0; > > + int ret; > > + > > + ret = tb_xdp_properties_request(tb->ctl, xd->route, xd- > > > local_uuid, > > > > + xd->remote_uuid, xd- > > > properties_retries, > > > > + &block, &gen); > > + if (ret < 0) { > > + if (xd->properties_retries-- > 0) { > > + queue_delayed_work(xd->tb->wq, &xd- > > > get_properties_work, > > > > + msecs_to_jiffies(1000)) > > ; > > + } else { > > + /* Give up now */ > > + dev_err(&xd->dev, > > + "failed read XDomain properties > > from > > %pUb\n", > > + xd->remote_uuid); > > + } > > + return; > > + } > > + > > + xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; > > + > > + mutex_lock(&xd->lock); > > + > > + /* Only accept newer generation properties */ > > + if (xd->properties && gen <= xd->property_block_gen) { > > + /* > > + * On resume it is likely that the properties > > block > > is > > + * not changed (unless the other end added or > > removed > > + * services). However, we need to make sure the > > existing > > + * DMA paths are restored properly. > > + */ > > + tb_xdomain_restore_paths(xd); > > + goto err_free_block; > > + } > > + > > + dir = tb_property_parse_dir(block, ret); > > + if (!dir) { > > + dev_err(&xd->dev, "failed to parse XDomain > > properties\n"); > > + goto err_free_block; > > + } > > + > > + ret = populate_properties(xd, dir); > > + if (ret) { > > + dev_err(&xd->dev, "missing XDomain properties in > > response\n"); > > + goto err_free_dir; > > + } > > + > > + /* Release the existing one */ > > + if (xd->properties) { > > + tb_property_free_dir(xd->properties); > > + update = true; > > + } > > + > > + xd->properties = dir; > > + xd->property_block_gen = gen; > > + > > + tb_xdomain_restore_paths(xd); > > + > > + mutex_unlock(&xd->lock); > > + > > + kfree(block); > > + > > + /* > > + * Now the device should be ready enough so we can add it > > to > > the > > + * bus and let userspace know about it. If the device is > > already > > + * registered, we notify the userspace that it has > > changed. > > + */ > > + if (!update) { > > + if (device_add(&xd->dev)) { > > + dev_err(&xd->dev, "failed to add XDomain > > device\n"); > > + return; > > + } > > + } else { > > + kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE); > > + } > > + > > + enumerate_services(xd); > > + return; > > + > > +err_free_dir: > > + tb_property_free_dir(dir); > > +err_free_block: > > + kfree(block); > > + mutex_unlock(&xd->lock); > > +} > > + > > +static void tb_xdomain_properties_changed(struct work_struct > > *work) > > +{ > > + struct tb_xdomain *xd = container_of(work, typeof(*xd), > > + properties_changed_wo > > rk > > .work); > > + int ret; > > + > > + ret = tb_xdp_properties_changed_request(xd->tb->ctl, xd- > > > route, > > > > + xd->properties_changed_retries, > > xd- > > > local_uuid); > > > > + if (ret) { > > + if (xd->properties_changed_retries-- > 0) > > + queue_delayed_work(xd->tb->wq, > > + &xd- > > > properties_changed_work, > > > > + msecs_to_jiffies(1000)) > > ; > > + return; > > + } > > + > > + xd->properties_changed_retries = > > XDOMAIN_PROPERTIES_CHANGED_RETRIES; > > +} > > + > > +static ssize_t device_show(struct device *dev, struct > > device_attribute *attr, > > + char *buf) > > +{ > > + struct tb_xdomain *xd = container_of(dev, struct > > tb_xdomain, > > dev); > > + > > + return sprintf(buf, "%#x\n", xd->device); > > +} > > +static DEVICE_ATTR_RO(device); > > + > > +static ssize_t > > +device_name_show(struct device *dev, struct device_attribute > > *attr, > > char *buf) > > +{ > > + struct tb_xdomain *xd = container_of(dev, struct > > tb_xdomain, > > dev); > > + int ret; > > + > > + if (mutex_lock_interruptible(&xd->lock)) > > + return -ERESTARTSYS; > > + ret = sprintf(buf, "%s\n", xd->device_name ? xd- > > >device_name > > : ""); > > + mutex_unlock(&xd->lock); > > + > > + return ret; > > +} > > +static DEVICE_ATTR_RO(device_name); > > + > > +static ssize_t vendor_show(struct device *dev, struct > > device_attribute *attr, > > + char *buf) > > +{ > > + struct tb_xdomain *xd = container_of(dev, struct > > tb_xdomain, > > dev); > > + > > + return sprintf(buf, "%#x\n", xd->vendor); > > +} > > +static DEVICE_ATTR_RO(vendor); > > + > > +static ssize_t > > +vendor_name_show(struct device *dev, struct device_attribute > > *attr, > > char *buf) > > +{ > > + struct tb_xdomain *xd = container_of(dev, struct > > tb_xdomain, > > dev); > > + int ret; > > + > > + if (mutex_lock_interruptible(&xd->lock)) > > + return -ERESTARTSYS; > > + ret = sprintf(buf, "%s\n", xd->vendor_name ? xd- > > >vendor_name > > : ""); > > + mutex_unlock(&xd->lock); > > + > > + return ret; > > +} > > +static DEVICE_ATTR_RO(vendor_name); > > + > > +static ssize_t unique_id_show(struct device *dev, struct > > device_attribute *attr, > > + char *buf) > > +{ > > + struct tb_xdomain *xd = container_of(dev, struct > > tb_xdomain, > > dev); > > + > > + return sprintf(buf, "%pUb\n", xd->remote_uuid); > > +} > > +static DEVICE_ATTR_RO(unique_id); > > + > > +static struct attribute *xdomain_attrs[] = { > > + &dev_attr_device.attr, > > + &dev_attr_device_name.attr, > > + &dev_attr_unique_id.attr, > > + &dev_attr_vendor.attr, > > + &dev_attr_vendor_name.attr, > > + NULL, > > +}; > > + > > +static struct attribute_group xdomain_attr_group = { > > + .attrs = xdomain_attrs, > > +}; > > + > > +static const struct attribute_group *xdomain_attr_groups[] = { > > + &xdomain_attr_group, > > + NULL, > > +}; > > + > > +static void tb_xdomain_release(struct device *dev) > > +{ > > + struct tb_xdomain *xd = container_of(dev, struct > > tb_xdomain, > > dev); > > + > > + put_device(xd->dev.parent); > > + > > + tb_property_free_dir(xd->properties); > > + ida_destroy(&xd->service_ids); > > + > > + kfree(xd->local_uuid); > > + kfree(xd->remote_uuid); > > + kfree(xd->device_name); > > + kfree(xd->vendor_name); > > + kfree(xd); > > +} > > + > > +static void start_handshake(struct tb_xdomain *xd) > > +{ > > + xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; > > + xd->properties_changed_retries = > > XDOMAIN_PROPERTIES_CHANGED_RETRIES; > > + > > + /* Start exchanging properties with the other host */ > > + queue_delayed_work(xd->tb->wq, &xd- > > >properties_changed_work, > > + msecs_to_jiffies(100)); > > + queue_delayed_work(xd->tb->wq, &xd->get_properties_work, > > + msecs_to_jiffies(1000)); > > +} > > + > > +static void stop_handshake(struct tb_xdomain *xd) > > +{ > > + xd->properties_retries = 0; > > + xd->properties_changed_retries = 0; > > + > > + cancel_delayed_work_sync(&xd->get_properties_work); > > + cancel_delayed_work_sync(&xd->properties_changed_work); > > +} > > + > > +static int __maybe_unused tb_xdomain_suspend(struct device *dev) > > +{ > > + stop_handshake(tb_to_xdomain(dev)); > > + return 0; > > +} > > + > > +static int __maybe_unused tb_xdomain_resume(struct device *dev) > > +{ > > + struct tb_xdomain *xd = tb_to_xdomain(dev); > > + > > + /* > > + * Ask tb_xdomain_get_properties() restore any existing > > DMA > > + * paths after properties are re-read. > > + */ > > + xd->resume = true; > > + start_handshake(xd); > > + > > + return 0; > > +} > > + > > +static const struct dev_pm_ops tb_xdomain_pm_ops = { > > + SET_SYSTEM_SLEEP_PM_OPS(tb_xdomain_suspend, > > tb_xdomain_resume) > > +}; > > + > > +struct device_type tb_xdomain_type = { > > + .name = "thunderbolt_xdomain", > > + .release = tb_xdomain_release, > > + .pm = &tb_xdomain_pm_ops, > > +}; > > +EXPORT_SYMBOL_GPL(tb_xdomain_type); > > + > > +/** > > + * tb_xdomain_alloc() - Allocate new XDomain object > > + * @tb: Domain where the XDomain belongs > > + * @parent: Parent device (the switch through the connection to > > the > > + * other domain is reached). > > + * @route: Route string used to reach the other domain > > + * @local_uuid: Our local domain UUID > > + * @remote_uuid: UUID of the other domain > > + * > > + * Allocates new XDomain structure and returns pointer to that. > > The > > + * object must be released by calling tb_xdomain_put(). > > + */ > > +struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device > > *parent, > > + u64 route, const uuid_t > > *local_uuid, > > + const uuid_t *remote_uuid) > > +{ > > + struct tb_xdomain *xd; > > + > > + xd = kzalloc(sizeof(*xd), GFP_KERNEL); > > + if (!xd) > > + return NULL; > > + > > + xd->tb = tb; > > + xd->route = route; > > + ida_init(&xd->service_ids); > > + mutex_init(&xd->lock); > > + INIT_DELAYED_WORK(&xd->get_properties_work, > > tb_xdomain_get_properties); > > + INIT_DELAYED_WORK(&xd->properties_changed_work, > > + tb_xdomain_properties_changed); > > + > > + xd->local_uuid = kmemdup(local_uuid, sizeof(uuid_t), > > GFP_KERNEL); > > + if (!xd->local_uuid) > > + goto err_free; > > + > > + xd->remote_uuid = kmemdup(remote_uuid, sizeof(uuid_t), > > GFP_KERNEL); > > + if (!xd->remote_uuid) > > + goto err_free_local_uuid; > > + > > + device_initialize(&xd->dev); > > + xd->dev.parent = get_device(parent); > > + xd->dev.bus = &tb_bus_type; > > + xd->dev.type = &tb_xdomain_type; > > + xd->dev.groups = xdomain_attr_groups; > > + dev_set_name(&xd->dev, "%u-%llx", tb->index, route); > > + > > + return xd; > > + > > +err_free_local_uuid: > > + kfree(xd->local_uuid); > > +err_free: > > + kfree(xd); > > + > > + return NULL; > > +} > > + > > +/** > > + * tb_xdomain_add() - Add XDomain to the bus > > + * @xd: XDomain to add > > + * > > + * This function starts XDomain discovery protocol handshake and > > + * eventually adds the XDomain to the bus. After calling this > > function > > + * the caller needs to call tb_xdomain_remove() in order to remove > > and > > + * release the object regardless whether the handshake succeeded > > or > > not. > > + */ > > +void tb_xdomain_add(struct tb_xdomain *xd) > > +{ > > + /* Start exchanging properties with the other host */ > > + start_handshake(xd); > > +} > > + > > +static int unregister_service(struct device *dev, void *data) > > +{ > > + device_unregister(dev); > > + return 0; > > +} > > + > > +/** > > + * tb_xdomain_remove() - Remove XDomain from the bus > > + * @xd: XDomain to remove > > + * > > + * This will stop all ongoing configuration work and remove the > > XDomain > > + * along with any services from the bus. When the last reference > > to > > @xd > > + * is released the object will be released as well. > > + */ > > +void tb_xdomain_remove(struct tb_xdomain *xd) > > +{ > > + stop_handshake(xd); > > + > > + device_for_each_child_reverse(&xd->dev, xd, > > unregister_service); > > + > > + if (!device_is_registered(&xd->dev)) > > + put_device(&xd->dev); > > + else > > + device_unregister(&xd->dev); > > +} > > + > > +/** > > + * tb_xdomain_enable_paths() - Enable DMA paths for XDomain > > connection > > + * @xd: XDomain connection > > + * @transmit_path: HopID of the transmit path the other end is > > using > > to > > + * send packets > > + * @transmit_ring: DMA ring used to receive packets from the other > > end > > + * @receive_path: HopID of the receive path the other end is using > > to > > + * receive packets > > + * @receive_ring: DMA ring used to send packets to the other end > > + * > > + * The function enables DMA paths accordingly so that after > > successful > > + * return the caller can send and receive packets using high-speed > > DMA > > + * path. > > + * > > + * Return: %0 in case of success and negative errno in case of > > error > > + */ > > +int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 > > transmit_path, > > + u16 transmit_ring, u16 receive_path, > > + u16 receive_ring) > > +{ > > + int ret; > > + > > + mutex_lock(&xd->lock); > > + > > + if (xd->transmit_path) { > > + ret = xd->transmit_path == transmit_path ? 0 : > > -EBUSY; > > + goto exit_unlock; > > + } > > + > > + xd->transmit_path = transmit_path; > > + xd->transmit_ring = transmit_ring; > > + xd->receive_path = receive_path; > > + xd->receive_ring = receive_ring; > > + > > + ret = tb_domain_approve_xdomain_paths(xd->tb, xd); > > + > > +exit_unlock: > > + mutex_unlock(&xd->lock); > > + > > + return ret; > > +} > > +EXPORT_SYMBOL_GPL(tb_xdomain_enable_paths); > > + > > +/** > > + * tb_xdomain_disable_paths() - Disable DMA paths for XDomain > > connection > > + * @xd: XDomain connection > > + * > > + * This does the opposite of tb_xdomain_enable_paths(). After call > > to > > + * this the caller is not expected to use the rings anymore. > > + * > > + * Return: %0 in case of success and negative errno in case of > > error > > + */ > > +int tb_xdomain_disable_paths(struct tb_xdomain *xd) > > +{ > > + int ret = 0; > > + > > + mutex_lock(&xd->lock); > > + if (xd->transmit_path) { > > + xd->transmit_path = 0; > > + xd->transmit_ring = 0; > > + xd->receive_path = 0; > > + xd->receive_ring = 0; > > + > > + ret = tb_domain_disconnect_xdomain_paths(xd->tb, > > xd); > > + } > > + mutex_unlock(&xd->lock); > > + > > + return ret; > > +} > > +EXPORT_SYMBOL_GPL(tb_xdomain_disable_paths); > > + > > +struct tb_xdomain_lookup { > > + const uuid_t *uuid; > > + u8 link; > > + u8 depth; > > +}; > > + > > +static struct tb_xdomain *switch_find_xdomain(struct tb_switch > > *sw, > > + const struct tb_xdomain_lookup *lookup) > > +{ > > + int i; > > + > > + for (i = 1; i <= sw->config.max_port_number; i++) { > > + struct tb_port *port = &sw->ports[i]; > > + struct tb_xdomain *xd; > > + > > + if (tb_is_upstream_port(port)) > > + continue; > > + > > + if (port->xdomain) { > > + xd = port->xdomain; > > + > > + if (lookup->uuid) { > > + if (uuid_equal(xd->remote_uuid, > > lookup->uuid)) > > + return xd; > > + } else if (lookup->link == xd->link && > > + lookup->depth == xd->depth) { > > + return xd; > > + } > > + } else if (port->remote) { > > + xd = switch_find_xdomain(port->remote->sw, > > lookup); > > + if (xd) > > + return xd; > > + } > > + } > > + > > + return NULL; > > +} > > + > > +/** > > + * tb_xdomain_find_by_uuid() - Find an XDomain by UUID > > + * @tb: Domain where the XDomain belongs to > > + * @uuid: UUID to look for > > + * > > + * Finds XDomain by walking through the Thunderbolt topology below > > @tb. > > + * The returned XDomain will have its reference count increased so > > the > > + * caller needs to call tb_xdomain_put() when it is done with the > > + * object. > > + * > > + * This will find all XDomains including the ones that are not yet > > added > > + * to the bus (handshake is still in progress). > > + * > > + * The caller needs to hold @tb->lock. > > + */ > > +struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const > > uuid_t *uuid) > > +{ > > + struct tb_xdomain_lookup lookup; > > + struct tb_xdomain *xd; > > + > > + memset(&lookup, 0, sizeof(lookup)); > > + lookup.uuid = uuid; > > + > > + xd = switch_find_xdomain(tb->root_switch, &lookup); > > + if (xd) { > > + get_device(&xd->dev); > > + return xd; > > + } > > + > > + return NULL; > > +} > > +EXPORT_SYMBOL_GPL(tb_xdomain_find_by_uuid); > > + > > +/** > > + * tb_xdomain_find_by_link_depth() - Find an XDomain by link and > > depth > > + * @tb: Domain where the XDomain belongs to > > + * @link: Root switch link number > > + * @depth: Depth in the link > > + * > > + * Finds XDomain by walking through the Thunderbolt topology below > > @tb. > > + * The returned XDomain will have its reference count increased so > > the > > + * caller needs to call tb_xdomain_put() when it is done with the > > + * object. > > + * > > + * This will find all XDomains including the ones that are not yet > > added > > + * to the bus (handshake is still in progress). > > + * > > + * The caller needs to hold @tb->lock. > > + */ > > +struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 > > link, > > + u8 depth) > > +{ > > + struct tb_xdomain_lookup lookup; > > + struct tb_xdomain *xd; > > + > > + memset(&lookup, 0, sizeof(lookup)); > > + lookup.link = link; > > + lookup.depth = depth; > > + > > + xd = switch_find_xdomain(tb->root_switch, &lookup); > > + if (xd) { > > + get_device(&xd->dev); > > + return xd; > > + } > > + > > + return NULL; > > +} > > + > > +bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type > > type, > > + const void *buf, size_t size) > > +{ > > + const struct tb_protocol_handler *handler, *tmp; > > + const struct tb_xdp_header *hdr = buf; > > + unsigned int length; > > + int ret = 0; > > + > > + /* We expect the packet is at least size of the header */ > > + length = hdr->xd_hdr.length_sn & TB_XDOMAIN_LENGTH_MASK; > > + if (length != size / 4 - sizeof(hdr->xd_hdr) / 4) > > + return true; > > + if (length < sizeof(*hdr) / 4 - sizeof(hdr->xd_hdr) / 4) > > + return true; > > + > > + /* > > + * Handle XDomain discovery protocol packets directly > > here. > > For > > + * other protocols (based on their UUID) we call > > registered > > + * handlers in turn. > > + */ > > + if (uuid_equal(&hdr->uuid, &tb_xdp_uuid)) { > > + if (type == TB_CFG_PKG_XDOMAIN_REQ) { > > + tb_xdp_schedule_request(tb, hdr, size); > > + return true; > > + } > > + return false; > > + } > > + > > + mutex_lock(&xdomain_lock); > > + list_for_each_entry_safe(handler, tmp, &protocol_handlers, > > list) { > > + if (!uuid_equal(&hdr->uuid, handler->uuid)) > > + continue; > > + > > + mutex_unlock(&xdomain_lock); > > + ret = handler->callback(buf, size, handler->data); > > + mutex_lock(&xdomain_lock); > > + > > + if (ret) > > + break; > > + } > > + mutex_unlock(&xdomain_lock); > > + > > + return ret > 0; > > +} > > + > > +static int rebuild_property_block(void) > > +{ > > + u32 *block, len; > > + int ret; > > + > > + ret = tb_property_format_dir(xdomain_property_dir, NULL, > > 0); > > + if (ret < 0) > > + return ret; > > + > > + len = ret; > > + > > + block = kcalloc(len, sizeof(u32), GFP_KERNEL); > > + if (!block) > > + return -ENOMEM; > > + > > + ret = tb_property_format_dir(xdomain_property_dir, block, > > len); > > + if (ret) { > > + kfree(block); > > + return ret; > > + } > > + > > + kfree(xdomain_property_block); > > + xdomain_property_block = block; > > + xdomain_property_block_len = len; > > + xdomain_property_block_gen++; > > + > > + return 0; > > +} > > + > > +static int update_xdomain(struct device *dev, void *data) > > +{ > > + struct tb_xdomain *xd; > > + > > + xd = tb_to_xdomain(dev); > > + if (xd) { > > + queue_delayed_work(xd->tb->wq, &xd- > > > properties_changed_work, > > > > + msecs_to_jiffies(50)); > > + } > > + > > + return 0; > > +} > > + > > +static void update_all_xdomains(void) > > +{ > > + bus_for_each_dev(&tb_bus_type, NULL, NULL, > > update_xdomain); > > +} > > + > > +static bool remove_directory(const char *key, const struct > > tb_property_dir *dir) > > +{ > > + struct tb_property *p; > > + > > + p = tb_property_find(xdomain_property_dir, key, > > + TB_PROPERTY_TYPE_DIRECTORY); > > + if (p && p->value.dir == dir) { > > + tb_property_remove(p); > > + return true; > > + } > > + return false; > > +} > > + > > +/** > > + * tb_register_property_dir() - Register property directory to the > > host > > + * @key: Key (name) of the directory to add > > + * @dir: Directory to add > > + * > > + * Service drivers can use this function to add new property > > directory > > + * to the host available properties. The other connected hosts are > > + * notified so they can re-read properties of this host if they > > are > > + * interested. > > + * > > + * Return: %0 on success and negative errno on failure > > + */ > > +int tb_register_property_dir(const char *key, struct > > tb_property_dir > > *dir) > > +{ > > + int ret; > > + > > + if (!key || strlen(key) > 8) > > + return -EINVAL; > > + > > + mutex_lock(&xdomain_lock); > > + if (tb_property_find(xdomain_property_dir, key, > > + TB_PROPERTY_TYPE_DIRECTORY)) { > > + ret = -EEXIST; > > + goto err_unlock; > > + } > > + > > + ret = tb_property_add_dir(xdomain_property_dir, key, dir); > > + if (ret) > > + goto err_unlock; > > + > > + ret = rebuild_property_block(); > > + if (ret) { > > + remove_directory(key, dir); > > + goto err_unlock; > > + } > > + > > + mutex_unlock(&xdomain_lock); > > + update_all_xdomains(); > > + return 0; > > + > > +err_unlock: > > + mutex_unlock(&xdomain_lock); > > + return ret; > > +} > > +EXPORT_SYMBOL_GPL(tb_register_property_dir); > > + > > +/** > > + * tb_unregister_property_dir() - Removes property directory from > > host > > + * @key: Key (name) of the directory > > + * @dir: Directory to remove > > + * > > + * This will remove the existing directory from this host and > > notify > > the > > + * connected hosts about the change. > > + */ > > +void tb_unregister_property_dir(const char *key, struct > > tb_property_dir *dir) > > +{ > > + int ret = 0; > > + > > + mutex_lock(&xdomain_lock); > > + if (remove_directory(key, dir)) > > + ret = rebuild_property_block(); > > + mutex_unlock(&xdomain_lock); > > + > > + if (!ret) > > + update_all_xdomains(); > > +} > > +EXPORT_SYMBOL_GPL(tb_unregister_property_dir); > > + > > +int tb_xdomain_init(void) > > +{ > > + int ret; > > + > > + xdomain_property_dir = tb_property_create_dir(NULL); > > + if (!xdomain_property_dir) > > + return -ENOMEM; > > + > > + /* > > + * Initialize standard set of properties without any > > service > > + * directories. Those will be added by service drivers > > + * themselves when they are loaded. > > + */ > > + tb_property_add_immediate(xdomain_property_dir, > > "vendorid", > > + PCI_VENDOR_ID_INTEL); > > + tb_property_add_text(xdomain_property_dir, "vendorid", > > "Intel Corp."); > > + tb_property_add_immediate(xdomain_property_dir, > > "deviceid", > > 0x1); > > + tb_property_add_text(xdomain_property_dir, "deviceid", > > + utsname()->nodename); > > + tb_property_add_immediate(xdomain_property_dir, > > "devicerv", > > 0x80000100); > > + > > + ret = rebuild_property_block(); > > + if (ret) { > > + tb_property_free_dir(xdomain_property_dir); > > + xdomain_property_dir = NULL; > > + } > > + > > + return ret; > > +} > > + > > +void tb_xdomain_exit(void) > > +{ > > + kfree(xdomain_property_block); > > + tb_property_free_dir(xdomain_property_dir); > > +} > > diff --git a/include/linux/mod_devicetable.h > > b/include/linux/mod_devicetable.h > > index 694cebb50f72..7625c3b81f84 100644 > > --- a/include/linux/mod_devicetable.h > > +++ b/include/linux/mod_devicetable.h > > @@ -683,5 +683,31 @@ struct fsl_mc_device_id { > > const char obj_type[16]; > > }; > > > > +/** > > + * struct tb_service_id - Thunderbolt service identifiers > > + * @match_flags: Flags used to match the structure > > + * @protocol_key: Protocol key the service supports > > + * @protocol_id: Protocol id the service supports > > + * @protocol_version: Version of the protocol > > + * @protocol_revision: Revision of the protocol software > > + * @driver_data: Driver specific data > > + * > > + * Thunderbolt XDomain services are exposed as devices where each > > device > > + * carries the protocol information the service supports. > > Thunderbolt > > + * XDomain service drivers match against that information. > > + */ > > +struct tb_service_id { > > + __u32 match_flags; > > + char protocol_key[8 + 1]; > > + __u32 protocol_id; > > + __u32 protocol_version; > > + __u32 protocol_revision; > > + kernel_ulong_t driver_data; > > +}; > > + > > +#define TBSVC_MATCH_PROTOCOL_KEY 0x0001 > > +#define TBSVC_MATCH_PROTOCOL_ID 0x0002 > > +#define TBSVC_MATCH_PROTOCOL_VERSION 0x0004 > > +#define TBSVC_MATCH_PROTOCOL_REVISION 0x0008 > > > > #endif /* LINUX_MOD_DEVICETABLE_H */ > > diff --git a/include/linux/thunderbolt.h > > b/include/linux/thunderbolt.h > > index 4011d6537a8c..79abdaf1c296 100644 > > --- a/include/linux/thunderbolt.h > > +++ b/include/linux/thunderbolt.h > > @@ -17,6 +17,7 @@ > > #include <linux/device.h> > > #include <linux/list.h> > > #include <linux/mutex.h> > > +#include <linux/mod_devicetable.h> > > #include <linux/uuid.h> > > > > enum tb_cfg_pkg_type { > > @@ -77,6 +78,8 @@ struct tb { > > }; > > > > extern struct bus_type tb_bus_type; > > +extern struct device_type tb_service_type; > > +extern struct device_type tb_xdomain_type; > > > > #define TB_LINKS_PER_PHY_PORT 2 > > > > @@ -155,4 +158,243 @@ struct tb_property > > *tb_property_get_next(struct > > tb_property_dir *dir, > > property; > > \ > > property = tb_property_get_next(dir, property)) > > > > +int tb_register_property_dir(const char *key, struct > > tb_property_dir > > *dir); > > +void tb_unregister_property_dir(const char *key, struct > > tb_property_dir *dir); > > + > > +/** > > + * struct tb_xdomain - Cross-domain (XDomain) connection > > + * @dev: XDomain device > > + * @tb: Pointer to the domain > > + * @remote_uuid: UUID of the remote domain (host) > > + * @local_uuid: Cached local UUID > > + * @route: Route string the other domain can be reached > > + * @vendor: Vendor ID of the remote domain > > + * @device: Device ID of the demote domain > > + * @lock: Lock to serialize access to the following fields of this > > structure > > + * @vendor_name: Name of the vendor (or %NULL if not known) > > + * @device_name: Name of the device (or %NULL if not known) > > + * @is_unplugged: The XDomain is unplugged > > + * @resume: The XDomain is being resumed > > + * @transmit_path: HopID which the remote end expects us to > > transmit > > + * @transmit_ring: Local ring (hop) where outgoing packets are > > pushed > > + * @receive_path: HopID which we expect the remote end to transmit > > + * @receive_ring: Local ring (hop) where incoming packets arrive > > + * @service_ids: Used to generate IDs for the services > > + * @properties: Properties exported by the remote domain > > + * @property_block_gen: Generation of @properties > > + * @properties_lock: Lock protecting @properties. > > + * @get_properties_work: Work used to get remote domain properties > > + * @properties_retries: Number of times left to read properties > > + * @properties_changed_work: Work used to notify the remote domain > > that > > + * our properties have changed > > + * @properties_changed_retries: Number of times left to send > > properties > > + * changed notification > > + * @link: Root switch link the remote domain is connected (ICM > > only) > > + * @depth: Depth in the chain the remote domain is connected (ICM > > only) > > + * > > + * This structure represents connection across two domains > > (hosts). > > + * Each XDomain contains zero or more services which are exposed > > as > > + * &struct tb_service objects. > > + * > > + * Service drivers may access this structure if they need to > > enumerate > > + * non-standard properties but they need hold @lock when doing so > > + * because properties can be changed asynchronously in response to > > + * changes in the remote domain. > > + */ > > +struct tb_xdomain { > > + struct device dev; > > + struct tb *tb; > > + uuid_t *remote_uuid; > > + const uuid_t *local_uuid; > > + u64 route; > > + u16 vendor; > > + u16 device; > > + struct mutex lock; > > + const char *vendor_name; > > + const char *device_name; > > + bool is_unplugged; > > + bool resume; > > + u16 transmit_path; > > + u16 transmit_ring; > > + u16 receive_path; > > + u16 receive_ring; > > + struct ida service_ids; > > + struct tb_property_dir *properties; > > + u32 property_block_gen; > > + struct delayed_work get_properties_work; > > + int properties_retries; > > + struct delayed_work properties_changed_work; > > + int properties_changed_retries; > > + u8 link; > > + u8 depth; > > +}; > > + > > +int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 > > transmit_path, > > + u16 transmit_ring, u16 receive_path, > > + u16 receive_ring); > > +int tb_xdomain_disable_paths(struct tb_xdomain *xd); > > +struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const > > uuid_t *uuid); > > + > > +static inline struct tb_xdomain * > > +tb_xdomain_find_by_uuid_locked(struct tb *tb, const uuid_t *uuid) > > +{ > > + struct tb_xdomain *xd; > > + > > + mutex_lock(&tb->lock); > > + xd = tb_xdomain_find_by_uuid(tb, uuid); > > + mutex_unlock(&tb->lock); > > + > > + return xd; > > +} > > + > > +static inline struct tb_xdomain *tb_xdomain_get(struct tb_xdomain > > *xd) > > +{ > > + if (xd) > > + get_device(&xd->dev); > > + return xd; > > +} > > + > > +static inline void tb_xdomain_put(struct tb_xdomain *xd) > > +{ > > + if (xd) > > + put_device(&xd->dev); > > +} > > + > > +static inline bool tb_is_xdomain(const struct device *dev) > > +{ > > + return dev->type == &tb_xdomain_type; > > +} > > + > > +static inline struct tb_xdomain *tb_to_xdomain(struct device *dev) > > +{ > > + if (tb_is_xdomain(dev)) > > + return container_of(dev, struct tb_xdomain, dev); > > + return NULL; > > +} > > + > > +int tb_xdomain_response(struct tb_xdomain *xd, const void > > *response, > > + size_t size, enum tb_cfg_pkg_type type); > > +int tb_xdomain_request(struct tb_xdomain *xd, const void *request, > > + size_t request_size, enum tb_cfg_pkg_type > > request_type, > > + void *response, size_t response_size, > > + enum tb_cfg_pkg_type response_type, > > + unsigned int timeout_msec); > > + > > +/** > > + * tb_protocol_handler - Protocol specific handler > > + * @uuid: XDomain messages with this UUID are dispatched to this > > handler > > + * @callback: Callback called with the XDomain message. Returning > > %1 > > + * here tells the XDomain core that the message was > > handled > > + * by this handler and should not be forwared to > > other > > + * handlers. > > + * @data: Data passed with the callback > > + * @list: Handlers are linked using this > > + * > > + * Thunderbolt services can hook into incoming XDomain requests by > > + * registering protocol handler. Only limitation is that the > > XDomain > > + * discovery protocol UUID cannot be registered since it is > > handled > > by > > + * the core XDomain code. > > + * > > + * The @callback must check that the message is really directed to > > the > > + * service the driver implements. > > + */ > > +struct tb_protocol_handler { > > + const uuid_t *uuid; > > + int (*callback)(const void *buf, size_t size, void *data); > > + void *data; > > + struct list_head list; > > +}; > > + > > +int tb_register_protocol_handler(struct tb_protocol_handler > > *handler); > > +void tb_unregister_protocol_handler(struct tb_protocol_handler > > *handler); > > + > > +/** > > + * struct tb_service - Thunderbolt service > > + * @dev: XDomain device > > + * @id: ID of the service (shown in sysfs) > > + * @key: Protocol key from the properties directory > > + * @prtcid: Protocol ID from the properties directory > > + * @prtcvers: Protocol version from the properties directory > > + * @prtcrevs: Protocol software revision from the properties > > directory > > + * @prtcstns: Protocol settings mask from the properties directory > > + * > > + * Each domain exposes set of services it supports as collection > > of > > + * properties. For each service there will be one corresponding > > + * &struct tb_service. Service drivers are bound to these. > > + */ > > +struct tb_service { > > + struct device dev; > > + int id; > > + const char *key; > > + u32 prtcid; > > + u32 prtcvers; > > + u32 prtcrevs; > > + u32 prtcstns; > > +}; > > + > > +static inline struct tb_service *tb_service_get(struct tb_service > > *svc) > > +{ > > + if (svc) > > + get_device(&svc->dev); > > + return svc; > > +} > > + > > +static inline void tb_service_put(struct tb_service *svc) > > +{ > > + if (svc) > > + put_device(&svc->dev); > > +} > > + > > +static inline bool tb_is_service(const struct device *dev) > > +{ > > + return dev->type == &tb_service_type; > > +} > > + > > +static inline struct tb_service *tb_to_service(struct device *dev) > > +{ > > + if (tb_is_service(dev)) > > + return container_of(dev, struct tb_service, dev); > > + return NULL; > > +} > > + > > +/** > > + * tb_service_driver - Thunderbolt service driver > > + * @driver: Driver structure > > + * @probe: Called when the driver is probed > > + * @remove: Called when the driver is removed (optional) > > + * @shutdown: Called at shutdown time to stop the service > > (optional) > > + * @id_table: Table of service identifiers the driver supports > > + */ > > +struct tb_service_driver { > > + struct device_driver driver; > > + int (*probe)(struct tb_service *svc, const struct > > tb_service_id *id); > > + void (*remove)(struct tb_service *svc); > > + void (*shutdown)(struct tb_service *svc); > > + const struct tb_service_id *id_table; > > +}; > > + > > +#define TB_SERVICE(key, id) \ > > + .match_flags = TBSVC_MATCH_PROTOCOL_KEY | \ > > + TBSVC_MATCH_PROTOCOL_ID, \ > > + .protocol_key = (key), \ > > + .protocol_id = (id) > > + > > +int tb_register_service_driver(struct tb_service_driver *drv); > > +void tb_unregister_service_driver(struct tb_service_driver *drv); > > + > > +static inline void *tb_service_get_drvdata(const struct tb_service > > *svc) > > +{ > > + return dev_get_drvdata(&svc->dev); > > +} > > + > > +static inline void tb_service_set_drvdata(struct tb_service *svc, > > void *data) > > +{ > > + dev_set_drvdata(&svc->dev, data); > > +} > > + > > +static inline struct tb_xdomain *tb_service_parent(struct > > tb_service > > *svc) > > +{ > > + return tb_to_xdomain(svc->dev.parent); > > +} > > + > > #endif /* THUNDERBOLT_H_ */ > > diff --git a/scripts/mod/devicetable-offsets.c > > b/scripts/mod/devicetable-offsets.c > > index e4d90e50f6fe..57263f2f8f2f 100644 > > --- a/scripts/mod/devicetable-offsets.c > > +++ b/scripts/mod/devicetable-offsets.c > > @@ -206,5 +206,12 @@ int main(void) > > DEVID_FIELD(fsl_mc_device_id, vendor); > > DEVID_FIELD(fsl_mc_device_id, obj_type); > > > > + DEVID(tb_service_id); > > + DEVID_FIELD(tb_service_id, match_flags); > > + DEVID_FIELD(tb_service_id, protocol_key); > > + DEVID_FIELD(tb_service_id, protocol_id); > > + DEVID_FIELD(tb_service_id, protocol_version); > > + DEVID_FIELD(tb_service_id, protocol_revision); > > + > > return 0; > > } > > diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c > > index 29d6699d5a06..6ef6e63f96fd 100644 > > --- a/scripts/mod/file2alias.c > > +++ b/scripts/mod/file2alias.c > > @@ -1301,6 +1301,31 @@ static int do_fsl_mc_entry(const char > > *filename, void *symval, > > } > > ADD_TO_DEVTABLE("fslmc", fsl_mc_device_id, do_fsl_mc_entry); > > > > +/* Looks like: tbsvc:kSpNvNrN */ > > +static int do_tbsvc_entry(const char *filename, void *symval, char > > *alias) > > +{ > > + DEF_FIELD(symval, tb_service_id, match_flags); > > + DEF_FIELD_ADDR(symval, tb_service_id, protocol_key); > > + DEF_FIELD(symval, tb_service_id, protocol_id); > > + DEF_FIELD(symval, tb_service_id, protocol_version); > > + DEF_FIELD(symval, tb_service_id, protocol_revision); > > + > > + strcpy(alias, "tbsvc:"); > > + if (match_flags & TBSVC_MATCH_PROTOCOL_KEY) > > + sprintf(alias + strlen(alias), "k%s", > > *protocol_key); > > + else > > + strcat(alias + strlen(alias), "k*"); > > + ADD(alias, "p", match_flags & TBSVC_MATCH_PROTOCOL_ID, > > protocol_id); > > + ADD(alias, "v", match_flags & > > TBSVC_MATCH_PROTOCOL_VERSION, > > + protocol_version); > > + ADD(alias, "r", match_flags & > > TBSVC_MATCH_PROTOCOL_REVISION, > > + protocol_revision); > > + > > + add_wildcard(alias); > > + return 1; > > +} > > +ADD_TO_DEVTABLE("tbsvc", tb_service_id, do_tbsvc_entry); > > + > > /* Does namelen bytes of name exactly match the symbol? */ > > static bool sym_is(const char *name, unsigned namelen, const char > > *symbol) > > {