extcon.c 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313
  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. #include <linux/module.h>
  27. #include <linux/types.h>
  28. #include <linux/init.h>
  29. #include <linux/device.h>
  30. #include <linux/fs.h>
  31. #include <linux/err.h>
  32. #include <linux/extcon.h>
  33. #include <linux/of.h>
  34. #include <linux/slab.h>
  35. #include <linux/sysfs.h>
  36. #define SUPPORTED_CABLE_MAX 32
  37. #define CABLE_NAME_MAX 30
  38. struct __extcon_info {
  39. unsigned int type;
  40. unsigned int id;
  41. const char *name;
  42. } extcon_info[] = {
  43. [EXTCON_NONE] = {
  44. .type = EXTCON_TYPE_MISC,
  45. .id = EXTCON_NONE,
  46. .name = "NONE",
  47. },
  48. /* USB external connector */
  49. [EXTCON_USB] = {
  50. .type = EXTCON_TYPE_USB,
  51. .id = EXTCON_USB,
  52. .name = "USB",
  53. },
  54. [EXTCON_USB_HOST] = {
  55. .type = EXTCON_TYPE_USB,
  56. .id = EXTCON_USB_HOST,
  57. .name = "USB_HOST",
  58. },
  59. /* Charging external connector */
  60. [EXTCON_CHG_USB_SDP] = {
  61. .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
  62. .id = EXTCON_CHG_USB_SDP,
  63. .name = "SDP",
  64. },
  65. [EXTCON_CHG_USB_DCP] = {
  66. .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
  67. .id = EXTCON_CHG_USB_DCP,
  68. .name = "DCP",
  69. },
  70. [EXTCON_CHG_USB_CDP] = {
  71. .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
  72. .id = EXTCON_CHG_USB_CDP,
  73. .name = "CDP",
  74. },
  75. [EXTCON_CHG_USB_ACA] = {
  76. .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
  77. .id = EXTCON_CHG_USB_ACA,
  78. .name = "ACA",
  79. },
  80. [EXTCON_CHG_USB_FAST] = {
  81. .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
  82. .id = EXTCON_CHG_USB_FAST,
  83. .name = "FAST-CHARGER",
  84. },
  85. [EXTCON_CHG_USB_SLOW] = {
  86. .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
  87. .id = EXTCON_CHG_USB_SLOW,
  88. .name = "SLOW-CHARGER",
  89. },
  90. /* Jack external connector */
  91. [EXTCON_JACK_MICROPHONE] = {
  92. .type = EXTCON_TYPE_JACK,
  93. .id = EXTCON_JACK_MICROPHONE,
  94. .name = "MICROPHONE",
  95. },
  96. [EXTCON_JACK_HEADPHONE] = {
  97. .type = EXTCON_TYPE_JACK,
  98. .id = EXTCON_JACK_HEADPHONE,
  99. .name = "HEADPHONE",
  100. },
  101. [EXTCON_JACK_LINE_IN] = {
  102. .type = EXTCON_TYPE_JACK,
  103. .id = EXTCON_JACK_LINE_IN,
  104. .name = "LINE-IN",
  105. },
  106. [EXTCON_JACK_LINE_OUT] = {
  107. .type = EXTCON_TYPE_JACK,
  108. .id = EXTCON_JACK_LINE_OUT,
  109. .name = "LINE-OUT",
  110. },
  111. [EXTCON_JACK_VIDEO_IN] = {
  112. .type = EXTCON_TYPE_JACK,
  113. .id = EXTCON_JACK_VIDEO_IN,
  114. .name = "VIDEO-IN",
  115. },
  116. [EXTCON_JACK_VIDEO_OUT] = {
  117. .type = EXTCON_TYPE_JACK,
  118. .id = EXTCON_JACK_VIDEO_OUT,
  119. .name = "VIDEO-OUT",
  120. },
  121. [EXTCON_JACK_SPDIF_IN] = {
  122. .type = EXTCON_TYPE_JACK,
  123. .id = EXTCON_JACK_SPDIF_IN,
  124. .name = "SPDIF-IN",
  125. },
  126. [EXTCON_JACK_SPDIF_OUT] = {
  127. .type = EXTCON_TYPE_JACK,
  128. .id = EXTCON_JACK_SPDIF_OUT,
  129. .name = "SPDIF-OUT",
  130. },
  131. /* Display external connector */
  132. [EXTCON_DISP_HDMI] = {
  133. .type = EXTCON_TYPE_DISP,
  134. .id = EXTCON_DISP_HDMI,
  135. .name = "HDMI",
  136. },
  137. [EXTCON_DISP_MHL] = {
  138. .type = EXTCON_TYPE_DISP,
  139. .id = EXTCON_DISP_MHL,
  140. .name = "MHL",
  141. },
  142. [EXTCON_DISP_DVI] = {
  143. .type = EXTCON_TYPE_DISP,
  144. .id = EXTCON_DISP_DVI,
  145. .name = "DVI",
  146. },
  147. [EXTCON_DISP_VGA] = {
  148. .type = EXTCON_TYPE_DISP,
  149. .id = EXTCON_DISP_VGA,
  150. .name = "VGA",
  151. },
  152. /* Miscellaneous external connector */
  153. [EXTCON_DOCK] = {
  154. .type = EXTCON_TYPE_MISC,
  155. .id = EXTCON_DOCK,
  156. .name = "DOCK",
  157. },
  158. [EXTCON_JIG] = {
  159. .type = EXTCON_TYPE_MISC,
  160. .id = EXTCON_JIG,
  161. .name = "JIG",
  162. },
  163. [EXTCON_MECHANICAL] = {
  164. .type = EXTCON_TYPE_MISC,
  165. .id = EXTCON_MECHANICAL,
  166. .name = "MECHANICAL",
  167. },
  168. { /* sentinel */ }
  169. };
  170. /**
  171. * struct extcon_cable - An internal data for each cable of extcon device.
  172. * @edev: The extcon device
  173. * @cable_index: Index of this cable in the edev
  174. * @attr_g: Attribute group for the cable
  175. * @attr_name: "name" sysfs entry
  176. * @attr_state: "state" sysfs entry
  177. * @attrs: Array pointing to attr_name and attr_state for attr_g
  178. */
  179. struct extcon_cable {
  180. struct extcon_dev *edev;
  181. int cable_index;
  182. struct attribute_group attr_g;
  183. struct device_attribute attr_name;
  184. struct device_attribute attr_state;
  185. struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
  186. union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT];
  187. union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT];
  188. union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT];
  189. union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT];
  190. unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)];
  191. unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)];
  192. unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)];
  193. unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)];
  194. };
  195. static struct class *extcon_class;
  196. #if defined(CONFIG_ANDROID)
  197. static struct class_compat *switch_class;
  198. #endif /* CONFIG_ANDROID */
  199. static LIST_HEAD(extcon_dev_list);
  200. static DEFINE_MUTEX(extcon_dev_list_lock);
  201. /**
  202. * check_mutually_exclusive - Check if new_state violates mutually_exclusive
  203. * condition.
  204. * @edev: the extcon device
  205. * @new_state: new cable attach status for @edev
  206. *
  207. * Returns 0 if nothing violates. Returns the index + 1 for the first
  208. * violated condition.
  209. */
  210. static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
  211. {
  212. int i = 0;
  213. if (!edev->mutually_exclusive)
  214. return 0;
  215. for (i = 0; edev->mutually_exclusive[i]; i++) {
  216. int weight;
  217. u32 correspondants = new_state & edev->mutually_exclusive[i];
  218. /* calculate the total number of bits set */
  219. weight = hweight32(correspondants);
  220. if (weight > 1)
  221. return i + 1;
  222. }
  223. return 0;
  224. }
  225. static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id)
  226. {
  227. int i;
  228. /* Find the the index of extcon cable in edev->supported_cable */
  229. for (i = 0; i < edev->max_supported; i++) {
  230. if (edev->supported_cable[i] == id)
  231. return i;
  232. }
  233. return -EINVAL;
  234. }
  235. static int get_extcon_type(unsigned int prop)
  236. {
  237. switch (prop) {
  238. case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
  239. return EXTCON_TYPE_USB;
  240. case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
  241. return EXTCON_TYPE_CHG;
  242. case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
  243. return EXTCON_TYPE_JACK;
  244. case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
  245. return EXTCON_TYPE_DISP;
  246. default:
  247. return -EINVAL;
  248. }
  249. }
  250. static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index)
  251. {
  252. return !!(edev->state & BIT(index));
  253. }
  254. static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
  255. {
  256. if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
  257. *attached = ((new >> idx) & 0x1) ? true : false;
  258. return true;
  259. }
  260. return false;
  261. }
  262. static bool is_extcon_property_supported(unsigned int id, unsigned int prop)
  263. {
  264. int type;
  265. /* Check whether the property is supported or not. */
  266. type = get_extcon_type(prop);
  267. if (type < 0)
  268. return false;
  269. /* Check whether a specific extcon id supports the property or not. */
  270. return !!(extcon_info[id].type & type);
  271. }
  272. static int is_extcon_property_capability(struct extcon_dev *edev,
  273. unsigned int id, int index,unsigned int prop)
  274. {
  275. struct extcon_cable *cable;
  276. int type, ret;
  277. /* Check whether the property is supported or not. */
  278. type = get_extcon_type(prop);
  279. if (type < 0)
  280. return type;
  281. cable = &edev->cables[index];
  282. switch (type) {
  283. case EXTCON_TYPE_USB:
  284. ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
  285. break;
  286. case EXTCON_TYPE_CHG:
  287. ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
  288. break;
  289. case EXTCON_TYPE_JACK:
  290. ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
  291. break;
  292. case EXTCON_TYPE_DISP:
  293. ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
  294. break;
  295. default:
  296. ret = -EINVAL;
  297. }
  298. return ret;
  299. }
  300. static void init_property(struct extcon_dev *edev, unsigned int id, int index)
  301. {
  302. unsigned int type = extcon_info[id].type;
  303. struct extcon_cable *cable = &edev->cables[index];
  304. if (EXTCON_TYPE_USB & type)
  305. memset(cable->usb_propval, 0, sizeof(cable->usb_propval));
  306. if (EXTCON_TYPE_CHG & type)
  307. memset(cable->chg_propval, 0, sizeof(cable->chg_propval));
  308. if (EXTCON_TYPE_JACK & type)
  309. memset(cable->jack_propval, 0, sizeof(cable->jack_propval));
  310. if (EXTCON_TYPE_DISP & type)
  311. memset(cable->disp_propval, 0, sizeof(cable->disp_propval));
  312. }
  313. static ssize_t state_show(struct device *dev, struct device_attribute *attr,
  314. char *buf)
  315. {
  316. int i, count = 0;
  317. struct extcon_dev *edev = dev_get_drvdata(dev);
  318. if (edev->max_supported == 0)
  319. return sprintf(buf, "%u\n", edev->state);
  320. for (i = 0; i < edev->max_supported; i++) {
  321. count += sprintf(buf + count, "%s=%d\n",
  322. extcon_info[edev->supported_cable[i]].name,
  323. !!(edev->state & (1 << i)));
  324. }
  325. return count;
  326. }
  327. static DEVICE_ATTR_RO(state);
  328. static ssize_t name_show(struct device *dev, struct device_attribute *attr,
  329. char *buf)
  330. {
  331. struct extcon_dev *edev = dev_get_drvdata(dev);
  332. return sprintf(buf, "%s\n", edev->name);
  333. }
  334. static DEVICE_ATTR_RO(name);
  335. static ssize_t cable_name_show(struct device *dev,
  336. struct device_attribute *attr, char *buf)
  337. {
  338. struct extcon_cable *cable = container_of(attr, struct extcon_cable,
  339. attr_name);
  340. int i = cable->cable_index;
  341. return sprintf(buf, "%s\n",
  342. extcon_info[cable->edev->supported_cable[i]].name);
  343. }
  344. static ssize_t cable_state_show(struct device *dev,
  345. struct device_attribute *attr, char *buf)
  346. {
  347. struct extcon_cable *cable = container_of(attr, struct extcon_cable,
  348. attr_state);
  349. int i = cable->cable_index;
  350. return sprintf(buf, "%d\n",
  351. extcon_get_cable_state_(cable->edev,
  352. cable->edev->supported_cable[i]));
  353. }
  354. /**
  355. * extcon_update_state() - Update the cable attach states of the extcon device
  356. * only for the masked bits.
  357. * @edev: the extcon device
  358. * @mask: the bit mask to designate updated bits.
  359. * @state: new cable attach status for @edev
  360. *
  361. * Changing the state sends uevent with environment variable containing
  362. * the name of extcon device (envp[0]) and the state output (envp[1]).
  363. * Tizen uses this format for extcon device to get events from ports.
  364. * Android uses this format as well.
  365. *
  366. * Note that the notifier provides which bits are changed in the state
  367. * variable with the val parameter (second) to the callback.
  368. */
  369. static int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
  370. {
  371. char name_buf[120];
  372. char state_buf[120];
  373. char *prop_buf;
  374. char *envp[3];
  375. int env_offset = 0;
  376. int length;
  377. int index;
  378. unsigned long flags;
  379. bool attached;
  380. if (!edev)
  381. return -EINVAL;
  382. spin_lock_irqsave(&edev->lock, flags);
  383. if (edev->state != ((edev->state & ~mask) | (state & mask))) {
  384. u32 old_state;
  385. if (check_mutually_exclusive(edev, (edev->state & ~mask) |
  386. (state & mask))) {
  387. spin_unlock_irqrestore(&edev->lock, flags);
  388. return -EPERM;
  389. }
  390. old_state = edev->state;
  391. edev->state &= ~mask;
  392. edev->state |= state & mask;
  393. for (index = 0; index < edev->max_supported; index++) {
  394. if (is_extcon_changed(old_state, edev->state, index,
  395. &attached))
  396. raw_notifier_call_chain(&edev->nh[index],
  397. attached, edev);
  398. }
  399. /* This could be in interrupt handler */
  400. prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
  401. if (prop_buf) {
  402. length = name_show(&edev->dev, NULL, prop_buf);
  403. if (length > 0) {
  404. if (prop_buf[length - 1] == '\n')
  405. prop_buf[length - 1] = 0;
  406. snprintf(name_buf, sizeof(name_buf),
  407. "NAME=%s", prop_buf);
  408. envp[env_offset++] = name_buf;
  409. }
  410. length = state_show(&edev->dev, NULL, prop_buf);
  411. if (length > 0) {
  412. if (prop_buf[length - 1] == '\n')
  413. prop_buf[length - 1] = 0;
  414. snprintf(state_buf, sizeof(state_buf),
  415. "STATE=%s", prop_buf);
  416. envp[env_offset++] = state_buf;
  417. }
  418. envp[env_offset] = NULL;
  419. /* Unlock early before uevent */
  420. spin_unlock_irqrestore(&edev->lock, flags);
  421. kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
  422. free_page((unsigned long)prop_buf);
  423. } else {
  424. /* Unlock early before uevent */
  425. spin_unlock_irqrestore(&edev->lock, flags);
  426. dev_err(&edev->dev, "out of memory in extcon_set_state\n");
  427. kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
  428. }
  429. } else {
  430. /* No changes */
  431. spin_unlock_irqrestore(&edev->lock, flags);
  432. }
  433. return 0;
  434. }
  435. /**
  436. * extcon_get_cable_state_() - Get the status of a specific cable.
  437. * @edev: the extcon device that has the cable.
  438. * @id: the unique id of each external connector in extcon enumeration.
  439. */
  440. int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
  441. {
  442. int index;
  443. if (!edev)
  444. return -EINVAL;
  445. index = find_cable_index_by_id(edev, id);
  446. if (index < 0)
  447. return index;
  448. if (edev->max_supported && edev->max_supported <= index)
  449. return -EINVAL;
  450. return is_extcon_attached(edev, index);
  451. }
  452. EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
  453. /**
  454. * extcon_set_cable_state_() - Set the status of a specific cable.
  455. * @edev: the extcon device that has the cable.
  456. * @id: the unique id of each external connector
  457. * in extcon enumeration.
  458. * @state: the new cable status. The default semantics is
  459. * true: attached / false: detached.
  460. */
  461. int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
  462. bool cable_state)
  463. {
  464. u32 state;
  465. int index;
  466. if (!edev)
  467. return -EINVAL;
  468. index = find_cable_index_by_id(edev, id);
  469. if (index < 0)
  470. return index;
  471. if (edev->max_supported && edev->max_supported <= index)
  472. return -EINVAL;
  473. /*
  474. * Initialize the value of extcon property before setting
  475. * the detached state for an external connector.
  476. */
  477. if (!cable_state)
  478. init_property(edev, id, index);
  479. state = cable_state ? (1 << index) : 0;
  480. return extcon_update_state(edev, 1 << index, state);
  481. }
  482. EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
  483. /**
  484. * extcon_get_property() - Get the property value of a specific cable.
  485. * @edev: the extcon device that has the cable.
  486. * @id: the unique id of each external connector
  487. * in extcon enumeration.
  488. * @prop: the property id among enum extcon_property.
  489. * @prop_val: the pointer which store the value of property.
  490. *
  491. * When getting the property value of external connector, the external connector
  492. * should be attached. If detached state, function just return 0 without
  493. * property value. Also, the each property should be included in the list of
  494. * supported properties according to the type of external connectors.
  495. *
  496. * Returns 0 if success or error number if fail
  497. */
  498. int extcon_get_property(struct extcon_dev *edev, unsigned int id,
  499. unsigned int prop,
  500. union extcon_property_value *prop_val)
  501. {
  502. struct extcon_cable *cable;
  503. unsigned long flags;
  504. int index, ret = 0;
  505. *prop_val = (union extcon_property_value)(0);
  506. if (!edev)
  507. return -EINVAL;
  508. /* Check whether the property is supported or not */
  509. if (!is_extcon_property_supported(id, prop))
  510. return -EINVAL;
  511. /* Find the cable index of external connector by using id */
  512. index = find_cable_index_by_id(edev, id);
  513. if (index < 0)
  514. return index;
  515. spin_lock_irqsave(&edev->lock, flags);
  516. /* Check whether the property is available or not. */
  517. if (!is_extcon_property_capability(edev, id, index, prop)) {
  518. spin_unlock_irqrestore(&edev->lock, flags);
  519. return -EPERM;
  520. }
  521. /*
  522. * Check whether the external connector is attached.
  523. * If external connector is detached, the user can not
  524. * get the property value.
  525. */
  526. if (!is_extcon_attached(edev, index)) {
  527. spin_unlock_irqrestore(&edev->lock, flags);
  528. return 0;
  529. }
  530. cable = &edev->cables[index];
  531. /* Get the property value according to extcon type */
  532. switch (prop) {
  533. case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
  534. *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN];
  535. break;
  536. case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
  537. *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN];
  538. break;
  539. case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
  540. *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN];
  541. break;
  542. case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
  543. *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN];
  544. break;
  545. default:
  546. ret = -EINVAL;
  547. break;
  548. }
  549. spin_unlock_irqrestore(&edev->lock, flags);
  550. return ret;
  551. }
  552. EXPORT_SYMBOL_GPL(extcon_get_property);
  553. /**
  554. * extcon_set_property() - Set the property value of a specific cable.
  555. * @edev: the extcon device that has the cable.
  556. * @id: the unique id of each external connector
  557. * in extcon enumeration.
  558. * @prop: the property id among enum extcon_property.
  559. * @prop_val: the pointer including the new value of property.
  560. *
  561. * The each property should be included in the list of supported properties
  562. * according to the type of external connectors.
  563. *
  564. * Returns 0 if success or error number if fail
  565. */
  566. int extcon_set_property(struct extcon_dev *edev, unsigned int id,
  567. unsigned int prop,
  568. union extcon_property_value prop_val)
  569. {
  570. struct extcon_cable *cable;
  571. unsigned long flags;
  572. int index, ret = 0;
  573. if (!edev)
  574. return -EINVAL;
  575. /* Check whether the property is supported or not */
  576. if (!is_extcon_property_supported(id, prop))
  577. return -EINVAL;
  578. /* Find the cable index of external connector by using id */
  579. index = find_cable_index_by_id(edev, id);
  580. if (index < 0)
  581. return index;
  582. spin_lock_irqsave(&edev->lock, flags);
  583. /* Check whether the property is available or not. */
  584. if (!is_extcon_property_capability(edev, id, index, prop)) {
  585. spin_unlock_irqrestore(&edev->lock, flags);
  586. return -EPERM;
  587. }
  588. cable = &edev->cables[index];
  589. /* Set the property value according to extcon type */
  590. switch (prop) {
  591. case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
  592. cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val;
  593. break;
  594. case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
  595. cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val;
  596. break;
  597. case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
  598. cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val;
  599. break;
  600. case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
  601. cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val;
  602. break;
  603. default:
  604. ret = -EINVAL;
  605. break;
  606. }
  607. spin_unlock_irqrestore(&edev->lock, flags);
  608. return ret;
  609. }
  610. EXPORT_SYMBOL_GPL(extcon_set_property);
  611. /**
  612. * extcon_get_property_capability() - Get the capability of property
  613. * of an external connector.
  614. * @edev: the extcon device that has the cable.
  615. * @id: the unique id of each external connector
  616. * in extcon enumeration.
  617. * @prop: the property id among enum extcon_property.
  618. *
  619. * Returns 1 if the property is available or 0 if not available.
  620. */
  621. int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id,
  622. unsigned int prop)
  623. {
  624. int index;
  625. if (!edev)
  626. return -EINVAL;
  627. /* Check whether the property is supported or not */
  628. if (!is_extcon_property_supported(id, prop))
  629. return -EINVAL;
  630. /* Find the cable index of external connector by using id */
  631. index = find_cable_index_by_id(edev, id);
  632. if (index < 0)
  633. return index;
  634. return is_extcon_property_capability(edev, id, index, prop);
  635. }
  636. EXPORT_SYMBOL_GPL(extcon_get_property_capability);
  637. /**
  638. * extcon_set_property_capability() - Set the capability of a property
  639. * of an external connector.
  640. * @edev: the extcon device that has the cable.
  641. * @id: the unique id of each external connector
  642. * in extcon enumeration.
  643. * @prop: the property id among enum extcon_property.
  644. *
  645. * This function set the capability of a property for an external connector
  646. * to mark the bit in capability bitmap which mean the available state of
  647. * a property.
  648. *
  649. * Returns 0 if success or error number if fail
  650. */
  651. int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id,
  652. unsigned int prop)
  653. {
  654. struct extcon_cable *cable;
  655. int index, type, ret = 0;
  656. if (!edev)
  657. return -EINVAL;
  658. /* Check whether the property is supported or not. */
  659. if (!is_extcon_property_supported(id, prop))
  660. return -EINVAL;
  661. /* Find the cable index of external connector by using id. */
  662. index = find_cable_index_by_id(edev, id);
  663. if (index < 0)
  664. return index;
  665. type = get_extcon_type(prop);
  666. if (type < 0)
  667. return type;
  668. cable = &edev->cables[index];
  669. switch (type) {
  670. case EXTCON_TYPE_USB:
  671. __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
  672. break;
  673. case EXTCON_TYPE_CHG:
  674. __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
  675. break;
  676. case EXTCON_TYPE_JACK:
  677. __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
  678. break;
  679. case EXTCON_TYPE_DISP:
  680. __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
  681. break;
  682. default:
  683. ret = -EINVAL;
  684. }
  685. return ret;
  686. }
  687. EXPORT_SYMBOL_GPL(extcon_set_property_capability);
  688. /**
  689. * extcon_get_extcon_dev() - Get the extcon device instance from the name
  690. * @extcon_name: The extcon name provided with extcon_dev_register()
  691. */
  692. struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
  693. {
  694. struct extcon_dev *sd;
  695. if (!extcon_name)
  696. return ERR_PTR(-EINVAL);
  697. mutex_lock(&extcon_dev_list_lock);
  698. list_for_each_entry(sd, &extcon_dev_list, entry) {
  699. if (!strcmp(sd->name, extcon_name))
  700. goto out;
  701. }
  702. sd = NULL;
  703. out:
  704. mutex_unlock(&extcon_dev_list_lock);
  705. return sd;
  706. }
  707. EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
  708. /**
  709. * extcon_register_notifier() - Register a notifiee to get notified by
  710. * any attach status changes from the extcon.
  711. * @edev: the extcon device that has the external connecotr.
  712. * @id: the unique id of each external connector in extcon enumeration.
  713. * @nb: a notifier block to be registered.
  714. *
  715. * Note that the second parameter given to the callback of nb (val) is
  716. * "old_state", not the current state. The current state can be retrieved
  717. * by looking at the third pameter (edev pointer)'s state value.
  718. */
  719. int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
  720. struct notifier_block *nb)
  721. {
  722. unsigned long flags;
  723. int ret, idx = -EINVAL;
  724. if (!nb)
  725. return -EINVAL;
  726. if (edev) {
  727. idx = find_cable_index_by_id(edev, id);
  728. if (idx < 0)
  729. return idx;
  730. spin_lock_irqsave(&edev->lock, flags);
  731. ret = raw_notifier_chain_register(&edev->nh[idx], nb);
  732. spin_unlock_irqrestore(&edev->lock, flags);
  733. } else {
  734. struct extcon_dev *extd;
  735. mutex_lock(&extcon_dev_list_lock);
  736. list_for_each_entry(extd, &extcon_dev_list, entry) {
  737. idx = find_cable_index_by_id(extd, id);
  738. if (idx >= 0)
  739. break;
  740. }
  741. mutex_unlock(&extcon_dev_list_lock);
  742. if (idx >= 0) {
  743. edev = extd;
  744. return extcon_register_notifier(extd, id, nb);
  745. } else {
  746. ret = -ENODEV;
  747. }
  748. }
  749. return ret;
  750. }
  751. EXPORT_SYMBOL_GPL(extcon_register_notifier);
  752. /**
  753. * extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
  754. * @edev: the extcon device that has the external connecotr.
  755. * @id: the unique id of each external connector in extcon enumeration.
  756. * @nb: a notifier block to be registered.
  757. */
  758. int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
  759. struct notifier_block *nb)
  760. {
  761. unsigned long flags;
  762. int ret, idx;
  763. if (!edev || !nb)
  764. return -EINVAL;
  765. idx = find_cable_index_by_id(edev, id);
  766. if (idx < 0)
  767. return idx;
  768. spin_lock_irqsave(&edev->lock, flags);
  769. ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
  770. spin_unlock_irqrestore(&edev->lock, flags);
  771. return ret;
  772. }
  773. EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
  774. static struct attribute *extcon_attrs[] = {
  775. &dev_attr_state.attr,
  776. &dev_attr_name.attr,
  777. NULL,
  778. };
  779. ATTRIBUTE_GROUPS(extcon);
  780. static int create_extcon_class(void)
  781. {
  782. if (!extcon_class) {
  783. extcon_class = class_create(THIS_MODULE, "extcon");
  784. if (IS_ERR(extcon_class))
  785. return PTR_ERR(extcon_class);
  786. extcon_class->dev_groups = extcon_groups;
  787. #if defined(CONFIG_ANDROID)
  788. switch_class = class_compat_register("switch");
  789. if (WARN(!switch_class, "cannot allocate"))
  790. return -ENOMEM;
  791. #endif /* CONFIG_ANDROID */
  792. }
  793. return 0;
  794. }
  795. static void extcon_dev_release(struct device *dev)
  796. {
  797. }
  798. static const char *muex_name = "mutually_exclusive";
  799. static void dummy_sysfs_dev_release(struct device *dev)
  800. {
  801. }
  802. /*
  803. * extcon_dev_allocate() - Allocate the memory of extcon device.
  804. * @supported_cable: Array of supported extcon ending with EXTCON_NONE.
  805. * If supported_cable is NULL, cable name related APIs
  806. * are disabled.
  807. *
  808. * This function allocates the memory for extcon device without allocating
  809. * memory in each extcon provider driver and initialize default setting for
  810. * extcon device.
  811. *
  812. * Return the pointer of extcon device if success or ERR_PTR(err) if fail
  813. */
  814. struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable)
  815. {
  816. struct extcon_dev *edev;
  817. if (!supported_cable)
  818. return ERR_PTR(-EINVAL);
  819. edev = kzalloc(sizeof(*edev), GFP_KERNEL);
  820. if (!edev)
  821. return ERR_PTR(-ENOMEM);
  822. edev->max_supported = 0;
  823. edev->supported_cable = supported_cable;
  824. return edev;
  825. }
  826. /*
  827. * extcon_dev_free() - Free the memory of extcon device.
  828. * @edev: the extcon device to free
  829. */
  830. void extcon_dev_free(struct extcon_dev *edev)
  831. {
  832. kfree(edev);
  833. }
  834. EXPORT_SYMBOL_GPL(extcon_dev_free);
  835. /**
  836. * extcon_dev_register() - Register a new extcon device
  837. * @edev : the new extcon device (should be allocated before calling)
  838. *
  839. * Among the members of edev struct, please set the "user initializing data"
  840. * in any case and set the "optional callbacks" if required. However, please
  841. * do not set the values of "internal data", which are initialized by
  842. * this function.
  843. */
  844. int extcon_dev_register(struct extcon_dev *edev)
  845. {
  846. int ret, index = 0;
  847. static atomic_t edev_no = ATOMIC_INIT(-1);
  848. if (!extcon_class) {
  849. ret = create_extcon_class();
  850. if (ret < 0)
  851. return ret;
  852. }
  853. if (!edev || !edev->supported_cable)
  854. return -EINVAL;
  855. for (; edev->supported_cable[index] != EXTCON_NONE; index++);
  856. edev->max_supported = index;
  857. if (index > SUPPORTED_CABLE_MAX) {
  858. dev_err(&edev->dev,
  859. "exceed the maximum number of supported cables\n");
  860. return -EINVAL;
  861. }
  862. edev->dev.class = extcon_class;
  863. edev->dev.release = extcon_dev_release;
  864. edev->name = dev_name(edev->dev.parent);
  865. if (IS_ERR_OR_NULL(edev->name)) {
  866. dev_err(&edev->dev,
  867. "extcon device name is null\n");
  868. return -EINVAL;
  869. }
  870. dev_set_name(&edev->dev, "extcon%lu",
  871. (unsigned long)atomic_inc_return(&edev_no));
  872. if (edev->max_supported) {
  873. char buf[10];
  874. char *str;
  875. struct extcon_cable *cable;
  876. edev->cables = kzalloc(sizeof(struct extcon_cable) *
  877. edev->max_supported, GFP_KERNEL);
  878. if (!edev->cables) {
  879. ret = -ENOMEM;
  880. goto err_sysfs_alloc;
  881. }
  882. for (index = 0; index < edev->max_supported; index++) {
  883. cable = &edev->cables[index];
  884. snprintf(buf, 10, "cable.%d", index);
  885. str = kzalloc(sizeof(char) * (strlen(buf) + 1),
  886. GFP_KERNEL);
  887. if (!str) {
  888. for (index--; index >= 0; index--) {
  889. cable = &edev->cables[index];
  890. kfree(cable->attr_g.name);
  891. }
  892. ret = -ENOMEM;
  893. goto err_alloc_cables;
  894. }
  895. strcpy(str, buf);
  896. cable->edev = edev;
  897. cable->cable_index = index;
  898. cable->attrs[0] = &cable->attr_name.attr;
  899. cable->attrs[1] = &cable->attr_state.attr;
  900. cable->attrs[2] = NULL;
  901. cable->attr_g.name = str;
  902. cable->attr_g.attrs = cable->attrs;
  903. sysfs_attr_init(&cable->attr_name.attr);
  904. cable->attr_name.attr.name = "name";
  905. cable->attr_name.attr.mode = 0444;
  906. cable->attr_name.show = cable_name_show;
  907. sysfs_attr_init(&cable->attr_state.attr);
  908. cable->attr_state.attr.name = "state";
  909. cable->attr_state.attr.mode = 0444;
  910. cable->attr_state.show = cable_state_show;
  911. }
  912. }
  913. if (edev->max_supported && edev->mutually_exclusive) {
  914. char buf[80];
  915. char *name;
  916. /* Count the size of mutually_exclusive array */
  917. for (index = 0; edev->mutually_exclusive[index]; index++)
  918. ;
  919. edev->attrs_muex = kzalloc(sizeof(struct attribute *) *
  920. (index + 1), GFP_KERNEL);
  921. if (!edev->attrs_muex) {
  922. ret = -ENOMEM;
  923. goto err_muex;
  924. }
  925. edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) *
  926. index, GFP_KERNEL);
  927. if (!edev->d_attrs_muex) {
  928. ret = -ENOMEM;
  929. kfree(edev->attrs_muex);
  930. goto err_muex;
  931. }
  932. for (index = 0; edev->mutually_exclusive[index]; index++) {
  933. sprintf(buf, "0x%x", edev->mutually_exclusive[index]);
  934. name = kzalloc(sizeof(char) * (strlen(buf) + 1),
  935. GFP_KERNEL);
  936. if (!name) {
  937. for (index--; index >= 0; index--) {
  938. kfree(edev->d_attrs_muex[index].attr.
  939. name);
  940. }
  941. kfree(edev->d_attrs_muex);
  942. kfree(edev->attrs_muex);
  943. ret = -ENOMEM;
  944. goto err_muex;
  945. }
  946. strcpy(name, buf);
  947. sysfs_attr_init(&edev->d_attrs_muex[index].attr);
  948. edev->d_attrs_muex[index].attr.name = name;
  949. edev->d_attrs_muex[index].attr.mode = 0000;
  950. edev->attrs_muex[index] = &edev->d_attrs_muex[index]
  951. .attr;
  952. }
  953. edev->attr_g_muex.name = muex_name;
  954. edev->attr_g_muex.attrs = edev->attrs_muex;
  955. }
  956. if (edev->max_supported) {
  957. edev->extcon_dev_type.groups =
  958. kzalloc(sizeof(struct attribute_group *) *
  959. (edev->max_supported + 2), GFP_KERNEL);
  960. if (!edev->extcon_dev_type.groups) {
  961. ret = -ENOMEM;
  962. goto err_alloc_groups;
  963. }
  964. edev->extcon_dev_type.name = dev_name(&edev->dev);
  965. edev->extcon_dev_type.release = dummy_sysfs_dev_release;
  966. for (index = 0; index < edev->max_supported; index++)
  967. edev->extcon_dev_type.groups[index] =
  968. &edev->cables[index].attr_g;
  969. if (edev->mutually_exclusive)
  970. edev->extcon_dev_type.groups[index] =
  971. &edev->attr_g_muex;
  972. edev->dev.type = &edev->extcon_dev_type;
  973. }
  974. ret = device_register(&edev->dev);
  975. if (ret) {
  976. put_device(&edev->dev);
  977. goto err_dev;
  978. }
  979. #if defined(CONFIG_ANDROID)
  980. if (switch_class)
  981. ret = class_compat_create_link(switch_class, &edev->dev, NULL);
  982. #endif /* CONFIG_ANDROID */
  983. spin_lock_init(&edev->lock);
  984. edev->nh = devm_kzalloc(&edev->dev,
  985. sizeof(*edev->nh) * edev->max_supported, GFP_KERNEL);
  986. if (!edev->nh) {
  987. ret = -ENOMEM;
  988. goto err_dev;
  989. }
  990. for (index = 0; index < edev->max_supported; index++)
  991. RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
  992. dev_set_drvdata(&edev->dev, edev);
  993. edev->state = 0;
  994. mutex_lock(&extcon_dev_list_lock);
  995. list_add(&edev->entry, &extcon_dev_list);
  996. mutex_unlock(&extcon_dev_list_lock);
  997. return 0;
  998. err_dev:
  999. if (edev->max_supported)
  1000. kfree(edev->extcon_dev_type.groups);
  1001. err_alloc_groups:
  1002. if (edev->max_supported && edev->mutually_exclusive) {
  1003. for (index = 0; edev->mutually_exclusive[index]; index++)
  1004. kfree(edev->d_attrs_muex[index].attr.name);
  1005. kfree(edev->d_attrs_muex);
  1006. kfree(edev->attrs_muex);
  1007. }
  1008. err_muex:
  1009. for (index = 0; index < edev->max_supported; index++)
  1010. kfree(edev->cables[index].attr_g.name);
  1011. err_alloc_cables:
  1012. if (edev->max_supported)
  1013. kfree(edev->cables);
  1014. err_sysfs_alloc:
  1015. return ret;
  1016. }
  1017. EXPORT_SYMBOL_GPL(extcon_dev_register);
  1018. /**
  1019. * extcon_dev_unregister() - Unregister the extcon device.
  1020. * @edev: the extcon device instance to be unregistered.
  1021. *
  1022. * Note that this does not call kfree(edev) because edev was not allocated
  1023. * by this class.
  1024. */
  1025. void extcon_dev_unregister(struct extcon_dev *edev)
  1026. {
  1027. int index;
  1028. if (!edev)
  1029. return;
  1030. mutex_lock(&extcon_dev_list_lock);
  1031. list_del(&edev->entry);
  1032. mutex_unlock(&extcon_dev_list_lock);
  1033. if (IS_ERR_OR_NULL(get_device(&edev->dev))) {
  1034. dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n",
  1035. dev_name(&edev->dev));
  1036. return;
  1037. }
  1038. device_unregister(&edev->dev);
  1039. if (edev->mutually_exclusive && edev->max_supported) {
  1040. for (index = 0; edev->mutually_exclusive[index];
  1041. index++)
  1042. kfree(edev->d_attrs_muex[index].attr.name);
  1043. kfree(edev->d_attrs_muex);
  1044. kfree(edev->attrs_muex);
  1045. }
  1046. for (index = 0; index < edev->max_supported; index++)
  1047. kfree(edev->cables[index].attr_g.name);
  1048. if (edev->max_supported) {
  1049. kfree(edev->extcon_dev_type.groups);
  1050. kfree(edev->cables);
  1051. }
  1052. #if defined(CONFIG_ANDROID)
  1053. if (switch_class)
  1054. class_compat_remove_link(switch_class, &edev->dev, NULL);
  1055. #endif
  1056. put_device(&edev->dev);
  1057. }
  1058. EXPORT_SYMBOL_GPL(extcon_dev_unregister);
  1059. #ifdef CONFIG_OF
  1060. /*
  1061. * extcon_get_edev_by_phandle - Get the extcon device from devicetree
  1062. * @dev - instance to the given device
  1063. * @index - index into list of extcon_dev
  1064. *
  1065. * return the instance of extcon device
  1066. */
  1067. struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
  1068. {
  1069. struct device_node *node;
  1070. struct extcon_dev *edev;
  1071. if (!dev)
  1072. return ERR_PTR(-EINVAL);
  1073. if (!dev->of_node) {
  1074. dev_dbg(dev, "device does not have a device node entry\n");
  1075. return ERR_PTR(-EINVAL);
  1076. }
  1077. node = of_parse_phandle(dev->of_node, "extcon", index);
  1078. if (!node) {
  1079. dev_dbg(dev, "failed to get phandle in %s node\n",
  1080. dev->of_node->full_name);
  1081. return ERR_PTR(-ENODEV);
  1082. }
  1083. mutex_lock(&extcon_dev_list_lock);
  1084. list_for_each_entry(edev, &extcon_dev_list, entry) {
  1085. if (edev->dev.parent && edev->dev.parent->of_node == node) {
  1086. mutex_unlock(&extcon_dev_list_lock);
  1087. of_node_put(node);
  1088. return edev;
  1089. }
  1090. }
  1091. mutex_unlock(&extcon_dev_list_lock);
  1092. of_node_put(node);
  1093. return ERR_PTR(-EPROBE_DEFER);
  1094. }
  1095. #else
  1096. struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
  1097. {
  1098. return ERR_PTR(-ENOSYS);
  1099. }
  1100. #endif /* CONFIG_OF */
  1101. EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
  1102. /**
  1103. * extcon_get_edev_name() - Get the name of the extcon device.
  1104. * @edev: the extcon device
  1105. */
  1106. const char *extcon_get_edev_name(struct extcon_dev *edev)
  1107. {
  1108. return !edev ? NULL : edev->name;
  1109. }
  1110. static int __init extcon_class_init(void)
  1111. {
  1112. return create_extcon_class();
  1113. }
  1114. module_init(extcon_class_init);
  1115. static void __exit extcon_class_exit(void)
  1116. {
  1117. #if defined(CONFIG_ANDROID)
  1118. class_compat_unregister(switch_class);
  1119. #endif
  1120. class_destroy(extcon_class);
  1121. }
  1122. module_exit(extcon_class_exit);
  1123. MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
  1124. MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
  1125. MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
  1126. MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
  1127. MODULE_DESCRIPTION("External connector (extcon) class driver");
  1128. MODULE_LICENSE("GPL");