@@ -1601,7 +1601,6 @@ static void typec_release(struct device *dev)
ida_destroy(&port->mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
- free_pld(port->pld);
kfree(port->cap);
kfree(port);
}
@@ -2027,7 +2026,9 @@ struct typec_port *typec_register_port(struct device *parent,
return ERR_PTR(ret);
}
- port->pld = get_pld(&port->dev);
+ ret = typec_link_ports(port);
+ if (ret)
+ dev_warn(&port->dev, "failed to create symlinks (%d)\n", ret);
return port;
}
@@ -2041,8 +2042,10 @@ EXPORT_SYMBOL_GPL(typec_register_port);
*/
void typec_unregister_port(struct typec_port *port)
{
- if (!IS_ERR_OR_NULL(port))
+ if (!IS_ERR_OR_NULL(port)) {
+ typec_unlink_ports(port);
device_unregister(&port->dev);
+ }
}
EXPORT_SYMBOL_GPL(typec_unregister_port);
@@ -80,15 +80,15 @@ extern struct class typec_mux_class;
extern struct class typec_class;
#ifdef CONFIG_ACPI
-void *get_pld(struct device *dev);
-void free_pld(void *pld);
+int typec_link_ports(struct typec_port *connector);
+void typec_unlink_ports(struct typec_port *connector);
#else
-static inline void *get_pld(struct device *dev)
+static inline int typec_link_ports(struct typec_port *connector)
{
- return NULL;
+ return 0;
}
-static inline void free_pld(void *pld) { }
+static inline void typec_unlink_ports(struct typec_port *connector) { }
#endif
#endif /* __USB_TYPEC_CLASS__ */
@@ -34,7 +34,7 @@ static int acpi_pld_match(const struct acpi_pld_info *pld1,
return 0;
}
-void *get_pld(struct device *dev)
+static void *get_pld(struct device *dev)
{
struct acpi_pld_info *pld;
acpi_status status;
@@ -49,7 +49,7 @@ void *get_pld(struct device *dev)
return pld;
}
-void free_pld(void *pld)
+static void free_pld(void *pld)
{
ACPI_FREE(pld);
}
@@ -223,3 +223,61 @@ void typec_unlink_port(struct device *port)
put_device(data.connector);
}
EXPORT_SYMBOL_GPL(typec_unlink_port);
+
+static int connector_match(struct device *port, void *connector)
+{
+ struct port_node *node;
+ int ret;
+
+ node = create_port_node(port);
+ if (IS_ERR(node))
+ return PTR_ERR(node);
+
+ if (!node->pld || !connector_pld_match(connector, node->pld)) {
+ remove_port_node(node);
+ return 0;
+ }
+
+ ret = link_port(to_typec_port(connector), node);
+ if (ret) {
+ remove_port_node(node->pld);
+ return ret;
+ }
+
+ get_device(connector);
+
+ return 0;
+}
+
+int typec_link_ports(struct typec_port *con)
+{
+ int ret;
+
+ con->pld = get_pld(&con->dev);
+ if (!con->pld)
+ return 0;
+
+ ret = usb_for_each_port(&con->dev, connector_match);
+ if (ret)
+ typec_unlink_ports(con);
+
+ return ret;
+}
+
+void typec_unlink_ports(struct typec_port *con)
+{
+ struct port_node *node;
+ struct port_node *tmp;
+
+ mutex_lock(&con->port_list_lock);
+
+ list_for_each_entry_safe(node, tmp, &con->port_list, list) {
+ __unlink_port(con, node);
+ remove_port_node(node);
+ put_device(&con->dev);
+ }
+
+ mutex_unlock(&con->port_list_lock);
+
+ free_pld(con->pld);
+}
The connectors may be registered after the ports, so the "connector" links need to be created for the ports also when ever a new connector gets registered. Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> --- drivers/usb/typec/class.c | 9 +++-- drivers/usb/typec/class.h | 10 +++--- drivers/usb/typec/port-mapper.c | 62 +++++++++++++++++++++++++++++++-- 3 files changed, 71 insertions(+), 10 deletions(-)