Skip to content

Commit ab11af0

Browse files
committed
extcon: Add the synchronization extcon APIs to support the notification
This patch adds the synchronization extcon APIs to support the notifications for both state and property. When extcon_*_sync() functions is called, the extcon informs the information from extcon provider to extcon client. The extcon driver may need to change the both state and multiple properties at the same time. After setting the data of a external connector, the extcon send the notification to client driver with the extcon_*_sync(). The list of new extcon APIs as following: - extcon_sync() : Send the notification for each external connector to synchronize the information between extcon provider driver and extcon client driver. - extcon_set_state_sync() : Set the state of external connector with noti. - extcon_set_property_sync() : Set the property of external connector with noti. For example, case 1, change the state of external connector and synchronized the data. extcon_set_state_sync(edev, EXTCON_USB, 1); case 2, change both the state and property of external connector and synchronized the data. extcon_set_state(edev, EXTCON_USB, 1); extcon_set_property(edev, EXTCON_USB, EXTCON_PROP_USB_VBUS 1); extcon_sync(edev, EXTCON_USB); case 3, change the property of external connector and synchronized the data. extcon_set_property(edev, EXTCON_USB, EXTCON_PROP_USB_VBUS, 0); extcon_sync(edev, EXTCON_USB); case 4, change the property of external connector and synchronized the data. extcon_set_property_sync(edev, EXTCON_USB, EXTCON_PROP_USB_VBUS, 0); Signed-off-by: Chanwoo Choi <[email protected]> Tested-by: Chris Zhong <[email protected]> Tested-by: Guenter Roeck <[email protected]> Reviewed-by: Guenter Roeck <[email protected]>
1 parent 575c2b8 commit ab11af0

File tree

2 files changed

+164
-76
lines changed

2 files changed

+164
-76
lines changed

drivers/extcon/extcon.c

Lines changed: 135 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -279,14 +279,11 @@ static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index)
279279
return !!(edev->state & BIT(index));
280280
}
281281

282-
static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
282+
static bool is_extcon_changed(struct extcon_dev *edev, int index,
283+
bool new_state)
283284
{
284-
if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
285-
*attached = ((new >> idx) & 0x1) ? true : false;
286-
return true;
287-
}
288-
289-
return false;
285+
int state = !!(edev->state & BIT(index));
286+
return (state != new_state);
290287
}
291288

292289
static bool is_extcon_property_supported(unsigned int id, unsigned int prop)
@@ -402,21 +399,13 @@ static ssize_t cable_state_show(struct device *dev,
402399
}
403400

