aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-class-extcon63
-rw-r--r--drivers/extcon/extcon_class.c439
-rw-r--r--include/linux/extcon.h185
3 files changed, 667 insertions, 20 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon
index 59a4b1c708d5..19b18e986a57 100644
--- a/Documentation/ABI/testing/sysfs-class-extcon
+++ b/Documentation/ABI/testing/sysfs-class-extcon
@@ -1,5 +1,5 @@
1What: /sys/class/extcon/.../ 1What: /sys/class/extcon/.../
2Date: December 2011 2Date: February 2012
3Contact: MyungJoo Ham <myungjoo.ham@samsung.com> 3Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
4Description: 4Description:
5 Provide a place in sysfs for the extcon objects. 5 Provide a place in sysfs for the extcon objects.
@@ -7,8 +7,16 @@ Description:
7 The name of extcon object denoted as ... is the name given 7 The name of extcon object denoted as ... is the name given
8 with extcon_dev_register. 8 with extcon_dev_register.
9 9
10 One extcon device denotes a single external connector
11 port. An external connector may have multiple cables
12 attached simultaneously. Many of docks, cradles, and
13 accessory cables have such capability. For example,
14 the 30-pin port of Nuri board (/arch/arm/mach-exynos)
15 may have both HDMI and Charger attached, or analog audio,
16 video, and USB cables attached simulteneously.
17
10What: /sys/class/extcon/.../name 18What: /sys/class/extcon/.../name
11Date: December 2011 19Date: February 2012
12Contact: MyungJoo Ham <myungjoo.ham@samsung.com> 20Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
13Description: 21Description:
14 The /sys/class/extcon/.../name shows the name of the extcon 22 The /sys/class/extcon/.../name shows the name of the extcon
@@ -17,10 +25,51 @@ Description:
17 this sysfs node. 25 this sysfs node.
18 26
19What: /sys/class/extcon/.../state 27What: /sys/class/extcon/.../state
20Date: December 2011 28Date: February 2012
29Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
30Description:
31 The /sys/class/extcon/.../state shows and stores the cable
32 attach/detach information of the corresponding extcon object.
33 If the extcon object has an optional callback "show_state"
34 defined, the showing function is overriden with the optional
35 callback.
36
37 If the default callback for showing function is used, the
38 format is like this:
39 # cat state
40 USB_OTG=1
41 HDMI=0
42 TA=1
43 EAR_JACK=0
44 #
45 In this example, the extcon device have USB_OTG and TA
46 cables attached and HDMI and EAR_JACK cables detached.
47
48 In order to update the state of an extcon device, enter a hex
49 state number starting with 0x.
50 echo 0xHEX > state
51
52 This updates the whole state of the extcon dev.
53 Inputs of all the methods are required to meet the
54 mutually_exclusive contidions if they exist.
55
56 It is recommended to use this "global" state interface if
57 you need to enter the value atomically. The later state
58 interface associated with each cable cannot update
59 multiple cable states of an extcon device simultaneously.
60
61What: /sys/class/extcon/.../cable.x/name
62Date: February 2012
63Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
64Description:
65 The /sys/class/extcon/.../cable.x/name shows the name of cable
66 "x" (integer between 0 and 31) of an extcon device.
67
68What: /sys/class/extcon/.../cable.x/state
69Date: February 2012
21Contact: MyungJoo Ham <myungjoo.ham@samsung.com> 70Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
22Description: 71Description:
23 The /sys/class/extcon/.../state shows the cable attach/detach 72 The /sys/class/extcon/.../cable.x/name shows and stores the
24 information of the corresponding extcon object. If the extcon 73 state of cable "x" (integer between 0 and 31) of an extcon
25 objecct has an optional callback "show_state" defined, the 74 device. The state value is either 0 (detached) or 1
26 callback will provide the name with this sysfs node. 75 (attached).
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 83a088f1edc4..403933bc3e9c 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -31,6 +31,39 @@
31#include <linux/extcon.h> 31#include <linux/extcon.h>
32#include <linux/slab.h> 32#include <linux/slab.h>
33 33
34/*
35 * extcon_cable_name suggests the standard cable names for commonly used
36 * cable types.
37 *
38 * However, please do not use extcon_cable_name directly for extcon_dev
39 * struct's supported_cable pointer unless your device really supports
40 * every single port-type of the following cable names. Please choose cable
41 * names that are actually used in your extcon device.
42 */
43const char *extcon_cable_name[] = {
44 [EXTCON_USB] = "USB",
45 [EXTCON_USB_HOST] = "USB-Host",
46 [EXTCON_TA] = "TA",
47 [EXTCON_FAST_CHARGER] = "Fast-charger",
48 [EXTCON_SLOW_CHARGER] = "Slow-charger",
49 [EXTCON_CHARGE_DOWNSTREAM] = "Charge-downstream",
50 [EXTCON_HDMI] = "HDMI",
51 [EXTCON_MHL] = "MHL",
52 [EXTCON_DVI] = "DVI",
53 [EXTCON_VGA] = "VGA",
54 [EXTCON_DOCK] = "Dock",
55 [EXTCON_LINE_IN] = "Line-in",
56 [EXTCON_LINE_OUT] = "Line-out",
57 [EXTCON_MIC_IN] = "Microphone",
58 [EXTCON_HEADPHONE_OUT] = "Headphone",
59 [EXTCON_SPDIF_IN] = "SPDIF-in",
60 [EXTCON_SPDIF_OUT] = "SPDIF-out",
61 [EXTCON_VIDEO_IN] = "Video-in",
62 [EXTCON_VIDEO_OUT] = "Video-out",
63
64 NULL,
65};
66
34struct class *extcon_class; 67struct class *extcon_class;
35#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH) 68#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
36static struct class_compat *switch_class; 69static struct class_compat *switch_class;
@@ -42,6 +75,7 @@ static DEFINE_MUTEX(extcon_dev_list_lock);
42static ssize_t state_show(struct device *dev, struct device_attribute *attr, 75static ssize_t state_show(struct device *dev, struct device_attribute *attr,
43 char *buf) 76 char *buf)
44{ 77{
78 int i, count = 0;
45 struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); 79 struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
46 80
47 if (edev->print_state) { 81 if (edev->print_state) {
@@ -51,7 +85,39 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
51 return ret; 85 return ret;
52 /* Use default if failed */ 86 /* Use default if failed */
53 } 87 }
54 return sprintf(buf, "%u\n", edev->state); 88
89 if (edev->max_supported == 0)
90 return sprintf(buf, "%u\n", edev->state);
91
92 for (i = 0; i < SUPPORTED_CABLE_MAX; i++) {
93 if (!edev->supported_cable[i])
94 break;
95 count += sprintf(buf + count, "%s=%d\n",
96 edev->supported_cable[i],
97 !!(edev->state & (1 << i)));
98 }
99
100 return count;
101}
102
103void extcon_set_state(struct extcon_dev *edev, u32 state);
104static ssize_t state_store(struct device *dev, struct device_attribute *attr,
105 const char *buf, size_t count)
106{
107 u32 state;
108 ssize_t ret = 0;
109 struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
110
111 ret = sscanf(buf, "0x%x", &state);
112 if (ret == 0)
113 ret = -EINVAL;
114 else
115 extcon_set_state(edev, state);
116
117 if (ret < 0)
118 return ret;
119
120 return count;
55} 121}
56 122
57static ssize_t name_show(struct device *dev, struct device_attribute *attr, 123static ssize_t name_show(struct device *dev, struct device_attribute *attr,
@@ -69,9 +135,52 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
69 return sprintf(buf, "%s\n", dev_name(edev->dev)); 135 return sprintf(buf, "%s\n", dev_name(edev->dev));
70} 136}
71 137
138static ssize_t cable_name_show(struct device *dev,
139 struct device_attribute *attr, char *buf)
140{
141 struct extcon_cable *cable = container_of(attr, struct extcon_cable,
142 attr_name);
143
144 return sprintf(buf, "%s\n",
145 cable->edev->supported_cable[cable->cable_index]);
146}
147
148static ssize_t cable_state_show(struct device *dev,
149 struct device_attribute *attr, char *buf)
150{
151 struct extcon_cable *cable = container_of(attr, struct extcon_cable,
152 attr_state);
153
154 return sprintf(buf, "%d\n",
155 extcon_get_cable_state_(cable->edev,
156 cable->cable_index));
157}
158
159static ssize_t cable_state_store(struct device *dev,
160 struct device_attribute *attr, const char *buf,
161 size_t count)
162{
163 struct extcon_cable *cable = container_of(attr, struct extcon_cable,
164 attr_state);
165 int ret, state;
166
167 ret = sscanf(buf, "%d", &state);
168 if (ret == 0)
169 ret = -EINVAL;
170 else
171 ret = extcon_set_cable_state_(cable->edev, cable->cable_index,
172 state);
173
174 if (ret < 0)
175 return ret;
176 return count;
177}
178
72/** 179/**
73 * extcon_set_state() - Set the cable attach states of the extcon device. 180 * extcon_update_state() - Update the cable attach states of the extcon device
181 * only for the masked bits.
74 * @edev: the extcon device 182 * @edev: the extcon device
183 * @mask: the bit mask to designate updated bits.
75 * @state: new cable attach status for @edev 184 * @state: new cable attach status for @edev
76 * 185 *
77 * Changing the state sends uevent with environment variable containing 186 * Changing the state sends uevent with environment variable containing
@@ -79,10 +188,10 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
79 * Tizen uses this format for extcon device to get events from ports. 188 * Tizen uses this format for extcon device to get events from ports.
80 * Android uses this format as well. 189 * Android uses this format as well.
81 * 190 *
82 * Note that notifier provides the which bits are changes in the state 191 * Note that the notifier provides which bits are changed in the state
83 * variable with "val" to the callback. 192 * variable with the val parameter (second) to the callback.
84 */ 193 */
85void extcon_set_state(struct extcon_dev *edev, u32 state) 194void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
86{ 195{
87 char name_buf[120]; 196 char name_buf[120];
88 char state_buf[120]; 197 char state_buf[120];
@@ -90,15 +199,20 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
90 char *envp[3]; 199 char *envp[3];
91 int env_offset = 0; 200 int env_offset = 0;
92 int length; 201 int length;
93 u32 old_state = edev->state; 202 unsigned long flags;
94 203
95 if (edev->state != state) { 204 spin_lock_irqsave(&edev->lock, flags);
96 edev->state = state;
97 205
98 raw_notifier_call_chain(&edev->nh, old_state ^ edev->state, 206 if (edev->state != ((edev->state & ~mask) | (state & mask))) {
99 edev); 207 u32 old_state = edev->state;
100 208
101 prop_buf = (char *)get_zeroed_page(GFP_KERNEL); 209 edev->state &= ~mask;
210 edev->state |= state & mask;
211
212 raw_notifier_call_chain(&edev->nh, old_state, edev);
213
214 /* This could be in interrupt handler */
215 prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
102 if (prop_buf) { 216 if (prop_buf) {
103 length = name_show(edev->dev, NULL, prop_buf); 217 length = name_show(edev->dev, NULL, prop_buf);
104 if (length > 0) { 218 if (length > 0) {
@@ -117,17 +231,132 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
117 envp[env_offset++] = state_buf; 231 envp[env_offset++] = state_buf;
118 } 232 }
119 envp[env_offset] = NULL; 233 envp[env_offset] = NULL;
234 /* Unlock early before uevent */
235 spin_unlock_irqrestore(&edev->lock, flags);
236
120 kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp); 237 kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
121 free_page((unsigned long)prop_buf); 238 free_page((unsigned long)prop_buf);
122 } else { 239 } else {
240 /* Unlock early before uevent */
241 spin_unlock_irqrestore(&edev->lock, flags);
242
123 dev_err(edev->dev, "out of memory in extcon_set_state\n"); 243 dev_err(edev->dev, "out of memory in extcon_set_state\n");
124 kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE); 244 kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
125 } 245 }
246 } else {
247 /* No changes */
248 spin_unlock_irqrestore(&edev->lock, flags);
126 } 249 }
127} 250}
251EXPORT_SYMBOL_GPL(extcon_update_state);
252
253/**
254 * extcon_set_state() - Set the cable attach states of the extcon device.
255 * @edev: the extcon device
256 * @state: new cable attach status for @edev
257 *
258 * Note that notifier provides which bits are changed in the state
259 * variable with the val parameter (second) to the callback.
260 */
261void extcon_set_state(struct extcon_dev *edev, u32 state)
262{
263 extcon_update_state(edev, 0xffffffff, state);
264}
128EXPORT_SYMBOL_GPL(extcon_set_state); 265EXPORT_SYMBOL_GPL(extcon_set_state);
129 266
130/** 267/**
268 * extcon_find_cable_index() - Get the cable index based on the cable name.
269 * @edev: the extcon device that has the cable.
270 * @cable_name: cable name to be searched.
271 *
272 * Note that accessing a cable state based on cable_index is faster than
273 * cable_name because using cable_name induces a loop with strncmp().
274 * Thus, when get/set_cable_state is repeatedly used, using cable_index
275 * is recommended.
276 */
277int extcon_find_cable_index(struct extcon_dev *edev, const char *cable_name)
278{
279 int i;
280
281 if (edev->supported_cable) {
282 for (i = 0; edev->supported_cable[i]; i++) {
283 if (!strncmp(edev->supported_cable[i],
284 cable_name, CABLE_NAME_MAX))
285 return i;
286 }
287 }
288
289 return -EINVAL;
290}
291EXPORT_SYMBOL_GPL(extcon_find_cable_index);
292
293/**
294 * extcon_get_cable_state_() - Get the status of a specific cable.
295 * @edev: the extcon device that has the cable.
296 * @index: cable index that can be retrieved by extcon_find_cable_index().
297 */
298int extcon_get_cable_state_(struct extcon_dev *edev, int index)
299{
300 if (index < 0 || (edev->max_supported && edev->max_supported <= index))
301 return -EINVAL;
302
303 return !!(edev->state & (1 << index));
304}
305EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
306
307/**
308 * extcon_get_cable_state() - Get the status of a specific cable.
309 * @edev: the extcon device that has the cable.
310 * @cable_name: cable name.
311 *
312 * Note that this is slower than extcon_get_cable_state_.
313 */
314int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name)
315{
316 return extcon_get_cable_state_(edev, extcon_find_cable_index
317 (edev, cable_name));
318}
319EXPORT_SYMBOL_GPL(extcon_get_cable_state);
320
321/**
322 * extcon_get_cable_state_() - Set the status of a specific cable.
323 * @edev: the extcon device that has the cable.
324 * @index: cable index that can be retrieved by extcon_find_cable_index().
325 * @cable_state: the new cable status. The default semantics is
326 * true: attached / false: detached.
327 */
328int extcon_set_cable_state_(struct extcon_dev *edev,
329 int index, bool cable_state)
330{
331 u32 state;
332
333 if (index < 0 || (edev->max_supported && edev->max_supported <= index))
334 return -EINVAL;
335
336 state = cable_state ? (1 << index) : 0;
337 extcon_update_state(edev, 1 << index, state);
338 return 0;
339}
340EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
341
342/**
343 * extcon_get_cable_state() - Set the status of a specific cable.
344 * @edev: the extcon device that has the cable.
345 * @cable_name: cable name.
346 * @cable_state: the new cable status. The default semantics is
347 * true: attached / false: detached.
348 *
349 * Note that this is slower than extcon_set_cable_state_.
350 */
351int extcon_set_cable_state(struct extcon_dev *edev,
352 const char *cable_name, bool cable_state)
353{
354 return extcon_set_cable_state_(edev, extcon_find_cable_index
355 (edev, cable_name), cable_state);
356}
357EXPORT_SYMBOL_GPL(extcon_set_cable_state);
358
359/**
131 * extcon_get_extcon_dev() - Get the extcon device instance from the name 360 * extcon_get_extcon_dev() - Get the extcon device instance from the name
132 * @extcon_name: The extcon name provided with extcon_dev_register() 361 * @extcon_name: The extcon name provided with extcon_dev_register()
133 */ 362 */
@@ -147,11 +376,88 @@ out:
147} 376}
148EXPORT_SYMBOL_GPL(extcon_get_extcon_dev); 377EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
149 378
379static int _call_per_cable(struct notifier_block *nb, unsigned long val,
380 void *ptr)
381{
382 struct extcon_specific_cable_nb *obj = container_of(nb,
383 struct extcon_specific_cable_nb, internal_nb);
384 struct extcon_dev *edev = ptr;
385
386 if ((val & (1 << obj->cable_index)) !=
387 (edev->state & (1 << obj->cable_index))) {
388 obj->previous_value = val;
389 return obj->user_nb->notifier_call(obj->user_nb, val, ptr);
390 }
391
392 return NOTIFY_OK;
393}
394
395/**
396 * extcon_register_interest() - Register a notifier for a state change of a
397 * specific cable, not a entier set of cables of a
398 * extcon device.
399 * @obj: an empty extcon_specific_cable_nb object to be returned.
400 * @extcon_name: the name of extcon device.
401 * @cable_name: the target cable name.
402 * @nb: the notifier block to get notified.
403 *
404 * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets
405 * the struct for you.
406 *
407 * extcon_register_interest is a helper function for those who want to get
408 * notification for a single specific cable's status change. If a user wants
409 * to get notification for any changes of all cables of a extcon device,
410 * he/she should use the general extcon_register_notifier().
411 *
412 * Note that the second parameter given to the callback of nb (val) is
413 * "old_state", not the current state. The current state can be retrieved
414 * by looking at the third pameter (edev pointer)'s state value.
415 */
416int extcon_register_interest(struct extcon_specific_cable_nb *obj,
417 const char *extcon_name, const char *cable_name,
418 struct notifier_block *nb)
419{
420 if (!obj || !extcon_name || !cable_name || !nb)
421 return -EINVAL;
422
423 obj->edev = extcon_get_extcon_dev(extcon_name);
424 if (!obj->edev)
425 return -ENODEV;
426
427 obj->cable_index = extcon_find_cable_index(obj->edev, cable_name);
428 if (obj->cable_index < 0)
429 return -ENODEV;
430
431 obj->user_nb = nb;
432
433 obj->internal_nb.notifier_call = _call_per_cable;
434
435 return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb);
436}
437
438/**
439 * extcon_unregister_interest() - Unregister the notifier registered by
440 * extcon_register_interest().
441 * @obj: the extcon_specific_cable_nb object returned by
442 * extcon_register_interest().
443 */
444int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
445{
446 if (!obj)
447 return -EINVAL;
448
449 return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
450}
451
150/** 452/**
151 * extcon_register_notifier() - Register a notifee to get notified by 453 * extcon_register_notifier() - Register a notifee to get notified by
152 * any attach status changes from the extcon. 454 * any attach status changes from the extcon.
153 * @edev: the extcon device. 455 * @edev: the extcon device.
154 * @nb: a notifier block to be registered. 456 * @nb: a notifier block to be registered.
457 *
458 * Note that the second parameter given to the callback of nb (val) is
459 * "old_state", not the current state. The current state can be retrieved
460 * by looking at the third pameter (edev pointer)'s state value.
155 */ 461 */
156int extcon_register_notifier(struct extcon_dev *edev, 462int extcon_register_notifier(struct extcon_dev *edev,
157 struct notifier_block *nb) 463 struct notifier_block *nb)
@@ -173,8 +479,9 @@ int extcon_unregister_notifier(struct extcon_dev *edev,
173EXPORT_SYMBOL_GPL(extcon_unregister_notifier); 479EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
174 480
175static struct device_attribute extcon_attrs[] = { 481static struct device_attribute extcon_attrs[] = {
176 __ATTR_RO(state), 482 __ATTR(state, S_IRUGO | S_IWUSR, state_show, state_store),
177 __ATTR_RO(name), 483 __ATTR_RO(name),
484 __ATTR_NULL,
178}; 485};
179 486
180static int create_extcon_class(void) 487static int create_extcon_class(void)
@@ -202,6 +509,16 @@ static void extcon_cleanup(struct extcon_dev *edev, bool skip)
202 mutex_unlock(&extcon_dev_list_lock); 509 mutex_unlock(&extcon_dev_list_lock);
203 510
204 if (!skip && get_device(edev->dev)) { 511 if (!skip && get_device(edev->dev)) {
512 int index;
513
514 for (index = 0; index < edev->max_supported; index++)
515 kfree(edev->cables[index].attr_g.name);
516
517 if (edev->max_supported) {
518 kfree(edev->extcon_dev_type.groups);
519 kfree(edev->cables);
520 }
521
205 device_unregister(edev->dev); 522 device_unregister(edev->dev);
206 put_device(edev->dev); 523 put_device(edev->dev);
207 } 524 }
@@ -216,6 +533,10 @@ static void extcon_dev_release(struct device *dev)
216 extcon_cleanup(edev, true); 533 extcon_cleanup(edev, true);
217} 534}
218 535
536static void dummy_sysfs_dev_release(struct device *dev)
537{
538}
539
219/** 540/**
220 * extcon_dev_register() - Register a new extcon device 541 * extcon_dev_register() - Register a new extcon device
221 * @edev : the new extcon device (should be allocated before calling) 542 * @edev : the new extcon device (should be allocated before calling)
@@ -228,7 +549,7 @@ static void extcon_dev_release(struct device *dev)
228 */ 549 */
229int extcon_dev_register(struct extcon_dev *edev, struct device *dev) 550int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
230{ 551{
231 int ret; 552 int ret, index = 0;
232 553
233 if (!extcon_class) { 554 if (!extcon_class) {
234 ret = create_extcon_class(); 555 ret = create_extcon_class();
@@ -236,12 +557,93 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
236 return ret; 557 return ret;
237 } 558 }
238 559
560 if (edev->supported_cable) {
561 /* Get size of array */
562 for (index = 0; edev->supported_cable[index]; index++)
563 ;
564 edev->max_supported = index;
565 } else {
566 edev->max_supported = 0;
567 }
568
569 if (index > SUPPORTED_CABLE_MAX) {
570 dev_err(edev->dev, "extcon: maximum number of supported cables exceeded.\n");
571 return -EINVAL;
572 }
573
239 edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL); 574 edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
240 edev->dev->parent = dev; 575 edev->dev->parent = dev;
241 edev->dev->class = extcon_class; 576 edev->dev->class = extcon_class;
242 edev->dev->release = extcon_dev_release; 577 edev->dev->release = extcon_dev_release;
243 578
244 dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev)); 579 dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev));
580
581 if (edev->max_supported) {
582 char buf[10];
583 char *str;
584 struct extcon_cable *cable;
585
586 edev->cables = kzalloc(sizeof(struct extcon_cable) *
587 edev->max_supported, GFP_KERNEL);
588 if (!edev->cables) {
589 ret = -ENOMEM;
590 goto err_sysfs_alloc;
591 }
592 for (index = 0; index < edev->max_supported; index++) {
593 cable = &edev->cables[index];
594
595 snprintf(buf, 10, "cable.%d", index);
596 str = kzalloc(sizeof(char) * (strlen(buf) + 1),
597 GFP_KERNEL);
598 if (!str) {
599 for (index--; index >= 0; index--) {
600 cable = &edev->cables[index];
601 kfree(cable->attr_g.name);
602 }
603 ret = -ENOMEM;
604
605 goto err_alloc_cables;
606 }
607 strcpy(str, buf);
608
609 cable->edev = edev;
610 cable->cable_index = index;
611 cable->attrs[0] = &cable->attr_name.attr;
612 cable->attrs[1] = &cable->attr_state.attr;
613 cable->attrs[2] = NULL;
614 cable->attr_g.name = str;
615 cable->attr_g.attrs = cable->attrs;
616
617 cable->attr_name.attr.name = "name";
618 cable->attr_name.attr.mode = 0444;
619 cable->attr_name.show = cable_name_show;
620
621 cable->attr_state.attr.name = "state";
622 cable->attr_state.attr.mode = 0644;
623 cable->attr_state.show = cable_state_show;
624 cable->attr_state.store = cable_state_store;
625 }
626 }
627
628 if (edev->max_supported) {
629 edev->extcon_dev_type.groups =
630 kzalloc(sizeof(struct attribute_group *) *
631 (edev->max_supported + 1), GFP_KERNEL);
632 if (!edev->extcon_dev_type.groups) {
633 ret = -ENOMEM;
634 goto err_alloc_groups;
635 }
636
637 edev->extcon_dev_type.name = dev_name(edev->dev);
638 edev->extcon_dev_type.release = dummy_sysfs_dev_release;
639
640 for (index = 0; index < edev->max_supported; index++)
641 edev->extcon_dev_type.groups[index] =
642 &edev->cables[index].attr_g;
643
644 edev->dev->type = &edev->extcon_dev_type;
645 }
646
245 ret = device_register(edev->dev); 647 ret = device_register(edev->dev);
246 if (ret) { 648 if (ret) {
247 put_device(edev->dev); 649 put_device(edev->dev);
@@ -253,6 +655,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
253 dev); 655 dev);
254#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ 656#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
255 657
658 spin_lock_init(&edev->lock);
659
256 RAW_INIT_NOTIFIER_HEAD(&edev->nh); 660 RAW_INIT_NOTIFIER_HEAD(&edev->nh);
257 661
258 dev_set_drvdata(edev->dev, edev); 662 dev_set_drvdata(edev->dev, edev);
@@ -265,6 +669,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
265 return 0; 669 return 0;
266 670
267err_dev: 671err_dev:
672 if (edev->max_supported)
673 kfree(edev->extcon_dev_type.groups);
674err_alloc_groups:
675 for (index = 0; index < edev->max_supported; index++)
676 kfree(edev->cables[index].attr_g.name);
677err_alloc_cables:
678 if (edev->max_supported)
679 kfree(edev->cables);
680err_sysfs_alloc:
268 kfree(edev->dev); 681 kfree(edev->dev);
269 return ret; 682 return ret;
270} 683}
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index c9c9afe12b47..20e24b32a17d 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -24,10 +24,60 @@
24#define __LINUX_EXTCON_H__ 24#define __LINUX_EXTCON_H__
25 25
26#include <linux/notifier.h> 26#include <linux/notifier.h>
27
28#define SUPPORTED_CABLE_MAX 32
29#define CABLE_NAME_MAX 30
30
31/*
32 * The standard cable name is to help support general notifier
33 * and notifee device drivers to share the common names.
34 * Please use standard cable names unless your notifier device has
35 * a very unique and abnormal cable or
36 * the cable type is supposed to be used with only one unique
37 * pair of notifier/notifee devices.
38 *
39 * Please add any other "standard" cables used with extcon dev.
40 *
41 * You may add a dot and number to specify version or specification
42 * of the specific cable if it is required. (e.g., "Fast-charger.18"
43 * and "Fast-charger.10" for 1.8A and 1.0A chargers)
44 * However, the notifee and notifier should be able to handle such
45 * string and if the notifee can negotiate the protocol or idenify,
46 * you don't need such convention. This convention is helpful when
47 * notifier can distinguish but notifiee cannot.
48 */
49enum extcon_cable_name {
50 EXTCON_USB = 0,
51 EXTCON_USB_HOST,
52 EXTCON_TA, /* Travel Adaptor */
53 EXTCON_FAST_CHARGER,
54 EXTCON_SLOW_CHARGER,
55 EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */
56 EXTCON_HDMI,
57 EXTCON_MHL,
58 EXTCON_DVI,
59 EXTCON_VGA,
60 EXTCON_DOCK,
61 EXTCON_LINE_IN,
62 EXTCON_LINE_OUT,
63 EXTCON_MIC_IN,
64 EXTCON_HEADPHONE_OUT,
65 EXTCON_SPDIF_IN,
66 EXTCON_SPDIF_OUT,
67 EXTCON_VIDEO_IN,
68 EXTCON_VIDEO_OUT,
69};
70extern const char *extcon_cable_name[];
71
72struct extcon_cable;
73
27/** 74/**
28 * struct extcon_dev - An extcon device represents one external connector. 75 * struct extcon_dev - An extcon device represents one external connector.
29 * @name The name of this extcon device. Parent device name is used 76 * @name The name of this extcon device. Parent device name is used
30 * if NULL. 77 * if NULL.
78 * @supported_cable Array of supported cable name ending with NULL.
79 * If supported_cable is NULL, cable name related APIs
80 * are disabled.
31 * @print_name An optional callback to override the method to print the 81 * @print_name An optional callback to override the method to print the
32 * name of the extcon device. 82 * name of the extcon device.
33 * @print_state An optional callback to override the method to print the 83 * @print_state An optional callback to override the method to print the
@@ -38,6 +88,11 @@
38 * @nh Notifier for the state change events from this extcon 88 * @nh Notifier for the state change events from this extcon
39 * @entry To support list of extcon devices so that uses can search 89 * @entry To support list of extcon devices so that uses can search
40 * for extcon devices based on the extcon name. 90 * for extcon devices based on the extcon name.
91 * @lock
92 * @max_supported Internal value to store the number of cables.
93 * @extcon_dev_type Device_type struct to provide attribute_groups
94 * customized for each extcon device.
95 * @cables Sysfs subdirectories. Each represents one cable.
41 * 96 *
42 * In most cases, users only need to provide "User initializing data" of 97 * In most cases, users only need to provide "User initializing data" of
43 * this struct when registering an extcon. In some exceptional cases, 98 * this struct when registering an extcon. In some exceptional cases,
@@ -47,6 +102,7 @@
47struct extcon_dev { 102struct extcon_dev {
48 /* --- Optional user initializing data --- */ 103 /* --- Optional user initializing data --- */
49 const char *name; 104 const char *name;
105 const char **supported_cable;
50 106
51 /* --- Optional callbacks to override class functions --- */ 107 /* --- Optional callbacks to override class functions --- */
52 ssize_t (*print_name)(struct extcon_dev *edev, char *buf); 108 ssize_t (*print_name)(struct extcon_dev *edev, char *buf);
@@ -57,6 +113,49 @@ struct extcon_dev {
57 u32 state; 113 u32 state;
58 struct raw_notifier_head nh; 114 struct raw_notifier_head nh;
59 struct list_head entry; 115 struct list_head entry;
116 spinlock_t lock; /* could be called by irq handler */
117 int max_supported;
118
119 /* /sys/class/extcon/.../cable.n/... */
120 struct device_type extcon_dev_type;
121 struct extcon_cable *cables;
122};
123
124/**
125 * struct extcon_cable - An internal data for each cable of extcon device.
126 * @edev The extcon device
127 * @cable_index Index of this cable in the edev
128 * @attr_g Attribute group for the cable
129 * @attr_name "name" sysfs entry
130 * @attr_state "state" sysfs entry
131 * @attrs Array pointing to attr_name and attr_state for attr_g
132 */
133struct extcon_cable {
134 struct extcon_dev *edev;
135 int cable_index;
136
137 struct attribute_group attr_g;
138 struct device_attribute attr_name;
139 struct device_attribute attr_state;
140
141 struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
142};
143
144/**
145 * struct extcon_specific_cable_nb - An internal data for
146 * extcon_register_interest().
147 * @internal_nb a notifier block bridging extcon notifier and cable notifier.
148 * @user_nb user provided notifier block for events from a specific cable.
149 * @cable_index the target cable.
150 * @edev the target extcon device.
151 * @previous_value the saved previous event value.
152 */
153struct extcon_specific_cable_nb {
154 struct notifier_block internal_nb;
155 struct notifier_block *user_nb;
156 int cable_index;
157 struct extcon_dev *edev;
158 unsigned long previous_value;
60}; 159};
61 160
62#if IS_ENABLED(CONFIG_EXTCON) 161#if IS_ENABLED(CONFIG_EXTCON)
@@ -69,16 +168,54 @@ extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
69extern void extcon_dev_unregister(struct extcon_dev *edev); 168extern void extcon_dev_unregister(struct extcon_dev *edev);
70extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); 169extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
71 170
171/*
172 * get/set/update_state access the 32b encoded state value, which represents
173 * states of all possible cables of the multistate port. For example, if one
174 * calls extcon_set_state(edev, 0x7), it may mean that all the three cables
175 * are attached to the port.
176 */
72static inline u32 extcon_get_state(struct extcon_dev *edev) 177static inline u32 extcon_get_state(struct extcon_dev *edev)
73{ 178{
74 return edev->state; 179 return edev->state;
75} 180}
76 181
77extern void extcon_set_state(struct extcon_dev *edev, u32 state); 182extern void extcon_set_state(struct extcon_dev *edev, u32 state);
183extern void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state);
184
185/*
186 * get/set_cable_state access each bit of the 32b encoded state value.
187 * They are used to access the status of each cable based on the cable_name
188 * or cable_index, which is retrived by extcon_find_cable_index
189 */
190extern int extcon_find_cable_index(struct extcon_dev *sdev,
191 const char *cable_name);
192extern int extcon_get_cable_state_(struct extcon_dev *edev, int cable_index);
193extern int extcon_set_cable_state_(struct extcon_dev *edev, int cable_index,
194 bool cable_state);
195
196extern int extcon_get_cable_state(struct extcon_dev *edev,
197 const char *cable_name);
198extern int extcon_set_cable_state(struct extcon_dev *edev,
199 const char *cable_name, bool cable_state);
200
201/*
202 * Following APIs are for notifiees (those who want to be notified)
203 * to register a callback for events from a specific cable of the extcon.
204 * Notifiees are the connected device drivers wanting to get notified by
205 * a specific external port of a connection device.
206 */
207extern int extcon_register_interest(struct extcon_specific_cable_nb *obj,
208 const char *extcon_name,
209 const char *cable_name,
210 struct notifier_block *nb);
211extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb);
78 212
79/* 213/*
80 * Following APIs are to monitor every action of a notifier. 214 * Following APIs are to monitor every action of a notifier.
81 * Registerer gets notified for every external port of a connection device. 215 * Registerer gets notified for every external port of a connection device.
216 * Probably this could be used to debug an action of notifier; however,
217 * we do not recommend to use this at normal 'notifiee' device drivers who
218 * want to be notified by a specific external port of the notifier.
82 */ 219 */
83extern int extcon_register_notifier(struct extcon_dev *edev, 220extern int extcon_register_notifier(struct extcon_dev *edev,
84 struct notifier_block *nb); 221 struct notifier_block *nb);
@@ -99,6 +236,41 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
99} 236}
100 237
101static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { } 238static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
239
240static inline void extcon_update_state(struct extcon_dev *edev, u32 mask,
241 u32 state)
242{ }
243
244static inline int extcon_find_cable_index(struct extcon_dev *edev,
245 const char *cable_name)
246{
247 return 0;
248}
249
250static inline int extcon_get_cable_state_(struct extcon_dev *edev,
251 int cable_index)
252{
253 return 0;
254}
255
256static inline int extcon_set_cable_state_(struct extcon_dev *edev,
257 int cable_index, bool cable_state)
258{
259 return 0;
260}
261
262static inline int extcon_get_cable_state(struct extcon_dev *edev,
263 const char *cable_name)
264{
265 return 0;
266}
267
268static inline int extcon_set_cable_state(struct extcon_dev *edev,
269 const char *cable_name, int state)
270{
271 return 0;
272}
273
102static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) 274static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
103{ 275{
104 return NULL; 276 return NULL;
@@ -116,5 +288,18 @@ static inline int extcon_unregister_notifier(struct extcon_dev *edev,
116 return 0; 288 return 0;
117} 289}
118 290
291static inline int extcon_register_interest(struct extcon_specific_cable_nb *obj,
292 const char *extcon_name,
293 const char *cable_name,
294 struct notifier_block *nb)
295{
296 return 0;
297}
298
299static inline int extcon_unregister_interest(struct extcon_specific_cable_nb
300 *obj)
301{
302 return 0;
303}
119#endif /* CONFIG_EXTCON */ 304#endif /* CONFIG_EXTCON */
120#endif /* __LINUX_EXTCON_H__ */ 305#endif /* __LINUX_EXTCON_H__ */