1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
// SPDX-License-Identifier: GPL-2.0
/**
* Device connections
*
* Copyright (C) 2018 Intel Corporation
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
*/
#include <linux/device.h>
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);
|