aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/extcon
diff options
context:
space:
mode:
authorMyungJoo Ham <myungjoo.ham@samsung.com>2012-04-20 01:16:25 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-04-20 12:23:37 -0400
commit806d9dd71ff52ef09764585baaeec23afbb98560 (patch)
treee81291b139bac10e08d1cb376a22cac5c89a3b65 /drivers/extcon
parent74c5d09bd562edc220d6e076b8f1e118819c178f (diff)
Extcon: support multiple states at a device.
One switch device (e.g., MUIC(MAX8997, MAX77686, ...), and some 30-pin devices) may have multiple cables attached. For example, one 30-pin port may inhabit a USB cable, an HDMI cable, and a mic. Thus, one switch device requires multiple state bits each representing a type of cable. For such purpose, we use the 32bit state variable; thus, up to 32 different type of cables may be defined for a switch device. The list of possible cables is defined by the array of cable names in the switch_dev struct given to the class. Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> -- Changes from V7 - Bugfixed in _call_per_cable() (incorrect nb) (Chanwoo Choi) - Compiler error in header for !CONFIG_EXTCON (Chanwoo Choi) Changes from V5 - Sysfs style reformed: subdirectory per cable. - Updated standard cable names - Removed unnecessary printf - Bugfixes after testing Changes from V4 - Bugfixes after more testing at Exynos4412 boards with userspace processses. Changes from V3 - Bugfixes after more testing at Exynos4412 boards. Changes from V2 - State can be stored by user - Documentation updated Changes from RFC - Switch is renamed to extcon - Added kerneldoc comments - Added APIs to support "standard" cable names - Added helper APIs to support notifier block registration with cable name. - Regrouped function list in the header file. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/extcon')
-rw-r--r--drivers/extcon/extcon_class.c439
1 files changed, 426 insertions, 13 deletions
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}