ipc_router_smd_xprt.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. */
  12. /*
  13. * IPC ROUTER SMD XPRT module.
  14. */
  15. #define DEBUG
  16. #include <linux/module.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/types.h>
  19. #include <mach/msm_smd.h>
  20. #include <mach/subsystem_restart.h>
  21. #include "ipc_router.h"
  22. #include "smd_private.h"
  23. static int msm_ipc_router_smd_xprt_debug_mask;
  24. module_param_named(debug_mask, msm_ipc_router_smd_xprt_debug_mask,
  25. int, S_IRUGO | S_IWUSR | S_IWGRP);
  26. #if defined(DEBUG)
  27. #define D(x...) do { \
  28. if (msm_ipc_router_smd_xprt_debug_mask) \
  29. pr_info(x); \
  30. } while (0)
  31. #else
  32. #define D(x...) do { } while (0)
  33. #endif
  34. #define MIN_FRAG_SZ (IPC_ROUTER_HDR_SIZE + sizeof(union rr_control_msg))
  35. #define NUM_SMD_XPRTS 4
  36. #define XPRT_NAME_LEN (SMD_MAX_CH_NAME_LEN + 12)
  37. struct msm_ipc_router_smd_xprt {
  38. struct msm_ipc_router_xprt xprt;
  39. smd_channel_t *channel;
  40. struct workqueue_struct *smd_xprt_wq;
  41. wait_queue_head_t write_avail_wait_q;
  42. struct rr_packet *in_pkt;
  43. int is_partial_in_pkt;
  44. struct delayed_work read_work;
  45. spinlock_t ss_reset_lock; /*Subsystem reset lock*/
  46. int ss_reset;
  47. void *pil;
  48. struct completion sft_close_complete;
  49. unsigned xprt_version;
  50. unsigned xprt_option;
  51. };
  52. struct msm_ipc_router_smd_xprt_work {
  53. struct msm_ipc_router_xprt *xprt;
  54. struct work_struct work;
  55. };
  56. static void smd_xprt_read_data(struct work_struct *work);
  57. static void smd_xprt_open_event(struct work_struct *work);
  58. static void smd_xprt_close_event(struct work_struct *work);
  59. struct msm_ipc_router_smd_xprt_config {
  60. char ch_name[SMD_MAX_CH_NAME_LEN];
  61. char xprt_name[XPRT_NAME_LEN];
  62. uint32_t edge;
  63. uint32_t link_id;
  64. unsigned xprt_version;
  65. };
  66. struct msm_ipc_router_smd_xprt_config smd_xprt_cfg[] = {
  67. {"RPCRPY_CNTL", "ipc_rtr_smd_rpcrpy_cntl", SMD_APPS_MODEM, 1, 1},
  68. {"IPCRTR", "ipc_rtr_smd_ipcrtr", SMD_APPS_MODEM, 1, 1},
  69. {"IPCRTR", "ipc_rtr_q6_ipcrtr", SMD_APPS_QDSP, 1, 1},
  70. {"IPCRTR", "ipc_rtr_wcnss_ipcrtr", SMD_APPS_WCNSS, 1, 1},
  71. };
  72. static struct msm_ipc_router_smd_xprt smd_remote_xprt[NUM_SMD_XPRTS];
  73. static int find_smd_xprt_cfg(struct platform_device *pdev)
  74. {
  75. int i;
  76. for (i = 0; i < NUM_SMD_XPRTS; i++) {
  77. if (!strncmp(pdev->name, smd_xprt_cfg[i].ch_name, 20) &&
  78. (pdev->id == smd_xprt_cfg[i].edge))
  79. return i;
  80. }
  81. return -ENODEV;
  82. }
  83. static int msm_ipc_router_smd_get_xprt_version(
  84. struct msm_ipc_router_xprt *xprt)
  85. {
  86. struct msm_ipc_router_smd_xprt *smd_xprtp;
  87. if (!xprt)
  88. return -EINVAL;
  89. smd_xprtp = container_of(xprt, struct msm_ipc_router_smd_xprt, xprt);
  90. return (int)smd_xprtp->xprt_version;
  91. }
  92. static int msm_ipc_router_smd_get_xprt_option(
  93. struct msm_ipc_router_xprt *xprt)
  94. {
  95. struct msm_ipc_router_smd_xprt *smd_xprtp;
  96. if (!xprt)
  97. return -EINVAL;
  98. smd_xprtp = container_of(xprt, struct msm_ipc_router_smd_xprt, xprt);
  99. return (int)smd_xprtp->xprt_option;
  100. }
  101. static int msm_ipc_router_smd_remote_write_avail(
  102. struct msm_ipc_router_xprt *xprt)
  103. {
  104. struct msm_ipc_router_smd_xprt *smd_xprtp =
  105. container_of(xprt, struct msm_ipc_router_smd_xprt, xprt);
  106. return smd_write_avail(smd_xprtp->channel);
  107. }
  108. static int msm_ipc_router_smd_remote_write(void *data,
  109. uint32_t len,
  110. struct msm_ipc_router_xprt *xprt)
  111. {
  112. struct rr_packet *pkt = (struct rr_packet *)data;
  113. struct sk_buff *ipc_rtr_pkt;
  114. int offset, sz_written = 0;
  115. int ret, num_retries = 0;
  116. unsigned long flags;
  117. struct msm_ipc_router_smd_xprt *smd_xprtp =
  118. container_of(xprt, struct msm_ipc_router_smd_xprt, xprt);
  119. if (!pkt)
  120. return -EINVAL;
  121. if (!len || pkt->length != len)
  122. return -EINVAL;
  123. while ((ret = smd_write_start(smd_xprtp->channel, len)) < 0) {
  124. spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags);
  125. if (smd_xprtp->ss_reset) {
  126. spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock,
  127. flags);
  128. pr_err("%s: %s chnl reset\n", __func__, xprt->name);
  129. return -ENETRESET;
  130. }
  131. spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags);
  132. if (num_retries >= 5) {
  133. pr_err("%s: Error %d @smd_write_start for %s\n",
  134. __func__, ret, xprt->name);
  135. return ret;
  136. }
  137. msleep(50);
  138. num_retries++;
  139. }
  140. D("%s: Ready to write %d bytes\n", __func__, len);
  141. skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) {
  142. offset = 0;
  143. while (offset < ipc_rtr_pkt->len) {
  144. if (!smd_write_segment_avail(smd_xprtp->channel))
  145. smd_enable_read_intr(smd_xprtp->channel);
  146. wait_event(smd_xprtp->write_avail_wait_q,
  147. (smd_write_segment_avail(smd_xprtp->channel) ||
  148. smd_xprtp->ss_reset));
  149. smd_disable_read_intr(smd_xprtp->channel);
  150. spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags);
  151. if (smd_xprtp->ss_reset) {
  152. spin_unlock_irqrestore(
  153. &smd_xprtp->ss_reset_lock, flags);
  154. pr_err("%s: %s chnl reset\n",
  155. __func__, xprt->name);
  156. return -ENETRESET;
  157. }
  158. spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock,
  159. flags);
  160. sz_written = smd_write_segment(smd_xprtp->channel,
  161. ipc_rtr_pkt->data + offset,
  162. (ipc_rtr_pkt->len - offset), 0);
  163. offset += sz_written;
  164. sz_written = 0;
  165. }
  166. D("%s: Wrote %d bytes over %s\n",
  167. __func__, offset, xprt->name);
  168. }
  169. if (!smd_write_end(smd_xprtp->channel))
  170. D("%s: Finished writing\n", __func__);
  171. return len;
  172. }
  173. static int msm_ipc_router_smd_remote_close(struct msm_ipc_router_xprt *xprt)
  174. {
  175. int rc;
  176. struct msm_ipc_router_smd_xprt *smd_xprtp =
  177. container_of(xprt, struct msm_ipc_router_smd_xprt, xprt);
  178. rc = smd_close(smd_xprtp->channel);
  179. if (smd_xprtp->pil) {
  180. subsystem_put(smd_xprtp->pil);
  181. smd_xprtp->pil = NULL;
  182. }
  183. return rc;
  184. }
  185. static void smd_xprt_sft_close_done(struct msm_ipc_router_xprt *xprt)
  186. {
  187. struct msm_ipc_router_smd_xprt *smd_xprtp =
  188. container_of(xprt, struct msm_ipc_router_smd_xprt, xprt);
  189. complete_all(&smd_xprtp->sft_close_complete);
  190. }
  191. static void smd_xprt_read_data(struct work_struct *work)
  192. {
  193. int pkt_size, sz_read, sz;
  194. struct sk_buff *ipc_rtr_pkt;
  195. void *data;
  196. unsigned long flags;
  197. struct delayed_work *rwork = to_delayed_work(work);
  198. struct msm_ipc_router_smd_xprt *smd_xprtp =
  199. container_of(rwork, struct msm_ipc_router_smd_xprt, read_work);
  200. spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags);
  201. if (smd_xprtp->ss_reset) {
  202. spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags);
  203. if (smd_xprtp->in_pkt)
  204. release_pkt(smd_xprtp->in_pkt);
  205. smd_xprtp->is_partial_in_pkt = 0;
  206. pr_err("%s: %s channel reset\n",
  207. __func__, smd_xprtp->xprt.name);
  208. return;
  209. }
  210. spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags);
  211. D("%s pkt_size: %d, read_avail: %d\n", __func__,
  212. smd_cur_packet_size(smd_xprtp->channel),
  213. smd_read_avail(smd_xprtp->channel));
  214. while ((pkt_size = smd_cur_packet_size(smd_xprtp->channel)) &&
  215. smd_read_avail(smd_xprtp->channel)) {
  216. if (!smd_xprtp->is_partial_in_pkt) {
  217. smd_xprtp->in_pkt = kzalloc(sizeof(struct rr_packet),
  218. GFP_KERNEL);
  219. if (!smd_xprtp->in_pkt) {
  220. pr_err("%s: Couldn't alloc rr_packet\n",
  221. __func__);
  222. return;
  223. }
  224. smd_xprtp->in_pkt->pkt_fragment_q =
  225. kmalloc(sizeof(struct sk_buff_head),
  226. GFP_KERNEL);
  227. if (!smd_xprtp->in_pkt->pkt_fragment_q) {
  228. pr_err("%s: Couldn't alloc pkt_fragment_q\n",
  229. __func__);
  230. kfree(smd_xprtp->in_pkt);
  231. return;
  232. }
  233. skb_queue_head_init(smd_xprtp->in_pkt->pkt_fragment_q);
  234. smd_xprtp->is_partial_in_pkt = 1;
  235. D("%s: Allocated rr_packet\n", __func__);
  236. }
  237. if (((pkt_size >= MIN_FRAG_SZ) &&
  238. (smd_read_avail(smd_xprtp->channel) < MIN_FRAG_SZ)) ||
  239. ((pkt_size < MIN_FRAG_SZ) &&
  240. (smd_read_avail(smd_xprtp->channel) < pkt_size)))
  241. return;
  242. sz = smd_read_avail(smd_xprtp->channel);
  243. do {
  244. ipc_rtr_pkt = alloc_skb(sz, GFP_KERNEL);
  245. if (!ipc_rtr_pkt) {
  246. if (sz <= (PAGE_SIZE/2)) {
  247. queue_delayed_work(
  248. smd_xprtp->smd_xprt_wq,
  249. &smd_xprtp->read_work,
  250. msecs_to_jiffies(100));
  251. return;
  252. }
  253. sz = sz / 2;
  254. }
  255. } while (!ipc_rtr_pkt);
  256. D("%s: Allocated the sk_buff of size %d\n", __func__, sz);
  257. data = skb_put(ipc_rtr_pkt, sz);
  258. sz_read = smd_read(smd_xprtp->channel, data, sz);
  259. if (sz_read != sz) {
  260. pr_err("%s: Couldn't read %s completely\n",
  261. __func__, smd_xprtp->xprt.name);
  262. kfree_skb(ipc_rtr_pkt);
  263. release_pkt(smd_xprtp->in_pkt);
  264. smd_xprtp->is_partial_in_pkt = 0;
  265. return;
  266. }
  267. skb_queue_tail(smd_xprtp->in_pkt->pkt_fragment_q, ipc_rtr_pkt);
  268. smd_xprtp->in_pkt->length += sz_read;
  269. if (sz_read != pkt_size)
  270. smd_xprtp->is_partial_in_pkt = 1;
  271. else
  272. smd_xprtp->is_partial_in_pkt = 0;
  273. if (!smd_xprtp->is_partial_in_pkt) {
  274. D("%s: Packet size read %d\n",
  275. __func__, smd_xprtp->in_pkt->length);
  276. msm_ipc_router_xprt_notify(&smd_xprtp->xprt,
  277. IPC_ROUTER_XPRT_EVENT_DATA,
  278. (void *)smd_xprtp->in_pkt);
  279. release_pkt(smd_xprtp->in_pkt);
  280. smd_xprtp->in_pkt = NULL;
  281. }
  282. }
  283. }
  284. static void smd_xprt_open_event(struct work_struct *work)
  285. {
  286. struct msm_ipc_router_smd_xprt_work *xprt_work =
  287. container_of(work, struct msm_ipc_router_smd_xprt_work, work);
  288. struct msm_ipc_router_smd_xprt *smd_xprtp =
  289. container_of(xprt_work->xprt,
  290. struct msm_ipc_router_smd_xprt, xprt);
  291. unsigned long flags;
  292. spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags);
  293. smd_xprtp->ss_reset = 0;
  294. spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags);
  295. msm_ipc_router_xprt_notify(xprt_work->xprt,
  296. IPC_ROUTER_XPRT_EVENT_OPEN, NULL);
  297. D("%s: Notified IPC Router of %s OPEN\n",
  298. __func__, xprt_work->xprt->name);
  299. kfree(xprt_work);
  300. }
  301. static void smd_xprt_close_event(struct work_struct *work)
  302. {
  303. struct msm_ipc_router_smd_xprt_work *xprt_work =
  304. container_of(work, struct msm_ipc_router_smd_xprt_work, work);
  305. struct msm_ipc_router_smd_xprt *smd_xprtp =
  306. container_of(xprt_work->xprt,
  307. struct msm_ipc_router_smd_xprt, xprt);
  308. init_completion(&smd_xprtp->sft_close_complete);
  309. msm_ipc_router_xprt_notify(xprt_work->xprt,
  310. IPC_ROUTER_XPRT_EVENT_CLOSE, NULL);
  311. D("%s: Notified IPC Router of %s CLOSE\n",
  312. __func__, xprt_work->xprt->name);
  313. wait_for_completion(&smd_xprtp->sft_close_complete);
  314. kfree(xprt_work);
  315. }
  316. static void msm_ipc_router_smd_remote_notify(void *_dev, unsigned event)
  317. {
  318. unsigned long flags;
  319. struct msm_ipc_router_smd_xprt *smd_xprtp;
  320. struct msm_ipc_router_smd_xprt_work *xprt_work;
  321. smd_xprtp = (struct msm_ipc_router_smd_xprt *)_dev;
  322. if (!smd_xprtp)
  323. return;
  324. switch (event) {
  325. case SMD_EVENT_DATA:
  326. if (smd_read_avail(smd_xprtp->channel))
  327. queue_delayed_work(smd_xprtp->smd_xprt_wq,
  328. &smd_xprtp->read_work, 0);
  329. if (smd_write_segment_avail(smd_xprtp->channel))
  330. wake_up(&smd_xprtp->write_avail_wait_q);
  331. break;
  332. case SMD_EVENT_OPEN:
  333. xprt_work = kmalloc(sizeof(struct msm_ipc_router_smd_xprt_work),
  334. GFP_ATOMIC);
  335. if (!xprt_work) {
  336. pr_err("%s: Couldn't notify %d event to IPC Router\n",
  337. __func__, event);
  338. return;
  339. }
  340. xprt_work->xprt = &smd_xprtp->xprt;
  341. INIT_WORK(&xprt_work->work, smd_xprt_open_event);
  342. queue_work(smd_xprtp->smd_xprt_wq, &xprt_work->work);
  343. break;
  344. case SMD_EVENT_CLOSE:
  345. spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags);
  346. smd_xprtp->ss_reset = 1;
  347. spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags);
  348. wake_up(&smd_xprtp->write_avail_wait_q);
  349. xprt_work = kmalloc(sizeof(struct msm_ipc_router_smd_xprt_work),
  350. GFP_ATOMIC);
  351. if (!xprt_work) {
  352. pr_err("%s: Couldn't notify %d event to IPC Router\n",
  353. __func__, event);
  354. return;
  355. }
  356. xprt_work->xprt = &smd_xprtp->xprt;
  357. INIT_WORK(&xprt_work->work, smd_xprt_close_event);
  358. queue_work(smd_xprtp->smd_xprt_wq, &xprt_work->work);
  359. break;
  360. }
  361. }
  362. static void *msm_ipc_load_subsystem(uint32_t edge)
  363. {
  364. void *pil = NULL;
  365. const char *peripheral;
  366. peripheral = smd_edge_to_subsystem(edge);
  367. if (peripheral) {
  368. pil = subsystem_get(peripheral);
  369. if (IS_ERR(pil)) {
  370. pr_err("%s: Failed to load %s\n",
  371. __func__, peripheral);
  372. pil = NULL;
  373. }
  374. }
  375. return pil;
  376. }
  377. static int msm_ipc_router_smd_remote_probe(struct platform_device *pdev)
  378. {
  379. int rc;
  380. int id; /*Index into the smd_xprt_cfg table*/
  381. id = find_smd_xprt_cfg(pdev);
  382. if (id < 0) {
  383. pr_err("%s: called for unknown ch %s\n",
  384. __func__, pdev->name);
  385. return id;
  386. }
  387. smd_remote_xprt[id].smd_xprt_wq =
  388. create_singlethread_workqueue(pdev->name);
  389. if (!smd_remote_xprt[id].smd_xprt_wq) {
  390. pr_err("%s: WQ creation failed for %s\n",
  391. __func__, pdev->name);
  392. return -EFAULT;
  393. }
  394. smd_remote_xprt[id].xprt.name = smd_xprt_cfg[id].xprt_name;
  395. smd_remote_xprt[id].xprt.link_id = smd_xprt_cfg[id].link_id;
  396. smd_remote_xprt[id].xprt.get_version =
  397. msm_ipc_router_smd_get_xprt_version;
  398. smd_remote_xprt[id].xprt.get_option =
  399. msm_ipc_router_smd_get_xprt_option;
  400. smd_remote_xprt[id].xprt.read_avail = NULL;
  401. smd_remote_xprt[id].xprt.read = NULL;
  402. smd_remote_xprt[id].xprt.write_avail =
  403. msm_ipc_router_smd_remote_write_avail;
  404. smd_remote_xprt[id].xprt.write = msm_ipc_router_smd_remote_write;
  405. smd_remote_xprt[id].xprt.close = msm_ipc_router_smd_remote_close;
  406. smd_remote_xprt[id].xprt.sft_close_done = smd_xprt_sft_close_done;
  407. smd_remote_xprt[id].xprt.priv = NULL;
  408. init_waitqueue_head(&smd_remote_xprt[id].write_avail_wait_q);
  409. smd_remote_xprt[id].in_pkt = NULL;
  410. smd_remote_xprt[id].is_partial_in_pkt = 0;
  411. INIT_DELAYED_WORK(&smd_remote_xprt[id].read_work, smd_xprt_read_data);
  412. spin_lock_init(&smd_remote_xprt[id].ss_reset_lock);
  413. smd_remote_xprt[id].ss_reset = 0;
  414. smd_remote_xprt[id].xprt_version = smd_xprt_cfg[id].xprt_version;
  415. smd_remote_xprt[id].xprt_option = FRAG_PKT_WRITE_ENABLE;
  416. smd_remote_xprt[id].pil = msm_ipc_load_subsystem(
  417. smd_xprt_cfg[id].edge);
  418. rc = smd_named_open_on_edge(smd_xprt_cfg[id].ch_name,
  419. smd_xprt_cfg[id].edge,
  420. &smd_remote_xprt[id].channel,
  421. &smd_remote_xprt[id],
  422. msm_ipc_router_smd_remote_notify);
  423. if (rc < 0) {
  424. pr_err("%s: Channel open failed for %s\n",
  425. __func__, smd_xprt_cfg[id].ch_name);
  426. if (smd_remote_xprt[id].pil) {
  427. subsystem_put(smd_remote_xprt[id].pil);
  428. smd_remote_xprt[id].pil = NULL;
  429. }
  430. destroy_workqueue(smd_remote_xprt[id].smd_xprt_wq);
  431. return rc;
  432. }
  433. smd_disable_read_intr(smd_remote_xprt[id].channel);
  434. smsm_change_state(SMSM_APPS_STATE, 0, SMSM_RPCINIT);
  435. return 0;
  436. }
  437. void *msm_ipc_load_default_node(void)
  438. {
  439. void *pil = NULL;
  440. const char *peripheral;
  441. peripheral = smd_edge_to_subsystem(SMD_APPS_MODEM);
  442. if (peripheral && !strncmp(peripheral, "modem", 6)) {
  443. pil = subsystem_get(peripheral);
  444. if (IS_ERR(pil)) {
  445. pr_err("%s: Failed to load %s\n",
  446. __func__, peripheral);
  447. pil = NULL;
  448. }
  449. }
  450. return pil;
  451. }
  452. EXPORT_SYMBOL(msm_ipc_load_default_node);
  453. void msm_ipc_unload_default_node(void *pil)
  454. {
  455. if (pil)
  456. subsystem_put(pil);
  457. }
  458. EXPORT_SYMBOL(msm_ipc_unload_default_node);
  459. static struct platform_driver msm_ipc_router_smd_remote_driver[] = {
  460. {
  461. .probe = msm_ipc_router_smd_remote_probe,
  462. .driver = {
  463. .name = "RPCRPY_CNTL",
  464. .owner = THIS_MODULE,
  465. },
  466. },
  467. {
  468. .probe = msm_ipc_router_smd_remote_probe,
  469. .driver = {
  470. .name = "IPCRTR",
  471. .owner = THIS_MODULE,
  472. },
  473. },
  474. };
  475. static int __init msm_ipc_router_smd_init(void)
  476. {
  477. int i, ret, rc = 0;
  478. BUG_ON(ARRAY_SIZE(smd_xprt_cfg) != NUM_SMD_XPRTS);
  479. for (i = 0; i < ARRAY_SIZE(msm_ipc_router_smd_remote_driver); i++) {
  480. ret = platform_driver_register(
  481. &msm_ipc_router_smd_remote_driver[i]);
  482. if (ret) {
  483. pr_err("%s: Failed to register platform driver for"
  484. " xprt%d. Continuing...\n", __func__, i);
  485. rc = ret;
  486. }
  487. }
  488. return rc;
  489. }
  490. module_init(msm_ipc_router_smd_init);
  491. MODULE_DESCRIPTION("IPC Router SMD XPRT");
  492. MODULE_LICENSE("GPL v2");