1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * drivers/extcon/extcon.c - External Connector (extcon) framework. 4 * 5 * Copyright (C) 2015 Samsung Electronics 6 * Author: Chanwoo Choi <cw00.choi@samsung.com> 7 * 8 * Copyright (C) 2012 Samsung Electronics 9 * Author: Donggeun Kim <dg77.kim@samsung.com> 10 * Author: MyungJoo Ham <myungjoo.ham@samsung.com> 11 * 12 * based on android/drivers/switch/switch_class.c 13 * Copyright (C) 2008 Google, Inc. 14 * Author: Mike Lockwood <lockwood@android.com> 15 */ 16 17 #include <linux/module.h> 18 #include <linux/types.h> 19 #include <linux/init.h> 20 #include <linux/device.h> 21 #include <linux/fs.h> 22 #include <linux/err.h> 23 #include <linux/of.h> 24 #include <linux/slab.h> 25 #include <linux/sysfs.h> 26 27 #include "extcon.h" 28 29 #define SUPPORTED_CABLE_MAX 32 30 31 static const struct __extcon_info { 32 unsigned int type; 33 unsigned int id; 34 const char *name; 35 36 } extcon_info[] = { 37 [EXTCON_NONE] = { 38 .type = EXTCON_TYPE_MISC, 39 .id = EXTCON_NONE, 40 .name = "NONE", 41 }, 42 43 /* USB external connector */ 44 [EXTCON_USB] = { 45 .type = EXTCON_TYPE_USB, 46 .id = EXTCON_USB, 47 .name = "USB", 48 }, 49 [EXTCON_USB_HOST] = { 50 .type = EXTCON_TYPE_USB, 51 .id = EXTCON_USB_HOST, 52 .name = "USB-HOST", 53 }, 54 55 /* Charging external connector */ 56 [EXTCON_CHG_USB_SDP] = { 57 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 58 .id = EXTCON_CHG_USB_SDP, 59 .name = "SDP", 60 }, 61 [EXTCON_CHG_USB_DCP] = { 62 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 63 .id = EXTCON_CHG_USB_DCP, 64 .name = "DCP", 65 }, 66 [EXTCON_CHG_USB_CDP] = { 67 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 68 .id = EXTCON_CHG_USB_CDP, 69 .name = "CDP", 70 }, 71 [EXTCON_CHG_USB_ACA] = { 72 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 73 .id = EXTCON_CHG_USB_ACA, 74 .name = "ACA", 75 }, 76 [EXTCON_CHG_USB_FAST] = { 77 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 78 .id = EXTCON_CHG_USB_FAST, 79 .name = "FAST-CHARGER", 80 }, 81 [EXTCON_CHG_USB_SLOW] = { 82 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 83 .id = EXTCON_CHG_USB_SLOW, 84 .name = "SLOW-CHARGER", 85 }, 86 [EXTCON_CHG_WPT] = { 87 .type = EXTCON_TYPE_CHG, 88 .id = EXTCON_CHG_WPT, 89 .name = "WPT", 90 }, 91 [EXTCON_CHG_USB_PD] = { 92 .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB, 93 .id = EXTCON_CHG_USB_PD, 94 .name = "PD", 95 }, 96 97 /* Jack external connector */ 98 [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 }, 138 139 /* Display external connector */ 140 [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 }, 160 [EXTCON_DISP_DP] = { 161 .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB, 162 .id = EXTCON_DISP_DP, 163 .name = "DP", 164 }, 165 [EXTCON_DISP_HMD] = { 166 .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB, 167 .id = EXTCON_DISP_HMD, 168 .name = "HMD", 169 }, 170 [EXTCON_DISP_CVBS] = { 171 .type = EXTCON_TYPE_DISP, 172 .id = EXTCON_DISP_CVBS, 173 .name = "CVBS", 174 }, 175 [EXTCON_DISP_EDP] = { 176 .type = EXTCON_TYPE_DISP, 177 .id = EXTCON_DISP_EDP, 178 .name = "EDP", 179 }, 180 181 /* Miscellaneous external connector */ 182 [EXTCON_DOCK] = { 183 .type = EXTCON_TYPE_MISC, 184 .id = EXTCON_DOCK, 185 .name = "DOCK", 186 }, 187 [EXTCON_JIG] = { 188 .type = EXTCON_TYPE_MISC, 189 .id = EXTCON_JIG, 190 .name = "JIG", 191 }, 192 [EXTCON_MECHANICAL] = { 193 .type = EXTCON_TYPE_MISC, 194 .id = EXTCON_MECHANICAL, 195 .name = "MECHANICAL", 196 }, 197 198 { /* sentinel */ } 199 }; 200 201 /** 202 * struct extcon_cable - An internal data for an external connector. 203 * @edev: the extcon device 204 * @cable_index: the index of this cable in the edev 205 * @attr_g: the attribute group for the cable 206 * @attr_name: "name" sysfs entry 207 * @attr_state: "state" sysfs entry 208 * @attrs: the array pointing to attr_name and attr_state for attr_g 209 */ 210 struct extcon_cable { 211 struct extcon_dev *edev; 212 int cable_index; 213 214 struct attribute_group attr_g; 215 struct device_attribute attr_name; 216 struct device_attribute attr_state; 217 218 struct attribute *attrs[3]; /* to be fed to attr_g.attrs */ 219 220 union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT]; 221 union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT]; 222 union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT]; 223 union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT]; 224 225 unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)]; 226 unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)]; 227 unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)]; 228 unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)]; 229 }; 230 231 static struct class *extcon_class; 232 233 static LIST_HEAD(extcon_dev_list); 234 static DEFINE_MUTEX(extcon_dev_list_lock); 235 236 static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state) 237 { 238 int i = 0; 239 240 if (!edev->mutually_exclusive) 241 return 0; 242 243 for (i = 0; edev->mutually_exclusive[i]; i++) { 244 int weight; 245 u32 correspondants = new_state & edev->mutually_exclusive[i]; 246 247 /* calculate the total number of bits set */ 248 weight = hweight32(correspondants); 249 if (weight > 1) 250 return i + 1; 251 } 252 253 return 0; 254 } 255 256 static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id) 257 { 258 int i; 259 260 /* Find the index of extcon cable in edev->supported_cable */ 261 for (i = 0; i < edev->max_supported; i++) { 262 if (edev->supported_cable[i] == id) 263 return i; 264 } 265 266 return -EINVAL; 267 } 268 269 static int get_extcon_type(unsigned int prop) 270 { 271 switch (prop) { 272 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: 273 return EXTCON_TYPE_USB; 274 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: 275 return EXTCON_TYPE_CHG; 276 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: 277 return EXTCON_TYPE_JACK; 278 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: 279 return EXTCON_TYPE_DISP; 280 default: 281 return -EINVAL; 282 } 283 } 284 285 static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index) 286 { 287 return !!(edev->state & BIT(index)); 288 } 289 290 static bool is_extcon_changed(struct extcon_dev *edev, int index, 291 bool new_state) 292 { 293 int state = !!(edev->state & BIT(index)); 294 return (state != new_state); 295 } 296 297 static bool is_extcon_property_supported(unsigned int id, unsigned int prop) 298 { 299 int type; 300 301 /* Check whether the property is supported or not. */ 302 type = get_extcon_type(prop); 303 if (type < 0) 304 return false; 305 306 /* Check whether a specific extcon id supports the property or not. */ 307 return !!(extcon_info[id].type & type); 308 } 309 310 static int is_extcon_property_capability(struct extcon_dev *edev, 311 unsigned int id, int index,unsigned int prop) 312 { 313 struct extcon_cable *cable; 314 int type, ret; 315 316 /* Check whether the property is supported or not. */ 317 type = get_extcon_type(prop); 318 if (type < 0) 319 return type; 320 321 cable = &edev->cables[index]; 322 323 switch (type) { 324 case EXTCON_TYPE_USB: 325 ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits); 326 break; 327 case EXTCON_TYPE_CHG: 328 ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits); 329 break; 330 case EXTCON_TYPE_JACK: 331 ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits); 332 break; 333 case EXTCON_TYPE_DISP: 334 ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits); 335 break; 336 default: 337 ret = -EINVAL; 338 } 339 340 return ret; 341 } 342 343 static void init_property(struct extcon_dev *edev, unsigned int id, int index) 344 { 345 unsigned int type = extcon_info[id].type; 346 struct extcon_cable *cable = &edev->cables[index]; 347 348 if (EXTCON_TYPE_USB & type) 349 memset(cable->usb_propval, 0, sizeof(cable->usb_propval)); 350 if (EXTCON_TYPE_CHG & type) 351 memset(cable->chg_propval, 0, sizeof(cable->chg_propval)); 352 if (EXTCON_TYPE_JACK & type) 353 memset(cable->jack_propval, 0, sizeof(cable->jack_propval)); 354 if (EXTCON_TYPE_DISP & type) 355 memset(cable->disp_propval, 0, sizeof(cable->disp_propval)); 356 } 357 358 static ssize_t state_show(struct device *dev, struct device_attribute *attr, 359 char *buf) 360 { 361 int i, count = 0; 362 struct extcon_dev *edev = dev_get_drvdata(dev); 363 364 if (edev->max_supported == 0) 365 return sprintf(buf, "%u\n", edev->state); 366 367 for (i = 0; i < edev->max_supported; i++) { 368 count += sprintf(buf + count, "%s=%d\n", 369 extcon_info[edev->supported_cable[i]].name, 370 !!(edev->state & BIT(i))); 371 } 372 373 return count; 374 } 375 static DEVICE_ATTR_RO(state); 376 377 static ssize_t name_show(struct device *dev, struct device_attribute *attr, 378 char *buf) 379 { 380 struct extcon_dev *edev = dev_get_drvdata(dev); 381 382 return sprintf(buf, "%s\n", edev->name); 383 } 384 static DEVICE_ATTR_RO(name); 385 386 static ssize_t cable_name_show(struct device *dev, 387 struct device_attribute *attr, char *buf) 388 { 389 struct extcon_cable *cable = container_of(attr, struct extcon_cable, 390 attr_name); 391 int i = cable->cable_index; 392 393 return sprintf(buf, "%s\n", 394 extcon_info[cable->edev->supported_cable[i]].name); 395 } 396 397 static ssize_t cable_state_show(struct device *dev, 398 struct device_attribute *attr, char *buf) 399 { 400 struct extcon_cable *cable = container_of(attr, struct extcon_cable, 401 attr_state); 402 403 int i = cable->cable_index; 404 405 return sprintf(buf, "%d\n", 406 extcon_get_state(cable->edev, cable->edev->supported_cable[i])); 407 } 408 409 /** 410 * extcon_sync() - Synchronize the state for an external connector. 411 * @edev: the extcon device 412 * @id: the unique id indicating an external connector 413 * 414 * Note that this function send a notification in order to synchronize 415 * the state and property of an external connector. 416 * 417 * Returns 0 if success or error number if fail. 418 */ 419 int extcon_sync(struct extcon_dev *edev, unsigned int id) 420 { 421 char name_buf[120]; 422 char state_buf[120]; 423 char *prop_buf; 424 char *envp[3]; 425 int env_offset = 0; 426 int length; 427 int index; 428 int state; 429 unsigned long flags; 430 431 if (!edev) 432 return -EINVAL; 433 434 index = find_cable_index_by_id(edev, id); 435 if (index < 0) 436 return index; 437 438 spin_lock_irqsave(&edev->lock, flags); 439 state = !!(edev->state & BIT(index)); 440 spin_unlock_irqrestore(&edev->lock, flags); 441 442 /* 443 * Call functions in a raw notifier chain for the specific one 444 * external connector. 445 */ 446 raw_notifier_call_chain(&edev->nh[index], state, edev); 447 448 /* 449 * Call functions in a raw notifier chain for the all supported 450 * external connectors. 451 */ 452 raw_notifier_call_chain(&edev->nh_all, state, edev); 453 454 spin_lock_irqsave(&edev->lock, flags); 455 /* This could be in interrupt handler */ 456 prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); 457 if (!prop_buf) { 458 /* Unlock early before uevent */ 459 spin_unlock_irqrestore(&edev->lock, flags); 460 461 dev_err(&edev->dev, "out of memory in extcon_set_state\n"); 462 kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE); 463 464 return -ENOMEM; 465 } 466 467 length = name_show(&edev->dev, NULL, prop_buf); 468 if (length > 0) { 469 if (prop_buf[length - 1] == '\n') 470 prop_buf[length - 1] = 0; 471 snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf); 472 envp[env_offset++] = name_buf; 473 } 474 475 length = state_show(&edev->dev, NULL, prop_buf); 476 if (length > 0) { 477 if (prop_buf[length - 1] == '\n') 478 prop_buf[length - 1] = 0; 479 snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf); 480 envp[env_offset++] = state_buf; 481 } 482 envp[env_offset] = NULL; 483 484 /* Unlock early before uevent */ 485 spin_unlock_irqrestore(&edev->lock, flags); 486 kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp); 487 free_page((unsigned long)prop_buf); 488 489 return 0; 490 } 491 EXPORT_SYMBOL_GPL(extcon_sync); 492 493 /** 494 * extcon_get_state() - Get the state of an external connector. 495 * @edev: the extcon device 496 * @id: the unique id indicating an external connector 497 * 498 * Returns 0 if success or error number if fail. 499 */ 500 int extcon_get_state(struct extcon_dev *edev, const unsigned int id) 501 { 502 int index, state; 503 unsigned long flags; 504 505 if (!edev) 506 return -EINVAL; 507 508 index = find_cable_index_by_id(edev, id); 509 if (index < 0) 510 return index; 511 512 spin_lock_irqsave(&edev->lock, flags); 513 state = is_extcon_attached(edev, index); 514 spin_unlock_irqrestore(&edev->lock, flags); 515 516 return state; 517 } 518 EXPORT_SYMBOL_GPL(extcon_get_state); 519 520 /** 521 * extcon_set_state() - Set the state of an external connector. 522 * @edev: the extcon device 523 * @id: the unique id indicating an external connector 524 * @state: the new state of an external connector. 525 * the default semantics is true: attached / false: detached. 526 * 527 * Note that this function set the state of an external connector without 528 * a notification. To synchronize the state of an external connector, 529 * have to use extcon_set_state_sync() and extcon_sync(). 530 * 531 * Returns 0 if success or error number if fail. 532 */ 533 int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool state) 534 { 535 unsigned long flags; 536 int index, ret = 0; 537 538 if (!edev) 539 return -EINVAL; 540 541 index = find_cable_index_by_id(edev, id); 542 if (index < 0) 543 return index; 544 545 spin_lock_irqsave(&edev->lock, flags); 546 547 /* Check whether the external connector's state is changed. */ 548 if (!is_extcon_changed(edev, index, state)) 549 goto out; 550 551 if (check_mutually_exclusive(edev, 552 (edev->state & ~BIT(index)) | (state & BIT(index)))) { 553 ret = -EPERM; 554 goto out; 555 } 556 557 /* 558 * Initialize the value of extcon property before setting 559 * the detached state for an external connector. 560 */ 561 if (!state) 562 init_property(edev, id, index); 563 564 /* Update the state for an external connector. */ 565 if (state) 566 edev->state |= BIT(index); 567 else 568 edev->state &= ~(BIT(index)); 569 out: 570 spin_unlock_irqrestore(&edev->lock, flags); 571 572 return ret; 573 } 574 EXPORT_SYMBOL_GPL(extcon_set_state); 575 576 /** 577 * extcon_set_state_sync() - Set the state of an external connector with sync. 578 * @edev: the extcon device 579 * @id: the unique id indicating an external connector 580 * @state: the new state of external connector. 581 * the default semantics is true: attached / false: detached. 582 * 583 * Note that this function set the state of external connector 584 * and synchronize the state by sending a notification. 585 * 586 * Returns 0 if success or error number if fail. 587 */ 588 int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state) 589 { 590 int ret; 591 592 ret = extcon_set_state(edev, id, state); 593 if (ret < 0) 594 return ret; 595 596 return extcon_sync(edev, id); 597 } 598 EXPORT_SYMBOL_GPL(extcon_set_state_sync); 599 600 /** 601 * extcon_get_property() - Get the property value of an external connector. 602 * @edev: the extcon device 603 * @id: the unique id indicating an external connector 604 * @prop: the property id indicating an extcon property 605 * @prop_val: the pointer which store the value of extcon property 606 * 607 * Note that when getting the property value of external connector, 608 * the external connector should be attached. If detached state, function 609 * return 0 without property value. Also, the each property should be 610 * included in the list of supported properties according to extcon type. 611 * 612 * Returns 0 if success or error number if fail. 613 */ 614 int extcon_get_property(struct extcon_dev *edev, unsigned int id, 615 unsigned int prop, 616 union extcon_property_value *prop_val) 617 { 618 struct extcon_cable *cable; 619 unsigned long flags; 620 int index, ret = 0; 621 622 *prop_val = (union extcon_property_value){0}; 623 624 if (!edev) 625 return -EINVAL; 626 627 /* Check whether the property is supported or not */ 628 if (!is_extcon_property_supported(id, prop)) 629 return -EINVAL; 630 631 /* Find the cable index of external connector by using id */ 632 index = find_cable_index_by_id(edev, id); 633 if (index < 0) 634 return index; 635 636 spin_lock_irqsave(&edev->lock, flags); 637 638 /* Check whether the property is available or not. */ 639 if (!is_extcon_property_capability(edev, id, index, prop)) { 640 spin_unlock_irqrestore(&edev->lock, flags); 641 return -EPERM; 642 } 643 644 /* 645 * Check whether the external connector is attached. 646 * If external connector is detached, the user can not 647 * get the property value. 648 */ 649 if (!is_extcon_attached(edev, index)) { 650 spin_unlock_irqrestore(&edev->lock, flags); 651 return 0; 652 } 653 654 cable = &edev->cables[index]; 655 656 /* Get the property value according to extcon type */ 657 switch (prop) { 658 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: 659 *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN]; 660 break; 661 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: 662 *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN]; 663 break; 664 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: 665 *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN]; 666 break; 667 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: 668 *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN]; 669 break; 670 default: 671 ret = -EINVAL; 672 break; 673 } 674 675 spin_unlock_irqrestore(&edev->lock, flags); 676 677 return ret; 678 } 679 EXPORT_SYMBOL_GPL(extcon_get_property); 680 681 /** 682 * extcon_set_property() - Set the property value of an external connector. 683 * @edev: the extcon device 684 * @id: the unique id indicating an external connector 685 * @prop: the property id indicating an extcon property 686 * @prop_val: the pointer including the new value of extcon property 687 * 688 * Note that each property should be included in the list of supported 689 * properties according to the extcon type. 690 * 691 * Returns 0 if success or error number if fail. 692 */ 693 int extcon_set_property(struct extcon_dev *edev, unsigned int id, 694 unsigned int prop, 695 union extcon_property_value prop_val) 696 { 697 struct extcon_cable *cable; 698 unsigned long flags; 699 int index, ret = 0; 700 701 if (!edev) 702 return -EINVAL; 703 704 /* Check whether the property is supported or not */ 705 if (!is_extcon_property_supported(id, prop)) 706 return -EINVAL; 707 708 /* Find the cable index of external connector by using id */ 709 index = find_cable_index_by_id(edev, id); 710 if (index < 0) 711 return index; 712 713 spin_lock_irqsave(&edev->lock, flags); 714 715 /* Check whether the property is available or not. */ 716 if (!is_extcon_property_capability(edev, id, index, prop)) { 717 spin_unlock_irqrestore(&edev->lock, flags); 718 return -EPERM; 719 } 720 721 cable = &edev->cables[index]; 722 723 /* Set the property value according to extcon type */ 724 switch (prop) { 725 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: 726 cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val; 727 break; 728 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: 729 cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val; 730 break; 731 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: 732 cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val; 733 break; 734 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: 735 cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val; 736 break; 737 default: 738 ret = -EINVAL; 739 break; 740 } 741 742 spin_unlock_irqrestore(&edev->lock, flags); 743 744 return ret; 745 } 746 EXPORT_SYMBOL_GPL(extcon_set_property); 747 748 /** 749 * extcon_set_property_sync() - Set property of an external connector with sync. 750 * @edev: the extcon device 751 * @id: the unique id indicating an external connector 752 * @prop: the property id indicating an extcon property 753 * @prop_val: the pointer including the new value of extcon property 754 * 755 * Note that when setting the property value of external connector, 756 * the external connector should be attached. The each property should 757 * be included in the list of supported properties according to extcon type. 758 * 759 * Returns 0 if success or error number if fail. 760 */ 761 int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, 762 unsigned int prop, 763 union extcon_property_value prop_val) 764 { 765 int ret; 766 767 ret = extcon_set_property(edev, id, prop, prop_val); 768 if (ret < 0) 769 return ret; 770 771 return extcon_sync(edev, id); 772 } 773 EXPORT_SYMBOL_GPL(extcon_set_property_sync); 774 775 /** 776 * extcon_get_property_capability() - Get the capability of the property 777 * for an external connector. 778 * @edev: the extcon device 779 * @id: the unique id indicating an external connector 780 * @prop: the property id indicating an extcon property 781 * 782 * Returns 1 if the property is available or 0 if not available. 783 */ 784 int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, 785 unsigned int prop) 786 { 787 int index; 788 789 if (!edev) 790 return -EINVAL; 791 792 /* Check whether the property is supported or not */ 793 if (!is_extcon_property_supported(id, prop)) 794 return -EINVAL; 795 796 /* Find the cable index of external connector by using id */ 797 index = find_cable_index_by_id(edev, id); 798 if (index < 0) 799 return index; 800 801 return is_extcon_property_capability(edev, id, index, prop); 802 } 803 EXPORT_SYMBOL_GPL(extcon_get_property_capability); 804 805 /** 806 * extcon_set_property_capability() - Set the capability of the property 807 * for an external connector. 808 * @edev: the extcon device 809 * @id: the unique id indicating an external connector 810 * @prop: the property id indicating an extcon property 811 * 812 * Note that this function set the capability of the property 813 * for an external connector in order to mark the bit in capability 814 * bitmap which mean the available state of the property. 815 * 816 * Returns 0 if success or error number if fail. 817 */ 818 int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id, 819 unsigned int prop) 820 { 821 struct extcon_cable *cable; 822 int index, type, ret = 0; 823 824 if (!edev) 825 return -EINVAL; 826 827 /* Check whether the property is supported or not. */ 828 if (!is_extcon_property_supported(id, prop)) 829 return -EINVAL; 830 831 /* Find the cable index of external connector by using id. */ 832 index = find_cable_index_by_id(edev, id); 833 if (index < 0) 834 return index; 835 836 type = get_extcon_type(prop); 837 if (type < 0) 838 return type; 839 840 cable = &edev->cables[index]; 841 842 switch (type) { 843 case EXTCON_TYPE_USB: 844 __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits); 845 break; 846 case EXTCON_TYPE_CHG: 847 __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits); 848 break; 849 case EXTCON_TYPE_JACK: 850 __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits); 851 break; 852 case EXTCON_TYPE_DISP: 853 __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits); 854 break; 855 default: 856 ret = -EINVAL; 857 } 858 859 return ret; 860 } 861 EXPORT_SYMBOL_GPL(extcon_set_property_capability); 862 863 /** 864 * extcon_get_extcon_dev() - Get the extcon device instance from the name. 865 * @extcon_name: the extcon name provided with extcon_dev_register() 866 * 867 * Return the pointer of extcon device if success or ERR_PTR(err) if fail. 868 * NOTE: This function returns -EPROBE_DEFER so it may only be called from 869 * probe() functions. 870 */ 871 struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) 872 { 873 struct extcon_dev *sd; 874 875 if (!extcon_name) 876 return ERR_PTR(-EINVAL); 877 878 mutex_lock(&extcon_dev_list_lock); 879 list_for_each_entry(sd, &extcon_dev_list, entry) { 880 if (!strcmp(sd->name, extcon_name)) 881 goto out; 882 } 883 sd = ERR_PTR(-EPROBE_DEFER); 884 out: 885 mutex_unlock(&extcon_dev_list_lock); 886 return sd; 887 } 888 EXPORT_SYMBOL_GPL(extcon_get_extcon_dev); 889 890 /** 891 * extcon_register_notifier() - Register a notifier block to get notified by 892 * any state changes from the extcon. 893 * @edev: the extcon device 894 * @id: the unique id indicating an external connector 895 * @nb: a notifier block to be registered 896 * 897 * Note that the second parameter given to the callback of nb (val) is 898 * the current state of an external connector and the third pameter 899 * is the pointer of extcon device. 900 * 901 * Returns 0 if success or error number if fail. 902 */ 903 int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, 904 struct notifier_block *nb) 905 { 906 unsigned long flags; 907 int ret, idx; 908 909 if (!edev || !nb) 910 return -EINVAL; 911 912 idx = find_cable_index_by_id(edev, id); 913 if (idx < 0) 914 return idx; 915 916 spin_lock_irqsave(&edev->lock, flags); 917 ret = raw_notifier_chain_register(&edev->nh[idx], nb); 918 spin_unlock_irqrestore(&edev->lock, flags); 919 920 return ret; 921 } 922 EXPORT_SYMBOL_GPL(extcon_register_notifier); 923 924 /** 925 * extcon_unregister_notifier() - Unregister a notifier block from the extcon. 926 * @edev: the extcon device 927 * @id: the unique id indicating an external connector 928 * @nb: a notifier block to be registered 929 * 930 * Returns 0 if success or error number if fail. 931 */ 932 int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id, 933 struct notifier_block *nb) 934 { 935 unsigned long flags; 936 int ret, idx; 937 938 if (!edev || !nb) 939 return -EINVAL; 940 941 idx = find_cable_index_by_id(edev, id); 942 if (idx < 0) 943 return idx; 944 945 spin_lock_irqsave(&edev->lock, flags); 946 ret = raw_notifier_chain_unregister(&edev->nh[idx], nb); 947 spin_unlock_irqrestore(&edev->lock, flags); 948 949 return ret; 950 } 951 EXPORT_SYMBOL_GPL(extcon_unregister_notifier); 952 953 /** 954 * extcon_register_notifier_all() - Register a notifier block for all connectors. 955 * @edev: the extcon device 956 * @nb: a notifier block to be registered 957 * 958 * Note that this function registers a notifier block in order to receive 959 * the state change of all supported external connectors from extcon device. 960 * And the second parameter given to the callback of nb (val) is 961 * the current state and the third pameter is the pointer of extcon device. 962 * 963 * Returns 0 if success or error number if fail. 964 */ 965 int extcon_register_notifier_all(struct extcon_dev *edev, 966 struct notifier_block *nb) 967 { 968 unsigned long flags; 969 int ret; 970 971 if (!edev || !nb) 972 return -EINVAL; 973 974 spin_lock_irqsave(&edev->lock, flags); 975 ret = raw_notifier_chain_register(&edev->nh_all, nb); 976 spin_unlock_irqrestore(&edev->lock, flags); 977 978 return ret; 979 } 980 EXPORT_SYMBOL_GPL(extcon_register_notifier_all); 981 982 /** 983 * extcon_unregister_notifier_all() - Unregister a notifier block from extcon. 984 * @edev: the extcon device 985 * @nb: a notifier block to be registered 986 * 987 * Returns 0 if success or error number if fail. 988 */ 989 int extcon_unregister_notifier_all(struct extcon_dev *edev, 990 struct notifier_block *nb) 991 { 992 unsigned long flags; 993 int ret; 994 995 if (!edev || !nb) 996 return -EINVAL; 997 998 spin_lock_irqsave(&edev->lock, flags); 999 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("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 * extcon_dev_allocate() - Allocate the memory of extcon device. 1036 * @supported_cable: the array of the supported external connectors 1037 * ending with EXTCON_NONE. 1038 * 1039 * Note that this function allocates the memory for extcon device 1040 * and initialize default setting for the extcon device. 1041 * 1042 * Returns the pointer memory of allocated extcon_dev if success 1043 * or ERR_PTR(err) if fail. 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 * extcon_dev_free() - Free the memory of extcon device. 1064 * @edev: the extcon device 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 * extcon_dev_register() - Register an new extcon device 1074 * @edev: the extcon device to be registered 1075 * 1076 * Among the members of edev struct, please set the "user initializing data" 1077 * do not set the values of "internal data", which are initialized by 1078 * this function. 1079 * 1080 * Note that before calling this funciton, have to allocate the memory 1081 * of an extcon device by using the extcon_dev_allocate(). And the extcon 1082 * dev should include the supported_cable information. 1083 * 1084 * Returns 0 if success or error number if fail. 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 /* Count the size of mutually_exclusive array */ 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 * extcon_dev_unregister() - Unregister the extcon device. 1293 * @edev: the extcon device to be unregistered. 1294 * 1295 * Note that this does not call kfree(edev) because edev was not allocated 1296 * by this class. 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 * extcon_find_edev_by_node - Find the extcon device from devicetree. 1342 * @node : OF node identifying edev 1343 * 1344 * Return the pointer of extcon device if success or ERR_PTR(err) if fail. 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 * extcon_get_edev_by_phandle - Get the extcon device from devicetree. 1363 * @dev : the instance to the given device 1364 * @index : the index into list of extcon_dev 1365 * 1366 * Return the pointer of extcon device if success or ERR_PTR(err) if fail. 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 /* CONFIG_OF */ 1407 1408 EXPORT_SYMBOL_GPL(extcon_find_edev_by_node); 1409 EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle); 1410 1411 /** 1412 * extcon_get_edev_name() - Get the name of the extcon device. 1413 * @edev: the extcon device 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"); 1437