]>
Commit | Line | Data |
---|---|---|
f2d9b66d HK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /** | |
3 | * Device connections | |
4 | * | |
5 | * Copyright (C) 2018 Intel Corporation | |
6 | * Author: Heikki Krogerus <[email protected]> | |
7 | */ | |
8 | ||
9 | #include <linux/device.h> | |
10 | ||
11 | static DEFINE_MUTEX(devcon_lock); | |
12 | static LIST_HEAD(devcon_list); | |
13 | ||
14 | /** | |
15 | * device_connection_find_match - Find physical connection to a device | |
16 | * @dev: Device with the connection | |
17 | * @con_id: Identifier for the connection | |
18 | * @data: Data for the match function | |
19 | * @match: Function to check and convert the connection description | |
20 | * | |
21 | * Find a connection with unique identifier @con_id between @dev and another | |
22 | * device. @match will be used to convert the connection description to data the | |
23 | * caller is expecting to be returned. | |
24 | */ | |
25 | void *device_connection_find_match(struct device *dev, const char *con_id, | |
26 | void *data, | |
27 | void *(*match)(struct device_connection *con, | |
28 | int ep, void *data)) | |
29 | { | |
30 | const char *devname = dev_name(dev); | |
31 | struct device_connection *con; | |
32 | void *ret = NULL; | |
33 | int ep; | |
34 | ||
35 | if (!match) | |
36 | return NULL; | |
37 | ||
38 | mutex_lock(&devcon_lock); | |
39 | ||
40 | list_for_each_entry(con, &devcon_list, list) { | |
41 | ep = match_string(con->endpoint, 2, devname); | |
42 | if (ep < 0) | |
43 | continue; | |
44 | ||
45 | if (con_id && strcmp(con->id, con_id)) | |
46 | continue; | |
47 | ||
48 | ret = match(con, !ep, data); | |
49 | if (ret) | |
50 | break; | |
51 | } | |
52 | ||
53 | mutex_unlock(&devcon_lock); | |
54 | ||
55 | return ret; | |
56 | } | |
57 | EXPORT_SYMBOL_GPL(device_connection_find_match); | |
58 | ||
59 | extern struct bus_type platform_bus_type; | |
60 | extern struct bus_type pci_bus_type; | |
61 | extern struct bus_type i2c_bus_type; | |
62 | extern struct bus_type spi_bus_type; | |
63 | ||
64 | static struct bus_type *generic_match_buses[] = { | |
65 | &platform_bus_type, | |
66 | #ifdef CONFIG_PCI | |
67 | &pci_bus_type, | |
68 | #endif | |
69 | #ifdef CONFIG_I2C | |
70 | &i2c_bus_type, | |
71 | #endif | |
72 | #ifdef CONFIG_SPI_MASTER | |
73 | &spi_bus_type, | |
74 | #endif | |
75 | NULL, | |
76 | }; | |
77 | ||
78 | /* This tries to find the device from the most common bus types by name. */ | |
79 | static void *generic_match(struct device_connection *con, int ep, void *data) | |
80 | { | |
81 | struct bus_type *bus; | |
82 | struct device *dev; | |
83 | ||
84 | for (bus = generic_match_buses[0]; bus; bus++) { | |
85 | dev = bus_find_device_by_name(bus, NULL, con->endpoint[ep]); | |
86 | if (dev) | |
87 | return dev; | |
88 | } | |
89 | ||
90 | /* | |
91 | * We only get called if a connection was found, tell the caller to | |
92 | * wait for the other device to show up. | |
93 | */ | |
94 | return ERR_PTR(-EPROBE_DEFER); | |
95 | } | |
96 | ||
97 | /** | |
98 | * device_connection_find - Find two devices connected together | |
99 | * @dev: Device with the connection | |
100 | * @con_id: Identifier for the connection | |
101 | * | |
102 | * Find a connection with unique identifier @con_id between @dev and | |
103 | * another device. On success returns handle to the device that is connected | |
104 | * to @dev, with the reference count for the found device incremented. Returns | |
105 | * NULL if no matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a | |
106 | * connection was found but the other device has not been enumerated yet. | |
107 | */ | |
108 | struct device *device_connection_find(struct device *dev, const char *con_id) | |
109 | { | |
110 | return device_connection_find_match(dev, con_id, NULL, generic_match); | |
111 | } | |
112 | EXPORT_SYMBOL_GPL(device_connection_find); | |
113 | ||
114 | /** | |
115 | * device_connection_add - Register a connection description | |
116 | * @con: The connection description to be registered | |
117 | */ | |
118 | void device_connection_add(struct device_connection *con) | |
119 | { | |
120 | mutex_lock(&devcon_lock); | |
121 | list_add_tail(&con->list, &devcon_list); | |
122 | mutex_unlock(&devcon_lock); | |
123 | } | |
124 | EXPORT_SYMBOL_GPL(device_connection_add); | |
125 | ||
126 | /** | |
127 | * device_connections_remove - Unregister connection description | |
128 | * @con: The connection description to be unregistered | |
129 | */ | |
130 | void device_connection_remove(struct device_connection *con) | |
131 | { | |
132 | mutex_lock(&devcon_lock); | |
133 | list_del(&con->list); | |
134 | mutex_unlock(&devcon_lock); | |
135 | } | |
136 | EXPORT_SYMBOL_GPL(device_connection_remove); |