Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 046050f6 authored by Chanwoo Choi's avatar Chanwoo Choi
Browse files

extcon: Update the prototype of extcon_register_notifier() with enum extcon



Previously, extcon consumer driver used the extcon_register_interest()
to register the notifier chain and then to receive the notifier event
when external connector's state is changed. When registering the notifier chain
for specific external connector with extcon_register_interest(), it used the
the string name of external connector directly. There are potential problem
because of unclear, non-standard and inconsequent cable name. Namely,
it is not appropriate method to identify each external connector.

So, this patch modify the prototype of extcon_register_notifier() by using
the 'enum extcon' which are the unique id for each external connector
instead of unclear string method.

- Previously, the extcon consumer driver used the extcon_register_interest()
with 'cable_name' to point out the specific external connector. Also. it used
the un-needed structure (struct extcon_specific_cable_nb).
: int extcon_register_interest(struct extcon_specific_cable_nb *obj,
			     const char *extcon_name, const char *cable_name,
			     struct notifier_block *nb)

- Newly, the updated extcon_register_notifier() would definitely support
the same feature to detech the changed state of external connector without
any specific structure (struct extcon_specific_cable_nb).
: int extcon_register_notifier(struct extcon_dev *edev, enum extcon id,
			     struct notifier_block *nb)

This patch support the both extcon_register_interest() and new extcon_register_
notifier(). But the extcon_{register|unregister}_interest() will be deprecated
because extcon core would support the notifier event for extcon consumer driver
with only updated extcon_register_notifier() and 'extcon_specific_cable_nb'
will be removed if there are no extcon consumer driver with legacy
extcon_{register|unregister}_interest().

Signed-off-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Reviewed-by: default avatarKrzysztof Kozlowski <k.kozlowski@samsung.com>
parent 8e9bc36d
Loading
Loading
Loading
Loading
+48 −43
Original line number Diff line number Diff line
@@ -146,6 +146,16 @@ static int find_cable_index_by_name(struct extcon_dev *edev, const char *name)
	return find_cable_index_by_id(edev, id);
}

static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
{
	if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
		*attached = new ? true : false;
		return true;
	}

	return false;
}

