audio_notifier.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved.
  4. */
  5. #include <linux/init.h>
  6. #include <linux/kernel.h>
  7. #include <linux/module.h>
  8. #include <linux/err.h>
  9. #include <linux/string.h>
  10. #include <linux/delay.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/of_device.h>
  13. #include <linux/slab.h>
  14. #include <linux/remoteproc.h>
  15. #include <linux/remoteproc/qcom_rproc.h>
  16. #include <dsp/audio_notifier.h>
  17. #include "audio_ssr.h"
  18. #include "audio_pdr.h"
  19. /* Audio states internal to notifier. Client */
  20. /* used states defined in audio_notifier.h */
  21. /* for AUDIO_NOTIFIER_SERVICE_DOWN & UP */
  22. #define NO_SERVICE -2
  23. #define UNINIT_SERVICE -1
  24. static struct platform_device *adsp_private;
  25. struct adsp_notify_private {
  26. struct rproc *rproc_h;
  27. };
  28. /*
  29. * Used for each client registered with audio notifier
  30. */
  31. struct client_data {
  32. struct list_head list;
  33. /* Notifier block given by client */
  34. struct notifier_block *nb;
  35. char client_name[20];
  36. int service;
  37. int domain;
  38. };
  39. /*
  40. * Used for each service and domain combination
  41. * Tracks information specific to the underlying
  42. * service.
  43. */
  44. struct service_info {
  45. const char name[20];
  46. int domain_id;
  47. int state;
  48. void *handle;
  49. /* Hook registered to service */
  50. union {
  51. void (*cb)(int, char *, void *);
  52. struct notifier_block *nb;
  53. } hook;
  54. /* Used to determine when to register and deregister service */
  55. int num_of_clients;
  56. /* List of all clients registered to the service and domain */
  57. struct srcu_notifier_head client_nb_list;
  58. };
  59. static int audio_notifier_ssr_adsp_cb(struct notifier_block *this,
  60. unsigned long opcode, void *data);
  61. static int audio_notifier_ssr_modem_cb(struct notifier_block *this,
  62. unsigned long opcode, void *data);
  63. static void audio_notifier_pdr_adsp_cb(int status, char *service_name, void *priv);
  64. static struct notifier_block notifier_ssr_adsp_nb = {
  65. .notifier_call = audio_notifier_ssr_adsp_cb,
  66. .priority = 0,
  67. };
  68. static struct notifier_block notifier_ssr_modem_nb = {
  69. .notifier_call = audio_notifier_ssr_modem_cb,
  70. .priority = 0,
  71. };
  72. static struct service_info service_data[AUDIO_NOTIFIER_MAX_SERVICES]
  73. [AUDIO_NOTIFIER_MAX_DOMAINS] = {
  74. {{
  75. .name = "SSR_ADSP",
  76. .domain_id = AUDIO_SSR_DOMAIN_ADSP,
  77. .state = AUDIO_NOTIFIER_SERVICE_DOWN,
  78. .hook.nb = &notifier_ssr_adsp_nb
  79. },
  80. {
  81. .name = "SSR_MODEM",
  82. .domain_id = AUDIO_SSR_DOMAIN_MODEM,
  83. .state = AUDIO_NOTIFIER_SERVICE_DOWN,
  84. .hook.nb = &notifier_ssr_modem_nb
  85. } },
  86. {{
  87. .name = "PDR_ADSP",
  88. .domain_id = AUDIO_PDR_DOMAIN_ADSP,
  89. .state = UNINIT_SERVICE,
  90. .hook.cb = &audio_notifier_pdr_adsp_cb
  91. },
  92. { /* PDR MODEM service not enabled */
  93. .name = "INVALID",
  94. .state = NO_SERVICE,
  95. .hook.nb = NULL
  96. } }
  97. };
  98. /* Master list of all audio notifier clients */
  99. struct list_head client_list;
  100. struct mutex notifier_mutex;
  101. static int audio_notifier_get_default_service(int domain)
  102. {
  103. int service = NO_SERVICE;
  104. /* initial service to connect per domain */
  105. switch (domain) {
  106. case AUDIO_NOTIFIER_ADSP_DOMAIN:
  107. service = AUDIO_NOTIFIER_PDR_SERVICE;
  108. break;
  109. case AUDIO_NOTIFIER_MODEM_DOMAIN:
  110. service = AUDIO_NOTIFIER_SSR_SERVICE;
  111. break;
  112. }
  113. return service;
  114. }
  115. #ifdef CONFIG_MSM_QDSP6_PDR
  116. static void audio_notifier_init_service(int service)
  117. {
  118. int i;
  119. for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) {
  120. if (service_data[service][i].state == UNINIT_SERVICE)
  121. service_data[service][i].state =
  122. AUDIO_NOTIFIER_SERVICE_DOWN;
  123. }
  124. }
  125. #else
  126. static void audio_notifier_init_service(int service)
  127. {
  128. int i;
  129. for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
  130. service_data[service][i].state = NO_SERVICE;
  131. }
  132. #endif
  133. static bool audio_notifier_is_service_enabled(int service)
  134. {
  135. int i;
  136. for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
  137. if (service_data[service][i].state != NO_SERVICE)
  138. return true;
  139. return false;
  140. }
  141. static int audio_notifier_reg_service(int service, int domain)
  142. {
  143. void *handle;
  144. int ret = -EINVAL;
  145. int curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
  146. struct platform_device *pdev = adsp_private;
  147. struct adsp_notify_private *priv = NULL;
  148. struct rproc *rproc;
  149. priv = platform_get_drvdata(pdev);
  150. if (!priv) {
  151. dev_err(&pdev->dev," %s: Private data get failed\n", __func__);
  152. return ret;;
  153. }
  154. rproc = priv->rproc_h;
  155. switch (service) {
  156. case AUDIO_NOTIFIER_SSR_SERVICE:
  157. handle = audio_ssr_register(rproc->name,
  158. service_data[service][domain].hook.nb);
  159. break;
  160. case AUDIO_NOTIFIER_PDR_SERVICE:
  161. handle = audio_pdr_service_register(
  162. service_data[service][domain].domain_id,
  163. service_data[service][domain].hook.cb);
  164. curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
  165. break;
  166. default:
  167. pr_err("%s: Invalid service %d\n",
  168. __func__, service);
  169. ret = -EINVAL;
  170. goto done;
  171. }
  172. if (IS_ERR_OR_NULL(handle)) {
  173. pr_err("%s: handle is incorrect for service %s\n",
  174. __func__, service_data[service][domain].name);
  175. ret = -EINVAL;
  176. goto done;
  177. }
  178. service_data[service][domain].state = curr_state;
  179. service_data[service][domain].handle = handle;
  180. pr_info("%s: service %s is in use\n",
  181. __func__, service_data[service][domain].name);
  182. pr_debug("%s: service %s has current state %d, handle 0x%pK\n",
  183. __func__, service_data[service][domain].name,
  184. service_data[service][domain].state,
  185. service_data[service][domain].handle);
  186. done:
  187. return ret;
  188. }
  189. static int audio_notifier_dereg_service(int service, int domain)
  190. {
  191. int ret;
  192. switch (service) {
  193. case AUDIO_NOTIFIER_SSR_SERVICE:
  194. ret = audio_ssr_deregister(
  195. service_data[service][domain].handle,
  196. service_data[service][domain].hook.nb);
  197. break;
  198. case AUDIO_NOTIFIER_PDR_SERVICE:
  199. ret = audio_pdr_service_deregister(
  200. service_data[service][domain].domain_id);
  201. break;
  202. default:
  203. pr_err("%s: Invalid service %d\n",
  204. __func__, service);
  205. ret = -EINVAL;
  206. goto done;
  207. }
  208. if (ret < 0) {
  209. pr_err("%s: deregister failed for service %s, ret %d\n",
  210. __func__, service_data[service][domain].name, ret);
  211. goto done;
  212. }
  213. pr_debug("%s: service %s with handle 0x%pK deregistered\n",
  214. __func__, service_data[service][domain].name,
  215. service_data[service][domain].handle);
  216. service_data[service][domain].state = AUDIO_NOTIFIER_SERVICE_DOWN;
  217. service_data[service][domain].handle = NULL;
  218. done:
  219. return ret;
  220. }
  221. static int audio_notifier_reg_client_service(struct client_data *client_data,
  222. int service)
  223. {
  224. int ret = 0;
  225. int domain = client_data->domain;
  226. struct audio_notifier_cb_data data;
  227. switch (service) {
  228. case AUDIO_NOTIFIER_SSR_SERVICE:
  229. case AUDIO_NOTIFIER_PDR_SERVICE:
  230. if (service_data[service][domain].num_of_clients == 0)
  231. ret = audio_notifier_reg_service(service, domain);
  232. break;
  233. default:
  234. pr_err("%s: Invalid service for client %s, service %d, domain %d\n",
  235. __func__, client_data->client_name, service, domain);
  236. ret = -EINVAL;
  237. goto done;
  238. }
  239. if (ret < 0) {
  240. pr_err("%s: service registration failed on service %s for client %s\n",
  241. __func__, service_data[service][domain].name,
  242. client_data->client_name);
  243. goto done;
  244. }
  245. client_data->service = service;
  246. srcu_notifier_chain_register(
  247. &service_data[service][domain].client_nb_list,
  248. client_data->nb);
  249. service_data[service][domain].num_of_clients++;
  250. pr_debug("%s: registered client %s on service %s, current state 0x%x\n",
  251. __func__, client_data->client_name,
  252. service_data[service][domain].name,
  253. service_data[service][domain].state);
  254. /*
  255. * PDR registration returns current state
  256. * Force callback of client with current state for PDR
  257. */
  258. if (client_data->service == AUDIO_NOTIFIER_PDR_SERVICE) {
  259. data.service = service;
  260. data.domain = domain;
  261. (void)client_data->nb->notifier_call(client_data->nb,
  262. service_data[service][domain].state, &data);
  263. }
  264. done:
  265. return ret;
  266. }
  267. static int audio_notifier_reg_client(struct client_data *client_data)
  268. {
  269. int ret = 0;
  270. int service;
  271. int domain = client_data->domain;
  272. service = audio_notifier_get_default_service(domain);
  273. if (service < 0) {
  274. pr_err("%s: service %d is incorrect\n", __func__, service);
  275. ret = -EINVAL;
  276. goto done;
  277. }
  278. /* Search through services to find a valid one to register client on. */
  279. for (; service >= 0; service--) {
  280. /* If a service is not initialized, wait for it to come up. */
  281. if (service_data[service][domain].state == UNINIT_SERVICE)
  282. goto done;
  283. /* Skip unsupported service and domain combinations. */
  284. if (service_data[service][domain].state < 0)
  285. continue;
  286. /* Only register clients who have not acquired a service. */
  287. if (client_data->service != NO_SERVICE)
  288. continue;
  289. /*
  290. * Only register clients, who have not acquired a service, on
  291. * the best available service for their domain. Uninitialized
  292. * services will try to register all of their clients after
  293. * they initialize correctly or will disable their service and
  294. * register clients on the next best avaialable service.
  295. */
  296. pr_debug("%s: register client %s on service %s",
  297. __func__, client_data->client_name,
  298. service_data[service][domain].name);
  299. ret = audio_notifier_reg_client_service(client_data, service);
  300. if (ret < 0)
  301. pr_err("%s: client %s failed to register on service %s",
  302. __func__, client_data->client_name,
  303. service_data[service][domain].name);
  304. }
  305. done:
  306. return ret;
  307. }
  308. static int audio_notifier_dereg_client(struct client_data *client_data)
  309. {
  310. int ret = 0;
  311. int service = client_data->service;
  312. int domain = client_data->domain;
  313. switch (client_data->service) {
  314. case AUDIO_NOTIFIER_SSR_SERVICE:
  315. case AUDIO_NOTIFIER_PDR_SERVICE:
  316. if (service_data[service][domain].num_of_clients == 1)
  317. ret = audio_notifier_dereg_service(service, domain);
  318. break;
  319. case NO_SERVICE:
  320. goto done;
  321. default:
  322. pr_err("%s: Invalid service for client %s, service %d\n",
  323. __func__, client_data->client_name,
  324. client_data->service);
  325. ret = -EINVAL;
  326. goto done;
  327. }
  328. if (ret < 0) {
  329. pr_err("%s: deregister failed for client %s on service %s, ret %d\n",
  330. __func__, client_data->client_name,
  331. service_data[service][domain].name, ret);
  332. goto done;
  333. }
  334. ret = srcu_notifier_chain_unregister(&service_data[service][domain].
  335. client_nb_list, client_data->nb);
  336. if (ret < 0) {
  337. pr_err("%s: srcu_notifier_chain_unregister failed, ret %d\n",
  338. __func__, ret);
  339. goto done;
  340. }
  341. pr_debug("%s: deregistered client %s on service %s\n",
  342. __func__, client_data->client_name,
  343. service_data[service][domain].name);
  344. client_data->service = NO_SERVICE;
  345. if (service_data[service][domain].num_of_clients > 0)
  346. service_data[service][domain].num_of_clients--;
  347. done:
  348. return ret;
  349. }
  350. static void audio_notifier_reg_all_clients(void)
  351. {
  352. struct list_head *ptr, *next;
  353. struct client_data *client_data;
  354. int ret;
  355. list_for_each_safe(ptr, next, &client_list) {
  356. client_data = list_entry(ptr, struct client_data, list);
  357. ret = audio_notifier_reg_client(client_data);
  358. if (ret < 0)
  359. pr_err("%s: audio_notifier_reg_client failed for client %s, ret %d\n",
  360. __func__, client_data->client_name,
  361. ret);
  362. }
  363. }
  364. static int audio_notifier_convert_opcode(unsigned long opcode,
  365. unsigned long *notifier_opcode)
  366. {
  367. int ret = 0;
  368. switch (opcode) {
  369. case QCOM_SSR_BEFORE_SHUTDOWN:
  370. case SERVREG_SERVICE_STATE_DOWN:
  371. *notifier_opcode = AUDIO_NOTIFIER_SERVICE_DOWN;
  372. break;
  373. case QCOM_SSR_AFTER_POWERUP:
  374. case SERVREG_SERVICE_STATE_UP:
  375. *notifier_opcode = AUDIO_NOTIFIER_SERVICE_UP;
  376. break;
  377. default:
  378. pr_debug("%s: Unused opcode 0x%lx\n", __func__, opcode);
  379. ret = -EINVAL;
  380. }
  381. return ret;
  382. }
  383. static int audio_notifier_service_cb(unsigned long opcode,
  384. int service, int domain)
  385. {
  386. int ret = 0;
  387. unsigned long notifier_opcode;
  388. struct audio_notifier_cb_data data;
  389. if (audio_notifier_convert_opcode(opcode, &notifier_opcode) < 0)
  390. goto done;
  391. data.service = service;
  392. data.domain = domain;
  393. pr_debug("%s: service %s, opcode 0x%lx\n",
  394. __func__, service_data[service][domain].name, notifier_opcode);
  395. mutex_lock(&notifier_mutex);
  396. service_data[service][domain].state = notifier_opcode;
  397. ret = srcu_notifier_call_chain(&service_data[service][domain].
  398. client_nb_list, notifier_opcode, &data);
  399. if (ret < 0)
  400. pr_err("%s: srcu_notifier_call_chain returned %d, service %s, opcode 0x%lx\n",
  401. __func__, ret, service_data[service][domain].name,
  402. notifier_opcode);
  403. mutex_unlock(&notifier_mutex);
  404. done:
  405. return NOTIFY_OK;
  406. }
  407. static void audio_notifier_pdr_adsp_cb(int status, char *service_name, void *priv)
  408. {
  409. audio_notifier_service_cb(status, AUDIO_NOTIFIER_PDR_SERVICE, AUDIO_NOTIFIER_ADSP_DOMAIN);
  410. }
  411. static int audio_notifier_ssr_adsp_cb(struct notifier_block *this,
  412. unsigned long opcode, void *data)
  413. {
  414. return audio_notifier_service_cb(opcode,
  415. AUDIO_NOTIFIER_SSR_SERVICE,
  416. AUDIO_NOTIFIER_ADSP_DOMAIN);
  417. }
  418. static int audio_notifier_ssr_modem_cb(struct notifier_block *this,
  419. unsigned long opcode, void *data)
  420. {
  421. return audio_notifier_service_cb(opcode,
  422. AUDIO_NOTIFIER_SSR_SERVICE,
  423. AUDIO_NOTIFIER_MODEM_DOMAIN);
  424. }
  425. int audio_notifier_deregister(char *client_name)
  426. {
  427. int ret = 0;
  428. int ret2;
  429. struct list_head *ptr, *next;
  430. struct client_data *client_data = NULL;
  431. if (client_name == NULL) {
  432. pr_err("%s: client_name is NULL\n", __func__);
  433. ret = -EINVAL;
  434. goto done;
  435. }
  436. mutex_lock(&notifier_mutex);
  437. list_for_each_safe(ptr, next, &client_list) {
  438. client_data = list_entry(ptr, struct client_data, list);
  439. if (!strcmp(client_name, client_data->client_name)) {
  440. ret2 = audio_notifier_dereg_client(client_data);
  441. if (ret2 < 0) {
  442. pr_err("%s: audio_notifier_dereg_client failed, ret %d\n, service %d, domain %d",
  443. __func__, ret2, client_data->service,
  444. client_data->domain);
  445. ret = ret2;
  446. continue;
  447. }
  448. list_del(&client_data->list);
  449. kfree(client_data);
  450. }
  451. }
  452. mutex_unlock(&notifier_mutex);
  453. done:
  454. return ret;
  455. }
  456. EXPORT_SYMBOL(audio_notifier_deregister);
  457. int audio_notifier_register(char *client_name, int domain,
  458. struct notifier_block *nb)
  459. {
  460. int ret;
  461. struct client_data *client_data;
  462. if (client_name == NULL) {
  463. pr_err("%s: client_name is NULL\n", __func__);
  464. ret = -EINVAL;
  465. goto done;
  466. } else if (nb == NULL) {
  467. pr_err("%s: Notifier block is NULL\n", __func__);
  468. ret = -EINVAL;
  469. goto done;
  470. }
  471. client_data = kmalloc(sizeof(*client_data), GFP_KERNEL);
  472. if (client_data == NULL) {
  473. ret = -ENOMEM;
  474. goto done;
  475. }
  476. INIT_LIST_HEAD(&client_data->list);
  477. client_data->nb = nb;
  478. strlcpy(client_data->client_name, client_name,
  479. sizeof(client_data->client_name));
  480. client_data->service = NO_SERVICE;
  481. client_data->domain = domain;
  482. mutex_lock(&notifier_mutex);
  483. ret = audio_notifier_reg_client(client_data);
  484. if (ret < 0) {
  485. mutex_unlock(&notifier_mutex);
  486. pr_err("%s: audio_notifier_reg_client for client %s failed ret = %d\n",
  487. __func__, client_data->client_name,
  488. ret);
  489. kfree(client_data);
  490. goto done;
  491. }
  492. list_add_tail(&client_data->list, &client_list);
  493. mutex_unlock(&notifier_mutex);
  494. done:
  495. return ret;
  496. }
  497. EXPORT_SYMBOL(audio_notifier_register);
  498. static int audio_notifier_subsys_init(void)
  499. {
  500. int i, j;
  501. mutex_init(&notifier_mutex);
  502. INIT_LIST_HEAD(&client_list);
  503. for (i = 0; i < AUDIO_NOTIFIER_MAX_SERVICES; i++) {
  504. for (j = 0; j < AUDIO_NOTIFIER_MAX_DOMAINS; j++) {
  505. if (service_data[i][j].state <= NO_SERVICE)
  506. continue;
  507. srcu_init_notifier_head(
  508. &service_data[i][j].client_nb_list);
  509. }
  510. }
  511. return 0;
  512. }
  513. static int audio_notifier_late_init(void)
  514. {
  515. /*
  516. * If pdr registration failed, register clients on next service
  517. * Do in late init to ensure that SSR subsystem is initialized
  518. */
  519. mutex_lock(&notifier_mutex);
  520. if (!audio_notifier_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE))
  521. audio_notifier_reg_all_clients();
  522. mutex_unlock(&notifier_mutex);
  523. return 0;
  524. }
  525. static int audio_notify_probe(struct platform_device *pdev)
  526. {
  527. int ret = -EINVAL;
  528. struct adsp_notify_private *priv = NULL;
  529. struct property *prop;
  530. int size;
  531. phandle rproc_phandle;
  532. adsp_private = NULL;
  533. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  534. if (!priv) {
  535. ret = -ENOMEM;
  536. return ret;
  537. }
  538. platform_set_drvdata(pdev, priv);
  539. prop = of_find_property(pdev->dev.of_node, "qcom,rproc-handle", &size);
  540. if (!prop) {
  541. dev_err(&pdev->dev, "Missing remotproc handle\n");
  542. return ret;
  543. }
  544. rproc_phandle = be32_to_cpup(prop->value);
  545. priv->rproc_h = rproc_get_by_phandle(rproc_phandle);
  546. if (!priv->rproc_h) {
  547. dev_err(&pdev->dev, "remotproc handle NULL\n");
  548. return ret;
  549. }
  550. adsp_private = pdev;
  551. audio_notifier_subsys_init();
  552. audio_notifier_init_service(AUDIO_NOTIFIER_PDR_SERVICE);
  553. /* Do not return error since PDR enablement is not critical */
  554. audio_notifier_late_init();
  555. return 0;
  556. }
  557. static int audio_notify_remove(struct platform_device *pdev)
  558. {
  559. return 0;
  560. }
  561. static const struct of_device_id adsp_notify_dt_match[] = {
  562. { .compatible = "qcom,adsp-notify" },
  563. { }
  564. };
  565. MODULE_DEVICE_TABLE(of, adsp_notify_dt_match);
  566. static struct platform_driver adsp_notify_driver = {
  567. .driver = {
  568. .name = "adsp-notify",
  569. .owner = THIS_MODULE,
  570. .of_match_table = adsp_notify_dt_match,
  571. .suppress_bind_attrs = true,
  572. },
  573. .probe = audio_notify_probe,
  574. .remove = audio_notify_remove,
  575. };
  576. static int __init audio_notifier_init(void)
  577. {
  578. return platform_driver_register(&adsp_notify_driver);
  579. }
  580. module_init(audio_notifier_init);
  581. static void __exit audio_notifier_exit(void)
  582. {
  583. platform_driver_unregister(&adsp_notify_driver);
  584. }
  585. module_exit(audio_notifier_exit);
  586. MODULE_DESCRIPTION("Audio notifier driver");
  587. MODULE_LICENSE("GPL v2");