162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * transport_class.c - implementation of generic transport classes
462306a36Sopenharmony_ci *                     using attribute_containers
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * The basic idea here is to allow any "device controller" (which
962306a36Sopenharmony_ci * would most often be a Host Bus Adapter to use the services of one
1062306a36Sopenharmony_ci * or more tranport classes for performing transport specific
1162306a36Sopenharmony_ci * services.  Transport specific services are things that the generic
1262306a36Sopenharmony_ci * command layer doesn't want to know about (speed settings, line
1362306a36Sopenharmony_ci * condidtioning, etc), but which the user might be interested in.
1462306a36Sopenharmony_ci * Thus, the HBA's use the routines exported by the transport classes
1562306a36Sopenharmony_ci * to perform these functions.  The transport classes export certain
1662306a36Sopenharmony_ci * values to the user via sysfs using attribute containers.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Note: because not every HBA will care about every transport
1962306a36Sopenharmony_ci * attribute, there's a many to one relationship that goes like this:
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * transport class<-----attribute container<----class device
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Usually the attribute container is per-HBA, but the design doesn't
2462306a36Sopenharmony_ci * mandate that.  Although most of the services will be specific to
2562306a36Sopenharmony_ci * the actual external storage connection used by the HBA, the generic
2662306a36Sopenharmony_ci * transport class is framed entirely in terms of generic devices to
2762306a36Sopenharmony_ci * allow it to be used by any physical HBA in the system.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci#include <linux/export.h>
3062306a36Sopenharmony_ci#include <linux/attribute_container.h>
3162306a36Sopenharmony_ci#include <linux/transport_class.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int transport_remove_classdev(struct attribute_container *cont,
3462306a36Sopenharmony_ci				     struct device *dev,
3562306a36Sopenharmony_ci				     struct device *classdev);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/**
3862306a36Sopenharmony_ci * transport_class_register - register an initial transport class
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * @tclass:	a pointer to the transport class structure to be initialised
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * The transport class contains an embedded class which is used to
4362306a36Sopenharmony_ci * identify it.  The caller should initialise this structure with
4462306a36Sopenharmony_ci * zeros and then generic class must have been initialised with the
4562306a36Sopenharmony_ci * actual transport class unique name.  There's a macro
4662306a36Sopenharmony_ci * DECLARE_TRANSPORT_CLASS() to do this (declared classes still must
4762306a36Sopenharmony_ci * be registered).
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * Returns 0 on success or error on failure.
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_ciint transport_class_register(struct transport_class *tclass)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	return class_register(&tclass->class);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(transport_class_register);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/**
5862306a36Sopenharmony_ci * transport_class_unregister - unregister a previously registered class
5962306a36Sopenharmony_ci *
6062306a36Sopenharmony_ci * @tclass: The transport class to unregister
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * Must be called prior to deallocating the memory for the transport
6362306a36Sopenharmony_ci * class.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_civoid transport_class_unregister(struct transport_class *tclass)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	class_unregister(&tclass->class);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(transport_class_unregister);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int anon_transport_dummy_function(struct transport_container *tc,
7262306a36Sopenharmony_ci					 struct device *dev,
7362306a36Sopenharmony_ci					 struct device *cdev)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	/* do nothing */
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/**
8062306a36Sopenharmony_ci * anon_transport_class_register - register an anonymous class
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * @atc: The anon transport class to register
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci * The anonymous transport class contains both a transport class and a
8562306a36Sopenharmony_ci * container.  The idea of an anonymous class is that it never
8662306a36Sopenharmony_ci * actually has any device attributes associated with it (and thus
8762306a36Sopenharmony_ci * saves on container storage).  So it can only be used for triggering
8862306a36Sopenharmony_ci * events.  Use prezero and then use DECLARE_ANON_TRANSPORT_CLASS() to
8962306a36Sopenharmony_ci * initialise the anon transport class storage.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_ciint anon_transport_class_register(struct anon_transport_class *atc)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	int error;
9462306a36Sopenharmony_ci	atc->container.class = &atc->tclass.class;
9562306a36Sopenharmony_ci	attribute_container_set_no_classdevs(&atc->container);
9662306a36Sopenharmony_ci	error = attribute_container_register(&atc->container);
9762306a36Sopenharmony_ci	if (error)
9862306a36Sopenharmony_ci		return error;
9962306a36Sopenharmony_ci	atc->tclass.setup = anon_transport_dummy_function;
10062306a36Sopenharmony_ci	atc->tclass.remove = anon_transport_dummy_function;
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(anon_transport_class_register);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/**
10662306a36Sopenharmony_ci * anon_transport_class_unregister - unregister an anon class
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * @atc: Pointer to the anon transport class to unregister
10962306a36Sopenharmony_ci *
11062306a36Sopenharmony_ci * Must be called prior to deallocating the memory for the anon
11162306a36Sopenharmony_ci * transport class.
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_civoid anon_transport_class_unregister(struct anon_transport_class *atc)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	if (unlikely(attribute_container_unregister(&atc->container)))
11662306a36Sopenharmony_ci		BUG();
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(anon_transport_class_unregister);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int transport_setup_classdev(struct attribute_container *cont,
12162306a36Sopenharmony_ci				    struct device *dev,
12262306a36Sopenharmony_ci				    struct device *classdev)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct transport_class *tclass = class_to_transport_class(cont->class);
12562306a36Sopenharmony_ci	struct transport_container *tcont = attribute_container_to_transport_container(cont);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (tclass->setup)
12862306a36Sopenharmony_ci		tclass->setup(tcont, dev, classdev);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/**
13462306a36Sopenharmony_ci * transport_setup_device - declare a new dev for transport class association but don't make it visible yet.
13562306a36Sopenharmony_ci * @dev: the generic device representing the entity being added
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * Usually, dev represents some component in the HBA system (either
13862306a36Sopenharmony_ci * the HBA itself or a device remote across the HBA bus).  This
13962306a36Sopenharmony_ci * routine is simply a trigger point to see if any set of transport
14062306a36Sopenharmony_ci * classes wishes to associate with the added device.  This allocates
14162306a36Sopenharmony_ci * storage for the class device and initialises it, but does not yet
14262306a36Sopenharmony_ci * add it to the system or add attributes to it (you do this with
14362306a36Sopenharmony_ci * transport_add_device).  If you have no need for a separate setup
14462306a36Sopenharmony_ci * and add operations, use transport_register_device (see
14562306a36Sopenharmony_ci * transport_class.h).
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_civoid transport_setup_device(struct device *dev)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	attribute_container_add_device(dev, transport_setup_classdev);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(transport_setup_device);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int transport_add_class_device(struct attribute_container *cont,
15562306a36Sopenharmony_ci				      struct device *dev,
15662306a36Sopenharmony_ci				      struct device *classdev)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct transport_class *tclass = class_to_transport_class(cont->class);
15962306a36Sopenharmony_ci	int error = attribute_container_add_class_device(classdev);
16062306a36Sopenharmony_ci	struct transport_container *tcont =
16162306a36Sopenharmony_ci		attribute_container_to_transport_container(cont);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (error)
16462306a36Sopenharmony_ci		goto err_remove;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (tcont->statistics) {
16762306a36Sopenharmony_ci		error = sysfs_create_group(&classdev->kobj, tcont->statistics);
16862306a36Sopenharmony_ci		if (error)
16962306a36Sopenharmony_ci			goto err_del;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cierr_del:
17562306a36Sopenharmony_ci	attribute_container_class_device_del(classdev);
17662306a36Sopenharmony_cierr_remove:
17762306a36Sopenharmony_ci	if (tclass->remove)
17862306a36Sopenharmony_ci		tclass->remove(tcont, dev, classdev);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return error;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/**
18562306a36Sopenharmony_ci * transport_add_device - declare a new dev for transport class association
18662306a36Sopenharmony_ci *
18762306a36Sopenharmony_ci * @dev: the generic device representing the entity being added
18862306a36Sopenharmony_ci *
18962306a36Sopenharmony_ci * Usually, dev represents some component in the HBA system (either
19062306a36Sopenharmony_ci * the HBA itself or a device remote across the HBA bus).  This
19162306a36Sopenharmony_ci * routine is simply a trigger point used to add the device to the
19262306a36Sopenharmony_ci * system and register attributes for it.
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_ciint transport_add_device(struct device *dev)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	return attribute_container_device_trigger_safe(dev,
19762306a36Sopenharmony_ci					transport_add_class_device,
19862306a36Sopenharmony_ci					transport_remove_classdev);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(transport_add_device);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int transport_configure(struct attribute_container *cont,
20362306a36Sopenharmony_ci			       struct device *dev,
20462306a36Sopenharmony_ci			       struct device *cdev)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct transport_class *tclass = class_to_transport_class(cont->class);
20762306a36Sopenharmony_ci	struct transport_container *tcont = attribute_container_to_transport_container(cont);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (tclass->configure)
21062306a36Sopenharmony_ci		tclass->configure(tcont, dev, cdev);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	return 0;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/**
21662306a36Sopenharmony_ci * transport_configure_device - configure an already set up device
21762306a36Sopenharmony_ci *
21862306a36Sopenharmony_ci * @dev: generic device representing device to be configured
21962306a36Sopenharmony_ci *
22062306a36Sopenharmony_ci * The idea of configure is simply to provide a point within the setup
22162306a36Sopenharmony_ci * process to allow the transport class to extract information from a
22262306a36Sopenharmony_ci * device after it has been setup.  This is used in SCSI because we
22362306a36Sopenharmony_ci * have to have a setup device to begin using the HBA, but after we
22462306a36Sopenharmony_ci * send the initial inquiry, we use configure to extract the device
22562306a36Sopenharmony_ci * parameters.  The device need not have been added to be configured.
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_civoid transport_configure_device(struct device *dev)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	attribute_container_device_trigger(dev, transport_configure);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(transport_configure_device);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int transport_remove_classdev(struct attribute_container *cont,
23462306a36Sopenharmony_ci				     struct device *dev,
23562306a36Sopenharmony_ci				     struct device *classdev)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct transport_container *tcont =
23862306a36Sopenharmony_ci		attribute_container_to_transport_container(cont);
23962306a36Sopenharmony_ci	struct transport_class *tclass = class_to_transport_class(cont->class);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (tclass->remove)
24262306a36Sopenharmony_ci		tclass->remove(tcont, dev, classdev);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (tclass->remove != anon_transport_dummy_function) {
24562306a36Sopenharmony_ci		if (tcont->statistics)
24662306a36Sopenharmony_ci			sysfs_remove_group(&classdev->kobj, tcont->statistics);
24762306a36Sopenharmony_ci		attribute_container_class_device_del(classdev);
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return 0;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/**
25562306a36Sopenharmony_ci * transport_remove_device - remove the visibility of a device
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci * @dev: generic device to remove
25862306a36Sopenharmony_ci *
25962306a36Sopenharmony_ci * This call removes the visibility of the device (to the user from
26062306a36Sopenharmony_ci * sysfs), but does not destroy it.  To eliminate a device entirely
26162306a36Sopenharmony_ci * you must also call transport_destroy_device.  If you don't need to
26262306a36Sopenharmony_ci * do remove and destroy as separate operations, use
26362306a36Sopenharmony_ci * transport_unregister_device() (see transport_class.h) which will
26462306a36Sopenharmony_ci * perform both calls for you.
26562306a36Sopenharmony_ci */
26662306a36Sopenharmony_civoid transport_remove_device(struct device *dev)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	attribute_container_device_trigger(dev, transport_remove_classdev);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(transport_remove_device);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void transport_destroy_classdev(struct attribute_container *cont,
27362306a36Sopenharmony_ci				      struct device *dev,
27462306a36Sopenharmony_ci				      struct device *classdev)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct transport_class *tclass = class_to_transport_class(cont->class);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (tclass->remove != anon_transport_dummy_function)
27962306a36Sopenharmony_ci		put_device(classdev);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/**
28462306a36Sopenharmony_ci * transport_destroy_device - destroy a removed device
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * @dev: device to eliminate from the transport class.
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * This call triggers the elimination of storage associated with the
28962306a36Sopenharmony_ci * transport classdev.  Note: all it really does is relinquish a
29062306a36Sopenharmony_ci * reference to the classdev.  The memory will not be freed until the
29162306a36Sopenharmony_ci * last reference goes to zero.  Note also that the classdev retains a
29262306a36Sopenharmony_ci * reference count on dev, so dev too will remain for as long as the
29362306a36Sopenharmony_ci * transport class device remains around.
29462306a36Sopenharmony_ci */
29562306a36Sopenharmony_civoid transport_destroy_device(struct device *dev)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	attribute_container_remove_device(dev, transport_destroy_classdev);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(transport_destroy_device);
300