404401
/**
405-
* extcon_update_state() - Update the cable attach states of the extcon device
406-
* only for the masked bits.
407-
* @edev: the extcon device
408-
* @mask: the bit mask to designate updated bits.
409-
* @state: new cable attach status for @edev
410-
*
411-
* Changing the state sends uevent with environment variable containing
412-
* the name of extcon device (envp[0]) and the state output (envp[1]).
413-
* Tizen uses this format for extcon device to get events from ports.
414-
* Android uses this format as well.
402+
* extcon_sync() - Synchronize the states for both the attached/detached
403+
* @edev: the extcon device that has the cable.
415404
*
416-
* Note that the notifier provides which bits are changed in the state
417-
* variable with the val parameter (second) to the callback.
405+
* This function send a notification to synchronize the all states of a
406+
* specific external connector
418407
*/
419-
static int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
408+
int extcon_sync(struct extcon_dev *edev, unsigned int id)
420409
{
421410
char name_buf[120];
422411
char state_buf[120];
@@ -425,73 +414,58 @@ static int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
425414
int env_offset = 0;
426415
int length;
427416
int index;
417+
int state;
428418
unsigned long flags;
429-
bool attached;
430419

431420
if (!edev)
432421
return -EINVAL;
433422

423+
index = find_cable_index_by_id(edev, id);
424+
if (index < 0)
425+
return index;
426+
434427
spin_lock_irqsave(&edev->lock, flags);
435428

436-
if (edev->state != ((edev->state & ~mask) | (state & mask))) {
437-
u32 old_state;
429+
state = !!(edev->state & BIT(index));
430+
raw_notifier_call_chain(&edev->nh[index], state, edev);
438431

439-
if (check_mutually_exclusive(edev, (edev->state & ~mask) |
440-
(state & mask))) {
441-
spin_unlock_irqrestore(&edev->lock, flags);
442-
return -EPERM;
443-
}
432+
/* This could be in interrupt handler */
433+
prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
434+
if (!prop_buf) {
435+
/* Unlock early before uevent */
436+
spin_unlock_irqrestore(&edev->lock, flags);
444437

445-
old_state = edev->state;
446-
edev->state &= ~mask;
447-
edev->state |= state & mask;
438+
dev_err(&edev->dev, "out of memory in extcon_set_state\n");
439+
kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
448440

449-
for (index = 0; index < edev->max_supported; index++) {
450-
if (is_extcon_changed(old_state, edev->state, index,
451-
&attached))
452-
raw_notifier_call_chain(&edev->nh[index],
453-
attached, edev);
454-
}
455-
456-
/* This could be in interrupt handler */
457-
prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
458-
if (prop_buf) {
459-
length = name_show(&edev->dev, NULL, prop_buf);
460-
if (length > 0) {
461-
if (prop_buf[length - 1] == '\n')
462-
prop_buf[length - 1] = 0;
463-
snprintf(name_buf, sizeof(name_buf),
464-
"NAME=%s", prop_buf);
465-
envp[env_offset++] = name_buf;
466-
}
467-
length = state_show(&edev->dev, NULL, prop_buf);
468-
if (length > 0) {
469-
if (prop_buf[length - 1] == '\n')
470-
prop_buf[length - 1] = 0;
471-
snprintf(state_buf, sizeof(state_buf),
472-
"STATE=%s", prop_buf);
473-
envp[env_offset++] = state_buf;
474-
}
475-
envp[env_offset] = NULL;
476-
/* Unlock early before uevent */
477-
spin_unlock_irqrestore(&edev->lock, flags);
441+
return 0;
442+
}
478443

479-
kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
480-
free_page((unsigned long)prop_buf);
481-
} else {
482-
/* Unlock early before uevent */
483-
spin_unlock_irqrestore(&edev->lock, flags);
444+
length = name_show(&edev->dev, NULL, prop_buf);
445+
if (length > 0) {
446+
if (prop_buf[length - 1] == '\n')
447+
prop_buf[length - 1] = 0;
448+
snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf);
449+
envp[env_offset++] = name_buf;
450+
}
484451

485-
dev_err(&edev->dev, "out of memory in extcon_set_state\n");
486-
kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
487-
}
488-
} else {
489-
/* No changes */
490-
spin_unlock_irqrestore(&edev->lock, flags);
452+
length = state_show(&edev->dev, NULL, prop_buf);
453+
if (length > 0) {
454+
if (prop_buf[length - 1] == '\n')
455+
prop_buf[length - 1] = 0;
456+
snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf);
457+
envp[env_offset++] = state_buf;
491458
}
459+
envp[env_offset] = NULL;
460+
461+
/* Unlock early before uevent */
462+
spin_unlock_irqrestore(&edev->lock, flags);
463+
kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
464+
free_page((unsigned long)prop_buf);
492465

493466
return 0;
494467
}
468+
EXPORT_SYMBOL_GPL(extcon_sync);
495469

