1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Surface System Aggregator Module bus and device integration. 4 * 5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com> 6 */ 7 8 #include <linux/device.h> 9 #include <linux/slab.h> 10 11 #include <linux/surface_aggregator/controller.h> 12 #include <linux/surface_aggregator/device.h> 13 14 #include "bus.h" 15 #include "controller.h" 16 17 static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 18 char *buf) 19 { 20 struct ssam_device *sdev = to_ssam_device(dev); 21 22 return sysfs_emit(buf, "ssam:d%02Xc%02Xt%02Xi%02Xf%02X\n", 23 sdev->uid.domain, sdev->uid.category, sdev->uid.target, 24 sdev->uid.instance, sdev->uid.function); 25 } 26 static DEVICE_ATTR_RO(modalias); 27 28 static struct attribute *ssam_device_attrs[] = { 29 &dev_attr_modalias.attr, 30 NULL, 31 }; 32 ATTRIBUTE_GROUPS(ssam_device); 33 34 static int ssam_device_uevent(struct device *dev, struct kobj_uevent_env *env) 35 { 36 struct ssam_device *sdev = to_ssam_device(dev); 37 38 return add_uevent_var(env, "MODALIAS=ssam:d%02Xc%02Xt%02Xi%02Xf%02X", 39 sdev->uid.domain, sdev->uid.category, 40 sdev->uid.target, sdev->uid.instance, 41 sdev->uid.function); 42 } 43 44 static void ssam_device_release(struct device *dev) 45 { 46 struct ssam_device *sdev = to_ssam_device(dev); 47 48 ssam_controller_put(sdev->ctrl); 49 kfree(sdev); 50 } 51 52 const struct device_type ssam_device_type = { 53 .name = "surface_aggregator_device", 54 .groups = ssam_device_groups, 55 .uevent = ssam_device_uevent, 56 .release = ssam_device_release, 57 }; 58 EXPORT_SYMBOL_GPL(ssam_device_type); 59 60 /** 61 * ssam_device_alloc() - Allocate and initialize a SSAM client device. 62 * @ctrl: The controller under which the device should be added. 63 * @uid: The UID of the device to be added. 64 * 65 * Allocates and initializes a new client device. The parent of the device 66 * will be set to the controller device and the name will be set based on the 67 * UID. Note that the device still has to be added via ssam_device_add(). 68 * Refer to that function for more details. 69 * 70 * Return: Returns the newly allocated and initialized SSAM client device, or 71 * %NULL if it could not be allocated. 72 */ 73 struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl, 74 struct ssam_device_uid uid) 75 { 76 struct ssam_device *sdev; 77 78 sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); 79 if (!sdev) 80 return NULL; 81 82 device_initialize(&sdev->dev); 83 sdev->dev.bus = &ssam_bus_type; 84 sdev->dev.type = &ssam_device_type; 85 sdev->dev.parent = ssam_controller_device(ctrl); 86 sdev->ctrl = ssam_controller_get(ctrl); 87 sdev->uid = uid; 88 89 dev_set_name(&sdev->dev, "%02x:%02x:%02x:%02x:%02x", 90 sdev->uid.domain, sdev->uid.category, sdev->uid.target, 91 sdev->uid.instance, sdev->uid.function); 92 93 return sdev; 94 } 95 EXPORT_SYMBOL_GPL(ssam_device_alloc); 96 97 /** 98 * ssam_device_add() - Add a SSAM client device. 99 * @sdev: The SSAM client device to be added. 100 * 101 * Added client devices must be guaranteed to always have a valid and active 102 * controller. Thus, this function will fail with %-ENODEV if the controller 103 * of the device has not been initialized yet, has been suspended, or has been 104 * shut down. 105 * 106 * The caller of this function should ensure that the corresponding call to 107 * ssam_device_remove() is issued before the controller is shut down. If the 108 * added device is a direct child of the controller device (default), it will 109 * be automatically removed when the controller is shut down. 110 * 111 * By default, the controller device will become the parent of the newly 112 * created client device. The parent may be changed before ssam_device_add is 113 * called, but care must be taken that a) the correct suspend/resume ordering 114 * is guaranteed and b) the client device does not outlive the controller, 115 * i.e. that the device is removed before the controller is being shut down. 116 * In case these guarantees have to be manually enforced, please refer to the 117 * ssam_client_link() and ssam_client_bind() functions, which are intended to 118 * set up device-links for this purpose. 119 * 120 * Return: Returns zero on success, a negative error code on failure. 121 */ 122 int ssam_device_add(struct ssam_device *sdev) 123 { 124 int status; 125 126 /* 127 * Ensure that we can only add new devices to a controller if it has 128 * been started and is not going away soon. This works in combination 129 * with ssam_controller_remove_clients to ensure driver presence for the 130 * controller device, i.e. it ensures that the controller (sdev->ctrl) 131 * is always valid and can be used for requests as long as the client 132 * device we add here is registered as child under it. This essentially 133 * guarantees that the client driver can always expect the preconditions 134 * for functions like ssam_request_sync (controller has to be started 135 * and is not suspended) to hold and thus does not have to check for 136 * them. 137 * 138 * Note that for this to work, the controller has to be a parent device. 139 * If it is not a direct parent, care has to be taken that the device is 140 * removed via ssam_device_remove(), as device_unregister does not 141 * remove child devices recursively. 142 */ 143 ssam_controller_statelock(sdev->ctrl); 144 145 if (sdev->ctrl->state != SSAM_CONTROLLER_STARTED) { 146 ssam_controller_stateunlock(sdev->ctrl); 147 return -ENODEV; 148 } 149 150 status = device_add(&sdev->dev); 151 152 ssam_controller_stateunlock(sdev->ctrl); 153 return status; 154 } 155 EXPORT_SYMBOL_GPL(ssam_device_add); 156 157 /** 158 * ssam_device_remove() - Remove a SSAM client device. 159 * @sdev: The device to remove. 160 * 161 * Removes and unregisters the provided SSAM client device. 162 */ 163 void ssam_device_remove(struct ssam_device *sdev) 164 { 165 device_unregister(&sdev->dev); 166 } 167 EXPORT_SYMBOL_GPL(ssam_device_remove); 168 169 /** 170 * ssam_device_id_compatible() - Check if a device ID matches a UID. 171 * @id: The device ID as potential match. 172 * @uid: The device UID matching against. 173 * 174 * Check if the given ID is a match for the given UID, i.e. if a device with 175 * the provided UID is compatible to the given ID following the match rules 176 * described in its &ssam_device_id.match_flags member. 177 * 178 * Return: Returns %true if the given UID is compatible to the match rule 179 * described by the given ID, %false otherwise. 180 */ 181 static bool ssam_device_id_compatible(const struct ssam_device_id *id, 182 struct ssam_device_uid uid) 183 { 184 if (id->domain != uid.domain || id->category != uid.category) 185 return false; 186 187 if ((id->match_flags & SSAM_MATCH_TARGET) && id->target != uid.target) 188 return false; 189 190 if ((id->match_flags & SSAM_MATCH_INSTANCE) && id->instance != uid.instance) 191 return false; 192 193 if ((id->match_flags & SSAM_MATCH_FUNCTION) && id->function != uid.function) 194 return false; 195 196 return true; 197 } 198 199 /** 200 * ssam_device_id_is_null() - Check if a device ID is null. 201 * @id: The device ID to check. 202 * 203 * Check if a given device ID is null, i.e. all zeros. Used to check for the 204 * end of ``MODULE_DEVICE_TABLE(ssam, ...)`` or similar lists. 205 * 206 * Return: Returns %true if the given ID represents a null ID, %false 207 * otherwise. 208 */ 209 static bool ssam_device_id_is_null(const struct ssam_device_id *id) 210 { 211 return id->match_flags == 0 && 212 id->domain == 0 && 213 id->category == 0 && 214 id->target == 0 && 215 id->instance == 0 && 216 id->function == 0 && 217 id->driver_data == 0; 218 } 219 220 /** 221 * ssam_device_id_match() - Find the matching ID table entry for the given UID. 222 * @table: The table to search in. 223 * @uid: The UID to matched against the individual table entries. 224 * 225 * Find the first match for the provided device UID in the provided ID table 226 * and return it. Returns %NULL if no match could be found. 227 */ 228 const struct ssam_device_id *ssam_device_id_match(const struct ssam_device_id *table, 229 const struct ssam_device_uid uid) 230 { 231 const struct ssam_device_id *id; 232 233 for (id = table; !ssam_device_id_is_null(id); ++id) 234 if (ssam_device_id_compatible(id, uid)) 235 return id; 236 237 return NULL; 238 } 239 EXPORT_SYMBOL_GPL(ssam_device_id_match); 240 241 /** 242 * ssam_device_get_match() - Find and return the ID matching the device in the 243 * ID table of the bound driver. 244 * @dev: The device for which to get the matching ID table entry. 245 * 246 * Find the fist match for the UID of the device in the ID table of the 247 * currently bound driver and return it. Returns %NULL if the device does not 248 * have a driver bound to it, the driver does not have match_table (i.e. it is 249 * %NULL), or there is no match in the driver's match_table. 250 * 251 * This function essentially calls ssam_device_id_match() with the ID table of 252 * the bound device driver and the UID of the device. 253 * 254 * Return: Returns the first match for the UID of the device in the device 255 * driver's match table, or %NULL if no such match could be found. 256 */ 257 const struct ssam_device_id *ssam_device_get_match(const struct ssam_device *dev) 258 { 259 const struct ssam_device_driver *sdrv; 260 261 sdrv = to_ssam_device_driver(dev->dev.driver); 262 if (!sdrv) 263 return NULL; 264 265 if (!sdrv->match_table) 266 return NULL; 267 268 return ssam_device_id_match(sdrv->match_table, dev->uid); 269 } 270 EXPORT_SYMBOL_GPL(ssam_device_get_match); 271 272 /** 273 * ssam_device_get_match_data() - Find the ID matching the device in the 274 * ID table of the bound driver and return its ``driver_data`` member. 275 * @dev: The device for which to get the match data. 276 * 277 * Find the fist match for the UID of the device in the ID table of the 278 * corresponding driver and return its driver_data. Returns %NULL if the 279 * device does not have a driver bound to it, the driver does not have 280 * match_table (i.e. it is %NULL), there is no match in the driver's 281 * match_table, or the match does not have any driver_data. 282 * 283 * This function essentially calls ssam_device_get_match() and, if any match 284 * could be found, returns its ``struct ssam_device_id.driver_data`` member. 285 * 286 * Return: Returns the driver data associated with the first match for the UID 287 * of the device in the device driver's match table, or %NULL if no such match 288 * could be found. 289 */ 290 const void *ssam_device_get_match_data(const struct ssam_device *dev) 291 { 292 const struct ssam_device_id *id; 293 294 id = ssam_device_get_match(dev); 295 if (!id) 296 return NULL; 297 298 return (const void *)id->driver_data; 299 } 300 EXPORT_SYMBOL_GPL(ssam_device_get_match_data); 301 302 static int ssam_bus_match(struct device *dev, struct device_driver *drv) 303 { 304 struct ssam_device_driver *sdrv = to_ssam_device_driver(drv); 305 struct ssam_device *sdev = to_ssam_device(dev); 306 307 if (!is_ssam_device(dev)) 308 return 0; 309 310 return !!ssam_device_id_match(sdrv->match_table, sdev->uid); 311 } 312 313 static int ssam_bus_probe(struct device *dev) 314 { 315 return to_ssam_device_driver(dev->driver) 316 ->probe(to_ssam_device(dev)); 317 } 318 319 static void ssam_bus_remove(struct device *dev) 320 { 321 struct ssam_device_driver *sdrv = to_ssam_device_driver(dev->driver); 322 323 if (sdrv->remove) 324 sdrv->remove(to_ssam_device(dev)); 325 } 326 327 struct bus_type ssam_bus_type = { 328 .name = "surface_aggregator", 329 .match = ssam_bus_match, 330 .probe = ssam_bus_probe, 331 .remove = ssam_bus_remove, 332 }; 333 EXPORT_SYMBOL_GPL(ssam_bus_type); 334 335 /** 336 * __ssam_device_driver_register() - Register a SSAM client device driver. 337 * @sdrv: The driver to register. 338 * @owner: The module owning the provided driver. 339 * 340 * Please refer to the ssam_device_driver_register() macro for the normal way 341 * to register a driver from inside its owning module. 342 */ 343 int __ssam_device_driver_register(struct ssam_device_driver *sdrv, 344 struct module *owner) 345 { 346 sdrv->driver.owner = owner; 347 sdrv->driver.bus = &ssam_bus_type; 348 349 /* force drivers to async probe so I/O is possible in probe */ 350 sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS; 351 352 return driver_register(&sdrv->driver); 353 } 354 EXPORT_SYMBOL_GPL(__ssam_device_driver_register); 355 356 /** 357 * ssam_device_driver_unregister - Unregister a SSAM device driver. 358 * @sdrv: The driver to unregister. 359 */ 360 void ssam_device_driver_unregister(struct ssam_device_driver *sdrv) 361 { 362 driver_unregister(&sdrv->driver); 363 } 364 EXPORT_SYMBOL_GPL(ssam_device_driver_unregister); 365 366 static int ssam_remove_device(struct device *dev, void *_data) 367 { 368 struct ssam_device *sdev = to_ssam_device(dev); 369 370 if (is_ssam_device(dev)) 371 ssam_device_remove(sdev); 372 373 return 0; 374 } 375 376 /** 377 * ssam_remove_clients() - Remove SSAM client devices registered as direct 378 * children under the given parent device. 379 * @dev: The (parent) device to remove all direct clients for. 380 * 381 * Remove all SSAM client devices registered as direct children under the given 382 * device. Note that this only accounts for direct children of the device. 383 * Refer to ssam_device_add()/ssam_device_remove() for more details. 384 */ 385 void ssam_remove_clients(struct device *dev) 386 { 387 device_for_each_child_reverse(dev, NULL, ssam_remove_device); 388 } 389 EXPORT_SYMBOL_GPL(ssam_remove_clients); 390 391 /** 392 * ssam_bus_register() - Register and set-up the SSAM client device bus. 393 */ 394 int ssam_bus_register(void) 395 { 396 return bus_register(&ssam_bus_type); 397 } 398 399 /** 400 * ssam_bus_unregister() - Unregister the SSAM client device bus. 401 */ 402 void ssam_bus_unregister(void) 403 { 404 return bus_unregister(&ssam_bus_type); 405 } 406