Hi, On 12/3/20 10:26 PM, Maximilian Luz wrote: > The Surface Aggregator EC provides varying functionality, depending on > the Surface device. To manage this functionality, we use dedicated > client devices for each subsystem or virtual device of the EC. While > some of these clients are described as standard devices in ACPI and the > corresponding client drivers can be implemented as platform drivers in > the kernel (making use of the controller API already present), many > devices, especially on newer Surface models, cannot be found there. > > To simplify management of these devices, we introduce a new bus and > client device type for the Surface Aggregator subsystem. The new device > type takes care of managing the controller reference, essentially > guaranteeing its validity for as long as the client device exists, thus > alleviating the need to manually establish device links for that purpose > in the client driver (as has to be done with the platform devices). > > Signed-off-by: Maximilian Luz <luzmaximilian@xxxxxxxxx> Thanks, patch looks good to me: Reviewed-by: Hans de Goede <hdegoede@xxxxxxxxxx> Regards, Hans > --- > > Changes in v1 (from RFC): > - add copyright lines > - change SPDX identifier to GPL-2.0+ (was GPL-2.0-or-later) > - remove unnecessary READ_ONCE on multiple occasions > - improve documentation of special values for SSAM_DEVICE() > - add NULL checks for ssam_device_get, ssam_device_put > > Changes in v2: > - return ENODEV instead of ENXIO if controller is not present > - use sysfs_emit for sysfs attributes > - spell check comments and strings, fix typos > - unify comment style > - run checkpatch --strict, fix warnings and style issues > > --- > drivers/platform/surface/aggregator/Kconfig | 12 + > drivers/platform/surface/aggregator/Makefile | 4 + > drivers/platform/surface/aggregator/bus.c | 415 ++++++++++++++++++ > drivers/platform/surface/aggregator/bus.h | 27 ++ > drivers/platform/surface/aggregator/core.c | 12 + > include/linux/mod_devicetable.h | 18 + > include/linux/surface_aggregator/device.h | 423 +++++++++++++++++++ > scripts/mod/devicetable-offsets.c | 8 + > scripts/mod/file2alias.c | 23 + > 9 files changed, 942 insertions(+) > create mode 100644 drivers/platform/surface/aggregator/bus.c > create mode 100644 drivers/platform/surface/aggregator/bus.h > create mode 100644 include/linux/surface_aggregator/device.h > > diff --git a/drivers/platform/surface/aggregator/Kconfig b/drivers/platform/surface/aggregator/Kconfig > index 48f40c345e29..44c2493706bc 100644 > --- a/drivers/platform/surface/aggregator/Kconfig > +++ b/drivers/platform/surface/aggregator/Kconfig > @@ -42,6 +42,18 @@ menuconfig SURFACE_AGGREGATOR > module, y if you want to build it into the kernel and n if you don't > want it at all. > > +config SURFACE_AGGREGATOR_BUS > + bool "Surface System Aggregator Module Bus" > + depends on SURFACE_AGGREGATOR > + default y > + help > + Expands the Surface System Aggregator Module (SSAM) core driver by > + providing a dedicated bus and client-device type. > + > + This bus and device type are intended to provide and simplify support > + for non-platform and non-ACPI SSAM devices, i.e. SSAM devices that are > + not auto-detectable via the conventional means (e.g. ACPI). > + > config SURFACE_AGGREGATOR_ERROR_INJECTION > bool "Surface System Aggregator Module Error Injection Capabilities" > depends on SURFACE_AGGREGATOR > diff --git a/drivers/platform/surface/aggregator/Makefile b/drivers/platform/surface/aggregator/Makefile > index b8b24c8ec310..c112e2c7112b 100644 > --- a/drivers/platform/surface/aggregator/Makefile > +++ b/drivers/platform/surface/aggregator/Makefile > @@ -11,3 +11,7 @@ surface_aggregator-objs += ssh_parser.o > surface_aggregator-objs += ssh_packet_layer.o > surface_aggregator-objs += ssh_request_layer.o > surface_aggregator-objs += controller.o > + > +ifeq ($(CONFIG_SURFACE_AGGREGATOR_BUS),y) > +surface_aggregator-objs += bus.o > +endif > diff --git a/drivers/platform/surface/aggregator/bus.c b/drivers/platform/surface/aggregator/bus.c > new file mode 100644 > index 000000000000..9f7b4f91a87e > --- /dev/null > +++ b/drivers/platform/surface/aggregator/bus.c > @@ -0,0 +1,415 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Surface System Aggregator Module bus and device integration. > + * > + * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@xxxxxxxxx> > + */ > + > +#include <linux/device.h> > +#include <linux/slab.h> > + > +#include <linux/surface_aggregator/controller.h> > +#include <linux/surface_aggregator/device.h> > + > +#include "bus.h" > +#include "controller.h" > + > +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, > + char *buf) > +{ > + struct ssam_device *sdev = to_ssam_device(dev); > + > + return sysfs_emit(buf, "ssam:d%02Xc%02Xt%02Xi%02Xf%02X\n", > + sdev->uid.domain, sdev->uid.category, sdev->uid.target, > + sdev->uid.instance, sdev->uid.function); > +} > +static DEVICE_ATTR_RO(modalias); > + > +static struct attribute *ssam_device_attrs[] = { > + &dev_attr_modalias.attr, > + NULL, > +}; > +ATTRIBUTE_GROUPS(ssam_device); > + > +static int ssam_device_uevent(struct device *dev, struct kobj_uevent_env *env) > +{ > + struct ssam_device *sdev = to_ssam_device(dev); > + > + return add_uevent_var(env, "MODALIAS=ssam:d%02Xc%02Xt%02Xi%02Xf%02X", > + sdev->uid.domain, sdev->uid.category, > + sdev->uid.target, sdev->uid.instance, > + sdev->uid.function); > +} > + > +static void ssam_device_release(struct device *dev) > +{ > + struct ssam_device *sdev = to_ssam_device(dev); > + > + ssam_controller_put(sdev->ctrl); > + kfree(sdev); > +} > + > +const struct device_type ssam_device_type = { > + .name = "surface_aggregator_device", > + .groups = ssam_device_groups, > + .uevent = ssam_device_uevent, > + .release = ssam_device_release, > +}; > +EXPORT_SYMBOL_GPL(ssam_device_type); > + > +/** > + * ssam_device_alloc() - Allocate and initialize a SSAM client device. > + * @ctrl: The controller under which the device should be added. > + * @uid: The UID of the device to be added. > + * > + * Allocates and initializes a new client device. The parent of the device > + * will be set to the controller device and the name will be set based on the > + * UID. Note that the device still has to be added via ssam_device_add(). > + * Refer to that function for more details. > + * > + * Return: Returns the newly allocated and initialized SSAM client device, or > + * %NULL if it could not be allocated. > + */ > +struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl, > + struct ssam_device_uid uid) > +{ > + struct ssam_device *sdev; > + > + sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); > + if (!sdev) > + return NULL; > + > + device_initialize(&sdev->dev); > + sdev->dev.bus = &ssam_bus_type; > + sdev->dev.type = &ssam_device_type; > + sdev->dev.parent = ssam_controller_device(ctrl); > + sdev->ctrl = ssam_controller_get(ctrl); > + sdev->uid = uid; > + > + dev_set_name(&sdev->dev, "%02x:%02x:%02x:%02x:%02x", > + sdev->uid.domain, sdev->uid.category, sdev->uid.target, > + sdev->uid.instance, sdev->uid.function); > + > + return sdev; > +} > +EXPORT_SYMBOL_GPL(ssam_device_alloc); > + > +/** > + * ssam_device_add() - Add a SSAM client device. > + * @sdev: The SSAM client device to be added. > + * > + * Added client devices must be guaranteed to always have a valid and active > + * controller. Thus, this function will fail with %-ENODEV if the controller > + * of the device has not been initialized yet, has been suspended, or has been > + * shut down. > + * > + * The caller of this function should ensure that the corresponding call to > + * ssam_device_remove() is issued before the controller is shut down. If the > + * added device is a direct child of the controller device (default), it will > + * be automatically removed when the controller is shut down. > + * > + * By default, the controller device will become the parent of the newly > + * created client device. The parent may be changed before ssam_device_add is > + * called, but care must be taken that a) the correct suspend/resume ordering > + * is guaranteed and b) the client device does not outlive the controller, > + * i.e. that the device is removed before the controller is being shut down. > + * In case these guarantees have to be manually enforced, please refer to the > + * ssam_client_link() and ssam_client_bind() functions, which are intended to > + * set up device-links for this purpose. > + * > + * Return: Returns zero on success, a negative error code on failure. > + */ > +int ssam_device_add(struct ssam_device *sdev) > +{ > + int status; > + > + /* > + * Ensure that we can only add new devices to a controller if it has > + * been started and is not going away soon. This works in combination > + * with ssam_controller_remove_clients to ensure driver presence for the > + * controller device, i.e. it ensures that the controller (sdev->ctrl) > + * is always valid and can be used for requests as long as the client > + * device we add here is registered as child under it. This essentially > + * guarantees that the client driver can always expect the preconditions > + * for functions like ssam_request_sync (controller has to be started > + * and is not suspended) to hold and thus does not have to check for > + * them. > + * > + * Note that for this to work, the controller has to be a parent device. > + * If it is not a direct parent, care has to be taken that the device is > + * removed via ssam_device_remove(), as device_unregister does not > + * remove child devices recursively. > + */ > + ssam_controller_statelock(sdev->ctrl); > + > + if (sdev->ctrl->state != SSAM_CONTROLLER_STARTED) { > + ssam_controller_stateunlock(sdev->ctrl); > + return -ENODEV; > + } > + > + status = device_add(&sdev->dev); > + > + ssam_controller_stateunlock(sdev->ctrl); > + return status; > +} > +EXPORT_SYMBOL_GPL(ssam_device_add); > + > +/** > + * ssam_device_remove() - Remove a SSAM client device. > + * @sdev: The device to remove. > + * > + * Removes and unregisters the provided SSAM client device. > + */ > +void ssam_device_remove(struct ssam_device *sdev) > +{ > + device_unregister(&sdev->dev); > +} > +EXPORT_SYMBOL_GPL(ssam_device_remove); > + > +/** > + * ssam_device_id_compatible() - Check if a device ID matches a UID. > + * @id: The device ID as potential match. > + * @uid: The device UID matching against. > + * > + * Check if the given ID is a match for the given UID, i.e. if a device with > + * the provided UID is compatible to the given ID following the match rules > + * described in its &ssam_device_id.match_flags member. > + * > + * Return: Returns %true iff the given UID is compatible to the match rule > + * described by the given ID, %false otherwise. > + */ > +static bool ssam_device_id_compatible(const struct ssam_device_id *id, > + struct ssam_device_uid uid) > +{ > + if (id->domain != uid.domain || id->category != uid.category) > + return false; > + > + if ((id->match_flags & SSAM_MATCH_TARGET) && id->target != uid.target) > + return false; > + > + if ((id->match_flags & SSAM_MATCH_INSTANCE) && id->instance != uid.instance) > + return false; > + > + if ((id->match_flags & SSAM_MATCH_FUNCTION) && id->function != uid.function) > + return false; > + > + return true; > +} > + > +/** > + * ssam_device_id_is_null() - Check if a device ID is null. > + * @id: The device ID to check. > + * > + * Check if a given device ID is null, i.e. all zeros. Used to check for the > + * end of ``MODULE_DEVICE_TABLE(ssam, ...)`` or similar lists. > + * > + * Return: Returns %true if the given ID represents a null ID, %false > + * otherwise. > + */ > +static bool ssam_device_id_is_null(const struct ssam_device_id *id) > +{ > + return id->match_flags == 0 && > + id->domain == 0 && > + id->category == 0 && > + id->target == 0 && > + id->instance == 0 && > + id->function == 0 && > + id->driver_data == 0; > +} > + > +/** > + * ssam_device_id_match() - Find the matching ID table entry for the given UID. > + * @table: The table to search in. > + * @uid: The UID to matched against the individual table entries. > + * > + * Find the first match for the provided device UID in the provided ID table > + * and return it. Returns %NULL if no match could be found. > + */ > +const struct ssam_device_id *ssam_device_id_match(const struct ssam_device_id *table, > + const struct ssam_device_uid uid) > +{ > + const struct ssam_device_id *id; > + > + for (id = table; !ssam_device_id_is_null(id); ++id) > + if (ssam_device_id_compatible(id, uid)) > + return id; > + > + return NULL; > +} > +EXPORT_SYMBOL_GPL(ssam_device_id_match); > + > +/** > + * ssam_device_get_match() - Find and return the ID matching the device in the > + * ID table of the bound driver. > + * @dev: The device for which to get the matching ID table entry. > + * > + * Find the fist match for the UID of the device in the ID table of the > + * currently bound driver and return it. Returns %NULL if the device does not > + * have a driver bound to it, the driver does not have match_table (i.e. it is > + * %NULL), or there is no match in the driver's match_table. > + * > + * This function essentially calls ssam_device_id_match() with the ID table of > + * the bound device driver and the UID of the device. > + * > + * Return: Returns the first match for the UID of the device in the device > + * driver's match table, or %NULL if no such match could be found. > + */ > +const struct ssam_device_id *ssam_device_get_match(const struct ssam_device *dev) > +{ > + const struct ssam_device_driver *sdrv; > + > + sdrv = to_ssam_device_driver(dev->dev.driver); > + if (!sdrv) > + return NULL; > + > + if (!sdrv->match_table) > + return NULL; > + > + return ssam_device_id_match(sdrv->match_table, dev->uid); > +} > +EXPORT_SYMBOL_GPL(ssam_device_get_match); > + > +/** > + * ssam_device_get_match_data() - Find the ID matching the device in the > + * ID table of the bound driver and return its ``driver_data`` member. > + * @dev: The device for which to get the match data. > + * > + * Find the fist match for the UID of the device in the ID table of the > + * corresponding driver and return its driver_data. Returns %NULL if the > + * device does not have a driver bound to it, the driver does not have > + * match_table (i.e. it is %NULL), there is no match in the driver's > + * match_table, or the match does not have any driver_data. > + * > + * This function essentially calls ssam_device_get_match() and, if any match > + * could be found, returns its ``struct ssam_device_id.driver_data`` member. > + * > + * Return: Returns the driver data associated with the first match for the UID > + * of the device in the device driver's match table, or %NULL if no such match > + * could be found. > + */ > +const void *ssam_device_get_match_data(const struct ssam_device *dev) > +{ > + const struct ssam_device_id *id; > + > + id = ssam_device_get_match(dev); > + if (!id) > + return NULL; > + > + return (const void *)id->driver_data; > +} > +EXPORT_SYMBOL_GPL(ssam_device_get_match_data); > + > +static int ssam_bus_match(struct device *dev, struct device_driver *drv) > +{ > + struct ssam_device_driver *sdrv = to_ssam_device_driver(drv); > + struct ssam_device *sdev = to_ssam_device(dev); > + > + if (!is_ssam_device(dev)) > + return 0; > + > + return !!ssam_device_id_match(sdrv->match_table, sdev->uid); > +} > + > +static int ssam_bus_probe(struct device *dev) > +{ > + return to_ssam_device_driver(dev->driver) > + ->probe(to_ssam_device(dev)); > +} > + > +static int ssam_bus_remove(struct device *dev) > +{ > + struct ssam_device_driver *sdrv = to_ssam_device_driver(dev->driver); > + > + if (sdrv->remove) > + sdrv->remove(to_ssam_device(dev)); > + > + return 0; > +} > + > +struct bus_type ssam_bus_type = { > + .name = "surface_aggregator", > + .match = ssam_bus_match, > + .probe = ssam_bus_probe, > + .remove = ssam_bus_remove, > +}; > +EXPORT_SYMBOL_GPL(ssam_bus_type); > + > +/** > + * __ssam_device_driver_register() - Register a SSAM client device driver. > + * @sdrv: The driver to register. > + * @owner: The module owning the provided driver. > + * > + * Please refer to the ssam_device_driver_register() macro for the normal way > + * to register a driver from inside its owning module. > + */ > +int __ssam_device_driver_register(struct ssam_device_driver *sdrv, > + struct module *owner) > +{ > + sdrv->driver.owner = owner; > + sdrv->driver.bus = &ssam_bus_type; > + > + /* force drivers to async probe so I/O is possible in probe */ > + sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS; > + > + return driver_register(&sdrv->driver); > +} > +EXPORT_SYMBOL_GPL(__ssam_device_driver_register); > + > +/** > + * ssam_device_driver_unregister - Unregister a SSAM device driver. > + * @sdrv: The driver to unregister. > + */ > +void ssam_device_driver_unregister(struct ssam_device_driver *sdrv) > +{ > + driver_unregister(&sdrv->driver); > +} > +EXPORT_SYMBOL_GPL(ssam_device_driver_unregister); > + > +static int ssam_remove_device(struct device *dev, void *_data) > +{ > + struct ssam_device *sdev = to_ssam_device(dev); > + > + if (is_ssam_device(dev)) > + ssam_device_remove(sdev); > + > + return 0; > +} > + > +/** > + * ssam_controller_remove_clients() - Remove SSAM client devices registered as > + * direct children under the given controller. > + * @ctrl: The controller to remove all direct clients for. > + * > + * Remove all SSAM client devices registered as direct children under the > + * given controller. Note that this only accounts for direct children of the > + * controller device. This does not take care of any client devices where the > + * parent device has been manually set before calling ssam_device_add. Refer > + * to ssam_device_add()/ssam_device_remove() for more details on those cases. > + * > + * To avoid new devices being added in parallel to this call, the main > + * controller lock (not statelock) must be held during this (and if > + * necessary, any subsequent deinitialization) call. > + */ > +void ssam_controller_remove_clients(struct ssam_controller *ctrl) > +{ > + struct device *dev; > + > + dev = ssam_controller_device(ctrl); > + device_for_each_child_reverse(dev, NULL, ssam_remove_device); > +} > + > +/** > + * ssam_bus_register() - Register and set-up the SSAM client device bus. > + */ > +int ssam_bus_register(void) > +{ > + return bus_register(&ssam_bus_type); > +} > + > +/** > + * ssam_bus_unregister() - Unregister the SSAM client device bus. > + */ > +void ssam_bus_unregister(void) > +{ > + return bus_unregister(&ssam_bus_type); > +} > diff --git a/drivers/platform/surface/aggregator/bus.h b/drivers/platform/surface/aggregator/bus.h > new file mode 100644 > index 000000000000..7712baaed6a5 > --- /dev/null > +++ b/drivers/platform/surface/aggregator/bus.h > @@ -0,0 +1,27 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Surface System Aggregator Module bus and device integration. > + * > + * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@xxxxxxxxx> > + */ > + > +#ifndef _SURFACE_AGGREGATOR_BUS_H > +#define _SURFACE_AGGREGATOR_BUS_H > + > +#include <linux/surface_aggregator/controller.h> > + > +#ifdef CONFIG_SURFACE_AGGREGATOR_BUS > + > +void ssam_controller_remove_clients(struct ssam_controller *ctrl); > + > +int ssam_bus_register(void); > +void ssam_bus_unregister(void); > + > +#else /* CONFIG_SURFACE_AGGREGATOR_BUS */ > + > +static inline void ssam_controller_remove_clients(struct ssam_controller *ctrl) {} > +static inline int ssam_bus_register(void) { return 0; } > +static inline void ssam_bus_unregister(void) {} > + > +#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */ > +#endif /* _SURFACE_AGGREGATOR_BUS_H */ > diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c > index b5d44ab61f06..cde66c497d3e 100644 > --- a/drivers/platform/surface/aggregator/core.c > +++ b/drivers/platform/surface/aggregator/core.c > @@ -22,6 +22,8 @@ > #include <linux/sysfs.h> > > #include <linux/surface_aggregator/controller.h> > + > +#include "bus.h" > #include "controller.h" > > #define CREATE_TRACE_POINTS > @@ -733,6 +735,9 @@ static void ssam_serial_hub_remove(struct serdev_device *serdev) > sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group); > ssam_controller_lock(ctrl); > > + /* Remove all client devices. */ > + ssam_controller_remove_clients(ctrl); > + > /* Act as if suspending to silence events. */ > status = ssam_ctrl_notif_display_off(ctrl); > if (status) { > @@ -785,6 +790,10 @@ static int __init ssam_core_init(void) > { > int status; > > + status = ssam_bus_register(); > + if (status) > + goto err_bus; > + > status = ssh_ctrl_packet_cache_init(); > if (status) > goto err_cpkg; > @@ -804,6 +813,8 @@ static int __init ssam_core_init(void) > err_evitem: > ssh_ctrl_packet_cache_destroy(); > err_cpkg: > + ssam_bus_unregister(); > +err_bus: > return status; > } > module_init(ssam_core_init); > @@ -813,6 +824,7 @@ static void __exit ssam_core_exit(void) > serdev_device_driver_unregister(&ssam_serial_hub); > ssam_event_item_cache_destroy(); > ssh_ctrl_packet_cache_destroy(); > + ssam_bus_unregister(); > } > module_exit(ssam_core_exit); > > diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h > index 5b08a473cdba..0b8f1feefe0e 100644 > --- a/include/linux/mod_devicetable.h > +++ b/include/linux/mod_devicetable.h > @@ -838,4 +838,22 @@ struct mhi_device_id { > kernel_ulong_t driver_data; > }; > > +/* Surface System Aggregator Module */ > + > +#define SSAM_MATCH_TARGET 0x1 > +#define SSAM_MATCH_INSTANCE 0x2 > +#define SSAM_MATCH_FUNCTION 0x4 > + > +struct ssam_device_id { > + __u8 match_flags; > + > + __u8 domain; > + __u8 category; > + __u8 target; > + __u8 instance; > + __u8 function; > + > + kernel_ulong_t driver_data; > +}; > + > #endif /* LINUX_MOD_DEVICETABLE_H */ > diff --git a/include/linux/surface_aggregator/device.h b/include/linux/surface_aggregator/device.h > new file mode 100644 > index 000000000000..7221d4a9c1c1 > --- /dev/null > +++ b/include/linux/surface_aggregator/device.h > @@ -0,0 +1,423 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Surface System Aggregator Module (SSAM) bus and client-device subsystem. > + * > + * Main interface for the surface-aggregator bus, surface-aggregator client > + * devices, and respective drivers building on top of the SSAM controller. > + * Provides support for non-platform/non-ACPI SSAM clients via dedicated > + * subsystem. > + * > + * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@xxxxxxxxx> > + */ > + > +#ifndef _LINUX_SURFACE_AGGREGATOR_DEVICE_H > +#define _LINUX_SURFACE_AGGREGATOR_DEVICE_H > + > +#include <linux/device.h> > +#include <linux/mod_devicetable.h> > +#include <linux/types.h> > + > +#include <linux/surface_aggregator/controller.h> > + > + > +/* -- Surface System Aggregator Module bus. --------------------------------- */ > + > +/** > + * enum ssam_device_domain - SAM device domain. > + * @SSAM_DOMAIN_VIRTUAL: Virtual device. > + * @SSAM_DOMAIN_SERIALHUB: Physical device connected via Surface Serial Hub. > + */ > +enum ssam_device_domain { > + SSAM_DOMAIN_VIRTUAL = 0x00, > + SSAM_DOMAIN_SERIALHUB = 0x01, > +}; > + > +/** > + * enum ssam_virtual_tc - Target categories for the virtual SAM domain. > + * @SSAM_VIRTUAL_TC_HUB: Device hub category. > + */ > +enum ssam_virtual_tc { > + SSAM_VIRTUAL_TC_HUB = 0x00, > +}; > + > +/** > + * struct ssam_device_uid - Unique identifier for SSAM device. > + * @domain: Domain of the device. > + * @category: Target category of the device. > + * @target: Target ID of the device. > + * @instance: Instance ID of the device. > + * @function: Sub-function of the device. This field can be used to split a > + * single SAM device into multiple virtual subdevices to separate > + * different functionality of that device and allow one driver per > + * such functionality. > + */ > +struct ssam_device_uid { > + u8 domain; > + u8 category; > + u8 target; > + u8 instance; > + u8 function; > +}; > + > +/* > + * Special values for device matching. > + * > + * These values are intended to be used with SSAM_DEVICE(), SSAM_VDEV(), and > + * SSAM_SDEV() exclusively. Specifically, they are used to initialize the > + * match_flags member of the device ID structure. Do not use them directly > + * with struct ssam_device_id or struct ssam_device_uid. > + */ > +#define SSAM_ANY_TID 0xffff > +#define SSAM_ANY_IID 0xffff > +#define SSAM_ANY_FUN 0xffff > + > +/** > + * SSAM_DEVICE() - Initialize a &struct ssam_device_id with the given > + * parameters. > + * @d: Domain of the device. > + * @cat: Target category of the device. > + * @tid: Target ID of the device. > + * @iid: Instance ID of the device. > + * @fun: Sub-function of the device. > + * > + * Initializes a &struct ssam_device_id with the given parameters. See &struct > + * ssam_device_uid for details regarding the parameters. The special values > + * %SSAM_ANY_TID, %SSAM_ANY_IID, and %SSAM_ANY_FUN can be used to specify that > + * matching should ignore target ID, instance ID, and/or sub-function, > + * respectively. This macro initializes the ``match_flags`` field based on the > + * given parameters. > + * > + * Note: The parameters @d and @cat must be valid &u8 values, the parameters > + * @tid, @iid, and @fun must be either valid &u8 values or %SSAM_ANY_TID, > + * %SSAM_ANY_IID, or %SSAM_ANY_FUN, respectively. Other non-&u8 values are not > + * allowed. > + */ > +#define SSAM_DEVICE(d, cat, tid, iid, fun) \ > + .match_flags = (((tid) != SSAM_ANY_TID) ? SSAM_MATCH_TARGET : 0) \ > + | (((iid) != SSAM_ANY_IID) ? SSAM_MATCH_INSTANCE : 0) \ > + | (((fun) != SSAM_ANY_FUN) ? SSAM_MATCH_FUNCTION : 0), \ > + .domain = d, \ > + .category = cat, \ > + .target = ((tid) != SSAM_ANY_TID) ? (tid) : 0, \ > + .instance = ((iid) != SSAM_ANY_IID) ? (iid) : 0, \ > + .function = ((fun) != SSAM_ANY_FUN) ? (fun) : 0 \ > + > +/** > + * SSAM_VDEV() - Initialize a &struct ssam_device_id as virtual device with > + * the given parameters. > + * @cat: Target category of the device. > + * @tid: Target ID of the device. > + * @iid: Instance ID of the device. > + * @fun: Sub-function of the device. > + * > + * Initializes a &struct ssam_device_id with the given parameters in the > + * virtual domain. See &struct ssam_device_uid for details regarding the > + * parameters. The special values %SSAM_ANY_TID, %SSAM_ANY_IID, and > + * %SSAM_ANY_FUN can be used to specify that matching should ignore target ID, > + * instance ID, and/or sub-function, respectively. This macro initializes the > + * ``match_flags`` field based on the given parameters. > + * > + * Note: The parameter @cat must be a valid &u8 value, the parameters @tid, > + * @iid, and @fun must be either valid &u8 values or %SSAM_ANY_TID, > + * %SSAM_ANY_IID, or %SSAM_ANY_FUN, respectively. Other non-&u8 values are not > + * allowed. > + */ > +#define SSAM_VDEV(cat, tid, iid, fun) \ > + SSAM_DEVICE(SSAM_DOMAIN_VIRTUAL, SSAM_VIRTUAL_TC_##cat, tid, iid, fun) > + > +/** > + * SSAM_SDEV() - Initialize a &struct ssam_device_id as physical SSH device > + * with the given parameters. > + * @cat: Target category of the device. > + * @tid: Target ID of the device. > + * @iid: Instance ID of the device. > + * @fun: Sub-function of the device. > + * > + * Initializes a &struct ssam_device_id with the given parameters in the SSH > + * domain. See &struct ssam_device_uid for details regarding the parameters. > + * The special values %SSAM_ANY_TID, %SSAM_ANY_IID, and %SSAM_ANY_FUN can be > + * used to specify that matching should ignore target ID, instance ID, and/or > + * sub-function, respectively. This macro initializes the ``match_flags`` > + * field based on the given parameters. > + * > + * Note: The parameter @cat must be a valid &u8 value, the parameters @tid, > + * @iid, and @fun must be either valid &u8 values or %SSAM_ANY_TID, > + * %SSAM_ANY_IID, or %SSAM_ANY_FUN, respectively. Other non-&u8 values are not > + * allowed. > + */ > +#define SSAM_SDEV(cat, tid, iid, fun) \ > + SSAM_DEVICE(SSAM_DOMAIN_SERIALHUB, SSAM_SSH_TC_##cat, tid, iid, fun) > + > +/** > + * struct ssam_device - SSAM client device. > + * @dev: Driver model representation of the device. > + * @ctrl: SSAM controller managing this device. > + * @uid: UID identifying the device. > + */ > +struct ssam_device { > + struct device dev; > + struct ssam_controller *ctrl; > + > + struct ssam_device_uid uid; > +}; > + > +/** > + * struct ssam_device_driver - SSAM client device driver. > + * @driver: Base driver model structure. > + * @match_table: Match table specifying which devices the driver should bind to. > + * @probe: Called when the driver is being bound to a device. > + * @remove: Called when the driver is being unbound from the device. > + */ > +struct ssam_device_driver { > + struct device_driver driver; > + > + const struct ssam_device_id *match_table; > + > + int (*probe)(struct ssam_device *sdev); > + void (*remove)(struct ssam_device *sdev); > +}; > + > +extern struct bus_type ssam_bus_type; > +extern const struct device_type ssam_device_type; > + > +/** > + * is_ssam_device() - Check if the given device is a SSAM client device. > + * @d: The device to test the type of. > + * > + * Return: Returns %true iff the specified device is of type &struct > + * ssam_device, i.e. the device type points to %ssam_device_type, and %false > + * otherwise. > + */ > +static inline bool is_ssam_device(struct device *d) > +{ > + return d->type == &ssam_device_type; > +} > + > +/** > + * to_ssam_device() - Casts the given device to a SSAM client device. > + * @d: The device to cast. > + * > + * Casts the given &struct device to a &struct ssam_device. The caller has to > + * ensure that the given device is actually enclosed in a &struct ssam_device, > + * e.g. by calling is_ssam_device(). > + * > + * Return: Returns a pointer to the &struct ssam_device wrapping the given > + * device @d. > + */ > +static inline struct ssam_device *to_ssam_device(struct device *d) > +{ > + return container_of(d, struct ssam_device, dev); > +} > + > +/** > + * to_ssam_device_driver() - Casts the given device driver to a SSAM client > + * device driver. > + * @d: The driver to cast. > + * > + * Casts the given &struct device_driver to a &struct ssam_device_driver. The > + * caller has to ensure that the given driver is actually enclosed in a > + * &struct ssam_device_driver. > + * > + * Return: Returns the pointer to the &struct ssam_device_driver wrapping the > + * given device driver @d. > + */ > +static inline > +struct ssam_device_driver *to_ssam_device_driver(struct device_driver *d) > +{ > + return container_of(d, struct ssam_device_driver, driver); > +} > + > +const struct ssam_device_id *ssam_device_id_match(const struct ssam_device_id *table, > + const struct ssam_device_uid uid); > + > +const struct ssam_device_id *ssam_device_get_match(const struct ssam_device *dev); > + > +const void *ssam_device_get_match_data(const struct ssam_device *dev); > + > +struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl, > + struct ssam_device_uid uid); > + > +int ssam_device_add(struct ssam_device *sdev); > +void ssam_device_remove(struct ssam_device *sdev); > + > +/** > + * ssam_device_get() - Increment reference count of SSAM client device. > + * @sdev: The device to increment the reference count of. > + * > + * Increments the reference count of the given SSAM client device by > + * incrementing the reference count of the enclosed &struct device via > + * get_device(). > + * > + * See ssam_device_put() for the counter-part of this function. > + * > + * Return: Returns the device provided as input. > + */ > +static inline struct ssam_device *ssam_device_get(struct ssam_device *sdev) > +{ > + return sdev ? to_ssam_device(get_device(&sdev->dev)) : NULL; > +} > + > +/** > + * ssam_device_put() - Decrement reference count of SSAM client device. > + * @sdev: The device to decrement the reference count of. > + * > + * Decrements the reference count of the given SSAM client device by > + * decrementing the reference count of the enclosed &struct device via > + * put_device(). > + * > + * See ssam_device_get() for the counter-part of this function. > + */ > +static inline void ssam_device_put(struct ssam_device *sdev) > +{ > + if (sdev) > + put_device(&sdev->dev); > +} > + > +/** > + * ssam_device_get_drvdata() - Get driver-data of SSAM client device. > + * @sdev: The device to get the driver-data from. > + * > + * Return: Returns the driver-data of the given device, previously set via > + * ssam_device_set_drvdata(). > + */ > +static inline void *ssam_device_get_drvdata(struct ssam_device *sdev) > +{ > + return dev_get_drvdata(&sdev->dev); > +} > + > +/** > + * ssam_device_set_drvdata() - Set driver-data of SSAM client device. > + * @sdev: The device to set the driver-data of. > + * @data: The data to set the device's driver-data pointer to. > + */ > +static inline void ssam_device_set_drvdata(struct ssam_device *sdev, void *data) > +{ > + dev_set_drvdata(&sdev->dev, data); > +} > + > +int __ssam_device_driver_register(struct ssam_device_driver *d, struct module *o); > +void ssam_device_driver_unregister(struct ssam_device_driver *d); > + > +/** > + * ssam_device_driver_register() - Register a SSAM client device driver. > + * @drv: The driver to register. > + */ > +#define ssam_device_driver_register(drv) \ > + __ssam_device_driver_register(drv, THIS_MODULE) > + > +/** > + * module_ssam_device_driver() - Helper macro for SSAM device driver > + * registration. > + * @drv: The driver managed by this module. > + * > + * Helper macro to register a SSAM device driver via module_init() and > + * module_exit(). This macro may only be used once per module and replaces the > + * aforementioned definitions. > + */ > +#define module_ssam_device_driver(drv) \ > + module_driver(drv, ssam_device_driver_register, \ > + ssam_device_driver_unregister) > + > + > +/* -- Helpers for client-device requests. ----------------------------------- */ > + > +/** > + * SSAM_DEFINE_SYNC_REQUEST_CL_N() - Define synchronous client-device SAM > + * request function with neither argument nor return value. > + * @name: Name of the generated function. > + * @spec: Specification (&struct ssam_request_spec_md) defining the request. > + * > + * Defines a function executing the synchronous SAM request specified by > + * @spec, with the request having neither argument nor return value. Device > + * specifying parameters are not hard-coded, but instead are provided via the > + * client device, specifically its UID, supplied when calling this function. > + * The generated function takes care of setting up the request struct, buffer > + * allocation, as well as execution of the request itself, returning once the > + * request has been fully completed. The required transport buffer will be > + * allocated on the stack. > + * > + * The generated function is defined as ``int name(struct ssam_device *sdev)``, > + * returning the status of the request, which is zero on success and negative > + * on failure. The ``sdev`` parameter specifies both the target device of the > + * request and by association the controller via which the request is sent. > + * > + * Refer to ssam_request_sync_onstack() for more details on the behavior of > + * the generated function. > + */ > +#define SSAM_DEFINE_SYNC_REQUEST_CL_N(name, spec...) \ > + SSAM_DEFINE_SYNC_REQUEST_MD_N(__raw_##name, spec) \ > + int name(struct ssam_device *sdev) \ > + { \ > + return __raw_##name(sdev->ctrl, sdev->uid.target, \ > + sdev->uid.instance); \ > + } > + > +/** > + * SSAM_DEFINE_SYNC_REQUEST_CL_W() - Define synchronous client-device SAM > + * request function with argument. > + * @name: Name of the generated function. > + * @atype: Type of the request's argument. > + * @spec: Specification (&struct ssam_request_spec_md) defining the request. > + * > + * Defines a function executing the synchronous SAM request specified by > + * @spec, with the request taking an argument of type @atype and having no > + * return value. Device specifying parameters are not hard-coded, but instead > + * are provided via the client device, specifically its UID, supplied when > + * calling this function. The generated function takes care of setting up the > + * request struct, buffer allocation, as well as execution of the request > + * itself, returning once the request has been fully completed. The required > + * transport buffer will be allocated on the stack. > + * > + * The generated function is defined as ``int name(struct ssam_device *sdev, > + * const atype *arg)``, returning the status of the request, which is zero on > + * success and negative on failure. The ``sdev`` parameter specifies both the > + * target device of the request and by association the controller via which > + * the request is sent. The request's argument is specified via the ``arg`` > + * pointer. > + * > + * Refer to ssam_request_sync_onstack() for more details on the behavior of > + * the generated function. > + */ > +#define SSAM_DEFINE_SYNC_REQUEST_CL_W(name, atype, spec...) \ > + SSAM_DEFINE_SYNC_REQUEST_MD_W(__raw_##name, atype, spec) \ > + int name(struct ssam_device *sdev, const atype *arg) \ > + { \ > + return __raw_##name(sdev->ctrl, sdev->uid.target, \ > + sdev->uid.instance, arg); \ > + } > + > +/** > + * SSAM_DEFINE_SYNC_REQUEST_CL_R() - Define synchronous client-device SAM > + * request function with return value. > + * @name: Name of the generated function. > + * @rtype: Type of the request's return value. > + * @spec: Specification (&struct ssam_request_spec_md) defining the request. > + * > + * Defines a function executing the synchronous SAM request specified by > + * @spec, with the request taking no argument but having a return value of > + * type @rtype. Device specifying parameters are not hard-coded, but instead > + * are provided via the client device, specifically its UID, supplied when > + * calling this function. The generated function takes care of setting up the > + * request struct, buffer allocation, as well as execution of the request > + * itself, returning once the request has been fully completed. The required > + * transport buffer will be allocated on the stack. > + * > + * The generated function is defined as ``int name(struct ssam_device *sdev, > + * rtype *ret)``, returning the status of the request, which is zero on > + * success and negative on failure. The ``sdev`` parameter specifies both the > + * target device of the request and by association the controller via which > + * the request is sent. The request's return value is written to the memory > + * pointed to by the ``ret`` parameter. > + * > + * Refer to ssam_request_sync_onstack() for more details on the behavior of > + * the generated function. > + */ > +#define SSAM_DEFINE_SYNC_REQUEST_CL_R(name, rtype, spec...) \ > + SSAM_DEFINE_SYNC_REQUEST_MD_R(__raw_##name, rtype, spec) \ > + int name(struct ssam_device *sdev, rtype *ret) \ > + { \ > + return __raw_##name(sdev->ctrl, sdev->uid.target, \ > + sdev->uid.instance, ret); \ > + } > + > +#endif /* _LINUX_SURFACE_AGGREGATOR_DEVICE_H */ > diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c > index 27007c18e754..4339377ad929 100644 > --- a/scripts/mod/devicetable-offsets.c > +++ b/scripts/mod/devicetable-offsets.c > @@ -243,5 +243,13 @@ int main(void) > DEVID(mhi_device_id); > DEVID_FIELD(mhi_device_id, chan); > > + DEVID(ssam_device_id); > + DEVID_FIELD(ssam_device_id, match_flags); > + DEVID_FIELD(ssam_device_id, domain); > + DEVID_FIELD(ssam_device_id, category); > + DEVID_FIELD(ssam_device_id, target); > + DEVID_FIELD(ssam_device_id, instance); > + DEVID_FIELD(ssam_device_id, function); > + > return 0; > } > diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c > index 2417dd1dee33..5b79fdc42641 100644 > --- a/scripts/mod/file2alias.c > +++ b/scripts/mod/file2alias.c > @@ -1368,6 +1368,28 @@ static int do_mhi_entry(const char *filename, void *symval, char *alias) > return 1; > } > > +/* > + * Looks like: ssam:dNcNtNiNfN > + * > + * N is exactly 2 digits, where each is an upper-case hex digit. > + */ > +static int do_ssam_entry(const char *filename, void *symval, char *alias) > +{ > + DEF_FIELD(symval, ssam_device_id, match_flags); > + DEF_FIELD(symval, ssam_device_id, domain); > + DEF_FIELD(symval, ssam_device_id, category); > + DEF_FIELD(symval, ssam_device_id, target); > + DEF_FIELD(symval, ssam_device_id, instance); > + DEF_FIELD(symval, ssam_device_id, function); > + > + sprintf(alias, "ssam:d%02Xc%02X", domain, category); > + ADD(alias, "t", match_flags & SSAM_MATCH_TARGET, target); > + ADD(alias, "i", match_flags & SSAM_MATCH_INSTANCE, instance); > + ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); > + > + return 1; > +} > + > /* Does namelen bytes of name exactly match the symbol? */ > static bool sym_is(const char *name, unsigned namelen, const char *symbol) > { > @@ -1442,6 +1464,7 @@ static const struct devtable devtable[] = { > {"tee", SIZE_tee_client_device_id, do_tee_entry}, > {"wmi", SIZE_wmi_device_id, do_wmi_entry}, > {"mhi", SIZE_mhi_device_id, do_mhi_entry}, > + {"ssam", SIZE_ssam_device_id, do_ssam_entry}, > }; > > /* Create MODULE_ALIAS() statements. > -- > 2.29.2 >