496470
/**
497471
* extcon_get_state() - Get the state of a external connector.
@@ -520,17 +494,22 @@ EXPORT_SYMBOL_GPL(extcon_get_state);
520494

521495
/**
522496
* extcon_set_state() - Set the state of a external connector.
497+
* without a notification.
523498
* @edev: the extcon device that has the cable.
524499
* @id: the unique id of each external connector
525500
* in extcon enumeration.
526501
* @state: the new cable status. The default semantics is
527502
* true: attached / false: detached.
503+
*
504+
* This function only set the state of a external connector without
505+
* a notification. To synchronize the data of a external connector,
506+
* use extcon_set_state_sync() and extcon_sync().
528507
*/
529508
int extcon_set_state(struct extcon_dev *edev, unsigned int id,
530509
bool cable_state)
531510
{
532-
u32 state;
533-
int index;
511+
unsigned long flags;
512+
int index, ret = 0;
534513

535514
if (!edev)
536515
return -EINVAL;
@@ -539,18 +518,74 @@ int extcon_set_state(struct extcon_dev *edev, unsigned int id,
539518
if (index < 0)
540519
return index;
541520

521+
spin_lock_irqsave(&edev->lock, flags);
522+
523+
/* Check whether the external connector's state is changed. */
524+
if (!is_extcon_changed(edev, index, cable_state))
525+
goto out;
526+
527+
if (check_mutually_exclusive(edev,
528+
(edev->state & ~BIT(index)) | (cable_state & BIT(index)))) {
529+
ret = -EPERM;
530+
goto out;
531+
}
532+
542533
/*
543534
* Initialize the value of extcon property before setting
544535
* the detached state for an external connector.
545536
*/
546537
if (!cable_state)
547538
init_property(edev, id, index);
548539

549-
state = cable_state ? (1 << index) : 0;
550-
return extcon_update_state(edev, 1 << index, state);
540+
/* Update the state for a external connector. */
541+
if (cable_state)
542+
edev->state |= BIT(index);
543+
else
544+
edev->state &= ~(BIT(index));
545+
out:
546+
spin_unlock_irqrestore(&edev->lock, flags);
547+
548+
return ret;
551549
}
552550
EXPORT_SYMBOL_GPL(extcon_set_state);
553551

552+
/**
553+
* extcon_set_state_sync() - Set the state of a external connector
554+
* with a notification.
555+
* @edev: the extcon device that has the cable.
556+
* @id: the unique id of each external connector
557+
* in extcon enumeration.
558+
* @state: the new cable status. The default semantics is
559+
* true: attached / false: detached.
560+
*
561+
* This function set the state of external connector and synchronize the data
562+
* by usning a notification.
563+
*/
564+
int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
565+
bool cable_state)
566+
{
567+
int ret, index;
568+
unsigned long flags;
569+
570+
index = find_cable_index_by_id(edev, id);
571+
if (index < 0)
572+
return index;
573+
574+
/* Check whether the external connector's state is changed. */
575+
spin_lock_irqsave(&edev->lock, flags);
576+
ret = is_extcon_changed(edev, index, cable_state);
577+
spin_unlock_irqrestore(&edev->lock, flags);
578+
if (!ret)
579+
return 0;
580+
581+
ret = extcon_set_state(edev, id, cable_state);
582+
if (ret < 0)
583+
return ret;
584+
585+
return extcon_sync(edev, id);
586+
}
587+
EXPORT_SYMBOL_GPL(extcon_set_state_sync);
588+
554589
/**
555590
* extcon_get_property() - Get the property value of a specific cable.
556591
* @edev: the extcon device that has the cable.
@@ -701,6 +736,31 @@ int extcon_set_property(struct extcon_dev *edev, unsigned int id,
701736
}
702737
EXPORT_SYMBOL_GPL(extcon_set_property);
703738

739+
/**
740+
* extcon_set_property_sync() - Set the property value of a specific cable
741+
with a notification.
742+
* @prop_val: the pointer including the new value of property.
743+
*
744+
* When setting the property value of external connector, the external connector
745+
* should be attached. The each property should be included in the list of
746+
* supported properties according to the type of external connectors.
747+
*
748+
* Returns 0 if success or error number if fail
749+
*/
750+
int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id,
751+
unsigned int prop,
752+
union extcon_property_value prop_val)
753+
{
754+
int ret;
755+
756+
ret = extcon_set_property(edev, id, prop, prop_val);
757+
if (ret < 0)
758+
return ret;
759+
760+
return extcon_sync(edev, id);
761+
}
762+
EXPORT_SYMBOL_GPL(extcon_set_property_sync);
763+
704764
/**
705765
* extcon_get_property_capability() - Get the capability of property
706766
* of an external connector.

include/linux/extcon.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev);
222222
extern int extcon_get_state(struct extcon_dev *edev, unsigned int id);
223223
extern int extcon_set_state(struct extcon_dev *edev, unsigned int id,
224224
bool cable_state);
225+
extern int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
226+
bool cable_state);
227+
228+
/*
229+
* Synchronize the state and property data for a specific external connector.
230+
*/
231+
extern int extcon_sync(struct extcon_dev *edev, unsigned int id);
225232

226233
/*
227234
* get/set_property access the property value of each external connector.
@@ -233,6 +240,9 @@ extern int extcon_get_property(struct extcon_dev *edev, unsigned int id,
233240
extern int extcon_set_property(struct extcon_dev *edev, unsigned int id,
234241
unsigned int prop,
235242
union extcon_property_value prop_val);
243+
extern int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id,
244+
unsigned int prop,
245+
union extcon_property_value prop_val);
236246

237247
/*
238248
* get/set_property_capability set the capability of the property for each
@@ -317,6 +327,17 @@ static inline int extcon_set_state(struct extcon_dev *edev, unsigned int id,
317327
return 0;
318328
}
319329

330+
static inline int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
331+
bool cable_state)
332+
{
333+
return 0;
334+
}
335+
336+
static inline int extcon_sync(struct extcon_dev *edev, unsigned int id)
337+
{
338+
return 0;
339+
}
340+
320341
static inline int extcon_get_property(struct extcon_dev *edev, unsigned int id,
321342
unsigned int prop,
322343
union extcon_property_value *prop_val)
@@ -330,6 +351,13 @@ static inline int extcon_set_property(struct extcon_dev *edev, unsigned int id,
330351
return 0;
331352
}
332353

354+
static inline int extcon_set_property_sync(struct extcon_dev *edev,
355+
unsigned int id, unsigned int prop,
356+
union extcon_property_value prop_val)
357+
{
358+
return 0;
359+
}
360+
333361
static inline int extcon_get_property_capability(struct extcon_dev *edev,
334362
unsigned int id, unsigned int prop)
335363
{
@@ -411,6 +439,6 @@ static inline int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int
411439
static inline int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
412440
bool cable_state)
413441
{
414-
return extcon_set_state(edev, id, cable_state);
442+
return extcon_set_state_sync(edev, id, cable_state);
415443
}
416444
#endif /* __LINUX_EXTCON_H__ */

0 commit comments

Comments
 (0)