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 437 state = !!(edev->state & BIT(index)); 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 /* This could be in interrupt handler */ 452 prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); 453 if (!prop_buf) { 454 /* Unlock early before uevent */ 455 spin_unlock_irqrestore(&edev->lock, flags); 456 457 dev_err(&edev->dev, "out of memory in extcon_set_state\n"); 458 kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE); 459 460 return -ENOMEM; 461 } 462 463 length = name_show(&edev->dev, NULL, prop_buf); 464 if (length > 0) { 465 if (prop_buf[length - 1] == '\n') 466 prop_buf[length - 1] = 0; 467 snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf); 468 envp[env_offset++] = name_buf; 469 } 470 471 length = state_show(&edev->dev, NULL, prop_buf); 472 if (length > 0) { 473 if (prop_buf[length - 1] == '\n') 474 prop_buf[length - 1] = 0; 475 snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf); 476 envp[env_offset++] = state_buf; 477 } 478 envp[env_offset] = NULL; 479 480 /* Unlock early before uevent */ 481 spin_unlock_irqrestore(&edev->lock, flags); 482 kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp); 483 free_page((unsigned long)prop_buf); 484 485 return 0; 486 } 487 EXPORT_SYMBOL_GPL(extcon_sync); 488 489 /** 490 * extcon_get_state() - Get the state of an external connector. 491 * @edev: the extcon device 492 * @id: the unique id indicating an external connector 493 * 494 * Returns 0 if success or error number if fail. 495 */ 496 int extcon_get_state(struct extcon_dev *edev, const unsigned int id) 497 { 498 int index, state; 499 unsigned long flags; 500 501 if (!edev) 502 return -EINVAL; 503 504 index = find_cable_index_by_id(edev, id); 505 if (index < 0) 506 return index; 507 508 spin_lock_irqsave(&edev->lock, flags); 509 state = is_extcon_attached(edev, index); 510 spin_unlock_irqrestore(&edev->lock, flags); 511 512 return state; 513 } 514 EXPORT_SYMBOL_GPL(extcon_get_state); 515 516 /** 517 * extcon_set_state() - Set the state of an external connector. 518 * @edev: the extcon device 519 * @id: the unique id indicating an external connector 520 * @state: the new state of an external connector. 521 * the default semantics is true: attached / false: detached. 522 * 523 * Note that this function set the state of an external connector without 524 * a notification. To synchronize the state of an external connector, 525 * have to use extcon_set_state_sync() and extcon_sync(). 526 * 527 * Returns 0 if success or error number if fail. 528 */ 529 int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool state) 530 { 531 unsigned long flags; 532 int index, ret = 0; 533 534 if (!edev) 535 return -EINVAL; 536 537 index = find_cable_index_by_id(edev, id); 538 if (index < 0) 539 return index; 540 541 spin_lock_irqsave(&edev->lock, flags); 542 543 /* Check whether the external connector's state is changed. */ 544 if (!is_extcon_changed(edev, index, state)) 545 goto out; 546 547 if (check_mutually_exclusive(edev, 548 (edev->state & ~BIT(index)) | (state & BIT(index)))) { 549 ret = -EPERM; 550 goto out; 551 } 552 553 /* 554 * Initialize the value of extcon property before setting 555 * the detached state for an external connector. 556 */ 557 if (!state) 558 init_property(edev, id, index); 559 560 /* Update the state for an external connector. */ 561 if (state) 562 edev->state |= BIT(index); 563 else 564 edev->state &= ~(BIT(index)); 565 out: 566 spin_unlock_irqrestore(&edev->lock, flags); 567 568 return ret; 569 } 570 EXPORT_SYMBOL_GPL(extcon_set_state); 571 572 /** 573 * extcon_set_state_sync() - Set the state of an external connector with sync. 574 * @edev: the extcon device 575 * @id: the unique id indicating an external connector 576 * @state: the new state of external connector. 577 * the default semantics is true: attached / false: detached. 578 * 579 * Note that this function set the state of external connector 580 * and synchronize the state by sending a notification. 581 * 582 * Returns 0 if success or error number if fail. 583 */ 584 int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state) 585 { 586 int ret, index; 587 unsigned long flags; 588 589 index = find_cable_index_by_id(edev, id); 590 if (index < 0) 591 return index; 592 593 /* Check whether the external connector's state is changed. */ 594 spin_lock_irqsave(&edev->lock, flags); 595 ret = is_extcon_changed(edev, index, state); 596 spin_unlock_irqrestore(&edev->lock, flags); 597 if (!ret) 598 return 0; 599 600 ret = extcon_set_state(edev, id, state); 601 if (ret < 0) 602 return ret; 603 604 return extcon_sync(edev, id); 605 } 606 EXPORT_SYMBOL_GPL(extcon_set_state_sync); 607 608 /** 609 * extcon_get_property() - Get the property value of an external connector. 610 * @edev: the extcon device 611 * @id: the unique id indicating an external connector 612 * @prop: the property id indicating an extcon property 613 * @prop_val: the pointer which store the value of extcon property 614 * 615 * Note that when getting the property value of external connector, 616 * the external connector should be attached. If detached state, function 617 * return 0 without property value. Also, the each property should be 618 * included in the list of supported properties according to extcon type. 619 * 620 * Returns 0 if success or error number if fail. 621 */ 622 int extcon_get_property(struct extcon_dev *edev, unsigned int id, 623 unsigned int prop, 624 union extcon_property_value *prop_val) 625 { 626 struct extcon_cable *cable; 627 unsigned long flags; 628 int index, ret = 0; 629 630 *prop_val = (union extcon_property_value)(0); 631 632 if (!edev) 633 return -EINVAL; 634 635 /* Check whether the property is supported or not */ 636 if (!is_extcon_property_supported(id, prop)) 637 return -EINVAL; 638 639 /* Find the cable index of external connector by using id */ 640 index = find_cable_index_by_id(edev, id); 641 if (index < 0) 642 return index; 643 644 spin_lock_irqsave(&edev->lock, flags); 645 646 /* Check whether the property is available or not. */ 647 if (!is_extcon_property_capability(edev, id, index, prop)) { 648 spin_unlock_irqrestore(&edev->lock, flags); 649 return -EPERM; 650 } 651 652 /* 653 * Check whether the external connector is attached. 654 * If external connector is detached, the user can not 655 * get the property value. 656 */ 657 if (!is_extcon_attached(edev, index)) { 658 spin_unlock_irqrestore(&edev->lock, flags); 659 return 0; 660 } 661 662 cable = &edev->cables[index]; 663 664 /* Get the property value according to extcon type */ 665 switch (prop) { 666 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: 667 *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN]; 668 break; 669 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: 670 *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN]; 671 break; 672 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: 673 *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN]; 674 break; 675 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: 676 *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN]; 677 break; 678 default: 679 ret = -EINVAL; 680 break; 681 } 682 683 spin_unlock_irqrestore(&edev->lock, flags); 684 685 return ret; 686 } 687 EXPORT_SYMBOL_GPL(extcon_get_property); 688 689 /** 690 * extcon_set_property() - Set the property value of an external connector. 691 * @edev: the extcon device 692 * @id: the unique id indicating an external connector 693 * @prop: the property id indicating an extcon property 694 * @prop_val: the pointer including the new value of extcon property 695 * 696 * Note that each property should be included in the list of supported 697 * properties according to the extcon type. 698 * 699 * Returns 0 if success or error number if fail. 700 */ 701 int extcon_set_property(struct extcon_dev *edev, unsigned int id, 702 unsigned int prop, 703 union extcon_property_value prop_val) 704 { 705 struct extcon_cable *cable; 706 unsigned long flags; 707 int index, ret = 0; 708 709 if (!edev) 710 return -EINVAL; 711 712 /* Check whether the property is supported or not */ 713 if (!is_extcon_property_supported(id, prop)) 714 return -EINVAL; 715 716 /* Find the cable index of external connector by using id */ 717 index = find_cable_index_by_id(edev, id); 718 if (index < 0) 719 return index; 720 721 spin_lock_irqsave(&edev->lock, flags); 722 723 /* Check whether the property is available or not. */ 724 if (!is_extcon_property_capability(edev, id, index, prop)) { 725 spin_unlock_irqrestore(&edev->lock, flags); 726 return -EPERM; 727 } 728 729 cable = &edev->cables[index]; 730 731 /* Set the property value according to extcon type */ 732 switch (prop) { 733 case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX: 734 cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val; 735 break; 736 case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX: 737 cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val; 738 break; 739 case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX: 740 cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val; 741 break; 742 case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX: 743 cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val; 744 break; 745 default: 746 ret = -EINVAL; 747 break; 748 } 749 750 spin_unlock_irqrestore(&edev->lock, flags); 751 752 return ret; 753 } 754 EXPORT_SYMBOL_GPL(extcon_set_property); 755 756 /** 757 * extcon_set_property_sync() - Set property of an external connector with sync. 758 * @prop_val: the pointer including the new value of extcon property 759 * 760 * Note that when setting the property value of external connector, 761 * the external connector should be attached. The each property should 762 * be included in the list of supported properties according to extcon type. 763 * 764 * Returns 0 if success or error number if fail. 765 */ 766 int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, 767 unsigned int prop, 768 union extcon_property_value prop_val) 769 { 770 int ret; 771 772 ret = extcon_set_property(edev, id, prop, prop_val); 773 if (ret < 0) 774 return ret; 775 776 return extcon_sync(edev, id); 777 } 778 EXPORT_SYMBOL_GPL(extcon_set_property_sync); 779 780 /** 781 * extcon_get_property_capability() - Get the capability of the property 782 * for an external connector. 783 * @edev: the extcon device 784 * @id: the unique id indicating an external connector 785 * @prop: the property id indicating an extcon property 786 * 787 * Returns 1 if the property is available or 0 if not available. 788 */ 789 int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, 790 unsigned int prop) 791 { 792 int index; 793 794 if (!edev) 795 return -EINVAL; 796 797 /* Check whether the property is supported or not */ 798 if (!is_extcon_property_supported(id, prop)) 799 return -EINVAL; 800 801 /* Find the cable index of external connector by using id */ 802 index = find_cable_index_by_id(edev, id); 803 if (index < 0) 804 return index; 805 806 return is_extcon_property_capability(edev, id, index, prop); 807 } 808 EXPORT_SYMBOL_GPL(extcon_get_property_capability); 809 810 /** 811 * extcon_set_property_capability() - Set the capability of the property 812 * for an external connector. 813 * @edev: the extcon device 814 * @id: the unique id indicating an external connector 815 * @prop: the property id indicating an extcon property 816 * 817 * Note that this function set the capability of the property 818 * for an external connector in order to mark the bit in capability 819 * bitmap which mean the available state of the property. 820 * 821 * Returns 0 if success or error number if fail. 822 */ 823 int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id, 824 unsigned int prop) 825 { 826 struct extcon_cable *cable; 827 int index, type, ret = 0; 828 829 if (!edev) 830 return -EINVAL; 831 832 /* Check whether the property is supported or not. */ 833 if (!is_extcon_property_supported(id, prop)) 834 return -EINVAL; 835 836 /* Find the cable index of external connector by using id. */ 837 index = find_cable_index_by_id(edev, id); 838 if (index < 0) 839 return index; 840 841 type = get_extcon_type(prop); 842 if (type < 0) 843 return type; 844 845 cable = &edev->cables[index]; 846 847 switch (type) { 848 case EXTCON_TYPE_USB: 849 __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits); 850 break; 851 case EXTCON_TYPE_CHG: 852 __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits); 853 break; 854 case EXTCON_TYPE_JACK: 855 __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits); 856 break; 857 case EXTCON_TYPE_DISP: 858 __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits); 859 break; 860 default: 861 ret = -EINVAL; 862 } 863 864 return ret; 865 } 866 EXPORT_SYMBOL_GPL(extcon_set_property_capability); 867 868 /** 869 * extcon_get_extcon_dev() - Get the extcon device instance from the name. 870 * @extcon_name: the extcon name provided with extcon_dev_register() 871 * 872 * Return the pointer of extcon device if success or ERR_PTR(err) if fail. 873 */ 874 struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) 875 { 876 struct extcon_dev *sd; 877 878 if (!extcon_name) 879 return ERR_PTR(-EINVAL); 880 881 mutex_lock(&extcon_dev_list_lock); 882 list_for_each_entry(sd, &extcon_dev_list, entry) { 883 if (!strcmp(sd->name, extcon_name)) 884 goto out; 885 } 886 sd = NULL; 887 out: 888 mutex_unlock(&extcon_dev_list_lock); 889 return sd; 890 } 891 EXPORT_SYMBOL_GPL(extcon_get_extcon_dev); 892 893 /** 894 * extcon_register_notifier() - Register a notifier block to get notified by 895 * any state changes from the extcon. 896 * @edev: the extcon device 897 * @id: the unique id indicating an external connector 898 * @nb: a notifier block to be registered 899 * 900 * Note that the second parameter given to the callback of nb (val) is 901 * the current state of an external connector and the third pameter 902 * is the pointer of extcon device. 903 * 904 * Returns 0 if success or error number if fail. 905 */ 906 int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, 907 struct notifier_block *nb) 908 { 909 unsigned long flags; 910 int ret, idx = -EINVAL; 911 912 if (!edev || !nb) 913 return -EINVAL; 914 915 idx = find_cable_index_by_id(edev, id); 916 if (idx < 0) 917 return idx; 918 919 spin_lock_irqsave(&edev->lock, flags); 920 ret = raw_notifier_chain_register(&edev->nh[idx], nb); 921 spin_unlock_irqrestore(&edev->lock, flags); 922 923 return ret; 924 } 925 EXPORT_SYMBOL_GPL(extcon_register_notifier); 926 927 /** 928 * extcon_unregister_notifier() - Unregister a notifier block from the extcon. 929 * @edev: the extcon device 930 * @id: the unique id indicating an external connector 931 * @nb: a notifier block to be registered 932 * 933 * Returns 0 if success or error number if fail. 934 */ 935 int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id, 936 struct notifier_block *nb) 937 { 938 unsigned long flags; 939 int ret, idx; 940 941 if (!edev || !nb) 942 return -EINVAL; 943 944 idx = find_cable_index_by_id(edev, id); 945 if (idx < 0) 946 return idx; 947 948 spin_lock_irqsave(&edev->lock, flags); 949 ret = raw_notifier_chain_unregister(&edev->nh[idx], nb); 950 spin_unlock_irqrestore(&edev->lock, flags); 951 952 return ret; 953 } 954 EXPORT_SYMBOL_GPL(extcon_unregister_notifier); 955 956 /** 957 * extcon_register_notifier_all() - Register a notifier block for all connectors. 958 * @edev: the extcon device 959 * @nb: a notifier block to be registered 960 * 961 * Note that this function registers a notifier block in order to receive 962 * the state change of all supported external connectors from extcon device. 963 * And the second parameter given to the callback of nb (val) is 964 * the current state and the third pameter is the pointer of extcon device. 965 * 966 * Returns 0 if success or error number if fail. 967 */ 968 int extcon_register_notifier_all(struct extcon_dev *edev, 969 struct notifier_block *nb) 970 { 971 unsigned long flags; 972 int ret; 973 974 if (!edev || !nb) 975 return -EINVAL; 976 977 spin_lock_irqsave(&edev->lock, flags); 978 ret = raw_notifier_chain_register(&edev->nh_all, nb); 979 spin_unlock_irqrestore(&edev->lock, flags); 980 981 return ret; 982 } 983 EXPORT_SYMBOL_GPL(extcon_register_notifier_all); 984 985 /** 986 * extcon_unregister_notifier_all() - Unregister a notifier block from extcon. 987 * @edev: the extcon device 988 * @nb: a notifier block to be registered 989 * 990 * Returns 0 if success or error number if fail. 991 */ 992 int extcon_unregister_notifier_all(struct extcon_dev *edev, 993 struct notifier_block *nb) 994 { 995 unsigned long flags; 996 int ret; 997 998 if (!edev || !nb) 999 return -EINVAL; 1000 1001 spin_lock_irqsave(&edev->lock, flags); 1002 ret = raw_notifier_chain_unregister(&edev->nh_all, nb); 1003 spin_unlock_irqrestore(&edev->lock, flags); 1004 1005 return ret; 1006 } 1007 EXPORT_SYMBOL_GPL(extcon_unregister_notifier_all); 1008 1009 static struct attribute *extcon_attrs[] = { 1010 &dev_attr_state.attr, 1011 &dev_attr_name.attr, 1012 NULL, 1013 }; 1014 ATTRIBUTE_GROUPS(extcon); 1015 1016 static int create_extcon_class(void) 1017 { 1018 if (!extcon_class) { 1019 extcon_class = class_create(THIS_MODULE, "extcon"); 1020 if (IS_ERR(extcon_class)) 1021 return PTR_ERR(extcon_class); 1022 extcon_class->dev_groups = extcon_groups; 1023 } 1024 1025 return 0; 1026 } 1027 1028 static void extcon_dev_release(struct device *dev) 1029 { 1030 } 1031 1032 static const char *muex_name = "mutually_exclusive"; 1033 static void dummy_sysfs_dev_release(struct device *dev) 1034 { 1035 } 1036 1037 /* 1038 * extcon_dev_allocate() - Allocate the memory of extcon device. 1039 * @supported_cable: the array of the supported external connectors 1040 * ending with EXTCON_NONE. 1041 * 1042 * Note that this function allocates the memory for extcon device 1043 * and initialize default setting for the extcon device. 1044 * 1045 * Returns the pointer memory of allocated extcon_dev if success 1046 * or ERR_PTR(err) if fail. 1047 */ 1048 struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable) 1049 { 1050 struct extcon_dev *edev; 1051 1052 if (!supported_cable) 1053 return ERR_PTR(-EINVAL); 1054 1055 edev = kzalloc(sizeof(*edev), GFP_KERNEL); 1056 if (!edev) 1057 return ERR_PTR(-ENOMEM); 1058 1059 edev->max_supported = 0; 1060 edev->supported_cable = supported_cable; 1061 1062 return edev; 1063 } 1064 1065 /* 1066 * extcon_dev_free() - Free the memory of extcon device. 1067 * @edev: the extcon device 1068 */ 1069 void extcon_dev_free(struct extcon_dev *edev) 1070 { 1071 kfree(edev); 1072 } 1073 EXPORT_SYMBOL_GPL(extcon_dev_free); 1074 1075 /** 1076 * extcon_dev_register() - Register an new extcon device 1077 * @edev: the extcon device to be registered 1078 * 1079 * Among the members of edev struct, please set the "user initializing data" 1080 * do not set the values of "internal data", which are initialized by 1081 * this function. 1082 * 1083 * Note that before calling this funciton, have to allocate the memory 1084 * of an extcon device by using the extcon_dev_allocate(). And the extcon 1085 * dev should include the supported_cable information. 1086 * 1087 * Returns 0 if success or error number if fail. 1088 */ 1089 int extcon_dev_register(struct extcon_dev *edev) 1090 { 1091 int ret, index = 0; 1092 static atomic_t edev_no = ATOMIC_INIT(-1); 1093 1094 if (!extcon_class) { 1095 ret = create_extcon_class(); 1096 if (ret < 0) 1097 return ret; 1098 } 1099 1100 if (!edev || !edev->supported_cable) 1101 return -EINVAL; 1102 1103 for (; edev->supported_cable[index] != EXTCON_NONE; index++); 1104 1105 edev->max_supported = index; 1106 if (index > SUPPORTED_CABLE_MAX) { 1107 dev_err(&edev->dev, 1108 "exceed the maximum number of supported cables\n"); 1109 return -EINVAL; 1110 } 1111 1112 edev->dev.class = extcon_class; 1113 edev->dev.release = extcon_dev_release; 1114 1115 edev->name = dev_name(edev->dev.parent); 1116 if (IS_ERR_OR_NULL(edev->name)) { 1117 dev_err(&edev->dev, 1118 "extcon device name is null\n"); 1119 return -EINVAL; 1120 } 1121 dev_set_name(&edev->dev, "extcon%lu", 1122 (unsigned long)atomic_inc_return(&edev_no)); 1123 1124 if (edev->max_supported) { 1125 char buf[10]; 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 snprintf(buf, 10, "cable.%d", index); 1140 str = kzalloc(strlen(buf) + 1, 1141 GFP_KERNEL); 1142 if (!str) { 1143 for (index--; index >= 0; index--) { 1144 cable = &edev->cables[index]; 1145 kfree(cable->attr_g.name); 1146 } 1147 ret = -ENOMEM; 1148 1149 goto err_alloc_cables; 1150 } 1151 strcpy(str, buf); 1152 1153 cable->edev = edev; 1154 cable->cable_index = index; 1155 cable->attrs[0] = &cable->attr_name.attr; 1156 cable->attrs[1] = &cable->attr_state.attr; 1157 cable->attrs[2] = NULL; 1158 cable->attr_g.name = str; 1159 cable->attr_g.attrs = cable->attrs; 1160 1161 sysfs_attr_init(&cable->attr_name.attr); 1162 cable->attr_name.attr.name = "name"; 1163 cable->attr_name.attr.mode = 0444; 1164 cable->attr_name.show = cable_name_show; 1165 1166 sysfs_attr_init(&cable->attr_state.attr); 1167 cable->attr_state.attr.name = "state"; 1168 cable->attr_state.attr.mode = 0444; 1169 cable->attr_state.show = cable_state_show; 1170 } 1171 } 1172 1173 if (edev->max_supported && edev->mutually_exclusive) { 1174 char buf[80]; 1175 char *name; 1176 1177 /* Count the size of mutually_exclusive array */ 1178 for (index = 0; edev->mutually_exclusive[index]; index++) 1179 ; 1180 1181 edev->attrs_muex = kcalloc(index + 1, 1182 sizeof(struct attribute *), 1183 GFP_KERNEL); 1184 if (!edev->attrs_muex) { 1185 ret = -ENOMEM; 1186 goto err_muex; 1187 } 1188 1189 edev->d_attrs_muex = kcalloc(index, 1190 sizeof(struct device_attribute), 1191 GFP_KERNEL); 1192 if (!edev->d_attrs_muex) { 1193 ret = -ENOMEM; 1194 kfree(edev->attrs_muex); 1195 goto err_muex; 1196 } 1197 1198 for (index = 0; edev->mutually_exclusive[index]; index++) { 1199 sprintf(buf, "0x%x", edev->mutually_exclusive[index]); 1200 name = kzalloc(strlen(buf) + 1, 1201 GFP_KERNEL); 1202 if (!name) { 1203 for (index--; index >= 0; index--) { 1204 kfree(edev->d_attrs_muex[index].attr. 1205 name); 1206 } 1207 kfree(edev->d_attrs_muex); 1208 kfree(edev->attrs_muex); 1209 ret = -ENOMEM; 1210 goto err_muex; 1211 } 1212 strcpy(name, buf); 1213 sysfs_attr_init(&edev->d_attrs_muex[index].attr); 1214 edev->d_attrs_muex[index].attr.name = name; 1215 edev->d_attrs_muex[index].attr.mode = 0000; 1216 edev->attrs_muex[index] = &edev->d_attrs_muex[index] 1217 .attr; 1218 } 1219 edev->attr_g_muex.name = muex_name; 1220 edev->attr_g_muex.attrs = edev->attrs_muex; 1221 1222 } 1223 1224 if (edev->max_supported) { 1225 edev->extcon_dev_type.groups = 1226 kcalloc(edev->max_supported + 2, 1227 sizeof(struct attribute_group *), 1228 GFP_KERNEL); 1229 if (!edev->extcon_dev_type.groups) { 1230 ret = -ENOMEM; 1231 goto err_alloc_groups; 1232 } 1233 1234 edev->extcon_dev_type.name = dev_name(&edev->dev); 1235 edev->extcon_dev_type.release = dummy_sysfs_dev_release; 1236 1237 for (index = 0; index < edev->max_supported; index++) 1238 edev->extcon_dev_type.groups[index] = 1239 &edev->cables[index].attr_g; 1240 if (edev->mutually_exclusive) 1241 edev->extcon_dev_type.groups[index] = 1242 &edev->attr_g_muex; 1243 1244 edev->dev.type = &edev->extcon_dev_type; 1245 } 1246 1247 ret = device_register(&edev->dev); 1248 if (ret) { 1249 put_device(&edev->dev); 1250 goto err_dev; 1251 } 1252 1253 spin_lock_init(&edev->lock); 1254 edev->nh = devm_kcalloc(&edev->dev, edev->max_supported, 1255 sizeof(*edev->nh), GFP_KERNEL); 1256 if (!edev->nh) { 1257 ret = -ENOMEM; 1258 goto err_dev; 1259 } 1260 1261 for (index = 0; index < edev->max_supported; index++) 1262 RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]); 1263 1264 RAW_INIT_NOTIFIER_HEAD(&edev->nh_all); 1265 1266 dev_set_drvdata(&edev->dev, edev); 1267 edev->state = 0; 1268 1269 mutex_lock(&extcon_dev_list_lock); 1270 list_add(&edev->entry, &extcon_dev_list); 1271 mutex_unlock(&extcon_dev_list_lock); 1272 1273 return 0; 1274 1275 err_dev: 1276 if (edev->max_supported) 1277 kfree(edev->extcon_dev_type.groups); 1278 err_alloc_groups: 1279 if (edev->max_supported && edev->mutually_exclusive) { 1280 for (index = 0; edev->mutually_exclusive[index]; index++) 1281 kfree(edev->d_attrs_muex[index].attr.name); 1282 kfree(edev->d_attrs_muex); 1283 kfree(edev->attrs_muex); 1284 } 1285 err_muex: 1286 for (index = 0; index < edev->max_supported; index++) 1287 kfree(edev->cables[index].attr_g.name); 1288 err_alloc_cables: 1289 if (edev->max_supported) 1290 kfree(edev->cables); 1291 err_sysfs_alloc: 1292 return ret; 1293 } 1294 EXPORT_SYMBOL_GPL(extcon_dev_register); 1295 1296 /** 1297 * extcon_dev_unregister() - Unregister the extcon device. 1298 * @edev: the extcon device to be unregistered. 1299 * 1300 * Note that this does not call kfree(edev) because edev was not allocated 1301 * by this class. 1302 */ 1303 void extcon_dev_unregister(struct extcon_dev *edev) 1304 { 1305 int index; 1306 1307 if (!edev) 1308 return; 1309 1310 mutex_lock(&extcon_dev_list_lock); 1311 list_del(&edev->entry); 1312 mutex_unlock(&extcon_dev_list_lock); 1313 1314 if (IS_ERR_OR_NULL(get_device(&edev->dev))) { 1315 dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n", 1316 dev_name(&edev->dev)); 1317 return; 1318 } 1319 1320 device_unregister(&edev->dev); 1321 1322 if (edev->mutually_exclusive && edev->max_supported) { 1323 for (index = 0; edev->mutually_exclusive[index]; 1324 index++) 1325 kfree(edev->d_attrs_muex[index].attr.name); 1326 kfree(edev->d_attrs_muex); 1327 kfree(edev->attrs_muex); 1328 } 1329 1330 for (index = 0; index < edev->max_supported; index++) 1331 kfree(edev->cables[index].attr_g.name); 1332 1333 if (edev->max_supported) { 1334 kfree(edev->extcon_dev_type.groups); 1335 kfree(edev->cables); 1336 } 1337 1338 put_device(&edev->dev); 1339 } 1340 EXPORT_SYMBOL_GPL(extcon_dev_unregister); 1341 1342 #ifdef CONFIG_OF 1343 1344 /* 1345 * extcon_find_edev_by_node - Find the extcon device from devicetree. 1346 * @node : OF node identifying edev 1347 * 1348 * Return the pointer of extcon device if success or ERR_PTR(err) if fail. 1349 */ 1350 struct extcon_dev *extcon_find_edev_by_node(struct device_node *node) 1351 { 1352 struct extcon_dev *edev; 1353 1354 mutex_lock(&extcon_dev_list_lock); 1355 list_for_each_entry(edev, &extcon_dev_list, entry) 1356 if (edev->dev.parent && edev->dev.parent->of_node == node) 1357 goto out; 1358 edev = ERR_PTR(-EPROBE_DEFER); 1359 out: 1360 mutex_unlock(&extcon_dev_list_lock); 1361 1362 return edev; 1363 } 1364 1365 /* 1366 * extcon_get_edev_by_phandle - Get the extcon device from devicetree. 1367 * @dev : the instance to the given device 1368 * @index : the index into list of extcon_dev 1369 * 1370 * Return the pointer of extcon device if success or ERR_PTR(err) if fail. 1371 */ 1372 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) 1373 { 1374 struct device_node *node; 1375 struct extcon_dev *edev; 1376 1377 if (!dev) 1378 return ERR_PTR(-EINVAL); 1379 1380 if (!dev->of_node) { 1381 dev_dbg(dev, "device does not have a device node entry\n"); 1382 return ERR_PTR(-EINVAL); 1383 } 1384 1385 node = of_parse_phandle(dev->of_node, "extcon", index); 1386 if (!node) { 1387 dev_dbg(dev, "failed to get phandle in %pOF node\n", 1388 dev->of_node); 1389 return ERR_PTR(-ENODEV); 1390 } 1391 1392 edev = extcon_find_edev_by_node(node); 1393 of_node_put(node); 1394 1395 return edev; 1396 } 1397 1398 #else 1399 1400 struct extcon_dev *extcon_find_edev_by_node(struct device_node *node) 1401 { 1402 return ERR_PTR(-ENOSYS); 1403 } 1404 1405 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) 1406 { 1407 return ERR_PTR(-ENOSYS); 1408 } 1409 1410 #endif /* CONFIG_OF */ 1411 1412 EXPORT_SYMBOL_GPL(extcon_find_edev_by_node); 1413 EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle); 1414 1415 /** 1416 * extcon_get_edev_name() - Get the name of the extcon device. 1417 * @edev: the extcon device 1418 */ 1419 const char *extcon_get_edev_name(struct extcon_dev *edev) 1420 { 1421 return !edev ? NULL : edev->name; 1422 } 1423 1424 static int __init extcon_class_init(void) 1425 { 1426 return create_extcon_class(); 1427 } 1428 module_init(extcon_class_init); 1429 1430 static void __exit extcon_class_exit(void) 1431 { 1432 class_destroy(extcon_class); 1433 } 1434 module_exit(extcon_class_exit); 1435 1436 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 1437 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 1438 MODULE_DESCRIPTION("External Connector (extcon) framework"); 1439 MODULE_LICENSE("GPL v2"); 1440