blob: 599fc377a9658f3929e5509d80946564b6a90de7 [file] [log] [blame]
MyungJoo Hamde55d872012-04-20 14:16:22 +09001/*
Chanwoo Choib9ec23c2015-04-24 14:48:52 +09002 * drivers/extcon/extcon.c - External Connector (extcon) framework.
MyungJoo Hamde55d872012-04-20 14:16:22 +09003 *
4 * External connector (extcon) class driver
5 *
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +09006 * Copyright (C) 2015 Samsung Electronics
7 * Author: Chanwoo Choi <cw00.choi@samsung.com>
8 *
MyungJoo Hamde55d872012-04-20 14:16:22 +09009 * Copyright (C) 2012 Samsung Electronics
10 * Author: Donggeun Kim <dg77.kim@samsung.com>
11 * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
12 *
13 * based on android/drivers/switch/switch_class.c
14 * Copyright (C) 2008 Google, Inc.
15 * Author: Mike Lockwood <lockwood@android.com>
16 *
17 * This software is licensed under the terms of the GNU General Public
18 * License version 2, as published by the Free Software Foundation, and
19 * may be copied, distributed, and modified under those terms.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
Chanwoo Choib9ec23c2015-04-24 14:48:52 +090025 */
MyungJoo Hamde55d872012-04-20 14:16:22 +090026
27#include <linux/module.h>
28#include <linux/types.h>
29#include <linux/init.h>
30#include <linux/device.h>
31#include <linux/fs.h>
32#include <linux/err.h>
33#include <linux/extcon.h>
Tomasz Figaf841afb2014-10-16 15:11:44 +020034#include <linux/of.h>
MyungJoo Hamde55d872012-04-20 14:16:22 +090035#include <linux/slab.h>
Mark Brown9baf3222012-08-16 20:03:21 +010036#include <linux/sysfs.h>
MyungJoo Hamde55d872012-04-20 14:16:22 +090037
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +090038#define SUPPORTED_CABLE_MAX 32
39#define CABLE_NAME_MAX 30
40
Chanwoo Choi55e4e2f2016-07-11 16:34:52 +090041struct __extcon_info {
42 unsigned int type;
43 unsigned int id;
44 const char *name;
45
46} extcon_info[] = {
47 [EXTCON_NONE] = {
48 .type = EXTCON_TYPE_MISC,
49 .id = EXTCON_NONE,
50 .name = "NONE",
51 },
Chanwoo Choi73b6ecd2015-06-12 11:10:06 +090052
Chanwoo Choi8e9bc362015-05-19 19:58:49 +090053 /* USB external connector */
Chanwoo Choi55e4e2f2016-07-11 16:34:52 +090054 [EXTCON_USB] = {
55 .type = EXTCON_TYPE_USB,
56 .id = EXTCON_USB,
57 .name = "USB",
58 },
59 [EXTCON_USB_HOST] = {
60 .type = EXTCON_TYPE_USB,
61 .id = EXTCON_USB_HOST,
62 .name = "USB_HOST",
63 },
Chanwoo Choi8e9bc362015-05-19 19:58:49 +090064
Chanwoo Choi11eecf92015-10-03 14:15:13 +090065 /* Charging external connector */
Chanwoo Choi55e4e2f2016-07-11 16:34:52 +090066 [EXTCON_CHG_USB_SDP] = {
67 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
68 .id = EXTCON_CHG_USB_SDP,
69 .name = "SDP",
70 },
71 [EXTCON_CHG_USB_DCP] = {
72 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
73 .id = EXTCON_CHG_USB_DCP,
74 .name = "DCP",
75 },
76 [EXTCON_CHG_USB_CDP] = {
77 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
78 .id = EXTCON_CHG_USB_CDP,
79 .name = "CDP",
80 },
81 [EXTCON_CHG_USB_ACA] = {
82 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
83 .id = EXTCON_CHG_USB_ACA,
84 .name = "ACA",
85 },
86 [EXTCON_CHG_USB_FAST] = {
87 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
88 .id = EXTCON_CHG_USB_FAST,
89 .name = "FAST-CHARGER",
90 },
91 [EXTCON_CHG_USB_SLOW] = {
92 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
93 .id = EXTCON_CHG_USB_SLOW,
94 .name = "SLOW-CHARGER",
95 },
Chanwoo Choi8e9bc362015-05-19 19:58:49 +090096
Chanwoo Choi11eecf92015-10-03 14:15:13 +090097 /* Jack external connector */
Chanwoo Choi55e4e2f2016-07-11 16:34:52 +090098 [EXTCON_JACK_MICROPHONE] = {
99 .type = EXTCON_TYPE_JACK,
100 .id = EXTCON_JACK_MICROPHONE,
101 .name = "MICROPHONE",
102 },
103 [EXTCON_JACK_HEADPHONE] = {
104 .type = EXTCON_TYPE_JACK,
105 .id = EXTCON_JACK_HEADPHONE,
106 .name = "HEADPHONE",
107 },
108 [EXTCON_JACK_LINE_IN] = {
109 .type = EXTCON_TYPE_JACK,
110 .id = EXTCON_JACK_LINE_IN,
111 .name = "LINE-IN",
112 },
113 [EXTCON_JACK_LINE_OUT] = {
114 .type = EXTCON_TYPE_JACK,
115 .id = EXTCON_JACK_LINE_OUT,
116 .name = "LINE-OUT",
117 },
118 [EXTCON_JACK_VIDEO_IN] = {
119 .type = EXTCON_TYPE_JACK,
120 .id = EXTCON_JACK_VIDEO_IN,
121 .name = "VIDEO-IN",
122 },
123 [EXTCON_JACK_VIDEO_OUT] = {
124 .type = EXTCON_TYPE_JACK,
125 .id = EXTCON_JACK_VIDEO_OUT,
126 .name = "VIDEO-OUT",
127 },
128 [EXTCON_JACK_SPDIF_IN] = {
129 .type = EXTCON_TYPE_JACK,
130 .id = EXTCON_JACK_SPDIF_IN,
131 .name = "SPDIF-IN",
132 },
133 [EXTCON_JACK_SPDIF_OUT] = {
134 .type = EXTCON_TYPE_JACK,
135 .id = EXTCON_JACK_SPDIF_OUT,
136 .name = "SPDIF-OUT",
137 },
Chanwoo Choi8e9bc362015-05-19 19:58:49 +0900138
Chanwoo Choi11eecf92015-10-03 14:15:13 +0900139 /* Display external connector */
Chanwoo Choi55e4e2f2016-07-11 16:34:52 +0900140 [EXTCON_DISP_HDMI] = {
141 .type = EXTCON_TYPE_DISP,
142 .id = EXTCON_DISP_HDMI,
143 .name = "HDMI",
144 },
145 [EXTCON_DISP_MHL] = {
146 .type = EXTCON_TYPE_DISP,
147 .id = EXTCON_DISP_MHL,
148 .name = "MHL",
149 },
150 [EXTCON_DISP_DVI] = {
151 .type = EXTCON_TYPE_DISP,
152 .id = EXTCON_DISP_DVI,
153 .name = "DVI",
154 },
155 [EXTCON_DISP_VGA] = {
156 .type = EXTCON_TYPE_DISP,
157 .id = EXTCON_DISP_VGA,
158 .name = "VGA",
159 },
Chanwoo Choi8e9bc362015-05-19 19:58:49 +0900160
Chanwoo Choi11eecf92015-10-03 14:15:13 +0900161 /* Miscellaneous external connector */
Chanwoo Choi55e4e2f2016-07-11 16:34:52 +0900162 [EXTCON_DOCK] = {
163 .type = EXTCON_TYPE_MISC,
164 .id = EXTCON_DOCK,
165 .name = "DOCK",
166 },
167 [EXTCON_JIG] = {
168 .type = EXTCON_TYPE_MISC,
169 .id = EXTCON_JIG,
170 .name = "JIG",
171 },
172 [EXTCON_MECHANICAL] = {
173 .type = EXTCON_TYPE_MISC,
174 .id = EXTCON_MECHANICAL,
175 .name = "MECHANICAL",
176 },
Chanwoo Choi8e9bc362015-05-19 19:58:49 +0900177
Chanwoo Choi55e4e2f2016-07-11 16:34:52 +0900178 { /* sentinel */ }
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900179};
180
Chanwoo Choi20f7b532016-06-27 19:17:06 +0900181/**
182 * struct extcon_cable - An internal data for each cable of extcon device.
183 * @edev: The extcon device
184 * @cable_index: Index of this cable in the edev
185 * @attr_g: Attribute group for the cable
186 * @attr_name: "name" sysfs entry
187 * @attr_state: "state" sysfs entry
188 * @attrs: Array pointing to attr_name and attr_state for attr_g
189 */
190struct extcon_cable {
191 struct extcon_dev *edev;
192 int cable_index;
193
194 struct attribute_group attr_g;
195 struct device_attribute attr_name;
196 struct device_attribute attr_state;
197
198 struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
Chanwoo Choi792e7e9e2016-07-11 19:30:43 +0900199
200 union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT];
201 union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT];
202 union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT];
203 union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT];
Chanwoo Choi7f2a0a12016-07-25 21:15:19 +0900204
205 unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)];
206 unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)];
207 unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)];
208 unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)];
Chanwoo Choi20f7b532016-06-27 19:17:06 +0900209};
210
Mark Brownbe3a07f2012-06-05 16:14:38 +0100211static struct class *extcon_class;
MyungJoo Ham449a2bf2012-04-23 20:19:57 +0900212#if defined(CONFIG_ANDROID)
MyungJoo Hamde55d872012-04-20 14:16:22 +0900213static struct class_compat *switch_class;
MyungJoo Ham449a2bf2012-04-23 20:19:57 +0900214#endif /* CONFIG_ANDROID */
MyungJoo Hamde55d872012-04-20 14:16:22 +0900215
Donggeun Kim74c5d092012-04-20 14:16:24 +0900216static LIST_HEAD(extcon_dev_list);
217static DEFINE_MUTEX(extcon_dev_list_lock);
218
MyungJoo Hambde68e62012-04-20 14:16:26 +0900219/**
220 * check_mutually_exclusive - Check if new_state violates mutually_exclusive
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900221 * condition.
MyungJoo Hambde68e62012-04-20 14:16:26 +0900222 * @edev: the extcon device
223 * @new_state: new cable attach status for @edev
224 *
225 * Returns 0 if nothing violates. Returns the index + 1 for the first
226 * violated condition.
227 */
228static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
229{
230 int i = 0;
231
232 if (!edev->mutually_exclusive)
233 return 0;
234
235 for (i = 0; edev->mutually_exclusive[i]; i++) {
anish kumar28c0ada2012-08-30 00:35:10 +0530236 int weight;
MyungJoo Hambde68e62012-04-20 14:16:26 +0900237 u32 correspondants = new_state & edev->mutually_exclusive[i];
MyungJoo Hambde68e62012-04-20 14:16:26 +0900238
anish kumar28c0ada2012-08-30 00:35:10 +0530239 /* calculate the total number of bits set */
240 weight = hweight32(correspondants);
241 if (weight > 1)
242 return i + 1;
MyungJoo Hambde68e62012-04-20 14:16:26 +0900243 }
244
245 return 0;
246}
247
Chanwoo Choi73b6ecd2015-06-12 11:10:06 +0900248static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id)
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900249{
250 int i;
251
252 /* Find the the index of extcon cable in edev->supported_cable */
253 for (i = 0; i < edev->max_supported; i++) {
254 if (edev->supported_cable[i] == id)
255 return i;
256 }
257
258 return -EINVAL;
259}
260
Chanwoo Choi792e7e9e2016-07-11 19:30:43 +0900261static int get_extcon_type(unsigned int prop)
262{
263 switch (prop) {
264 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
265 return EXTCON_TYPE_USB;
266 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
267 return EXTCON_TYPE_CHG;
268 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
269 return EXTCON_TYPE_JACK;
270 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
271 return EXTCON_TYPE_DISP;
272 default:
273 return -EINVAL;
274 }
275}
276
277static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index)
278{
279 return !!(edev->state & BIT(index));
280}
281
Chanwoo Choi046050f2015-05-19 20:01:12 +0900282static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
283{
284 if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
Hans de Goedef4513b02015-08-24 00:35:36 +0200285 *attached = ((new >> idx) & 0x1) ? true : false;
Chanwoo Choi046050f2015-05-19 20:01:12 +0900286 return true;
287 }
288
289 return false;
290}
291
Chanwoo Choi792e7e9e2016-07-11 19:30:43 +0900292static bool is_extcon_property_supported(unsigned int id, unsigned int prop)
293{
294 int type;
295
296 /* Check whether the property is supported or not. */
297 type = get_extcon_type(prop);
298 if (type < 0)
299 return false;
300
301 /* Check whether a specific extcon id supports the property or not. */
302 return !!(extcon_info[id].type & type);
303}
304
Chanwoo Choi7f2a0a12016-07-25 21:15:19 +0900305static int is_extcon_property_capability(struct extcon_dev *edev,
306 unsigned int id, int index,unsigned int prop)
307{
308 struct extcon_cable *cable;
309 int type, ret;
310
311 /* Check whether the property is supported or not. */
312 type = get_extcon_type(prop);
313 if (type < 0)
314 return type;
315
316 cable = &edev->cables[index];
317
318 switch (type) {
319 case EXTCON_TYPE_USB:
320 ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
321 break;
322 case EXTCON_TYPE_CHG:
323 ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
324 break;
325 case EXTCON_TYPE_JACK:
326 ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
327 break;
328 case EXTCON_TYPE_DISP:
329 ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
330 break;
331 default:
332 ret = -EINVAL;
333 }
334
335 return ret;
336}
337
Chanwoo Choi792e7e9e2016-07-11 19:30:43 +0900338static void init_property(struct extcon_dev *edev, unsigned int id, int index)
339{
340 unsigned int type = extcon_info[id].type;
341 struct extcon_cable *cable = &edev->cables[index];
342
343 if (EXTCON_TYPE_USB & type)
344 memset(cable->usb_propval, 0, sizeof(cable->usb_propval));
345 if (EXTCON_TYPE_CHG & type)
346 memset(cable->chg_propval, 0, sizeof(cable->chg_propval));
347 if (EXTCON_TYPE_JACK & type)
348 memset(cable->jack_propval, 0, sizeof(cable->jack_propval));
349 if (EXTCON_TYPE_DISP & type)
350 memset(cable->disp_propval, 0, sizeof(cable->disp_propval));
351}
352
MyungJoo Hamde55d872012-04-20 14:16:22 +0900353static ssize_t state_show(struct device *dev, struct device_attribute *attr,
354 char *buf)
355{
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900356 int i, count = 0;
Jingoo Hancb8bb3a2013-09-09 14:33:32 +0900357 struct extcon_dev *edev = dev_get_drvdata(dev);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900358
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900359 if (edev->max_supported == 0)
360 return sprintf(buf, "%u\n", edev->state);
361
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900362 for (i = 0; i < edev->max_supported; i++) {
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900363 count += sprintf(buf + count, "%s=%d\n",
Chanwoo Choi55e4e2f2016-07-11 16:34:52 +0900364 extcon_info[edev->supported_cable[i]].name,
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900365 !!(edev->state & (1 << i)));
366 }
367
368 return count;
369}
Chanwoo Choi5d5321e2016-07-18 15:39:28 +0900370static DEVICE_ATTR_RO(state);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900371
372static ssize_t name_show(struct device *dev, struct device_attribute *attr,
373 char *buf)
374{
Jingoo Hancb8bb3a2013-09-09 14:33:32 +0900375 struct extcon_dev *edev = dev_get_drvdata(dev);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900376
Chanwoo Choi71c3ffa2015-04-15 15:02:01 +0900377 return sprintf(buf, "%s\n", edev->name);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900378}
Greg Kroah-Hartmanaf01da02013-07-24 15:05:10 -0700379static DEVICE_ATTR_RO(name);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900380
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900381static ssize_t cable_name_show(struct device *dev,
382 struct device_attribute *attr, char *buf)
383{
384 struct extcon_cable *cable = container_of(attr, struct extcon_cable,
385 attr_name);
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900386 int i = cable->cable_index;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900387
388 return sprintf(buf, "%s\n",
Chanwoo Choi55e4e2f2016-07-11 16:34:52 +0900389 extcon_info[cable->edev->supported_cable[i]].name);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900390}
391
392static ssize_t cable_state_show(struct device *dev,
393 struct device_attribute *attr, char *buf)
394{
395 struct extcon_cable *cable = container_of(attr, struct extcon_cable,
396 attr_state);
397
Roger Quadrosbe052cc2015-07-07 16:06:15 +0300398 int i = cable->cable_index;
399
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900400 return sprintf(buf, "%d\n",
401 extcon_get_cable_state_(cable->edev,
Roger Quadrosbe052cc2015-07-07 16:06:15 +0300402 cable->edev->supported_cable[i]));
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900403}
404
MyungJoo Hamde55d872012-04-20 14:16:22 +0900405/**
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900406 * extcon_update_state() - Update the cable attach states of the extcon device
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900407 * only for the masked bits.
MyungJoo Hamde55d872012-04-20 14:16:22 +0900408 * @edev: the extcon device
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900409 * @mask: the bit mask to designate updated bits.
MyungJoo Hamde55d872012-04-20 14:16:22 +0900410 * @state: new cable attach status for @edev
411 *
412 * Changing the state sends uevent with environment variable containing
413 * the name of extcon device (envp[0]) and the state output (envp[1]).
414 * Tizen uses this format for extcon device to get events from ports.
415 * Android uses this format as well.
Donggeun Kim74c5d092012-04-20 14:16:24 +0900416 *
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900417 * Note that the notifier provides which bits are changed in the state
418 * variable with the val parameter (second) to the callback.
MyungJoo Hamde55d872012-04-20 14:16:22 +0900419 */
Chanwoo Choi912465b2016-07-01 02:41:18 +0900420static int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
MyungJoo Hamde55d872012-04-20 14:16:22 +0900421{
422 char name_buf[120];
423 char state_buf[120];
424 char *prop_buf;
425 char *envp[3];
426 int env_offset = 0;
427 int length;
Chanwoo Choi046050f2015-05-19 20:01:12 +0900428 int index;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900429 unsigned long flags;
Chanwoo Choi046050f2015-05-19 20:01:12 +0900430 bool attached;
MyungJoo Hamde55d872012-04-20 14:16:22 +0900431
Chanwoo Choi7eae43a2015-06-21 23:48:36 +0900432 if (!edev)
433 return -EINVAL;
434
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900435 spin_lock_irqsave(&edev->lock, flags);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900436
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900437 if (edev->state != ((edev->state & ~mask) | (state & mask))) {
Roger Quadrosf7a89812015-07-06 17:46:58 +0300438 u32 old_state;
439
MyungJoo Hambde68e62012-04-20 14:16:26 +0900440 if (check_mutually_exclusive(edev, (edev->state & ~mask) |
441 (state & mask))) {
442 spin_unlock_irqrestore(&edev->lock, flags);
443 return -EPERM;
444 }
445
Roger Quadrosf7a89812015-07-06 17:46:58 +0300446 old_state = edev->state;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900447 edev->state &= ~mask;
448 edev->state |= state & mask;
449
Roger Quadrosf7a89812015-07-06 17:46:58 +0300450 for (index = 0; index < edev->max_supported; index++) {
451 if (is_extcon_changed(old_state, edev->state, index,
452 &attached))
453 raw_notifier_call_chain(&edev->nh[index],
454 attached, edev);
455 }
456
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900457 /* This could be in interrupt handler */
458 prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900459 if (prop_buf) {
Chanwoo Choidae61652013-09-27 09:19:40 +0900460 length = name_show(&edev->dev, NULL, prop_buf);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900461 if (length > 0) {
462 if (prop_buf[length - 1] == '\n')
463 prop_buf[length - 1] = 0;
464 snprintf(name_buf, sizeof(name_buf),
465 "NAME=%s", prop_buf);
466 envp[env_offset++] = name_buf;
467 }
Chanwoo Choidae61652013-09-27 09:19:40 +0900468 length = state_show(&edev->dev, NULL, prop_buf);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900469 if (length > 0) {
470 if (prop_buf[length - 1] == '\n')
471 prop_buf[length - 1] = 0;
472 snprintf(state_buf, sizeof(state_buf),
473 "STATE=%s", prop_buf);
474 envp[env_offset++] = state_buf;
475 }
476 envp[env_offset] = NULL;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900477 /* Unlock early before uevent */
478 spin_unlock_irqrestore(&edev->lock, flags);
479
Chanwoo Choidae61652013-09-27 09:19:40 +0900480 kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900481 free_page((unsigned long)prop_buf);
482 } else {
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900483 /* Unlock early before uevent */
484 spin_unlock_irqrestore(&edev->lock, flags);
485
Chanwoo Choidae61652013-09-27 09:19:40 +0900486 dev_err(&edev->dev, "out of memory in extcon_set_state\n");
487 kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900488 }
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900489 } else {
490 /* No changes */
491 spin_unlock_irqrestore(&edev->lock, flags);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900492 }
MyungJoo Hambde68e62012-04-20 14:16:26 +0900493
494 return 0;
MyungJoo Hamde55d872012-04-20 14:16:22 +0900495}
MyungJoo Hamde55d872012-04-20 14:16:22 +0900496
Donggeun Kim74c5d092012-04-20 14:16:24 +0900497/**
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900498 * extcon_get_cable_state_() - Get the status of a specific cable.
499 * @edev: the extcon device that has the cable.
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900500 * @id: the unique id of each external connector in extcon enumeration.
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900501 */
Chanwoo Choi73b6ecd2015-06-12 11:10:06 +0900502int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900503{
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900504 int index;
505
Chanwoo Choi7eae43a2015-06-21 23:48:36 +0900506 if (!edev)
507 return -EINVAL;
508
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900509 index = find_cable_index_by_id(edev, id);
510 if (index < 0)
511 return index;
512
513 if (edev->max_supported && edev->max_supported <= index)
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900514 return -EINVAL;
515
Chanwoo Choi792e7e9e2016-07-11 19:30:43 +0900516 return is_extcon_attached(edev, index);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900517}
518EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
519
520/**
Axel Lin909f9ec2012-10-04 09:53:45 +0900521 * extcon_set_cable_state_() - Set the status of a specific cable.
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900522 * @edev: the extcon device that has the cable.
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900523 * @id: the unique id of each external connector
524 * in extcon enumeration.
525 * @state: the new cable status. The default semantics is
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900526 * true: attached / false: detached.
527 */
Chanwoo Choi73b6ecd2015-06-12 11:10:06 +0900528int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900529 bool cable_state)
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900530{
531 u32 state;
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900532 int index;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900533
Chanwoo Choi7eae43a2015-06-21 23:48:36 +0900534 if (!edev)
535 return -EINVAL;
536
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900537 index = find_cable_index_by_id(edev, id);
538 if (index < 0)
539 return index;
540
541 if (edev->max_supported && edev->max_supported <= index)
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900542 return -EINVAL;
543
Chanwoo Choi792e7e9e2016-07-11 19:30:43 +0900544 /*
545 * Initialize the value of extcon property before setting
546 * the detached state for an external connector.
547 */
548 if (!cable_state)
549 init_property(edev, id, index);
550
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900551 state = cable_state ? (1 << index) : 0;
MyungJoo Hambde68e62012-04-20 14:16:26 +0900552 return extcon_update_state(edev, 1 << index, state);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900553}
554EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
555
556/**
Chanwoo Choi792e7e9e2016-07-11 19:30:43 +0900557 * extcon_get_property() - Get the property value of a specific cable.
558 * @edev: the extcon device that has the cable.
559 * @id: the unique id of each external connector
560 * in extcon enumeration.
561 * @prop: the property id among enum extcon_property.
562 * @prop_val: the pointer which store the value of property.
563 *
564 * When getting the property value of external connector, the external connector
565 * should be attached. If detached state, function just return 0 without
566 * property value. Also, the each property should be included in the list of
567 * supported properties according to the type of external connectors.
568 *
569 * Returns 0 if success or error number if fail
570 */
571int extcon_get_property(struct extcon_dev *edev, unsigned int id,
572 unsigned int prop,
573 union extcon_property_value *prop_val)
574{
575 struct extcon_cable *cable;
576 unsigned long flags;
577 int index, ret = 0;
578
579 *prop_val = (union extcon_property_value)(0);
580
581 if (!edev)
582 return -EINVAL;
583
584 /* Check whether the property is supported or not */
585 if (!is_extcon_property_supported(id, prop))
586 return -EINVAL;
587
588 /* Find the cable index of external connector by using id */
589 index = find_cable_index_by_id(edev, id);
590 if (index < 0)
591 return index;
592
593 spin_lock_irqsave(&edev->lock, flags);
594
Chanwoo Choi7f2a0a12016-07-25 21:15:19 +0900595 /* Check whether the property is available or not. */
596 if (!is_extcon_property_capability(edev, id, index, prop)) {
597 spin_unlock_irqrestore(&edev->lock, flags);
598 return -EPERM;
599 }
600
Chanwoo Choi792e7e9e2016-07-11 19:30:43 +0900601 /*
602 * Check whether the external connector is attached.
603 * If external connector is detached, the user can not
604 * get the property value.
605 */
606 if (!is_extcon_attached(edev, index)) {
607 spin_unlock_irqrestore(&edev->lock, flags);
608 return 0;
609 }
610
611 cable = &edev->cables[index];
612
613 /* Get the property value according to extcon type */
614 switch (prop) {
615 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
616 *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN];
617 break;
618 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
619 *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN];
620 break;
621 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
622 *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN];
623 break;
624 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
625 *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN];
626 break;
627 default:
628 ret = -EINVAL;
629 break;
630 }
631
632 spin_unlock_irqrestore(&edev->lock, flags);
633
634 return ret;
635}
636EXPORT_SYMBOL_GPL(extcon_get_property);
637
638/**
639 * extcon_set_property() - Set the property value of a specific cable.
640 * @edev: the extcon device that has the cable.
641 * @id: the unique id of each external connector
642 * in extcon enumeration.
643 * @prop: the property id among enum extcon_property.
644 * @prop_val: the pointer including the new value of property.
645 *
646 * The each property should be included in the list of supported properties
647 * according to the type of external connectors.
648 *
649 * Returns 0 if success or error number if fail
650 */
651int extcon_set_property(struct extcon_dev *edev, unsigned int id,
652 unsigned int prop,
653 union extcon_property_value prop_val)
654{
655 struct extcon_cable *cable;
656 unsigned long flags;
657 int index, ret = 0;
658
659 if (!edev)
660 return -EINVAL;
661
662 /* Check whether the property is supported or not */
663 if (!is_extcon_property_supported(id, prop))
664 return -EINVAL;
665
666 /* Find the cable index of external connector by using id */
667 index = find_cable_index_by_id(edev, id);
668 if (index < 0)
669 return index;
670
671 spin_lock_irqsave(&edev->lock, flags);
672
Chanwoo Choi7f2a0a12016-07-25 21:15:19 +0900673 /* Check whether the property is available or not. */
674 if (!is_extcon_property_capability(edev, id, index, prop)) {
675 spin_unlock_irqrestore(&edev->lock, flags);
676 return -EPERM;
677 }
678
Chanwoo Choi792e7e9e2016-07-11 19:30:43 +0900679 cable = &edev->cables[index];
680
681 /* Set the property value according to extcon type */
682 switch (prop) {
683 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
684 cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val;
685 break;
686 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
687 cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val;
688 break;
689 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
690 cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val;
691 break;
692 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
693 cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val;
694 break;
695 default:
696 ret = -EINVAL;
697 break;
698 }
699
700 spin_unlock_irqrestore(&edev->lock, flags);
701
702 return ret;
703}
704EXPORT_SYMBOL_GPL(extcon_set_property);
705
706/**
Chanwoo Choi7f2a0a12016-07-25 21:15:19 +0900707 * extcon_get_property_capability() - Get the capability of property
708 * of an external connector.
709 * @edev: the extcon device that has the cable.
710 * @id: the unique id of each external connector
711 * in extcon enumeration.
712 * @prop: the property id among enum extcon_property.
713 *
714 * Returns 1 if the property is available or 0 if not available.
715 */
716int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id,
717 unsigned int prop)
718{
719 int index;
720
721 if (!edev)
722 return -EINVAL;
723
724 /* Check whether the property is supported or not */
725 if (!is_extcon_property_supported(id, prop))
726 return -EINVAL;
727
728 /* Find the cable index of external connector by using id */
729 index = find_cable_index_by_id(edev, id);
730 if (index < 0)
731 return index;
732
733 return is_extcon_property_capability(edev, id, index, prop);
734}
735EXPORT_SYMBOL_GPL(extcon_get_property_capability);
736
737/**
738 * extcon_set_property_capability() - Set the capability of a property
739 * of an external connector.
740 * @edev: the extcon device that has the cable.
741 * @id: the unique id of each external connector
742 * in extcon enumeration.
743 * @prop: the property id among enum extcon_property.
744 *
745 * This function set the capability of a property for an external connector
746 * to mark the bit in capability bitmap which mean the available state of
747 * a property.
748 *
749 * Returns 0 if success or error number if fail
750 */
751int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id,
752 unsigned int prop)
753{
754 struct extcon_cable *cable;
755 int index, type, ret = 0;
756
757 if (!edev)
758 return -EINVAL;
759
760 /* Check whether the property is supported or not. */
761 if (!is_extcon_property_supported(id, prop))
762 return -EINVAL;
763
764 /* Find the cable index of external connector by using id. */
765 index = find_cable_index_by_id(edev, id);
766 if (index < 0)
767 return index;
768
769 type = get_extcon_type(prop);
770 if (type < 0)
771 return type;
772
773 cable = &edev->cables[index];
774
775 switch (type) {
776 case EXTCON_TYPE_USB:
777 __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
778 break;
779 case EXTCON_TYPE_CHG:
780 __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
781 break;
782 case EXTCON_TYPE_JACK:
783 __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
784 break;
785 case EXTCON_TYPE_DISP:
786 __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
787 break;
788 default:
789 ret = -EINVAL;
790 }
791
792 return ret;
793}
794EXPORT_SYMBOL_GPL(extcon_set_property_capability);
795
796/**
Donggeun Kim74c5d092012-04-20 14:16:24 +0900797 * extcon_get_extcon_dev() - Get the extcon device instance from the name
798 * @extcon_name: The extcon name provided with extcon_dev_register()
799 */
800struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
801{
802 struct extcon_dev *sd;
803
Chanwoo Choi7eae43a2015-06-21 23:48:36 +0900804 if (!extcon_name)
805 return ERR_PTR(-EINVAL);
806
Donggeun Kim74c5d092012-04-20 14:16:24 +0900807 mutex_lock(&extcon_dev_list_lock);
808 list_for_each_entry(sd, &extcon_dev_list, entry) {
809 if (!strcmp(sd->name, extcon_name))
810 goto out;
811 }
812 sd = NULL;
813out:
814 mutex_unlock(&extcon_dev_list_lock);
815 return sd;
816}
817EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
818
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900819/**
Peter Meerwaldc338bb02012-08-23 09:11:54 +0900820 * extcon_register_notifier() - Register a notifiee to get notified by
Chanwoo Choia75e1c72013-08-31 13:16:49 +0900821 * any attach status changes from the extcon.
Chanwoo Choi046050f2015-05-19 20:01:12 +0900822 * @edev: the extcon device that has the external connecotr.
823 * @id: the unique id of each external connector in extcon enumeration.
Donggeun Kim74c5d092012-04-20 14:16:24 +0900824 * @nb: a notifier block to be registered.
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900825 *
826 * Note that the second parameter given to the callback of nb (val) is
827 * "old_state", not the current state. The current state can be retrieved
828 * by looking at the third pameter (edev pointer)'s state value.
Donggeun Kim74c5d092012-04-20 14:16:24 +0900829 */
Chanwoo Choi73b6ecd2015-06-12 11:10:06 +0900830int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
Chanwoo Choi046050f2015-05-19 20:01:12 +0900831 struct notifier_block *nb)
Donggeun Kim74c5d092012-04-20 14:16:24 +0900832{
Hans de Goede66bee352015-03-21 17:26:24 +0100833 unsigned long flags;
Maninder Singh2c8116a2016-08-01 14:51:30 +0530834 int ret, idx = -EINVAL;
Chanwoo Choi046050f2015-05-19 20:01:12 +0900835
Chanwoo Choi830ae442016-05-31 17:32:30 +0900836 if (!nb)
Chanwoo Choi7eae43a2015-06-21 23:48:36 +0900837 return -EINVAL;
838
Chanwoo Choi830ae442016-05-31 17:32:30 +0900839 if (edev) {
840 idx = find_cable_index_by_id(edev, id);
Stephen Boyda05f44c2016-06-23 19:34:30 +0900841 if (idx < 0)
842 return idx;
Hans de Goede66bee352015-03-21 17:26:24 +0100843
Chanwoo Choi830ae442016-05-31 17:32:30 +0900844 spin_lock_irqsave(&edev->lock, flags);
845 ret = raw_notifier_chain_register(&edev->nh[idx], nb);
846 spin_unlock_irqrestore(&edev->lock, flags);
847 } else {
848 struct extcon_dev *extd;
849
850 mutex_lock(&extcon_dev_list_lock);
851 list_for_each_entry(extd, &extcon_dev_list, entry) {
852 idx = find_cable_index_by_id(extd, id);
853 if (idx >= 0)
854 break;
855 }
856 mutex_unlock(&extcon_dev_list_lock);
857
858 if (idx >= 0) {
859 edev = extd;
860 return extcon_register_notifier(extd, id, nb);
861 } else {
862 ret = -ENODEV;
863 }
864 }
Hans de Goede66bee352015-03-21 17:26:24 +0100865
866 return ret;
Donggeun Kim74c5d092012-04-20 14:16:24 +0900867}
868EXPORT_SYMBOL_GPL(extcon_register_notifier);
869
870/**
Peter Meerwaldc338bb02012-08-23 09:11:54 +0900871 * extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
Chanwoo Choi046050f2015-05-19 20:01:12 +0900872 * @edev: the extcon device that has the external connecotr.
873 * @id: the unique id of each external connector in extcon enumeration.
874 * @nb: a notifier block to be registered.
Donggeun Kim74c5d092012-04-20 14:16:24 +0900875 */
Chanwoo Choi73b6ecd2015-06-12 11:10:06 +0900876int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
Chanwoo Choi046050f2015-05-19 20:01:12 +0900877 struct notifier_block *nb)
Donggeun Kim74c5d092012-04-20 14:16:24 +0900878{
Hans de Goede66bee352015-03-21 17:26:24 +0100879 unsigned long flags;
Chanwoo Choi046050f2015-05-19 20:01:12 +0900880 int ret, idx;
881
Chanwoo Choi7eae43a2015-06-21 23:48:36 +0900882 if (!edev || !nb)
883 return -EINVAL;
884
Chanwoo Choi046050f2015-05-19 20:01:12 +0900885 idx = find_cable_index_by_id(edev, id);
Stephen Boyda05f44c2016-06-23 19:34:30 +0900886 if (idx < 0)
887 return idx;
Hans de Goede66bee352015-03-21 17:26:24 +0100888
889 spin_lock_irqsave(&edev->lock, flags);
Chanwoo Choi046050f2015-05-19 20:01:12 +0900890 ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
Hans de Goede66bee352015-03-21 17:26:24 +0100891 spin_unlock_irqrestore(&edev->lock, flags);
892
893 return ret;
Donggeun Kim74c5d092012-04-20 14:16:24 +0900894}
895EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
896
Greg Kroah-Hartmanaf01da02013-07-24 15:05:10 -0700897static struct attribute *extcon_attrs[] = {
898 &dev_attr_state.attr,
899 &dev_attr_name.attr,
900 NULL,
MyungJoo Hamde55d872012-04-20 14:16:22 +0900901};
Greg Kroah-Hartmanaf01da02013-07-24 15:05:10 -0700902ATTRIBUTE_GROUPS(extcon);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900903
904static int create_extcon_class(void)
905{
906 if (!extcon_class) {
907 extcon_class = class_create(THIS_MODULE, "extcon");
908 if (IS_ERR(extcon_class))
909 return PTR_ERR(extcon_class);
Greg Kroah-Hartmanaf01da02013-07-24 15:05:10 -0700910 extcon_class->dev_groups = extcon_groups;
MyungJoo Hamde55d872012-04-20 14:16:22 +0900911
MyungJoo Ham449a2bf2012-04-23 20:19:57 +0900912#if defined(CONFIG_ANDROID)
MyungJoo Hamde55d872012-04-20 14:16:22 +0900913 switch_class = class_compat_register("switch");
914 if (WARN(!switch_class, "cannot allocate"))
915 return -ENOMEM;
MyungJoo Ham449a2bf2012-04-23 20:19:57 +0900916#endif /* CONFIG_ANDROID */
MyungJoo Hamde55d872012-04-20 14:16:22 +0900917 }
918
919 return 0;
920}
921
MyungJoo Hamde55d872012-04-20 14:16:22 +0900922static void extcon_dev_release(struct device *dev)
923{
MyungJoo Hamde55d872012-04-20 14:16:22 +0900924}
925
MyungJoo Hambde68e62012-04-20 14:16:26 +0900926static const char *muex_name = "mutually_exclusive";
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900927static void dummy_sysfs_dev_release(struct device *dev)
928{
929}
930
Chanwoo Choia9af6522014-04-24 19:46:49 +0900931/*
932 * extcon_dev_allocate() - Allocate the memory of extcon device.
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900933 * @supported_cable: Array of supported extcon ending with EXTCON_NONE.
Chanwoo Choia9af6522014-04-24 19:46:49 +0900934 * If supported_cable is NULL, cable name related APIs
935 * are disabled.
936 *
937 * This function allocates the memory for extcon device without allocating
938 * memory in each extcon provider driver and initialize default setting for
939 * extcon device.
940 *
941 * Return the pointer of extcon device if success or ERR_PTR(err) if fail
942 */
Chanwoo Choi73b6ecd2015-06-12 11:10:06 +0900943struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable)
Chanwoo Choia9af6522014-04-24 19:46:49 +0900944{
945 struct extcon_dev *edev;
946
Chanwoo Choi7eae43a2015-06-21 23:48:36 +0900947 if (!supported_cable)
948 return ERR_PTR(-EINVAL);
949
Chanwoo Choia9af6522014-04-24 19:46:49 +0900950 edev = kzalloc(sizeof(*edev), GFP_KERNEL);
951 if (!edev)
952 return ERR_PTR(-ENOMEM);
953
954 edev->max_supported = 0;
955 edev->supported_cable = supported_cable;
956
957 return edev;
958}
959
960/*
961 * extcon_dev_free() - Free the memory of extcon device.
962 * @edev: the extcon device to free
963 */
964void extcon_dev_free(struct extcon_dev *edev)
965{
966 kfree(edev);
967}
968EXPORT_SYMBOL_GPL(extcon_dev_free);
969
MyungJoo Hamde55d872012-04-20 14:16:22 +0900970/**
971 * extcon_dev_register() - Register a new extcon device
972 * @edev : the new extcon device (should be allocated before calling)
MyungJoo Hamde55d872012-04-20 14:16:22 +0900973 *
974 * Among the members of edev struct, please set the "user initializing data"
975 * in any case and set the "optional callbacks" if required. However, please
976 * do not set the values of "internal data", which are initialized by
977 * this function.
978 */
Chanwoo Choi42d7d752013-09-27 09:20:26 +0900979int extcon_dev_register(struct extcon_dev *edev)
MyungJoo Hamde55d872012-04-20 14:16:22 +0900980{
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900981 int ret, index = 0;
Chanwoo Choi71c3ffa2015-04-15 15:02:01 +0900982 static atomic_t edev_no = ATOMIC_INIT(-1);
MyungJoo Hamde55d872012-04-20 14:16:22 +0900983
984 if (!extcon_class) {
985 ret = create_extcon_class();
986 if (ret < 0)
987 return ret;
988 }
989
Chanwoo Choi7eae43a2015-06-21 23:48:36 +0900990 if (!edev || !edev->supported_cable)
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900991 return -EINVAL;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900992
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900993 for (; edev->supported_cable[index] != EXTCON_NONE; index++);
994
995 edev->max_supported = index;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900996 if (index > SUPPORTED_CABLE_MAX) {
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +0900997 dev_err(&edev->dev,
998 "exceed the maximum number of supported cables\n");
MyungJoo Ham806d9dd2012-04-20 14:16:25 +0900999 return -EINVAL;
1000 }
1001
Chanwoo Choidae61652013-09-27 09:19:40 +09001002 edev->dev.class = extcon_class;
1003 edev->dev.release = extcon_dev_release;
MyungJoo Hamde55d872012-04-20 14:16:22 +09001004
Chanwoo Choi71c3ffa2015-04-15 15:02:01 +09001005 edev->name = dev_name(edev->dev.parent);
Chanwoo Choi42d7d752013-09-27 09:20:26 +09001006 if (IS_ERR_OR_NULL(edev->name)) {
1007 dev_err(&edev->dev,
1008 "extcon device name is null\n");
1009 return -EINVAL;
1010 }
Chanwoo Choi71c3ffa2015-04-15 15:02:01 +09001011 dev_set_name(&edev->dev, "extcon%lu",
1012 (unsigned long)atomic_inc_return(&edev_no));
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001013
1014 if (edev->max_supported) {
1015 char buf[10];
1016 char *str;
1017 struct extcon_cable *cable;
1018
1019 edev->cables = kzalloc(sizeof(struct extcon_cable) *
1020 edev->max_supported, GFP_KERNEL);
1021 if (!edev->cables) {
1022 ret = -ENOMEM;
1023 goto err_sysfs_alloc;
1024 }
1025 for (index = 0; index < edev->max_supported; index++) {
1026 cable = &edev->cables[index];
1027
1028 snprintf(buf, 10, "cable.%d", index);
1029 str = kzalloc(sizeof(char) * (strlen(buf) + 1),
1030 GFP_KERNEL);
1031 if (!str) {
1032 for (index--; index >= 0; index--) {
1033 cable = &edev->cables[index];
1034 kfree(cable->attr_g.name);
1035 }
1036 ret = -ENOMEM;
1037
1038 goto err_alloc_cables;
1039 }
1040 strcpy(str, buf);
1041
1042 cable->edev = edev;
1043 cable->cable_index = index;
1044 cable->attrs[0] = &cable->attr_name.attr;
1045 cable->attrs[1] = &cable->attr_state.attr;
1046 cable->attrs[2] = NULL;
1047 cable->attr_g.name = str;
1048 cable->attr_g.attrs = cable->attrs;
1049
Mark Brown9baf3222012-08-16 20:03:21 +01001050 sysfs_attr_init(&cable->attr_name.attr);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001051 cable->attr_name.attr.name = "name";
1052 cable->attr_name.attr.mode = 0444;
1053 cable->attr_name.show = cable_name_show;
1054
Mark Brown9baf3222012-08-16 20:03:21 +01001055 sysfs_attr_init(&cable->attr_state.attr);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001056 cable->attr_state.attr.name = "state";
Chanwoo Choiea9dd9d2013-05-22 19:31:59 +09001057 cable->attr_state.attr.mode = 0444;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001058 cable->attr_state.show = cable_state_show;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001059 }
1060 }
1061
MyungJoo Hambde68e62012-04-20 14:16:26 +09001062 if (edev->max_supported && edev->mutually_exclusive) {
1063 char buf[80];
1064 char *name;
1065
1066 /* Count the size of mutually_exclusive array */
1067 for (index = 0; edev->mutually_exclusive[index]; index++)
1068 ;
1069
1070 edev->attrs_muex = kzalloc(sizeof(struct attribute *) *
1071 (index + 1), GFP_KERNEL);
1072 if (!edev->attrs_muex) {
1073 ret = -ENOMEM;
1074 goto err_muex;
1075 }
1076
1077 edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) *
1078 index, GFP_KERNEL);
1079 if (!edev->d_attrs_muex) {
1080 ret = -ENOMEM;
1081 kfree(edev->attrs_muex);
1082 goto err_muex;
1083 }
1084
1085 for (index = 0; edev->mutually_exclusive[index]; index++) {
1086 sprintf(buf, "0x%x", edev->mutually_exclusive[index]);
1087 name = kzalloc(sizeof(char) * (strlen(buf) + 1),
1088 GFP_KERNEL);
1089 if (!name) {
1090 for (index--; index >= 0; index--) {
1091 kfree(edev->d_attrs_muex[index].attr.
1092 name);
1093 }
1094 kfree(edev->d_attrs_muex);
1095 kfree(edev->attrs_muex);
1096 ret = -ENOMEM;
1097 goto err_muex;
1098 }
1099 strcpy(name, buf);
Mark Brown9baf3222012-08-16 20:03:21 +01001100 sysfs_attr_init(&edev->d_attrs_muex[index].attr);
MyungJoo Hambde68e62012-04-20 14:16:26 +09001101 edev->d_attrs_muex[index].attr.name = name;
1102 edev->d_attrs_muex[index].attr.mode = 0000;
1103 edev->attrs_muex[index] = &edev->d_attrs_muex[index]
1104 .attr;
1105 }
1106 edev->attr_g_muex.name = muex_name;
1107 edev->attr_g_muex.attrs = edev->attrs_muex;
1108
1109 }
1110
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001111 if (edev->max_supported) {
1112 edev->extcon_dev_type.groups =
1113 kzalloc(sizeof(struct attribute_group *) *
MyungJoo Hambde68e62012-04-20 14:16:26 +09001114 (edev->max_supported + 2), GFP_KERNEL);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001115 if (!edev->extcon_dev_type.groups) {
1116 ret = -ENOMEM;
1117 goto err_alloc_groups;
1118 }
1119
Chanwoo Choidae61652013-09-27 09:19:40 +09001120 edev->extcon_dev_type.name = dev_name(&edev->dev);
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001121 edev->extcon_dev_type.release = dummy_sysfs_dev_release;
1122
1123 for (index = 0; index < edev->max_supported; index++)
1124 edev->extcon_dev_type.groups[index] =
1125 &edev->cables[index].attr_g;
MyungJoo Hambde68e62012-04-20 14:16:26 +09001126 if (edev->mutually_exclusive)
1127 edev->extcon_dev_type.groups[index] =
1128 &edev->attr_g_muex;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001129
Chanwoo Choidae61652013-09-27 09:19:40 +09001130 edev->dev.type = &edev->extcon_dev_type;
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001131 }
1132
Chanwoo Choidae61652013-09-27 09:19:40 +09001133 ret = device_register(&edev->dev);
MyungJoo Hamde55d872012-04-20 14:16:22 +09001134 if (ret) {
Chanwoo Choidae61652013-09-27 09:19:40 +09001135 put_device(&edev->dev);
MyungJoo Hamde55d872012-04-20 14:16:22 +09001136 goto err_dev;
1137 }
MyungJoo Ham449a2bf2012-04-23 20:19:57 +09001138#if defined(CONFIG_ANDROID)
MyungJoo Hamde55d872012-04-20 14:16:22 +09001139 if (switch_class)
Chanwoo Choidae61652013-09-27 09:19:40 +09001140 ret = class_compat_create_link(switch_class, &edev->dev, NULL);
MyungJoo Ham449a2bf2012-04-23 20:19:57 +09001141#endif /* CONFIG_ANDROID */
MyungJoo Hamde55d872012-04-20 14:16:22 +09001142
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001143 spin_lock_init(&edev->lock);
1144
Chanwoo Choi046050f2015-05-19 20:01:12 +09001145 edev->nh = devm_kzalloc(&edev->dev,
1146 sizeof(*edev->nh) * edev->max_supported, GFP_KERNEL);
1147 if (!edev->nh) {
1148 ret = -ENOMEM;
1149 goto err_dev;
1150 }
1151
1152 for (index = 0; index < edev->max_supported; index++)
1153 RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
Donggeun Kim74c5d092012-04-20 14:16:24 +09001154
Chanwoo Choidae61652013-09-27 09:19:40 +09001155 dev_set_drvdata(&edev->dev, edev);
MyungJoo Hamde55d872012-04-20 14:16:22 +09001156 edev->state = 0;
Donggeun Kim74c5d092012-04-20 14:16:24 +09001157
1158 mutex_lock(&extcon_dev_list_lock);
1159 list_add(&edev->entry, &extcon_dev_list);
1160 mutex_unlock(&extcon_dev_list_lock);
1161
MyungJoo Hamde55d872012-04-20 14:16:22 +09001162 return 0;
1163
1164err_dev:
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001165 if (edev->max_supported)
1166 kfree(edev->extcon_dev_type.groups);
1167err_alloc_groups:
MyungJoo Hambde68e62012-04-20 14:16:26 +09001168 if (edev->max_supported && edev->mutually_exclusive) {
1169 for (index = 0; edev->mutually_exclusive[index]; index++)
1170 kfree(edev->d_attrs_muex[index].attr.name);
1171 kfree(edev->d_attrs_muex);
1172 kfree(edev->attrs_muex);
1173 }
1174err_muex:
MyungJoo Ham806d9dd2012-04-20 14:16:25 +09001175 for (index = 0; index < edev->max_supported; index++)
1176 kfree(edev->cables[index].attr_g.name);
1177err_alloc_cables:
1178 if (edev->max_supported)
1179 kfree(edev->cables);
1180err_sysfs_alloc:
MyungJoo Hamde55d872012-04-20 14:16:22 +09001181 return ret;
1182}
1183EXPORT_SYMBOL_GPL(extcon_dev_register);
1184
1185/**
1186 * extcon_dev_unregister() - Unregister the extcon device.
Peter Meerwaldc338bb02012-08-23 09:11:54 +09001187 * @edev: the extcon device instance to be unregistered.
MyungJoo Hamde55d872012-04-20 14:16:22 +09001188 *
1189 * Note that this does not call kfree(edev) because edev was not allocated
1190 * by this class.
1191 */
1192void extcon_dev_unregister(struct extcon_dev *edev)
1193{
anish kumar57e7cd32012-10-22 09:43:33 +09001194 int index;
1195
Chanwoo Choi7eae43a2015-06-21 23:48:36 +09001196 if (!edev)
1197 return;
1198
anish kumar57e7cd32012-10-22 09:43:33 +09001199 mutex_lock(&extcon_dev_list_lock);
1200 list_del(&edev->entry);
1201 mutex_unlock(&extcon_dev_list_lock);
1202
Chanwoo Choidae61652013-09-27 09:19:40 +09001203 if (IS_ERR_OR_NULL(get_device(&edev->dev))) {
1204 dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n",
1205 dev_name(&edev->dev));
anish kumar57e7cd32012-10-22 09:43:33 +09001206 return;
1207 }
1208
Wang, Xiaoming7585ca02013-11-01 18:48:14 -04001209 device_unregister(&edev->dev);
1210
anish kumar57e7cd32012-10-22 09:43:33 +09001211 if (edev->mutually_exclusive && edev->max_supported) {
1212 for (index = 0; edev->mutually_exclusive[index];
1213 index++)
1214 kfree(edev->d_attrs_muex[index].attr.name);
1215 kfree(edev->d_attrs_muex);
1216 kfree(edev->attrs_muex);
1217 }
1218
1219 for (index = 0; index < edev->max_supported; index++)
1220 kfree(edev->cables[index].attr_g.name);
1221
1222 if (edev->max_supported) {
1223 kfree(edev->extcon_dev_type.groups);
1224 kfree(edev->cables);
1225 }
1226
1227#if defined(CONFIG_ANDROID)
1228 if (switch_class)
Chanwoo Choidae61652013-09-27 09:19:40 +09001229 class_compat_remove_link(switch_class, &edev->dev, NULL);
anish kumar57e7cd32012-10-22 09:43:33 +09001230#endif
Chanwoo Choidae61652013-09-27 09:19:40 +09001231 put_device(&edev->dev);
MyungJoo Hamde55d872012-04-20 14:16:22 +09001232}
1233EXPORT_SYMBOL_GPL(extcon_dev_unregister);
1234
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001235#ifdef CONFIG_OF
1236/*
1237 * extcon_get_edev_by_phandle - Get the extcon device from devicetree
1238 * @dev - instance to the given device
1239 * @index - index into list of extcon_dev
1240 *
1241 * return the instance of extcon device
1242 */
1243struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1244{
1245 struct device_node *node;
1246 struct extcon_dev *edev;
1247
Chanwoo Choi7eae43a2015-06-21 23:48:36 +09001248 if (!dev)
1249 return ERR_PTR(-EINVAL);
1250
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001251 if (!dev->of_node) {
Stephen Boyde8752b72016-07-05 11:57:05 -07001252 dev_dbg(dev, "device does not have a device node entry\n");
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001253 return ERR_PTR(-EINVAL);
1254 }
1255
1256 node = of_parse_phandle(dev->of_node, "extcon", index);
1257 if (!node) {
Stephen Boyde8752b72016-07-05 11:57:05 -07001258 dev_dbg(dev, "failed to get phandle in %s node\n",
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001259 dev->of_node->full_name);
1260 return ERR_PTR(-ENODEV);
1261 }
1262
Tomasz Figaf841afb2014-10-16 15:11:44 +02001263 mutex_lock(&extcon_dev_list_lock);
1264 list_for_each_entry(edev, &extcon_dev_list, entry) {
1265 if (edev->dev.parent && edev->dev.parent->of_node == node) {
1266 mutex_unlock(&extcon_dev_list_lock);
Peter Chen5d5c4c12016-07-01 18:41:55 +09001267 of_node_put(node);
Tomasz Figaf841afb2014-10-16 15:11:44 +02001268 return edev;
1269 }
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001270 }
Tomasz Figaf841afb2014-10-16 15:11:44 +02001271 mutex_unlock(&extcon_dev_list_lock);
Peter Chen5d5c4c12016-07-01 18:41:55 +09001272 of_node_put(node);
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001273
Tomasz Figaf841afb2014-10-16 15:11:44 +02001274 return ERR_PTR(-EPROBE_DEFER);
Chanwoo Choi1ad94ff2014-03-18 19:55:46 +09001275}
1276#else
1277struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1278{
1279 return ERR_PTR(-ENOSYS);
1280}
1281#endif /* CONFIG_OF */
1282EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
1283
Chanwoo Choi707d7552015-04-15 13:57:51 +09001284/**
1285 * extcon_get_edev_name() - Get the name of the extcon device.
1286 * @edev: the extcon device
1287 */
1288const char *extcon_get_edev_name(struct extcon_dev *edev)
1289{
1290 return !edev ? NULL : edev->name;
1291}
1292
MyungJoo Hamde55d872012-04-20 14:16:22 +09001293static int __init extcon_class_init(void)
1294{
1295 return create_extcon_class();
1296}
1297module_init(extcon_class_init);
1298
1299static void __exit extcon_class_exit(void)
1300{
Peter Huewe0dc77b62012-09-24 15:32:31 +09001301#if defined(CONFIG_ANDROID)
1302 class_compat_unregister(switch_class);
1303#endif
MyungJoo Hamde55d872012-04-20 14:16:22 +09001304 class_destroy(extcon_class);
1305}
1306module_exit(extcon_class_exit);
1307
Chanwoo Choi2a9de9c2015-04-24 19:16:05 +09001308MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
MyungJoo Hamde55d872012-04-20 14:16:22 +09001309MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
1310MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
1311MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
1312MODULE_DESCRIPTION("External connector (extcon) class driver");
1313MODULE_LICENSE("GPL");