0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include <linux/module.h>
0018 #include <linux/types.h>
0019 #include <linux/init.h>
0020 #include <linux/device.h>
0021 #include <linux/fs.h>
0022 #include <linux/err.h>
0023 #include <linux/of.h>
0024 #include <linux/slab.h>
0025 #include <linux/sysfs.h>
0026
0027 #include "extcon.h"
0028
0029 #define SUPPORTED_CABLE_MAX 32
0030
0031 static const struct __extcon_info {
0032 unsigned int type;
0033 unsigned int id;
0034 const char *name;
0035
0036 } extcon_info[] = {
0037 [EXTCON_NONE] = {
0038 .type = EXTCON_TYPE_MISC,
0039 .id = EXTCON_NONE,
0040 .name = "NONE",
0041 },
0042
0043
0044 [EXTCON_USB] = {
0045 .type = EXTCON_TYPE_USB,
0046 .id = EXTCON_USB,
0047 .name = "USB",
0048 },
0049 [EXTCON_USB_HOST] = {
0050 .type = EXTCON_TYPE_USB,
0051 .id = EXTCON_USB_HOST,
0052 .name = "USB-HOST",
0053 },
0054
0055
0056 [EXTCON_CHG_USB_SDP] = {
0057 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0058 .id = EXTCON_CHG_USB_SDP,
0059 .name = "SDP",
0060 },
0061 [EXTCON_CHG_USB_DCP] = {
0062 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0063 .id = EXTCON_CHG_USB_DCP,
0064 .name = "DCP",
0065 },
0066 [EXTCON_CHG_USB_CDP] = {
0067 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0068 .id = EXTCON_CHG_USB_CDP,
0069 .name = "CDP",
0070 },
0071 [EXTCON_CHG_USB_ACA] = {
0072 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0073 .id = EXTCON_CHG_USB_ACA,
0074 .name = "ACA",
0075 },
0076 [EXTCON_CHG_USB_FAST] = {
0077 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0078 .id = EXTCON_CHG_USB_FAST,
0079 .name = "FAST-CHARGER",
0080 },
0081 [EXTCON_CHG_USB_SLOW] = {
0082 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0083 .id = EXTCON_CHG_USB_SLOW,
0084 .name = "SLOW-CHARGER",
0085 },
0086 [EXTCON_CHG_WPT] = {
0087 .type = EXTCON_TYPE_CHG,
0088 .id = EXTCON_CHG_WPT,
0089 .name = "WPT",
0090 },
0091 [EXTCON_CHG_USB_PD] = {
0092 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
0093 .id = EXTCON_CHG_USB_PD,
0094 .name = "PD",
0095 },
0096
0097
0098 [EXTCON_JACK_MICROPHONE] = {
0099 .type = EXTCON_TYPE_JACK,
0100 .id = EXTCON_JACK_MICROPHONE,
0101 .name = "MICROPHONE",
0102 },
0103 [EXTCON_JACK_HEADPHONE] = {
0104 .type = EXTCON_TYPE_JACK,
0105 .id = EXTCON_JACK_HEADPHONE,
0106 .name = "HEADPHONE",
0107 },
0108 [EXTCON_JACK_LINE_IN] = {
0109 .type = EXTCON_TYPE_JACK,
0110 .id = EXTCON_JACK_LINE_IN,
0111 .name = "LINE-IN",
0112 },
0113 [EXTCON_JACK_LINE_OUT] = {
0114 .type = EXTCON_TYPE_JACK,
0115 .id = EXTCON_JACK_LINE_OUT,
0116 .name = "LINE-OUT",
0117 },
0118 [EXTCON_JACK_VIDEO_IN] = {
0119 .type = EXTCON_TYPE_JACK,
0120 .id = EXTCON_JACK_VIDEO_IN,
0121 .name = "VIDEO-IN",
0122 },
0123 [EXTCON_JACK_VIDEO_OUT] = {
0124 .type = EXTCON_TYPE_JACK,
0125 .id = EXTCON_JACK_VIDEO_OUT,
0126 .name = "VIDEO-OUT",
0127 },
0128 [EXTCON_JACK_SPDIF_IN] = {
0129 .type = EXTCON_TYPE_JACK,
0130 .id = EXTCON_JACK_SPDIF_IN,
0131 .name = "SPDIF-IN",
0132 },
0133 [EXTCON_JACK_SPDIF_OUT] = {
0134 .type = EXTCON_TYPE_JACK,
0135 .id = EXTCON_JACK_SPDIF_OUT,
0136 .name = "SPDIF-OUT",
0137 },
0138
0139
0140 [EXTCON_DISP_HDMI] = {
0141 .type = EXTCON_TYPE_DISP,
0142 .id = EXTCON_DISP_HDMI,
0143 .name = "HDMI",
0144 },
0145 [EXTCON_DISP_MHL] = {
0146 .type = EXTCON_TYPE_DISP,
0147 .id = EXTCON_DISP_MHL,
0148 .name = "MHL",
0149 },
0150 [EXTCON_DISP_DVI] = {
0151 .type = EXTCON_TYPE_DISP,
0152 .id = EXTCON_DISP_DVI,
0153 .name = "DVI",
0154 },
0155 [EXTCON_DISP_VGA] = {
0156 .type = EXTCON_TYPE_DISP,
0157 .id = EXTCON_DISP_VGA,
0158 .name = "VGA",
0159 },
0160 [EXTCON_DISP_DP] = {
0161 .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
0162 .id = EXTCON_DISP_DP,
0163 .name = "DP",
0164 },
0165 [EXTCON_DISP_HMD] = {
0166 .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
0167 .id = EXTCON_DISP_HMD,
0168 .name = "HMD",
0169 },
0170 [EXTCON_DISP_CVBS] = {
0171 .type = EXTCON_TYPE_DISP,
0172 .id = EXTCON_DISP_CVBS,
0173 .name = "CVBS",
0174 },
0175 [EXTCON_DISP_EDP] = {
0176 .type = EXTCON_TYPE_DISP,
0177 .id = EXTCON_DISP_EDP,
0178 .name = "EDP",
0179 },
0180
0181
0182 [EXTCON_DOCK] = {
0183 .type = EXTCON_TYPE_MISC,
0184 .id = EXTCON_DOCK,
0185 .name = "DOCK",
0186 },
0187 [EXTCON_JIG] = {
0188 .type = EXTCON_TYPE_MISC,
0189 .id = EXTCON_JIG,
0190 .name = "JIG",
0191 },
0192 [EXTCON_MECHANICAL] = {
0193 .type = EXTCON_TYPE_MISC,
0194 .id = EXTCON_MECHANICAL,
0195 .name = "MECHANICAL",
0196 },
0197
0198 { }
0199 };
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210 struct extcon_cable {
0211 struct extcon_dev *edev;
0212 int cable_index;
0213
0214 struct attribute_group attr_g;
0215 struct device_attribute attr_name;
0216 struct device_attribute attr_state;
0217
0218 struct attribute *attrs[3];
0219
0220 union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT];
0221 union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT];
0222 union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT];
0223 union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT];
0224
0225 unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)];
0226 unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)];
0227 unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)];
0228 unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)];
0229 };
0230
0231 static struct class *extcon_class;
0232
0233 static LIST_HEAD(extcon_dev_list);
0234 static DEFINE_MUTEX(extcon_dev_list_lock);
0235
0236 static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
0237 {
0238 int i = 0;
0239
0240 if (!edev->mutually_exclusive)
0241 return 0;
0242
0243 for (i = 0; edev->mutually_exclusive[i]; i++) {
0244 int weight;
0245 u32 correspondants = new_state & edev->mutually_exclusive[i];
0246
0247
0248 weight = hweight32(correspondants);
0249 if (weight > 1)
0250 return i + 1;
0251 }
0252
0253 return 0;
0254 }
0255
0256 static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id)
0257 {
0258 int i;
0259
0260
0261 for (i = 0; i < edev->max_supported; i++) {
0262 if (edev->supported_cable[i] == id)
0263 return i;
0264 }
0265
0266 return -EINVAL;
0267 }
0268
0269 static int get_extcon_type(unsigned int prop)
0270 {
0271 switch (prop) {
0272 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
0273 return EXTCON_TYPE_USB;
0274 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
0275 return EXTCON_TYPE_CHG;
0276 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
0277 return EXTCON_TYPE_JACK;
0278 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
0279 return EXTCON_TYPE_DISP;
0280 default:
0281 return -EINVAL;
0282 }
0283 }
0284
0285 static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index)
0286 {
0287 return !!(edev->state & BIT(index));
0288 }
0289
0290 static bool is_extcon_changed(struct extcon_dev *edev, int index,
0291 bool new_state)
0292 {
0293 int state = !!(edev->state & BIT(index));
0294 return (state != new_state);
0295 }
0296
0297 static bool is_extcon_property_supported(unsigned int id, unsigned int prop)
0298 {
0299 int type;
0300
0301
0302 type = get_extcon_type(prop);
0303 if (type < 0)
0304 return false;
0305
0306
0307 return !!(extcon_info[id].type & type);
0308 }
0309
0310 static int is_extcon_property_capability(struct extcon_dev *edev,
0311 unsigned int id, int index,unsigned int prop)
0312 {
0313 struct extcon_cable *cable;
0314 int type, ret;
0315
0316
0317 type = get_extcon_type(prop);
0318 if (type < 0)
0319 return type;
0320
0321 cable = &edev->cables[index];
0322
0323 switch (type) {
0324 case EXTCON_TYPE_USB:
0325 ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
0326 break;
0327 case EXTCON_TYPE_CHG:
0328 ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
0329 break;
0330 case EXTCON_TYPE_JACK:
0331 ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
0332 break;
0333 case EXTCON_TYPE_DISP:
0334 ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
0335 break;
0336 default:
0337 ret = -EINVAL;
0338 }
0339
0340 return ret;
0341 }
0342
0343 static void init_property(struct extcon_dev *edev, unsigned int id, int index)
0344 {
0345 unsigned int type = extcon_info[id].type;
0346 struct extcon_cable *cable = &edev->cables[index];
0347
0348 if (EXTCON_TYPE_USB & type)
0349 memset(cable->usb_propval, 0, sizeof(cable->usb_propval));
0350 if (EXTCON_TYPE_CHG & type)
0351 memset(cable->chg_propval, 0, sizeof(cable->chg_propval));
0352 if (EXTCON_TYPE_JACK & type)
0353 memset(cable->jack_propval, 0, sizeof(cable->jack_propval));
0354 if (EXTCON_TYPE_DISP & type)
0355 memset(cable->disp_propval, 0, sizeof(cable->disp_propval));
0356 }
0357
0358 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
0359 char *buf)
0360 {
0361 int i, count = 0;
0362 struct extcon_dev *edev = dev_get_drvdata(dev);
0363
0364 if (edev->max_supported == 0)
0365 return sprintf(buf, "%u\n", edev->state);
0366
0367 for (i = 0; i < edev->max_supported; i++) {
0368 count += sprintf(buf + count, "%s=%d\n",
0369 extcon_info[edev->supported_cable[i]].name,
0370 !!(edev->state & BIT(i)));
0371 }
0372
0373 return count;
0374 }
0375 static DEVICE_ATTR_RO(state);
0376
0377 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
0378 char *buf)
0379 {
0380 struct extcon_dev *edev = dev_get_drvdata(dev);
0381
0382 return sprintf(buf, "%s\n", edev->name);
0383 }
0384 static DEVICE_ATTR_RO(name);
0385
0386 static ssize_t cable_name_show(struct device *dev,
0387 struct device_attribute *attr, char *buf)
0388 {
0389 struct extcon_cable *cable = container_of(attr, struct extcon_cable,
0390 attr_name);
0391 int i = cable->cable_index;
0392
0393 return sprintf(buf, "%s\n",
0394 extcon_info[cable->edev->supported_cable[i]].name);
0395 }
0396
0397 static ssize_t cable_state_show(struct device *dev,
0398 struct device_attribute *attr, char *buf)
0399 {
0400 struct extcon_cable *cable = container_of(attr, struct extcon_cable,
0401 attr_state);
0402
0403 int i = cable->cable_index;
0404
0405 return sprintf(buf, "%d\n",
0406 extcon_get_state(cable->edev, cable->edev->supported_cable[i]));
0407 }
0408
0409
0410
0411
0412
0413
0414
0415
0416
0417
0418
0419 int extcon_sync(struct extcon_dev *edev, unsigned int id)
0420 {
0421 char name_buf[120];
0422 char state_buf[120];
0423 char *prop_buf;
0424 char *envp[3];
0425 int env_offset = 0;
0426 int length;
0427 int index;
0428 int state;
0429 unsigned long flags;
0430
0431 if (!edev)
0432 return -EINVAL;
0433
0434 index = find_cable_index_by_id(edev, id);
0435 if (index < 0)
0436 return index;
0437
0438 spin_lock_irqsave(&edev->lock, flags);
0439 state = !!(edev->state & BIT(index));
0440 spin_unlock_irqrestore(&edev->lock, flags);
0441
0442
0443
0444
0445
0446 raw_notifier_call_chain(&edev->nh[index], state, edev);
0447
0448
0449
0450
0451
0452 raw_notifier_call_chain(&edev->nh_all, state, edev);
0453
0454 spin_lock_irqsave(&edev->lock, flags);
0455
0456 prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
0457 if (!prop_buf) {
0458
0459 spin_unlock_irqrestore(&edev->lock, flags);
0460
0461 dev_err(&edev->dev, "out of memory in extcon_set_state\n");
0462 kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
0463
0464 return -ENOMEM;
0465 }
0466
0467 length = name_show(&edev->dev, NULL, prop_buf);
0468 if (length > 0) {
0469 if (prop_buf[length - 1] == '\n')
0470 prop_buf[length - 1] = 0;
0471 snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf);
0472 envp[env_offset++] = name_buf;
0473 }
0474
0475 length = state_show(&edev->dev, NULL, prop_buf);
0476 if (length > 0) {
0477 if (prop_buf[length - 1] == '\n')
0478 prop_buf[length - 1] = 0;
0479 snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf);
0480 envp[env_offset++] = state_buf;
0481 }
0482 envp[env_offset] = NULL;
0483
0484
0485 spin_unlock_irqrestore(&edev->lock, flags);
0486 kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
0487 free_page((unsigned long)prop_buf);
0488
0489 return 0;
0490 }
0491 EXPORT_SYMBOL_GPL(extcon_sync);
0492
0493
0494
0495
0496
0497
0498
0499
0500 int extcon_get_state(struct extcon_dev *edev, const unsigned int id)
0501 {
0502 int index, state;
0503 unsigned long flags;
0504
0505 if (!edev)
0506 return -EINVAL;
0507
0508 index = find_cable_index_by_id(edev, id);
0509 if (index < 0)
0510 return index;
0511
0512 spin_lock_irqsave(&edev->lock, flags);
0513 state = is_extcon_attached(edev, index);
0514 spin_unlock_irqrestore(&edev->lock, flags);
0515
0516 return state;
0517 }
0518 EXPORT_SYMBOL_GPL(extcon_get_state);
0519
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533 int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool state)
0534 {
0535 unsigned long flags;
0536 int index, ret = 0;
0537
0538 if (!edev)
0539 return -EINVAL;
0540
0541 index = find_cable_index_by_id(edev, id);
0542 if (index < 0)
0543 return index;
0544
0545 spin_lock_irqsave(&edev->lock, flags);
0546
0547
0548 if (!is_extcon_changed(edev, index, state))
0549 goto out;
0550
0551 if (check_mutually_exclusive(edev,
0552 (edev->state & ~BIT(index)) | (state & BIT(index)))) {
0553 ret = -EPERM;
0554 goto out;
0555 }
0556
0557
0558
0559
0560
0561 if (!state)
0562 init_property(edev, id, index);
0563
0564
0565 if (state)
0566 edev->state |= BIT(index);
0567 else
0568 edev->state &= ~(BIT(index));
0569 out:
0570 spin_unlock_irqrestore(&edev->lock, flags);
0571
0572 return ret;
0573 }
0574 EXPORT_SYMBOL_GPL(extcon_set_state);
0575
0576
0577
0578
0579
0580
0581
0582
0583
0584
0585
0586
0587
0588 int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state)
0589 {
0590 int ret;
0591
0592 ret = extcon_set_state(edev, id, state);
0593 if (ret < 0)
0594 return ret;
0595
0596 return extcon_sync(edev, id);
0597 }
0598 EXPORT_SYMBOL_GPL(extcon_set_state_sync);
0599
0600
0601
0602
0603
0604
0605
0606
0607
0608
0609
0610
0611
0612
0613
0614 int extcon_get_property(struct extcon_dev *edev, unsigned int id,
0615 unsigned int prop,
0616 union extcon_property_value *prop_val)
0617 {
0618 struct extcon_cable *cable;
0619 unsigned long flags;
0620 int index, ret = 0;
0621
0622 *prop_val = (union extcon_property_value){0};
0623
0624 if (!edev)
0625 return -EINVAL;
0626
0627
0628 if (!is_extcon_property_supported(id, prop))
0629 return -EINVAL;
0630
0631
0632 index = find_cable_index_by_id(edev, id);
0633 if (index < 0)
0634 return index;
0635
0636 spin_lock_irqsave(&edev->lock, flags);
0637
0638
0639 if (!is_extcon_property_capability(edev, id, index, prop)) {
0640 spin_unlock_irqrestore(&edev->lock, flags);
0641 return -EPERM;
0642 }
0643
0644
0645
0646
0647
0648
0649 if (!is_extcon_attached(edev, index)) {
0650 spin_unlock_irqrestore(&edev->lock, flags);
0651 return 0;
0652 }
0653
0654 cable = &edev->cables[index];
0655
0656
0657 switch (prop) {
0658 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
0659 *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN];
0660 break;
0661 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
0662 *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN];
0663 break;
0664 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
0665 *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN];
0666 break;
0667 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
0668 *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN];
0669 break;
0670 default:
0671 ret = -EINVAL;
0672 break;
0673 }
0674
0675 spin_unlock_irqrestore(&edev->lock, flags);
0676
0677 return ret;
0678 }
0679 EXPORT_SYMBOL_GPL(extcon_get_property);
0680
0681
0682
0683
0684
0685
0686
0687
0688
0689
0690
0691
0692
0693 int extcon_set_property(struct extcon_dev *edev, unsigned int id,
0694 unsigned int prop,
0695 union extcon_property_value prop_val)
0696 {
0697 struct extcon_cable *cable;
0698 unsigned long flags;
0699 int index, ret = 0;
0700
0701 if (!edev)
0702 return -EINVAL;
0703
0704
0705 if (!is_extcon_property_supported(id, prop))
0706 return -EINVAL;
0707
0708
0709 index = find_cable_index_by_id(edev, id);
0710 if (index < 0)
0711 return index;
0712
0713 spin_lock_irqsave(&edev->lock, flags);
0714
0715
0716 if (!is_extcon_property_capability(edev, id, index, prop)) {
0717 spin_unlock_irqrestore(&edev->lock, flags);
0718 return -EPERM;
0719 }
0720
0721 cable = &edev->cables[index];
0722
0723
0724 switch (prop) {
0725 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
0726 cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val;
0727 break;
0728 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
0729 cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val;
0730 break;
0731 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
0732 cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val;
0733 break;
0734 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
0735 cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val;
0736 break;
0737 default:
0738 ret = -EINVAL;
0739 break;
0740 }
0741
0742 spin_unlock_irqrestore(&edev->lock, flags);
0743
0744 return ret;
0745 }
0746 EXPORT_SYMBOL_GPL(extcon_set_property);
0747
0748
0749
0750
0751
0752
0753
0754
0755
0756
0757
0758
0759
0760
0761 int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id,
0762 unsigned int prop,
0763 union extcon_property_value prop_val)
0764 {
0765 int ret;
0766
0767 ret = extcon_set_property(edev, id, prop, prop_val);
0768 if (ret < 0)
0769 return ret;
0770
0771 return extcon_sync(edev, id);
0772 }
0773 EXPORT_SYMBOL_GPL(extcon_set_property_sync);
0774
0775
0776
0777
0778
0779
0780
0781
0782
0783
0784 int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id,
0785 unsigned int prop)
0786 {
0787 int index;
0788
0789 if (!edev)
0790 return -EINVAL;
0791
0792
0793 if (!is_extcon_property_supported(id, prop))
0794 return -EINVAL;
0795
0796
0797 index = find_cable_index_by_id(edev, id);
0798 if (index < 0)
0799 return index;
0800
0801 return is_extcon_property_capability(edev, id, index, prop);
0802 }
0803 EXPORT_SYMBOL_GPL(extcon_get_property_capability);
0804
0805
0806
0807
0808
0809
0810
0811
0812
0813
0814
0815
0816
0817
0818 int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id,
0819 unsigned int prop)
0820 {
0821 struct extcon_cable *cable;
0822 int index, type, ret = 0;
0823
0824 if (!edev)
0825 return -EINVAL;
0826
0827
0828 if (!is_extcon_property_supported(id, prop))
0829 return -EINVAL;
0830
0831
0832 index = find_cable_index_by_id(edev, id);
0833 if (index < 0)
0834 return index;
0835
0836 type = get_extcon_type(prop);
0837 if (type < 0)
0838 return type;
0839
0840 cable = &edev->cables[index];
0841
0842 switch (type) {
0843 case EXTCON_TYPE_USB:
0844 __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
0845 break;
0846 case EXTCON_TYPE_CHG:
0847 __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
0848 break;
0849 case EXTCON_TYPE_JACK:
0850 __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
0851 break;
0852 case EXTCON_TYPE_DISP:
0853 __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
0854 break;
0855 default:
0856 ret = -EINVAL;
0857 }
0858
0859 return ret;
0860 }
0861 EXPORT_SYMBOL_GPL(extcon_set_property_capability);
0862
0863
0864
0865
0866
0867
0868
0869
0870
0871 struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
0872 {
0873 struct extcon_dev *sd;
0874
0875 if (!extcon_name)
0876 return ERR_PTR(-EINVAL);
0877
0878 mutex_lock(&extcon_dev_list_lock);
0879 list_for_each_entry(sd, &extcon_dev_list, entry) {
0880 if (!strcmp(sd->name, extcon_name))
0881 goto out;
0882 }
0883 sd = ERR_PTR(-EPROBE_DEFER);
0884 out:
0885 mutex_unlock(&extcon_dev_list_lock);
0886 return sd;
0887 }
0888 EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
0889
0890
0891
0892
0893
0894
0895
0896
0897
0898
0899
0900
0901
0902
0903 int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
0904 struct notifier_block *nb)
0905 {
0906 unsigned long flags;
0907 int ret, idx;
0908
0909 if (!edev || !nb)
0910 return -EINVAL;
0911
0912 idx = find_cable_index_by_id(edev, id);
0913 if (idx < 0)
0914 return idx;
0915
0916 spin_lock_irqsave(&edev->lock, flags);
0917 ret = raw_notifier_chain_register(&edev->nh[idx], nb);
0918 spin_unlock_irqrestore(&edev->lock, flags);
0919
0920 return ret;
0921 }
0922 EXPORT_SYMBOL_GPL(extcon_register_notifier);
0923
0924
0925
0926
0927
0928
0929
0930
0931
0932 int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
0933 struct notifier_block *nb)
0934 {
0935 unsigned long flags;
0936 int ret, idx;
0937
0938 if (!edev || !nb)
0939 return -EINVAL;
0940
0941 idx = find_cable_index_by_id(edev, id);
0942 if (idx < 0)
0943 return idx;
0944
0945 spin_lock_irqsave(&edev->lock, flags);
0946 ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
0947 spin_unlock_irqrestore(&edev->lock, flags);
0948
0949 return ret;
0950 }
0951 EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
0952
0953
0954
0955
0956
0957
0958
0959
0960
0961
0962
0963
0964
0965 int extcon_register_notifier_all(struct extcon_dev *edev,
0966 struct notifier_block *nb)
0967 {
0968 unsigned long flags;
0969 int ret;
0970
0971 if (!edev || !nb)
0972 return -EINVAL;
0973
0974 spin_lock_irqsave(&edev->lock, flags);
0975 ret = raw_notifier_chain_register(&edev->nh_all, nb);
0976 spin_unlock_irqrestore(&edev->lock, flags);
0977
0978 return ret;
0979 }
0980 EXPORT_SYMBOL_GPL(extcon_register_notifier_all);
0981
0982
0983
0984
0985
0986
0987
0988
0989 int extcon_unregister_notifier_all(struct extcon_dev *edev,
0990 struct notifier_block *nb)
0991 {
0992 unsigned long flags;
0993 int ret;
0994
0995 if (!edev || !nb)
0996 return -EINVAL;
0997
0998 spin_lock_irqsave(&edev->lock, flags);
0999 ret = raw_notifier_chain_unregister(&edev->nh_all, nb);
1000 spin_unlock_irqrestore(&edev->lock, flags);
1001
1002 return ret;
1003 }
1004 EXPORT_SYMBOL_GPL(extcon_unregister_notifier_all);
1005
1006 static struct attribute *extcon_attrs[] = {
1007 &dev_attr_state.attr,
1008 &dev_attr_name.attr,
1009 NULL,
1010 };
1011 ATTRIBUTE_GROUPS(extcon);
1012
1013 static int create_extcon_class(void)
1014 {
1015 if (!extcon_class) {
1016 extcon_class = class_create(THIS_MODULE, "extcon");
1017 if (IS_ERR(extcon_class))
1018 return PTR_ERR(extcon_class);
1019 extcon_class->dev_groups = extcon_groups;
1020 }
1021
1022 return 0;
1023 }
1024
1025 static void extcon_dev_release(struct device *dev)
1026 {
1027 }
1028
1029 static const char *muex_name = "mutually_exclusive";
1030 static void dummy_sysfs_dev_release(struct device *dev)
1031 {
1032 }
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045 struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable)
1046 {
1047 struct extcon_dev *edev;
1048
1049 if (!supported_cable)
1050 return ERR_PTR(-EINVAL);
1051
1052 edev = kzalloc(sizeof(*edev), GFP_KERNEL);
1053 if (!edev)
1054 return ERR_PTR(-ENOMEM);
1055
1056 edev->max_supported = 0;
1057 edev->supported_cable = supported_cable;
1058
1059 return edev;
1060 }
1061
1062
1063
1064
1065
1066 void extcon_dev_free(struct extcon_dev *edev)
1067 {
1068 kfree(edev);
1069 }
1070 EXPORT_SYMBOL_GPL(extcon_dev_free);
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086 int extcon_dev_register(struct extcon_dev *edev)
1087 {
1088 int ret, index = 0;
1089 static atomic_t edev_no = ATOMIC_INIT(-1);
1090
1091 if (!extcon_class) {
1092 ret = create_extcon_class();
1093 if (ret < 0)
1094 return ret;
1095 }
1096
1097 if (!edev || !edev->supported_cable)
1098 return -EINVAL;
1099
1100 for (; edev->supported_cable[index] != EXTCON_NONE; index++);
1101
1102 edev->max_supported = index;
1103 if (index > SUPPORTED_CABLE_MAX) {
1104 dev_err(&edev->dev,
1105 "exceed the maximum number of supported cables\n");
1106 return -EINVAL;
1107 }
1108
1109 edev->dev.class = extcon_class;
1110 edev->dev.release = extcon_dev_release;
1111
1112 edev->name = dev_name(edev->dev.parent);
1113 if (IS_ERR_OR_NULL(edev->name)) {
1114 dev_err(&edev->dev,
1115 "extcon device name is null\n");
1116 return -EINVAL;
1117 }
1118 dev_set_name(&edev->dev, "extcon%lu",
1119 (unsigned long)atomic_inc_return(&edev_no));
1120
1121 if (edev->max_supported) {
1122 char *str;
1123 struct extcon_cable *cable;
1124
1125 edev->cables = kcalloc(edev->max_supported,
1126 sizeof(struct extcon_cable),
1127 GFP_KERNEL);
1128 if (!edev->cables) {
1129 ret = -ENOMEM;
1130 goto err_sysfs_alloc;
1131 }
1132 for (index = 0; index < edev->max_supported; index++) {
1133 cable = &edev->cables[index];
1134
1135 str = kasprintf(GFP_KERNEL, "cable.%d", index);
1136 if (!str) {
1137 for (index--; index >= 0; index--) {
1138 cable = &edev->cables[index];
1139 kfree(cable->attr_g.name);
1140 }
1141 ret = -ENOMEM;
1142
1143 goto err_alloc_cables;
1144 }
1145
1146 cable->edev = edev;
1147 cable->cable_index = index;
1148 cable->attrs[0] = &cable->attr_name.attr;
1149 cable->attrs[1] = &cable->attr_state.attr;
1150 cable->attrs[2] = NULL;
1151 cable->attr_g.name = str;
1152 cable->attr_g.attrs = cable->attrs;
1153
1154 sysfs_attr_init(&cable->attr_name.attr);
1155 cable->attr_name.attr.name = "name";
1156 cable->attr_name.attr.mode = 0444;
1157 cable->attr_name.show = cable_name_show;
1158
1159 sysfs_attr_init(&cable->attr_state.attr);
1160 cable->attr_state.attr.name = "state";
1161 cable->attr_state.attr.mode = 0444;
1162 cable->attr_state.show = cable_state_show;
1163 }
1164 }
1165
1166 if (edev->max_supported && edev->mutually_exclusive) {
1167 char *name;
1168
1169
1170 for (index = 0; edev->mutually_exclusive[index]; index++)
1171 ;
1172
1173 edev->attrs_muex = kcalloc(index + 1,
1174 sizeof(struct attribute *),
1175 GFP_KERNEL);
1176 if (!edev->attrs_muex) {
1177 ret = -ENOMEM;
1178 goto err_muex;
1179 }
1180
1181 edev->d_attrs_muex = kcalloc(index,
1182 sizeof(struct device_attribute),
1183 GFP_KERNEL);
1184 if (!edev->d_attrs_muex) {
1185 ret = -ENOMEM;
1186 kfree(edev->attrs_muex);
1187 goto err_muex;
1188 }
1189
1190 for (index = 0; edev->mutually_exclusive[index]; index++) {
1191 name = kasprintf(GFP_KERNEL, "0x%x",
1192 edev->mutually_exclusive[index]);
1193 if (!name) {
1194 for (index--; index >= 0; index--) {
1195 kfree(edev->d_attrs_muex[index].attr.
1196 name);
1197 }
1198 kfree(edev->d_attrs_muex);
1199 kfree(edev->attrs_muex);
1200 ret = -ENOMEM;
1201 goto err_muex;
1202 }
1203 sysfs_attr_init(&edev->d_attrs_muex[index].attr);
1204 edev->d_attrs_muex[index].attr.name = name;
1205 edev->d_attrs_muex[index].attr.mode = 0000;
1206 edev->attrs_muex[index] = &edev->d_attrs_muex[index]
1207 .attr;
1208 }
1209 edev->attr_g_muex.name = muex_name;
1210 edev->attr_g_muex.attrs = edev->attrs_muex;
1211
1212 }
1213
1214 if (edev->max_supported) {
1215 edev->extcon_dev_type.groups =
1216 kcalloc(edev->max_supported + 2,
1217 sizeof(struct attribute_group *),
1218 GFP_KERNEL);
1219 if (!edev->extcon_dev_type.groups) {
1220 ret = -ENOMEM;
1221 goto err_alloc_groups;
1222 }
1223
1224 edev->extcon_dev_type.name = dev_name(&edev->dev);
1225 edev->extcon_dev_type.release = dummy_sysfs_dev_release;
1226
1227 for (index = 0; index < edev->max_supported; index++)
1228 edev->extcon_dev_type.groups[index] =
1229 &edev->cables[index].attr_g;
1230 if (edev->mutually_exclusive)
1231 edev->extcon_dev_type.groups[index] =
1232 &edev->attr_g_muex;
1233
1234 edev->dev.type = &edev->extcon_dev_type;
1235 }
1236
1237 spin_lock_init(&edev->lock);
1238 if (edev->max_supported) {
1239 edev->nh = kcalloc(edev->max_supported, sizeof(*edev->nh),
1240 GFP_KERNEL);
1241 if (!edev->nh) {
1242 ret = -ENOMEM;
1243 goto err_alloc_nh;
1244 }
1245 }
1246
1247 for (index = 0; index < edev->max_supported; index++)
1248 RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
1249
1250 RAW_INIT_NOTIFIER_HEAD(&edev->nh_all);
1251
1252 dev_set_drvdata(&edev->dev, edev);
1253 edev->state = 0;
1254
1255 ret = device_register(&edev->dev);
1256 if (ret) {
1257 put_device(&edev->dev);
1258 goto err_dev;
1259 }
1260
1261 mutex_lock(&extcon_dev_list_lock);
1262 list_add(&edev->entry, &extcon_dev_list);
1263 mutex_unlock(&extcon_dev_list_lock);
1264
1265 return 0;
1266
1267 err_dev:
1268 if (edev->max_supported)
1269 kfree(edev->nh);
1270 err_alloc_nh:
1271 if (edev->max_supported)
1272 kfree(edev->extcon_dev_type.groups);
1273 err_alloc_groups:
1274 if (edev->max_supported && edev->mutually_exclusive) {
1275 for (index = 0; edev->mutually_exclusive[index]; index++)
1276 kfree(edev->d_attrs_muex[index].attr.name);
1277 kfree(edev->d_attrs_muex);
1278 kfree(edev->attrs_muex);
1279 }
1280 err_muex:
1281 for (index = 0; index < edev->max_supported; index++)
1282 kfree(edev->cables[index].attr_g.name);
1283 err_alloc_cables:
1284 if (edev->max_supported)
1285 kfree(edev->cables);
1286 err_sysfs_alloc:
1287 return ret;
1288 }
1289 EXPORT_SYMBOL_GPL(extcon_dev_register);
1290
1291
1292
1293
1294
1295
1296
1297
1298 void extcon_dev_unregister(struct extcon_dev *edev)
1299 {
1300 int index;
1301
1302 if (!edev)
1303 return;
1304
1305 mutex_lock(&extcon_dev_list_lock);
1306 list_del(&edev->entry);
1307 mutex_unlock(&extcon_dev_list_lock);
1308
1309 if (IS_ERR_OR_NULL(get_device(&edev->dev))) {
1310 dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n",
1311 dev_name(&edev->dev));
1312 return;
1313 }
1314
1315 device_unregister(&edev->dev);
1316
1317 if (edev->mutually_exclusive && edev->max_supported) {
1318 for (index = 0; edev->mutually_exclusive[index];
1319 index++)
1320 kfree(edev->d_attrs_muex[index].attr.name);
1321 kfree(edev->d_attrs_muex);
1322 kfree(edev->attrs_muex);
1323 }
1324
1325 for (index = 0; index < edev->max_supported; index++)
1326 kfree(edev->cables[index].attr_g.name);
1327
1328 if (edev->max_supported) {
1329 kfree(edev->extcon_dev_type.groups);
1330 kfree(edev->cables);
1331 kfree(edev->nh);
1332 }
1333
1334 put_device(&edev->dev);
1335 }
1336 EXPORT_SYMBOL_GPL(extcon_dev_unregister);
1337
1338 #ifdef CONFIG_OF
1339
1340
1341
1342
1343
1344
1345
1346 struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
1347 {
1348 struct extcon_dev *edev;
1349
1350 mutex_lock(&extcon_dev_list_lock);
1351 list_for_each_entry(edev, &extcon_dev_list, entry)
1352 if (edev->dev.parent && edev->dev.parent->of_node == node)
1353 goto out;
1354 edev = ERR_PTR(-EPROBE_DEFER);
1355 out:
1356 mutex_unlock(&extcon_dev_list_lock);
1357
1358 return edev;
1359 }
1360
1361
1362
1363
1364
1365
1366
1367
1368 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1369 {
1370 struct device_node *node;
1371 struct extcon_dev *edev;
1372
1373 if (!dev)
1374 return ERR_PTR(-EINVAL);
1375
1376 if (!dev->of_node) {
1377 dev_dbg(dev, "device does not have a device node entry\n");
1378 return ERR_PTR(-EINVAL);
1379 }
1380
1381 node = of_parse_phandle(dev->of_node, "extcon", index);
1382 if (!node) {
1383 dev_dbg(dev, "failed to get phandle in %pOF node\n",
1384 dev->of_node);
1385 return ERR_PTR(-ENODEV);
1386 }
1387
1388 edev = extcon_find_edev_by_node(node);
1389 of_node_put(node);
1390
1391 return edev;
1392 }
1393
1394 #else
1395
1396 struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
1397 {
1398 return ERR_PTR(-ENOSYS);
1399 }
1400
1401 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
1402 {
1403 return ERR_PTR(-ENOSYS);
1404 }
1405
1406 #endif
1407
1408 EXPORT_SYMBOL_GPL(extcon_find_edev_by_node);
1409 EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
1410
1411
1412
1413
1414
1415 const char *extcon_get_edev_name(struct extcon_dev *edev)
1416 {
1417 return !edev ? NULL : edev->name;
1418 }
1419 EXPORT_SYMBOL_GPL(extcon_get_edev_name);
1420
1421 static int __init extcon_class_init(void)
1422 {
1423 return create_extcon_class();
1424 }
1425 module_init(extcon_class_init);
1426
1427 static void __exit extcon_class_exit(void)
1428 {
1429 class_destroy(extcon_class);
1430 }
1431 module_exit(extcon_class_exit);
1432
1433 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
1434 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
1435 MODULE_DESCRIPTION("External Connector (extcon) framework");
1436 MODULE_LICENSE("GPL v2");