static ssize_t state_show(struct device *dev, struct device_attribute *attr,
			  char *buf)
{
@@ -254,23 +264,27 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
	char *envp[3];
	int env_offset = 0;
	int length;
	int index;
	unsigned long flags;
	bool attached;

	spin_lock_irqsave(&edev->lock, flags);

	if (edev->state != ((edev->state & ~mask) | (state & mask))) {
		u32 old_state = edev->state;

		if (check_mutually_exclusive(edev, (edev->state & ~mask) |
						   (state & mask))) {
			spin_unlock_irqrestore(&edev->lock, flags);
			return -EPERM;
		}

		for (index = 0; index < edev->max_supported; index++) {
			if (is_extcon_changed(edev->state, state, index, &attached))
				raw_notifier_call_chain(&edev->nh[index], attached, edev);
		}

		edev->state &= ~mask;
		edev->state |= state & mask;

		raw_notifier_call_chain(&edev->nh, old_state, edev);
		/* This could be in interrupt handler */
		prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
		if (prop_buf) {
@@ -423,29 +437,6 @@ out:
}
EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);

static int _call_per_cable(struct notifier_block *nb, unsigned long val,
			   void *ptr)
{
	struct extcon_specific_cable_nb *obj = container_of(nb,
			struct extcon_specific_cable_nb, internal_nb);
	struct extcon_dev *edev = ptr;

	if ((val & (1 << obj->cable_index)) !=
	    (edev->state & (1 << obj->cable_index))) {
		bool cable_state = true;

		obj->previous_value = val;

		if (val & (1 << obj->cable_index))
			cable_state = false;

		return obj->user_nb->notifier_call(obj->user_nb,
				cable_state, ptr);
	}

	return NOTIFY_OK;
}

/**
 * extcon_register_interest() - Register a notifier for a state change of a
 *				specific cable, not an entier set of cables of a
@@ -491,11 +482,10 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,

		obj->user_nb = nb;

		obj->internal_nb.notifier_call = _call_per_cable;

		spin_lock_irqsave(&obj->edev->lock, flags);
		ret = raw_notifier_chain_register(&obj->edev->nh,
						  &obj->internal_nb);
		ret = raw_notifier_chain_register(
					&obj->edev->nh[obj->cable_index],
					obj->user_nb);
		spin_unlock_irqrestore(&obj->edev->lock, flags);
	} else {
		struct class_dev_iter iter;
@@ -538,7 +528,8 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
		return -EINVAL;

	spin_lock_irqsave(&obj->edev->lock, flags);
	ret = raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
	ret = raw_notifier_chain_unregister(
			&obj->edev->nh[obj->cable_index], obj->user_nb);
	spin_unlock_irqrestore(&obj->edev->lock, flags);

	return ret;
@@ -548,21 +539,24 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest);
/**
 * extcon_register_notifier() - Register a notifiee to get notified by
 *				any attach status changes from the extcon.
 * @edev:	the extcon device.
 * @edev:	the extcon device that has the external connecotr.
 * @id:		the unique id of each external connector in extcon enumeration.
 * @nb:		a notifier block to be registered.
 *
 * Note that the second parameter given to the callback of nb (val) is
 * "old_state", not the current state. The current state can be retrieved
 * by looking at the third pameter (edev pointer)'s state value.
 */
int extcon_register_notifier(struct extcon_dev *edev,
int extcon_register_notifier(struct extcon_dev *edev, enum extcon id,
			     struct notifier_block *nb)
{
	unsigned long flags;
	int ret;
	int ret, idx;

	idx = find_cable_index_by_id(edev, id);

	spin_lock_irqsave(&edev->lock, flags);
	ret = raw_notifier_chain_register(&edev->nh, nb);
	ret = raw_notifier_chain_register(&edev->nh[idx], nb);
	spin_unlock_irqrestore(&edev->lock, flags);

	return ret;
@@ -571,17 +565,20 @@ EXPORT_SYMBOL_GPL(extcon_register_notifier);

/**
 * extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
 * @edev:	the extcon device.
 * @nb:		a registered notifier block to be unregistered.
 * @edev:	the extcon device that has the external connecotr.
 * @id:		the unique id of each external connector in extcon enumeration.
 * @nb:		a notifier block to be registered.
 */
int extcon_unregister_notifier(struct extcon_dev *edev,
int extcon_unregister_notifier(struct extcon_dev *edev, enum extcon id,
				struct notifier_block *nb)
{
	unsigned long flags;
	int ret;
	int ret, idx;

	idx = find_cable_index_by_id(edev, id);

	spin_lock_irqsave(&edev->lock, flags);
	ret = raw_notifier_chain_unregister(&edev->nh, nb);
	ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
	spin_unlock_irqrestore(&edev->lock, flags);

	return ret;
@@ -893,7 +890,15 @@ int extcon_dev_register(struct extcon_dev *edev)

	spin_lock_init(&edev->lock);

	RAW_INIT_NOTIFIER_HEAD(&edev->nh);
	edev->nh = devm_kzalloc(&edev->dev,
			sizeof(*edev->nh) * edev->max_supported, GFP_KERNEL);
	if (!edev->nh) {
		ret = -ENOMEM;
		goto err_dev;
	}

	for (index = 0; index < edev->max_supported; index++)
		RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);

	dev_set_drvdata(&edev->dev, edev);
	edev->state = 0;
+8 −9
Original line number Diff line number Diff line
@@ -116,7 +116,7 @@ struct extcon_dev {

	/* Internal data. Please do not set. */
	struct device dev;
	struct raw_notifier_head nh;
	struct raw_notifier_head *nh;
	struct list_head entry;
	int max_supported;
	spinlock_t lock;	/* could be called by irq handler */
@@ -155,8 +155,6 @@ struct extcon_cable {
/**
 * struct extcon_specific_cable_nb - An internal data for
 *				     extcon_register_interest().
 * @internal_nb:	A notifier block bridging extcon notifier
 *			and cable notifier.
 * @user_nb:		user provided notifier block for events from
 *			a specific cable.
 * @cable_index:	the target cable.
@@ -164,7 +162,6 @@ struct extcon_cable {
 * @previous_value:	the saved previous event value.
 */
struct extcon_specific_cable_nb {
	struct notifier_block internal_nb;
	struct notifier_block *user_nb;
	int cable_index;
	struct extcon_dev *edev;
@@ -240,9 +237,9 @@ extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb);
 * we do not recommend to use this for normal 'notifiee' device drivers who
 * want to be notified by a specific external port of the notifier.
 */
extern int extcon_register_notifier(struct extcon_dev *edev,
extern int extcon_register_notifier(struct extcon_dev *edev, enum extcon id,
				    struct notifier_block *nb);
extern int extcon_unregister_notifier(struct extcon_dev *edev,
extern int extcon_unregister_notifier(struct extcon_dev *edev, enum extcon id,
				    struct notifier_block *nb);

/*
@@ -333,12 +330,14 @@ static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
}

static inline int extcon_register_notifier(struct extcon_dev *edev,
					enum extcon id,
					struct notifier_block *nb)
{
	return 0;
}

static inline int extcon_unregister_notifier(struct extcon_dev *edev,
					enum extcon id,
					struct notifier_block *nb)
{
	return 0;