2 * drivers/extcon/extcon_class.c
4 * External connector (extcon) class driver
6 * Copyright (C) 2012 Samsung Electronics
7 * Author: Donggeun Kim <dg77.kim@samsung.com>
8 * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
10 * based on android/drivers/switch/switch_class.c
11 * Copyright (C) 2008 Google, Inc.
12 * Author: Mike Lockwood <lockwood@android.com>
14 * This software is licensed under the terms of the GNU General Public
15 * License version 2, as published by the Free Software Foundation, and
16 * may be copied, distributed, and modified under those terms.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
25 #include <linux/module.h>
26 #include <linux/types.h>
27 #include <linux/init.h>
28 #include <linux/device.h>
30 #include <linux/err.h>
31 #include <linux/extcon.h>
32 #include <linux/slab.h>
34 struct class *extcon_class
;
35 #if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
36 static struct class_compat
*switch_class
;
37 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
39 static ssize_t
state_show(struct device
*dev
, struct device_attribute
*attr
,
42 struct extcon_dev
*edev
= (struct extcon_dev
*) dev_get_drvdata(dev
);
44 if (edev
->print_state
) {
45 int ret
= edev
->print_state(edev
, buf
);
49 /* Use default if failed */
51 return sprintf(buf
, "%u\n", edev
->state
);
54 static ssize_t
name_show(struct device
*dev
, struct device_attribute
*attr
,
57 struct extcon_dev
*edev
= (struct extcon_dev
*) dev_get_drvdata(dev
);
59 /* Optional callback given by the user */
60 if (edev
->print_name
) {
61 int ret
= edev
->print_name(edev
, buf
);
66 return sprintf(buf
, "%s\n", dev_name(edev
->dev
));
70 * extcon_set_state() - Set the cable attach states of the extcon device.
71 * @edev: the extcon device
72 * @state: new cable attach status for @edev
74 * Changing the state sends uevent with environment variable containing
75 * the name of extcon device (envp[0]) and the state output (envp[1]).
76 * Tizen uses this format for extcon device to get events from ports.
77 * Android uses this format as well.
79 void extcon_set_state(struct extcon_dev
*edev
, u32 state
)
88 if (edev
->state
!= state
) {
91 prop_buf
= (char *)get_zeroed_page(GFP_KERNEL
);
93 length
= name_show(edev
->dev
, NULL
, prop_buf
);
95 if (prop_buf
[length
- 1] == '\n')
96 prop_buf
[length
- 1] = 0;
97 snprintf(name_buf
, sizeof(name_buf
),
99 envp
[env_offset
++] = name_buf
;
101 length
= state_show(edev
->dev
, NULL
, prop_buf
);
103 if (prop_buf
[length
- 1] == '\n')
104 prop_buf
[length
- 1] = 0;
105 snprintf(state_buf
, sizeof(state_buf
),
106 "STATE=%s", prop_buf
);
107 envp
[env_offset
++] = state_buf
;
109 envp
[env_offset
] = NULL
;
110 kobject_uevent_env(&edev
->dev
->kobj
, KOBJ_CHANGE
, envp
);
111 free_page((unsigned long)prop_buf
);
113 dev_err(edev
->dev
, "out of memory in extcon_set_state\n");
114 kobject_uevent(&edev
->dev
->kobj
, KOBJ_CHANGE
);
118 EXPORT_SYMBOL_GPL(extcon_set_state
);
120 static struct device_attribute extcon_attrs
[] = {
125 static int create_extcon_class(void)
128 extcon_class
= class_create(THIS_MODULE
, "extcon");
129 if (IS_ERR(extcon_class
))
130 return PTR_ERR(extcon_class
);
131 extcon_class
->dev_attrs
= extcon_attrs
;
133 #if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
134 switch_class
= class_compat_register("switch");
135 if (WARN(!switch_class
, "cannot allocate"))
137 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
143 static void extcon_cleanup(struct extcon_dev
*edev
, bool skip
)
145 if (!skip
&& get_device(edev
->dev
)) {
146 device_unregister(edev
->dev
);
147 put_device(edev
->dev
);
153 static void extcon_dev_release(struct device
*dev
)
155 struct extcon_dev
*edev
= (struct extcon_dev
*) dev_get_drvdata(dev
);
157 extcon_cleanup(edev
, true);
161 * extcon_dev_register() - Register a new extcon device
162 * @edev : the new extcon device (should be allocated before calling)
163 * @dev : the parent device for this extcon device.
165 * Among the members of edev struct, please set the "user initializing data"
166 * in any case and set the "optional callbacks" if required. However, please
167 * do not set the values of "internal data", which are initialized by
170 int extcon_dev_register(struct extcon_dev
*edev
, struct device
*dev
)
175 ret
= create_extcon_class();
180 edev
->dev
= kzalloc(sizeof(struct device
), GFP_KERNEL
);
181 edev
->dev
->parent
= dev
;
182 edev
->dev
->class = extcon_class
;
183 edev
->dev
->release
= extcon_dev_release
;
185 dev_set_name(edev
->dev
, edev
->name
? edev
->name
: dev_name(dev
));
186 ret
= device_register(edev
->dev
);
188 put_device(edev
->dev
);
191 #if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
193 ret
= class_compat_create_link(switch_class
, edev
->dev
,
195 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
197 dev_set_drvdata(edev
->dev
, edev
);
205 EXPORT_SYMBOL_GPL(extcon_dev_register
);
208 * extcon_dev_unregister() - Unregister the extcon device.
209 * @edev: the extcon device instance to be unregitered.
211 * Note that this does not call kfree(edev) because edev was not allocated
214 void extcon_dev_unregister(struct extcon_dev
*edev
)
216 extcon_cleanup(edev
, false);
218 EXPORT_SYMBOL_GPL(extcon_dev_unregister
);
220 static int __init
extcon_class_init(void)
222 return create_extcon_class();
224 module_init(extcon_class_init
);
226 static void __exit
extcon_class_exit(void)
228 class_destroy(extcon_class
);
230 module_exit(extcon_class_exit
);
232 MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
233 MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
234 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
235 MODULE_DESCRIPTION("External connector (extcon) class driver");
236 MODULE_LICENSE("GPL");