// SPDX-License-Identifier: GPL-2.0 /** * Device connections * * Copyright (C) 2018 Intel Corporation * Author: Heikki Krogerus */ #include static DEFINE_MUTEX(devcon_lock); static LIST_HEAD(devcon_list); /** * device_connection_find_match - Find physical connection to a device * @dev: Device with the connection * @con_id: Identifier for the connection * @data: Data for the match function * @match: Function to check and convert the connection description * * Find a connection with unique identifier @con_id between @dev and another * device. @match will be used to convert the connection description to data the * caller is expecting to be returned. */ void *device_connection_find_match(struct device *dev, const char *con_id, void *data, void *(*match)(struct device_connection *con, int ep, void *data)) { const char *devname = dev_name(dev); struct device_connection *con; void *ret = NULL; int ep; if (!match) return NULL; mutex_lock(&devcon_lock); list_for_each_entry(con, &devcon_list, list) { ep = match_string(con->endpoint, 2, devname); if (ep < 0) continue; if (con_id && strcmp(con->id, con_id)) continue; ret = match(con, !ep, data); if (ret) break; } mutex_unlock(&devcon_lock); return ret; } EXPORT_SYMBOL_GPL(device_connection_find_match); extern struct bus_type platform_bus_type; extern struct bus_type pci_bus_type; extern struct bus_type i2c_bus_type; extern struct bus_type spi_bus_type; static struct bus_type *generic_match_buses[] = { &platform_bus_type, #ifdef CONFIG_PCI &pci_bus_type, #endif #ifdef CONFIG_I2C &i2c_bus_type, #endif #ifdef CONFIG_SPI_MASTER &spi_bus_type, #endif NULL, }; /* This tries to find the device from the most common bus types by name. */ static void *generic_match(struct device_connection *con, int ep, void *data) { struct bus_type *bus; struct device *dev; for (bus = generic_match_buses[0]; bus; bus++) { dev = bus_find_device_by_name(bus, NULL, con->endpoint[ep]); if (dev) return dev; } /* * We only get called if a connection was found, tell the caller to * wait for the other device to show up. */ return ERR_PTR(-EPROBE_DEFER); } /** * device_connection_find - Find two devices connected together * @dev: Device with the connection * @con_id: Identifier for the connection * * Find a connection with unique identifier @con_id between @dev and * another device. On success returns handle to the device that is connected * to @dev, with the reference count for the found device incremented. Returns * NULL if no matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a * connection was found but the other device has not been enumerated yet. */ struct device *device_connection_find(struct device *dev, const char *con_id) { return device_connection_find_match(dev, con_id, NULL, generic_match); } EXPORT_SYMBOL_GPL(device_connection_find); /** * device_connection_add - Register a connection description * @con: The connection description to be registered */ void device_connection_add(struct device_connection *con) { mutex_lock(&devcon_lock); list_add_tail(&con->list, &devcon_list); mutex_unlock(&devcon_lock); } EXPORT_SYMBOL_GPL(device_connection_add); /** * device_connections_remove - Unregister connection description * @con: The connection description to be unregistered */ void device_connection_remove(struct device_connection *con) { mutex_lock(&devcon_lock); list_del(&con->list); mutex_unlock(&devcon_lock); } EXPORT_SYMBOL_GPL(device_connection_remove);