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