1 /* 2 * drivers/extcon/extcon.c - External Connector (extcon) framework. 3 * 4 * Copyright (C) 2015 Samsung Electronics 5 * Author: Chanwoo Choi <cw00.choi@samsung.com> 6 * 7 * Copyright (C) 2012 Samsung Electronics 8 * Author: Donggeun Kim <dg77.kim@samsung.com> 9 * Author: MyungJoo Ham <myungjoo.ham@samsung.com> 10 * 11 * based on android/drivers/switch/switch_class.c 12 * Copyright (C) 2008 Google, Inc. 13 * Author: Mike Lockwood <lockwood@android.com> 14 * 15 * This software is licensed under the terms of the GNU General Public 16 * License version 2, as published by the Free Software Foundation, and 17 * may be copied, distributed, and modified under those terms. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 */ 24 25 #include <linux/module.h> 26 #include <linux/types.h> 27 #include <linux/init.h> 28 #include <linux/device.h> 29 #include <linux/fs.h> 30 #include <linux/err.h> 31 #include <linux/of.h> 32 #include <linux/slab.h> 33 #include <linux/sysfs.h> 34 35 #include "extcon.h" 36 37 #define SUPPORTED_CABLE_MAX 32 38 39 static const struct __extcon_info { 40 unsigned int type; 41 unsigned int id; 42 const char *name; 43 44 } extcon_info[] = { 45 [EXTCON_NONE] = { 46 .type = EXTCON_TYPE_MISC, 47 .id = EXTCON_NONE, 48 .name = "NONE", 49 }, 50 51 /* USB external connector */ 52 [EXTCON_USB] = { 53 .type = EXTCON_TYPE_USB, 54 .id = EXTCON_USB, 55 .name = "USB", 56 }, 57 [EXTCON_USB_HOST] = { 58 .type = EXTCON_TYPE_USB, 59 .id = EXTCON_USB_HOST, 60 .name = "USB-HOST", 61 }, 62 63 /* Charging external connector */ 64 [EXTCON_CHG_USB_SDP] = { 65 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 66 .id = EXTCON_CHG_USB_SDP, 67 .name = "SDP", 68 }, 69 [EXTCON_CHG_USB_DCP] = { 70 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 71 .id = EXTCON_CHG_USB_DCP, 72 .name = "DCP", 73 }, 74 [EXTCON_CHG_USB_CDP] = { 75 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 76 .id = EXTCON_CHG_USB_CDP, 77 .name = "CDP", 78 }, 79 [EXTCON_CHG_USB_ACA] = { 80 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 81 .id = EXTCON_CHG_USB_ACA, 82 .name = "ACA", 83 }, 84 [EXTCON_CHG_USB_FAST] = { 85 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 86 .id = EXTCON_CHG_USB_FAST, 87 .name = "FAST-CHARGER", 88 }, 89 [EXTCON_CHG_USB_SLOW] = { 90 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 91 .id = EXTCON_CHG_USB_SLOW, 92 .name = "SLOW-CHARGER", 93 }, 94 [EXTCON_CHG_WPT] = { 95 .type = EXTCON_TYPE_CHG, 96 .id = EXTCON_CHG_WPT, 97 .name = "WPT", 98 }, 99 [EXTCON_CHG_USB_PD] = { 100 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 101 .id = EXTCON_CHG_USB_PD, 102 .name = "PD", 103 }, 104 105 /* Jack external connector */ 106 [EXTCON_JACK_MICROPHONE] = { 107 .type = EXTCON_TYPE_JACK, 108 .id = EXTCON_JACK_MICROPHONE, 109 .name = "MICROPHONE", 110 }, 111 [EXTCON_JACK_HEADPHONE] = { 112 .type = EXTCON_TYPE_JACK, 113 .id = EXTCON_JACK_HEADPHONE, 114 .name = "HEADPHONE", 115 }, 116 [EXTCON_JACK_LINE_IN] = { 117 .type = EXTCON_TYPE_JACK, 118 .id = EXTCON_JACK_LINE_IN, 119 .name = "LINE-IN", 120 }, 121 [EXTCON_JACK_LINE_OUT] = { 122 .type = EXTCON_TYPE_JACK, 123 .id = EXTCON_JACK_LINE_OUT, 124 .name = "LINE-OUT", 125 }, 126 [EXTCON_JACK_VIDEO_IN] = { 127 .type = EXTCON_TYPE_JACK, 128 .id = EXTCON_JACK_VIDEO_IN, 129 .name = "VIDEO-IN", 130 }, 131 [EXTCON_JACK_VIDEO_OUT] = { 132 .type = EXTCON_TYPE_JACK, 133 .id = EXTCON_JACK_VIDEO_OUT, 134 .name = "VIDEO-OUT", 135 }, 136 [EXTCON_JACK_SPDIF_IN] = { 137 .type = EXTCON_TYPE_JACK, 138 .id = EXTCON_JACK_SPDIF_IN, 139 .name = "SPDIF-IN", 140 }, 141 [EXTCON_JACK_SPDIF_OUT] = { 142 .type = EXTCON_TYPE_JACK, 143 .id = EXTCON_JACK_SPDIF_OUT, 144 .name = "SPDIF-OUT", 145 }, 146 147 /* Display external connector */ 148 [EXTCON_DISP_HDMI] = { 149 .type = EXTCON_TYPE_DISP, 150 .id = EXTCON_DISP_HDMI, 151 .name = "HDMI", 152 }, 153 [EXTCON_DISP_MHL] = { 154 .type = EXTCON_TYPE_DISP, 155 .id = EXTCON_DISP_MHL, 156 .name = "MHL", 157 }, 158 [EXTCON_DISP_DVI] = { 159 .type = EXTCON_TYPE_DISP, 160 .id = EXTCON_DISP_DVI, 161 .name = "DVI", 162 }, 163 [EXTCON_DISP_VGA] = { 164 .type = EXTCON_TYPE_DISP, 165 .id = EXTCON_DISP_VGA, 166 .name = "VGA", 167 }, 168 [EXTCON_DISP_DP] = { 169 .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB, 170 .id = EXTCON_DISP_DP, 171 .name = "DP", 172 }, 173 [EXTCON_DISP_HMD] = { 174 .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB, 175 .id = EXTCON_DISP_HMD, 176 .name = "HMD", 177 }, 178 179 /* Miscellaneous external connector */ 180 [EXTCON_DOCK] = { 181 .type = EXTCON_TYPE_MISC, 182 .id = EXTCON_DOCK, 183 .name = "DOCK", 184 }, 185 [EXTCON_JIG] = { 186 .type = EXTCON_TYPE_MISC, 187 .id = EXTCON_JIG, 188 .name = "JIG", 189 }, 190 [EXTCON_MECHANICAL] = { 191 .type = EXTCON_TYPE_MISC, 192 .id = EXTCON_MECHANICAL, 193 .name = "MECHANICAL", 194 }, 195 196 { /* sentinel */ } 197 }; 198 199 /** 200 * struct extcon_cable - An internal data for an external connector. 201 * @edev: the extcon device 202 * @cable_index: the index of this cable in the edev 203 * @attr_g: the attribute group for the cable 204 * @attr_name: "name" sysfs entry 205 * @attr_state: "state" sysfs entry 206 * @attrs: the array pointing to attr_name and attr_state for attr_g 207 */ 208 struct extcon_cable { 209 struct extcon_dev *edev; 210 int cable_index; 211 212 struct attribute_group attr_g; 213 struct device_attribute attr_name; 214 struct device_attribute attr_state; 215 216 struct attribute *attrs[3]; /* to be fed to attr_g.attrs */ 217 218 union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT]; 219 union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT]; 220 union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT]; 221 union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT]; 222 223 unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)]; 224 unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)]; 225 unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)]; 226 unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)]; 227 }; 228 229 static struct class *extcon_class; 230 231 static LIST_HEAD(extcon_dev_list); 232 static DEFINE_MUTEX(extcon_dev_list_lock); 233 234 static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state) 235 { 236 int i = 0; 237 238 if (!edev->mutually_exclusive) 239 return 0; 240 241 for (i = 0; edev->mutually_exclusive[i]; i++) { 242 int weight; 243 u32 correspondants = new_state & edev->mutually_exclusive[i]; 244 245 /* calculate the total number of bits set */ 246 weight = hweight32(correspondants); 247 if (weight > 1) 248 return i + 1; 249 } 250 251 return 0; 252 } 253 254 static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id) 255 { 256 int i; 257 258 /* Find the the index of extcon cable in edev->supported_cable */ 259 for (i = 0; i < edev->max_supported; i++) { 260 if (edev->supported_cable[i] == id) 261 return i; 262 } 263 264 return -EINVAL; 265 } 266 267 static int get_extcon_type(unsigned int prop) 268 { 269 switch (prop) { 270 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: 271 return EXTCON_TYPE_USB; 272 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: 273 return EXTCON_TYPE_CHG; 274 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: 275 return EXTCON_TYPE_JACK; 276 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: 277 return EXTCON_TYPE_DISP; 278 default: 279 return -EINVAL; 280 } 281 } 282 283 static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index) 284 { 285 return !!(edev->state & BIT(index)); 286 } 287 288 static bool is_extcon_changed(struct extcon_dev *edev, int index, 289 bool new_state) 290 { 291 int state = !!(edev->state & BIT(index)); 292 return (state != new_state); 293 } 294 295 static bool is_extcon_property_supported(unsigned int id, unsigned int prop) 296 { 297 int type; 298 299 /* Check whether the property is supported or not. */ 300 type = get_extcon_type(prop); 301 if (type < 0) 302 return false; 303 304 /* Check whether a specific extcon id supports the property or not. */ 305 return !!(extcon_info[id].type & type); 306 } 307 308 static int is_extcon_property_capability(struct extcon_dev *edev, 309 unsigned int id, int index,unsigned int prop) 310 { 311 struct extcon_cable *cable; 312 int type, ret; 313 314 /* Check whether the property is supported or not. */ 315 type = get_extcon_type(prop); 316 if (type < 0) 317 return type; 318 319 cable = &edev->cables[index]; 320 321 switch (type) { 322 case EXTCON_TYPE_USB: 323 ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits); 324 break; 325 case EXTCON_TYPE_CHG: 326 ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits); 327 break; 328 case EXTCON_TYPE_JACK: 329 ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits); 330 break; 331 case EXTCON_TYPE_DISP: 332 ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits); 333 break; 334 default: 335 ret = -EINVAL; 336 } 337 338 return ret; 339 } 340 341 static void init_property(struct extcon_dev *edev, unsigned int id, int index) 342 { 343 unsigned int type = extcon_info[id].type; 344 struct extcon_cable *cable = &edev->cables[index]; 345 346 if (EXTCON_TYPE_USB & type) 347 memset(cable->usb_propval, 0, sizeof(cable->usb_propval)); 348 if (EXTCON_TYPE_CHG & type) 349 memset(cable->chg_propval, 0, sizeof(cable->chg_propval)); 350 if (EXTCON_TYPE_JACK & type) 351 memset(cable->jack_propval, 0, sizeof(cable->jack_propval)); 352 if (EXTCON_TYPE_DISP & type) 353 memset(cable->disp_propval, 0, sizeof(cable->disp_propval)); 354 } 355 356 static ssize_t state_show(struct device *dev, struct device_attribute *attr, 357 char *buf) 358 { 359 int i, count = 0; 360 struct extcon_dev *edev = dev_get_drvdata(dev); 361 362 if (edev->max_supported == 0) 363 return sprintf(buf, "%u\n", edev->state); 364 365 for (i = 0; i < edev->max_supported; i++) { 366 count += sprintf(buf + count, "%s=%d\n", 367 extcon_info[edev->supported_cable[i]].name, 368 !!(edev->state & BIT(i))); 369 } 370 371 return count; 372 } 373 static DEVICE_ATTR_RO(state); 374 375 static ssize_t name_show(struct device *dev, struct device_attribute *attr, 376 char *buf) 377 { 378 struct extcon_dev *edev = dev_get_drvdata(dev); 379 380 return sprintf(buf, "%s\n", edev->name); 381 } 382 static DEVICE_ATTR_RO(name); 383 384 static ssize_t cable_name_show(struct device *dev, 385 struct device_attribute *attr, char *buf) 386 { 387 struct extcon_cable *cable = container_of(attr, struct extcon_cable, 388 attr_name); 389 int i = cable->cable_index; 390 391 return sprintf(buf, "%s\n", 392 extcon_info[cable->edev->supported_cable[i]].name); 393 } 394 395 static ssize_t cable_state_show(struct device *dev, 396 struct device_attribute *attr, char *buf) 397 { 398 struct extcon_cable *cable = container_of(attr, struct extcon_cable, 399 attr_state); 400 401 int i = cable->cable_index; 402 403 return sprintf(buf, "%d\n", 404 extcon_get_state(cable->edev, cable->edev->supported_cable[i])); 405 } 406 407 /** 408 * extcon_sync() - Synchronize the state for an external connector. 409 * @edev: the extcon device 410 * 411 * Note that this function send a notification in order to synchronize 412 * the state and property of an external connector. 413 * 414 * Returns 0 if success or error number if fail. 415 */ 416 int extcon_sync(struct extcon_dev *edev, unsigned int id) 417 { 418 char name_buf[120]; 419 char state_buf[120]; 420 char *prop_buf; 421 char *envp[3]; 422 int env_offset = 0; 423 int length; 424 int index; 425 int state; 426 unsigned long flags; 427 428 if (!edev) 429 return -EINVAL; 430 431 index = find_cable_index_by_id(edev, id); 432 if (index < 0) 433 return index; 434 435 spin_lock_irqsave(&edev->lock, flags); 436 state = !!(edev->state & BIT(index)); 437 spin_unlock_irqrestore(&edev->lock, flags); 438 439 /* 440 * Call functions in a raw notifier chain for the specific one 441 * external connector. 442 */ 443 raw_notifier_call_chain(&edev->nh[index], state, edev); 444 445 /* 446 * Call functions in a raw notifier chain for the all supported 447 * external connectors. 448 */ 449 raw_notifier_call_chain(&edev->nh_all, state, edev); 450 451 spin_lock_irqsave(&edev->lock, flags); 452 /* This could be in interrupt handler */ 453 prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); 454 if (!prop_buf) { 455 /* Unlock early before uevent */ 456 spin_unlock_irqrestore(&edev->lock, flags); 457 458 dev_err(&edev->dev, "out of memory in extcon_set_state\n"); 459 kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE); 460 461 return -ENOMEM; 462 } 463 464 length = name_show(&edev->dev, NULL, prop_buf); 465 if (length > 0) { 466 if (prop_buf[length - 1] == '\n') 467 prop_buf[length - 1] = 0; 468 snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf); 469 envp[env_offset++] = name_buf; 470 } 471 472 length = state_show(&edev->dev, NULL, prop_buf); 473 if (length > 0) { 474 if (prop_buf[length - 1] == '\n') 475 prop_buf[length - 1] = 0; 476 snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf); 477 envp[env_offset++] = state_buf; 478 } 479 envp[env_offset] = NULL; 480 481 /* Unlock early before uevent */ 482 spin_unlock_irqrestore(&edev->lock, flags); 483 kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp); 484 free_page((unsigned long)prop_buf); 485 486 return 0; 487 } 488 EXPORT_SYMBOL_GPL(extcon_sync); 489 490 /** 491 * extcon_get_state() - Get the state of an external connector. 492 * @edev: the extcon device 493 * @id: the unique id indicating an external connector 494 * 495 * Returns 0 if success or error number if fail. 496 */ 497 int extcon_get_state(struct extcon_dev *edev, const unsigned int id) 498 { 499 int index, state; 500 unsigned long flags; 501 502 if (!edev) 503 return -EINVAL; 504 505 index = find_cable_index_by_id(edev, id); 506 if (index < 0) 507 return index; 508 509 spin_lock_irqsave(&edev->lock, flags); 510 state = is_extcon_attached(edev, index); 511 spin_unlock_irqrestore(&edev->lock, flags); 512 513 return state; 514 } 515 EXPORT_SYMBOL_GPL(extcon_get_state); 516 517 /** 518 * extcon_set_state() - Set the state of an external connector. 519 * @edev: the extcon device 520 * @id: the unique id indicating an external connector 521 * @state: the new state of an external connector. 522 * the default semantics is true: attached / false: detached. 523 * 524 * Note that this function set the state of an external connector without 525 * a notification. To synchronize the state of an external connector, 526 * have to use extcon_set_state_sync() and extcon_sync(). 527 * 528 * Returns 0 if success or error number if fail. 529 */ 530 int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool state) 531 { 532 unsigned long flags; 533 int index, ret = 0; 534 535 if (!edev) 536 return -EINVAL; 537 538 index = find_cable_index_by_id(edev, id); 539 if (index < 0) 540 return index; 541 542 spin_lock_irqsave(&edev->lock, flags); 543 544 /* Check whether the external connector's state is changed. */ 545 if (!is_extcon_changed(edev, index, state)) 546 goto out; 547 548 if (check_mutually_exclusive(edev, 549 (edev->state & ~BIT(index)) | (state & BIT(index)))) { 550 ret = -EPERM; 551 goto out; 552 } 553 554 /* 555 * Initialize the value of extcon property before setting 556 * the detached state for an external connector. 557 */ 558 if (!state) 559 init_property(edev, id, index); 560 561 /* Update the state for an external connector. */ 562 if (state) 563 edev->state |= BIT(index); 564 else 565 edev->state &= ~(BIT(index)); 566 out: 567 spin_unlock_irqrestore(&edev->lock, flags); 568 569 return ret; 570 } 571 EXPORT_SYMBOL_GPL(extcon_set_state); 572 573 /** 574 * extcon_set_state_sync() - Set the state of an external connector with sync. 575 * @edev: the extcon device 576 * @id: the unique id indicating an external connector 577 * @state: the new state of external connector. 578 * the default semantics is true: attached / false: detached. 579 * 580 * Note that this function set the state of external connector 581 * and synchronize the state by sending a notification. 582 * 583 * Returns 0 if success or error number if fail. 584 */ 585 int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state) 586 { 587 int ret, index; 588 unsigned long flags; 589 590 index = find_cable_index_by_id(edev, id); 591 if (index < 0) 592 return index; 593 594 /* Check whether the external connector's state is changed. */ 595 spin_lock_irqsave(&edev->lock, flags); 596 ret = is_extcon_changed(edev, index, state); 597 spin_unlock_irqrestore(&edev->lock, flags); 598 if (!ret) 599 return 0; 600 601 ret = extcon_set_state(edev, id, state); 602 if (ret < 0) 603 return ret; 604 605 return extcon_sync(edev, id); 606 } 607 EXPORT_SYMBOL_GPL(extcon_set_state_sync); 608 609 /** 610 * extcon_get_property() - Get the property value of an external connector. 611 * @edev: the extcon device 612 * @id: the unique id indicating an external connector 613 * @prop: the property id indicating an extcon property 614 * @prop_val: the pointer which store the value of extcon property 615 * 616 * Note that when getting the property value of external connector, 617 * the external connector should be attached. If detached state, function 618 * return 0 without property value. Also, the each property should be 619 * included in the list of supported properties according to extcon type. 620 * 621 * Returns 0 if success or error number if fail. 622 */ 623 int extcon_get_property(struct extcon_dev *edev, unsigned int id, 624 unsigned int prop, 625 union extcon_property_value *prop_val) 626 { 627 struct extcon_cable *cable; 628 unsigned long flags; 629 int index, ret = 0; 630 631 *prop_val = (union extcon_property_value){0}; 632 633 if (!edev) 634 return -EINVAL; 635 636 /* Check whether the property is supported or not */ 637 if (!is_extcon_property_supported(id, prop)) 638 return -EINVAL; 639 640 /* Find the cable index of external connector by using id */ 641 index = find_cable_index_by_id(edev, id); 642 if (index < 0) 643 return index; 644 645 spin_lock_irqsave(&edev->lock, flags); 646 647 /* Check whether the property is available or not. */ 648 if (!is_extcon_property_capability(edev, id, index, prop)) { 649 spin_unlock_irqrestore(&edev->lock, flags); 650 return -EPERM; 651 } 652 653 /* 654 * Check whether the external connector is attached. 655 * If external connector is detached, the user can not 656 * get the property value. 657 */ 658 if (!is_extcon_attached(edev, index)) { 659 spin_unlock_irqrestore(&edev->lock, flags); 660 return 0; 661 } 662 663 cable = &edev->cables[index]; 664 665 /* Get the property value according to extcon type */ 666 switch (prop) { 667 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: 668 *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN]; 669 break; 670 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: 671 *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN]; 672 break; 673 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: 674 *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN]; 675 break; 676 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: 677 *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN]; 678 break; 679 default: 680 ret = -EINVAL; 681 break; 682 } 683 684 spin_unlock_irqrestore(&edev->lock, flags); 685 686 return ret; 687 } 688 EXPORT_SYMBOL_GPL(extcon_get_property); 689 690 /** 691 * extcon_set_property() - Set the property value of an external connector. 692 * @edev: the extcon device 693 * @id: the unique id indicating an external connector 694 * @prop: the property id indicating an extcon property 695 * @prop_val: the pointer including the new value of extcon property 696 * 697 * Note that each property should be included in the list of supported 698 * properties according to the extcon type. 699 * 700 * Returns 0 if success or error number if fail. 701 */ 702 int extcon_set_property(struct extcon_dev *edev, unsigned int id, 703 unsigned int prop, 704 union extcon_property_value prop_val) 705 { 706 struct extcon_cable *cable; 707 unsigned long flags; 708 int index, ret = 0; 709 710 if (!edev) 711 return -EINVAL; 712 713 /* Check whether the property is supported or not */ 714 if (!is_extcon_property_supported(id, prop)) 715 return -EINVAL; 716 717 /* Find the cable index of external connector by using id */ 718 index = find_cable_index_by_id(edev, id); 719 if (index < 0) 720 return index; 721 722 spin_lock_irqsave(&edev->lock, flags); 723 724 /* Check whether the property is available or not. */ 725 if (!is_extcon_property_capability(edev, id, index, prop)) { 726 spin_unlock_irqrestore(&edev->lock, flags); 727 return -EPERM; 728 } 729 730 cable = &edev->cables[index]; 731 732 /* Set the property value according to extcon type */ 733 switch (prop) { 734 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: 735 cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val; 736 break; 737 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: 738 cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val; 739 break; 740 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: 741 cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val; 742 break; 743 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: 744 cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val; 745 break; 746 default: 747 ret = -EINVAL; 748 break; 749 } 750 751 spin_unlock_irqrestore(&edev->lock, flags); 752 753 return ret; 754 } 755 EXPORT_SYMBOL_GPL(extcon_set_property); 756 757 /** 758 * extcon_set_property_sync() - Set property of an external connector with sync. 759 * @prop_val: the pointer including the new value of extcon property 760 * 761 * Note that when setting the property value of external connector, 762 * the external connector should be attached. The each property should 763 * be included in the list of supported properties according to extcon type. 764 * 765 * Returns 0 if success or error number if fail. 766 */ 767 int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, 768 unsigned int prop, 769 union extcon_property_value prop_val) 770 { 771 int ret; 772 773 ret = extcon_set_property(edev, id, prop, prop_val); 774 if (ret < 0) 775 return ret; 776 777 return extcon_sync(edev, id); 778 } 779 EXPORT_SYMBOL_GPL(extcon_set_property_sync); 780 781 /** 782 * extcon_get_property_capability() - Get the capability of the property 783 * for an external connector. 784 * @edev: the extcon device 785 * @id: the unique id indicating an external connector 786 * @prop: the property id indicating an extcon property 787 * 788 * Returns 1 if the property is available or 0 if not available. 789 */ 790 int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, 791 unsigned int prop) 792 { 793 int index; 794 795 if (!edev) 796 return -EINVAL; 797 798 /* Check whether the property is supported or not */ 799 if (!is_extcon_property_supported(id, prop)) 800 return -EINVAL; 801 802 /* Find the cable index of external connector by using id */ 803 index = find_cable_index_by_id(edev, id); 804 if (index < 0) 805 return index; 806 807 return is_extcon_property_capability(edev, id, index, prop); 808 } 809 EXPORT_SYMBOL_GPL(extcon_get_property_capability); 810 811 /** 812 * extcon_set_property_capability() - Set the capability of the property 813 * for an external connector. 814 * @edev: the extcon device 815 * @id: the unique id indicating an external connector 816 * @prop: the property id indicating an extcon property 817 * 818 * Note that this function set the capability of the property 819 * for an external connector in order to mark the bit in capability 820 * bitmap which mean the available state of the property. 821 * 822 * Returns 0 if success or error number if fail. 823 */ 824 int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id, 825 unsigned int prop) 826 { 827 struct extcon_cable *cable; 828 int index, type, ret = 0; 829 830 if (!edev) 831 return -EINVAL; 832 833 /* Check whether the property is supported or not. */ 834 if (!is_extcon_property_supported(id, prop)) 835 return -EINVAL; 836 837 /* Find the cable index of external connector by using id. */ 838 index = find_cable_index_by_id(edev, id); 839 if (index < 0) 840 return index; 841 842 type = get_extcon_type(prop); 843 if (type < 0) 844 return type; 845 846 cable = &edev->cables[index]; 847 848 switch (type) { 849 case EXTCON_TYPE_USB: 850 __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits); 851 break; 852 case EXTCON_TYPE_CHG: 853 __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits); 854 break; 855 case EXTCON_TYPE_JACK: 856 __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits); 857 break; 858 case EXTCON_TYPE_DISP: 859 __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits); 860 break; 861 default: 862 ret = -EINVAL; 863 } 864 865 return ret; 866 } 867 EXPORT_SYMBOL_GPL(extcon_set_property_capability); 868 869 /** 870 * extcon_get_extcon_dev() - Get the extcon device instance from the name. 871 * @extcon_name: the extcon name provided with extcon_dev_register() 872 * 873 * Return the pointer of extcon device if success or ERR_PTR(err) if fail. 874 */ 875 struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) 876 { 877 struct extcon_dev *sd; 878 879 if (!extcon_name) 880 return ERR_PTR(-EINVAL); 881 882 mutex_lock(&extcon_dev_list_lock); 883 list_for_each_entry(sd, &extcon_dev_list, entry) { 884 if (!strcmp(sd->name, extcon_name)) 885 goto out; 886 } 887 sd = NULL; 888 out: 889 mutex_unlock(&extcon_dev_list_lock); 890 return sd; 891 } 892 EXPORT_SYMBOL_GPL(extcon_get_extcon_dev); 893 894 /** 895 * extcon_register_notifier() - Register a notifier block to get notified by 896 * any state changes from the extcon. 897 * @edev: the extcon device 898 * @id: the unique id indicating an external connector 899 * @nb: a notifier block to be registered 900 * 901 * Note that the second parameter given to the callback of nb (val) is 902 * the current state of an external connector and the third pameter 903 * is the pointer of extcon device. 904 * 905 * Returns 0 if success or error number if fail. 906 */ 907 int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, 908 struct notifier_block *nb) 909 { 910 unsigned long flags; 911 int ret, idx = -EINVAL; 912 913 if (!edev || !nb) 914 return -EINVAL; 915 916 idx = find_cable_index_by_id(edev, id); 917 if (idx < 0) 918 return idx; 919 920 spin_lock_irqsave(&edev->lock, flags); 921 ret = raw_notifier_chain_register(&edev->nh[idx], nb); 922 spin_unlock_irqrestore(&edev->lock, flags); 923 924 return ret; 925 } 926 EXPORT_SYMBOL_GPL(extcon_register_notifier); 927 928 /** 929 * extcon_unregister_notifier() - Unregister a notifier block from the extcon. 930 * @edev: the extcon device 931 * @id: the unique id indicating an external connector 932 * @nb: a notifier block to be registered 933 * 934 * Returns 0 if success or error number if fail. 935 */ 936 int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id, 937 struct notifier_block *nb) 938 { 939 unsigned long flags; 940 int ret, idx; 941 942 if (!edev || !nb) 943 return -EINVAL; 944 945 idx = find_cable_index_by_id(edev, id); 946 if (idx < 0) 947 return idx; 948 949 spin_lock_irqsave(&edev->lock, flags); 950 ret = raw_notifier_chain_unregister(&edev->nh[idx], nb); 951 spin_unlock_irqrestore(&edev->lock, flags); 952 953 return ret; 954 } 955 EXPORT_SYMBOL_GPL(extcon_unregister_notifier); 956 957 /** 958 * extcon_register_notifier_all() - Register a notifier block for all connectors. 959 * @edev: the extcon device 960 * @nb: a notifier block to be registered 961 * 962 * Note that this function registers a notifier block in order to receive 963 * the state change of all supported external connectors from extcon device. 964 * And the second parameter given to the callback of nb (val) is 965 * the current state and the third pameter is the pointer of extcon device. 966 * 967 * Returns 0 if success or error number if fail. 968 */ 969 int extcon_register_notifier_all(struct extcon_dev *edev, 970 struct notifier_block *nb) 971 { 972 unsigned long flags; 973 int ret; 974 975 if (!edev || !nb) 976 return -EINVAL; 977 978 spin_lock_irqsave(&edev->lock, flags); 979 ret = raw_notifier_chain_register(&edev->nh_all, nb); 980 spin_unlock_irqrestore(&edev->lock, flags); 981 982 return ret; 983 } 984 EXPORT_SYMBOL_GPL(extcon_register_notifier_all); 985 986 /** 987 * extcon_unregister_notifier_all() - Unregister a notifier block from extcon. 988 * @edev: the extcon device 989 * @nb: a notifier block to be registered 990 * 991 * Returns 0 if success or error number if fail. 992 */ 993 int extcon_unregister_notifier_all(struct extcon_dev *edev, 994 struct notifier_block *nb) 995 { 996 unsigned long flags; 997 int ret; 998 999 if (!edev || !nb) 1000 return -EINVAL; 1001 1002 spin_lock_irqsave(&edev->lock, flags); 1003 ret = raw_notifier_chain_unregister(&edev->nh_all, nb); 1004 spin_unlock_irqrestore(&edev->lock, flags); 1005 1006 return ret; 1007 } 1008 EXPORT_SYMBOL_GPL(extcon_unregister_notifier_all); 1009 1010 static struct attribute *extcon_attrs[] = { 1011 &dev_attr_state.attr, 1012 &dev_attr_name.attr, 1013 NULL, 1014 }; 1015 ATTRIBUTE_GROUPS(extcon); 1016 1017 static int create_extcon_class(void) 1018 { 1019 if (!extcon_class) { 1020 extcon_class = class_create(THIS_MODULE, "extcon"); 1021 if (IS_ERR(extcon_class)) 1022 return PTR_ERR(extcon_class); 1023 extcon_class->dev_groups = extcon_groups; 1024 } 1025 1026 return 0; 1027 } 1028 1029 static void extcon_dev_release(struct device *dev) 1030 { 1031 } 1032 1033 static const char *muex_name = "mutually_exclusive"; 1034 static void dummy_sysfs_dev_release(struct device *dev) 1035 { 1036 } 1037 1038 /* 1039 * extcon_dev_allocate() - Allocate the memory of extcon device. 1040 * @supported_cable: the array of the supported external connectors 1041 * ending with EXTCON_NONE. 1042 * 1043 * Note that this function allocates the memory for extcon device 1044 * and initialize default setting for the extcon device. 1045 * 1046 * Returns the pointer memory of allocated extcon_dev if success 1047 * or ERR_PTR(err) if fail. 1048 */ 1049 struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable) 1050 { 1051 struct extcon_dev *edev; 1052 1053 if (!supported_cable) 1054 return ERR_PTR(-EINVAL); 1055 1056 edev = kzalloc(sizeof(*edev), GFP_KERNEL); 1057 if (!edev) 1058 return ERR_PTR(-ENOMEM); 1059 1060 edev->max_supported = 0; 1061 edev->supported_cable = supported_cable; 1062 1063 return edev; 1064 } 1065 1066 /* 1067 * extcon_dev_free() - Free the memory of extcon device. 1068 * @edev: the extcon device 1069 */ 1070 void extcon_dev_free(struct extcon_dev *edev) 1071 { 1072 kfree(edev); 1073 } 1074 EXPORT_SYMBOL_GPL(extcon_dev_free); 1075 1076 /** 1077 * extcon_dev_register() - Register an new extcon device 1078 * @edev: the extcon device to be registered 1079 * 1080 * Among the members of edev struct, please set the "user initializing data" 1081 * do not set the values of "internal data", which are initialized by 1082 * this function. 1083 * 1084 * Note that before calling this funciton, have to allocate the memory 1085 * of an extcon device by using the extcon_dev_allocate(). And the extcon 1086 * dev should include the supported_cable information. 1087 * 1088 * Returns 0 if success or error number if fail. 1089 */ 1090 int extcon_dev_register(struct extcon_dev *edev) 1091 { 1092 int ret, index = 0; 1093 static atomic_t edev_no = ATOMIC_INIT(-1); 1094 1095 if (!extcon_class) { 1096 ret = create_extcon_class(); 1097 if (ret < 0) 1098 return ret; 1099 } 1100 1101 if (!edev || !edev->supported_cable) 1102 return -EINVAL; 1103 1104 for (; edev->supported_cable[index] != EXTCON_NONE; index++); 1105 1106 edev->max_supported = index; 1107 if (index > SUPPORTED_CABLE_MAX) { 1108 dev_err(&edev->dev, 1109 "exceed the maximum number of supported cables\n"); 1110 return -EINVAL; 1111 } 1112 1113 edev->dev.class = extcon_class; 1114 edev->dev.release = extcon_dev_release; 1115 1116 edev->name = dev_name(edev->dev.parent); 1117 if (IS_ERR_OR_NULL(edev->name)) { 1118 dev_err(&edev->dev, 1119 "extcon device name is null\n"); 1120 return -EINVAL; 1121 } 1122 dev_set_name(&edev->dev, "extcon%lu", 1123 (unsigned long)atomic_inc_return(&edev_no)); 1124 1125 if (edev->max_supported) { 1126 char *str; 1127 struct extcon_cable *cable; 1128 1129 edev->cables = kcalloc(edev->max_supported, 1130 sizeof(struct extcon_cable), 1131 GFP_KERNEL); 1132 if (!edev->cables) { 1133 ret = -ENOMEM; 1134 goto err_sysfs_alloc; 1135 } 1136 for (index = 0; index < edev->max_supported; index++) { 1137 cable = &edev->cables[index]; 1138 1139 str = kasprintf(GFP_KERNEL, "cable.%d", index); 1140 if (!str) { 1141 for (index--; index >= 0; index--) { 1142 cable = &edev->cables[index]; 1143 kfree(cable->attr_g.name); 1144 } 1145 ret = -ENOMEM; 1146 1147 goto err_alloc_cables; 1148 } 1149 1150 cable->edev = edev; 1151 cable->cable_index = index; 1152 cable->attrs[0] = &cable->attr_name.attr; 1153 cable->attrs[1] = &cable->attr_state.attr; 1154 cable->attrs[2] = NULL; 1155 cable->attr_g.name = str; 1156 cable->attr_g.attrs = cable->attrs; 1157 1158 sysfs_attr_init(&cable->attr_name.attr); 1159 cable->attr_name.attr.name = "name"; 1160 cable->attr_name.attr.mode = 0444; 1161 cable->attr_name.show = cable_name_show; 1162 1163 sysfs_attr_init(&cable->attr_state.attr); 1164 cable->attr_state.attr.name = "state"; 1165 cable->attr_state.attr.mode = 0444; 1166 cable->attr_state.show = cable_state_show; 1167 } 1168 } 1169 1170 if (edev->max_supported && edev->mutually_exclusive) { 1171 char *name; 1172 1173 /* Count the size of mutually_exclusive array */ 1174 for (index = 0; edev->mutually_exclusive[index]; index++) 1175 ; 1176 1177 edev->attrs_muex = kcalloc(index + 1, 1178 sizeof(struct attribute *), 1179 GFP_KERNEL); 1180 if (!edev->attrs_muex) { 1181 ret = -ENOMEM; 1182 goto err_muex; 1183 } 1184 1185 edev->d_attrs_muex = kcalloc(index, 1186 sizeof(struct device_attribute), 1187 GFP_KERNEL); 1188 if (!edev->d_attrs_muex) { 1189 ret = -ENOMEM; 1190 kfree(edev->attrs_muex); 1191 goto err_muex; 1192 } 1193 1194 for (index = 0; edev->mutually_exclusive[index]; index++) { 1195 name = kasprintf(GFP_KERNEL, "0x%x", 1196 edev->mutually_exclusive[index]); 1197 if (!name) { 1198 for (index--; index >= 0; index--) { 1199 kfree(edev->d_attrs_muex[index].attr. 1200 name); 1201 } 1202 kfree(edev->d_attrs_muex); 1203 kfree(edev->attrs_muex); 1204 ret = -ENOMEM; 1205 goto err_muex; 1206 } 1207 sysfs_attr_init(&edev->d_attrs_muex[index].attr); 1208 edev->d_attrs_muex[index].attr.name = name; 1209 edev->d_attrs_muex[index].attr.mode = 0000; 1210 edev->attrs_muex[index] = &edev->d_attrs_muex[index] 1211 .attr; 1212 } 1213 edev->attr_g_muex.name = muex_name; 1214 edev->attr_g_muex.attrs = edev->attrs_muex; 1215 1216 } 1217 1218 if (edev->max_supported) { 1219 edev->extcon_dev_type.groups = 1220 kcalloc(edev->max_supported + 2, 1221 sizeof(struct attribute_group *), 1222 GFP_KERNEL); 1223 if (!edev->extcon_dev_type.groups) { 1224 ret = -ENOMEM; 1225 goto err_alloc_groups; 1226 } 1227 1228 edev->extcon_dev_type.name = dev_name(&edev->dev); 1229 edev->extcon_dev_type.release = dummy_sysfs_dev_release; 1230 1231 for (index = 0; index < edev->max_supported; index++) 1232 edev->extcon_dev_type.groups[index] = 1233 &edev->cables[index].attr_g; 1234 if (edev->mutually_exclusive) 1235 edev->extcon_dev_type.groups[index] = 1236 &edev->attr_g_muex; 1237 1238 edev->dev.type = &edev->extcon_dev_type; 1239 } 1240 1241 ret = device_register(&edev->dev); 1242 if (ret) { 1243 put_device(&edev->dev); 1244 goto err_dev; 1245 } 1246 1247 spin_lock_init(&edev->lock); 1248 edev->nh = devm_kcalloc(&edev->dev, edev->max_supported, 1249 sizeof(*edev->nh), GFP_KERNEL); 1250 if (!edev->nh) { 1251 ret = -ENOMEM; 1252 goto err_dev; 1253 } 1254 1255 for (index = 0; index < edev->max_supported; index++) 1256 RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]); 1257 1258 RAW_INIT_NOTIFIER_HEAD(&edev->nh_all); 1259 1260 dev_set_drvdata(&edev->dev, edev); 1261 edev->state = 0; 1262 1263 mutex_lock(&extcon_dev_list_lock); 1264 list_add(&edev->entry, &extcon_dev_list); 1265 mutex_unlock(&extcon_dev_list_lock); 1266 1267 return 0; 1268 1269 err_dev: 1270 if (edev->max_supported) 1271 kfree(edev->extcon_dev_type.groups); 1272 err_alloc_groups: 1273 if (edev->max_supported && edev->mutually_exclusive) { 1274 for (index = 0; edev->mutually_exclusive[index]; index++) 1275 kfree(edev->d_attrs_muex[index].attr.name); 1276 kfree(edev->d_attrs_muex); 1277 kfree(edev->attrs_muex); 1278 } 1279 err_muex: 1280 for (index = 0; index < edev->max_supported; index++) 1281 kfree(edev->cables[index].attr_g.name); 1282 err_alloc_cables: 1283 if (edev->max_supported) 1284 kfree(edev->cables); 1285 err_sysfs_alloc: 1286 return ret; 1287 } 1288 EXPORT_SYMBOL_GPL(extcon_dev_register); 1289 1290 /** 1291 * extcon_dev_unregister() - Unregister the extcon device. 1292 * @edev: the extcon device to be unregistered. 1293 * 1294 * Note that this does not call kfree(edev) because edev was not allocated 1295 * by this class. 1296 */ 1297 void extcon_dev_unregister(struct extcon_dev *edev) 1298 { 1299 int index; 1300 1301 if (!edev) 1302 return; 1303 1304 mutex_lock(&extcon_dev_list_lock); 1305 list_del(&edev->entry); 1306 mutex_unlock(&extcon_dev_list_lock); 1307 1308 if (IS_ERR_OR_NULL(get_device(&edev->dev))) { 1309 dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n", 1310 dev_name(&edev->dev)); 1311 return; 1312 } 1313 1314 device_unregister(&edev->dev); 1315 1316 if (edev->mutually_exclusive && edev->max_supported) { 1317 for (index = 0; edev->mutually_exclusive[index]; 1318 index++) 1319 kfree(edev->d_attrs_muex[index].attr.name); 1320 kfree(edev->d_attrs_muex); 1321 kfree(edev->attrs_muex); 1322 } 1323 1324 for (index = 0; index < edev->max_supported; index++) 1325 kfree(edev->cables[index].attr_g.name); 1326 1327 if (edev->max_supported) { 1328 kfree(edev->extcon_dev_type.groups); 1329 kfree(edev->cables); 1330 } 1331 1332 put_device(&edev->dev); 1333 } 1334 EXPORT_SYMBOL_GPL(extcon_dev_unregister); 1335 1336 #ifdef CONFIG_OF 1337 1338 /* 1339 * extcon_find_edev_by_node - Find the extcon device from devicetree. 1340 * @node : OF node identifying edev 1341 * 1342 * Return the pointer of extcon device if success or ERR_PTR(err) if fail. 1343 */ 1344 struct extcon_dev *extcon_find_edev_by_node(struct device_node *node) 1345 { 1346 struct extcon_dev *edev; 1347 1348 mutex_lock(&extcon_dev_list_lock); 1349 list_for_each_entry(edev, &extcon_dev_list, entry) 1350 if (edev->dev.parent && edev->dev.parent->of_node == node) 1351 goto out; 1352 edev = ERR_PTR(-EPROBE_DEFER); 1353 out: 1354 mutex_unlock(&extcon_dev_list_lock); 1355 1356 return edev; 1357 } 1358 1359 /* 1360 * extcon_get_edev_by_phandle - Get the extcon device from devicetree. 1361 * @dev : the instance to the given device 1362 * @index : the index into list of extcon_dev 1363 * 1364 * Return the pointer of extcon device if success or ERR_PTR(err) if fail. 1365 */ 1366 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) 1367 { 1368 struct device_node *node; 1369 struct extcon_dev *edev; 1370 1371 if (!dev) 1372 return ERR_PTR(-EINVAL); 1373 1374 if (!dev->of_node) { 1375 dev_dbg(dev, "device does not have a device node entry\n"); 1376 return ERR_PTR(-EINVAL); 1377 } 1378 1379 node = of_parse_phandle(dev->of_node, "extcon", index); 1380 if (!node) { 1381 dev_dbg(dev, "failed to get phandle in %pOF node\n", 1382 dev->of_node); 1383 return ERR_PTR(-ENODEV); 1384 } 1385 1386 edev = extcon_find_edev_by_node(node); 1387 of_node_put(node); 1388 1389 return edev; 1390 } 1391 1392 #else 1393 1394 struct extcon_dev *extcon_find_edev_by_node(struct device_node *node) 1395 { 1396 return ERR_PTR(-ENOSYS); 1397 } 1398 1399 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) 1400 { 1401 return ERR_PTR(-ENOSYS); 1402 } 1403 1404 #endif /* CONFIG_OF */ 1405 1406 EXPORT_SYMBOL_GPL(extcon_find_edev_by_node); 1407 EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle); 1408 1409 /** 1410 * extcon_get_edev_name() - Get the name of the extcon device. 1411 * @edev: the extcon device 1412 */ 1413 const char *extcon_get_edev_name(struct extcon_dev *edev) 1414 { 1415 return !edev ? NULL : edev->name; 1416 } 1417 1418 static int __init extcon_class_init(void) 1419 { 1420 return create_extcon_class(); 1421 } 1422 module_init(extcon_class_init); 1423 1424 static void __exit extcon_class_exit(void) 1425 { 1426 class_destroy(extcon_class); 1427 } 1428 module_exit(extcon_class_exit); 1429 1430 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 1431 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 1432 MODULE_DESCRIPTION("External Connector (extcon) framework"); 1433 MODULE_LICENSE("GPL v2"); 1434