blob: 997d4b5897851df4542be4b883d831be68d877b5 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. 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/*
14 * IPC ROUTER SMD XPRT module.
15 */
16#define DEBUG
17
18#include <linux/platform_device.h>
19#include <linux/types.h>
20
21#include <mach/msm_smd.h>
22
23#include "ipc_router.h"
24#include "smd_private.h"
25
26static int msm_ipc_router_smd_xprt_debug_mask;
27module_param_named(debug_mask, msm_ipc_router_smd_xprt_debug_mask,
28 int, S_IRUGO | S_IWUSR | S_IWGRP);
29
30#if defined(DEBUG)
31#define D(x...) do { \
32if (msm_ipc_router_smd_xprt_debug_mask) \
33 pr_info(x); \
34} while (0)
35#else
36#define D(x...) do { } while (0)
37#endif
38
39#define MIN_FRAG_SZ (IPC_ROUTER_HDR_SIZE + sizeof(union rr_control_msg))
40
41struct msm_ipc_router_smd_xprt {
42 struct msm_ipc_router_xprt xprt;
43
44 smd_channel_t *channel;
45};
46
47static struct msm_ipc_router_smd_xprt smd_remote_xprt;
48
Karthikeyan Ramasubramaniandd7daab2011-09-30 15:16:59 -060049struct msm_ipc_router_smd_xprt_work {
50 struct msm_ipc_router_xprt *xprt;
51 struct work_struct work;
52};
53
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054static void smd_xprt_read_data(struct work_struct *work);
Karthikeyan Ramasubramaniandd7daab2011-09-30 15:16:59 -060055static void smd_xprt_open_event(struct work_struct *work);
56static void smd_xprt_close_event(struct work_struct *work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057static DECLARE_DELAYED_WORK(work_read_data, smd_xprt_read_data);
58static struct workqueue_struct *smd_xprt_workqueue;
59
60static wait_queue_head_t write_avail_wait_q;
61static struct rr_packet *in_pkt;
62static int is_partial_in_pkt;
63
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060064static DEFINE_SPINLOCK(modem_reset_lock);
65static int modem_reset;
66
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067static int msm_ipc_router_smd_remote_write_avail(void)
68{
69 return smd_write_avail(smd_remote_xprt.channel);
70}
71
72static int msm_ipc_router_smd_remote_write(void *data,
73 uint32_t len,
74 uint32_t type)
75{
76 struct rr_packet *pkt = (struct rr_packet *)data;
77 struct sk_buff *ipc_rtr_pkt;
78 int align_sz, align_data = 0;
79 int offset, sz_written = 0;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060080 int ret, num_retries = 0;
81 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082
83 if (!pkt)
84 return -EINVAL;
85
86 if (!len || pkt->length != len)
87 return -EINVAL;
88
89 align_sz = ALIGN_SIZE(pkt->length);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060090 while ((ret = smd_write_start(smd_remote_xprt.channel,
91 (len + align_sz))) < 0) {
92 spin_lock_irqsave(&modem_reset_lock, flags);
93 if (modem_reset) {
94 spin_unlock_irqrestore(&modem_reset_lock, flags);
95 pr_err("%s: Modem reset\n", __func__);
96 return -ENETRESET;
97 }
98 spin_unlock_irqrestore(&modem_reset_lock, flags);
99 if (num_retries >= 5) {
100 pr_err("%s: Error %d @ smd_write_start\n",
101 __func__, ret);
102 return ret;
103 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104 msleep(50);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600105 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106
107 D("%s: Ready to write\n", __func__);
108 skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) {
109 offset = 0;
110 while (offset < ipc_rtr_pkt->len) {
111 if (!smd_write_avail(smd_remote_xprt.channel))
112 smd_enable_read_intr(smd_remote_xprt.channel);
113
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600114 wait_event(write_avail_wait_q,
115 (smd_write_avail(smd_remote_xprt.channel) ||
116 modem_reset));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117 smd_disable_read_intr(smd_remote_xprt.channel);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600118 spin_lock_irqsave(&modem_reset_lock, flags);
119 if (modem_reset) {
120 spin_unlock_irqrestore(&modem_reset_lock,
121 flags);
122 pr_err("%s: Modem reset\n", __func__);
123 return -ENETRESET;
124 }
125 spin_unlock_irqrestore(&modem_reset_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126
127 sz_written = smd_write_segment(smd_remote_xprt.channel,
128 ipc_rtr_pkt->data + offset,
129 (ipc_rtr_pkt->len - offset), 0);
130 offset += sz_written;
131 sz_written = 0;
132 }
133 D("%s: Wrote %d bytes\n", __func__, offset);
134 }
135
136 if (align_sz) {
137 if (smd_write_avail(smd_remote_xprt.channel) < align_sz)
138 smd_enable_read_intr(smd_remote_xprt.channel);
139
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600140 wait_event(write_avail_wait_q,
141 ((smd_write_avail(smd_remote_xprt.channel) >=
142 align_sz) || modem_reset));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143 smd_disable_read_intr(smd_remote_xprt.channel);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600144 spin_lock_irqsave(&modem_reset_lock, flags);
145 if (modem_reset) {
146 spin_unlock_irqrestore(&modem_reset_lock, flags);
147 pr_err("%s: Modem reset\n", __func__);
148 return -ENETRESET;
149 }
150 spin_unlock_irqrestore(&modem_reset_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151
152 smd_write_segment(smd_remote_xprt.channel,
153 &align_data, align_sz, 0);
154 D("%s: Wrote %d align bytes\n", __func__, align_sz);
155 }
156 if (!smd_write_end(smd_remote_xprt.channel))
157 D("%s: Finished writing\n", __func__);
158 return len;
159}
160
161static int msm_ipc_router_smd_remote_close(void)
162{
163 smsm_change_state(SMSM_APPS_STATE, SMSM_RPCINIT, 0);
164 return smd_close(smd_remote_xprt.channel);
165}
166
167static void smd_xprt_read_data(struct work_struct *work)
168{
169 int pkt_size, sz_read, sz;
170 struct sk_buff *ipc_rtr_pkt;
171 void *data;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600172 unsigned long flags;
173
174 spin_lock_irqsave(&modem_reset_lock, flags);
175 if (modem_reset) {
176 spin_unlock_irqrestore(&modem_reset_lock, flags);
177 release_pkt(in_pkt);
178 is_partial_in_pkt = 0;
179 pr_err("%s: Modem reset\n", __func__);
180 return;
181 }
182 spin_unlock_irqrestore(&modem_reset_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183
184 D("%s pkt_size: %d, read_avail: %d\n", __func__,
185 smd_cur_packet_size(smd_remote_xprt.channel),
186 smd_read_avail(smd_remote_xprt.channel));
187 while ((pkt_size = smd_cur_packet_size(smd_remote_xprt.channel)) &&
188 smd_read_avail(smd_remote_xprt.channel)) {
189 if (!is_partial_in_pkt) {
190 in_pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
191 if (!in_pkt) {
192 pr_err("%s: Couldn't alloc rr_packet\n",
193 __func__);
194 return;
195 }
196
197 in_pkt->pkt_fragment_q = kmalloc(
198 sizeof(struct sk_buff_head),
199 GFP_KERNEL);
200 if (!in_pkt->pkt_fragment_q) {
201 pr_err("%s: Couldn't alloc pkt_fragment_q\n",
202 __func__);
203 kfree(in_pkt);
204 return;
205 }
206 skb_queue_head_init(in_pkt->pkt_fragment_q);
207 is_partial_in_pkt = 1;
208 D("%s: Allocated rr_packet\n", __func__);
209 }
210
Karthikeyan Ramasubramanian51247a02011-10-12 14:53:15 -0600211 if (((pkt_size >= MIN_FRAG_SZ) &&
212 (smd_read_avail(smd_remote_xprt.channel) < MIN_FRAG_SZ)) ||
213 ((pkt_size < MIN_FRAG_SZ) &&
214 (smd_read_avail(smd_remote_xprt.channel) < pkt_size)))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 return;
216
217 sz = smd_read_avail(smd_remote_xprt.channel);
218 do {
219 ipc_rtr_pkt = alloc_skb(sz, GFP_KERNEL);
220 if (!ipc_rtr_pkt) {
221 if (sz <= (PAGE_SIZE/2)) {
222 queue_delayed_work(smd_xprt_workqueue,
223 &work_read_data,
224 msecs_to_jiffies(100));
225 return;
226 }
227 sz = sz / 2;
228 }
229 } while (!ipc_rtr_pkt);
230
231 D("%s: Allocated the sk_buff of size %d\n",
232 __func__, sz);
233 data = skb_put(ipc_rtr_pkt, sz);
234 sz_read = smd_read(smd_remote_xprt.channel, data, sz);
235 if (sz_read != sz) {
236 pr_err("%s: Couldn't read completely\n", __func__);
237 kfree_skb(ipc_rtr_pkt);
238 release_pkt(in_pkt);
239 is_partial_in_pkt = 0;
240 return;
241 }
242 skb_queue_tail(in_pkt->pkt_fragment_q, ipc_rtr_pkt);
243 in_pkt->length += sz_read;
244 if (sz_read != pkt_size)
245 is_partial_in_pkt = 1;
246 else
247 is_partial_in_pkt = 0;
248
249 if (!is_partial_in_pkt) {
250 D("%s: Packet size read %d\n",
251 __func__, in_pkt->length);
252 msm_ipc_router_xprt_notify(&smd_remote_xprt.xprt,
253 IPC_ROUTER_XPRT_EVENT_DATA,
254 (void *)in_pkt);
255 release_pkt(in_pkt);
256 in_pkt = NULL;
257 }
258 }
259}
260
Karthikeyan Ramasubramaniandd7daab2011-09-30 15:16:59 -0600261static void smd_xprt_open_event(struct work_struct *work)
262{
263 struct msm_ipc_router_smd_xprt_work *xprt_work =
264 container_of(work, struct msm_ipc_router_smd_xprt_work, work);
265
266 msm_ipc_router_xprt_notify(xprt_work->xprt,
267 IPC_ROUTER_XPRT_EVENT_OPEN, NULL);
268 D("%s: Notified IPC Router of OPEN Event\n", __func__);
269 kfree(xprt_work);
270}
271
272static void smd_xprt_close_event(struct work_struct *work)
273{
274 struct msm_ipc_router_smd_xprt_work *xprt_work =
275 container_of(work, struct msm_ipc_router_smd_xprt_work, work);
276
277 msm_ipc_router_xprt_notify(xprt_work->xprt,
278 IPC_ROUTER_XPRT_EVENT_CLOSE, NULL);
279 D("%s: Notified IPC Router of CLOSE Event\n", __func__);
280 kfree(xprt_work);
281}
282
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700283static void msm_ipc_router_smd_remote_notify(void *_dev, unsigned event)
284{
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600285 unsigned long flags;
Karthikeyan Ramasubramaniandd7daab2011-09-30 15:16:59 -0600286 struct msm_ipc_router_smd_xprt_work *xprt_work;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600287
288 switch (event) {
289 case SMD_EVENT_DATA:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700290 if (smd_read_avail(smd_remote_xprt.channel))
291 queue_delayed_work(smd_xprt_workqueue,
292 &work_read_data, 0);
293 if (smd_write_avail(smd_remote_xprt.channel))
294 wake_up(&write_avail_wait_q);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600295 break;
296
297 case SMD_EVENT_OPEN:
298 spin_lock_irqsave(&modem_reset_lock, flags);
299 modem_reset = 0;
300 spin_unlock_irqrestore(&modem_reset_lock, flags);
Karthikeyan Ramasubramaniandd7daab2011-09-30 15:16:59 -0600301 xprt_work = kmalloc(sizeof(struct msm_ipc_router_smd_xprt_work),
302 GFP_ATOMIC);
303 if (!xprt_work) {
304 pr_err("%s: Couldn't notify %d event to IPC Router\n",
305 __func__, event);
306 return;
307 }
308 xprt_work->xprt = &smd_remote_xprt.xprt;
309 INIT_WORK(&xprt_work->work, smd_xprt_open_event);
310 queue_work(smd_xprt_workqueue, &xprt_work->work);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600311 break;
312
313 case SMD_EVENT_CLOSE:
314 spin_lock_irqsave(&modem_reset_lock, flags);
315 modem_reset = 1;
316 spin_unlock_irqrestore(&modem_reset_lock, flags);
317 wake_up(&write_avail_wait_q);
Karthikeyan Ramasubramaniandd7daab2011-09-30 15:16:59 -0600318 xprt_work = kmalloc(sizeof(struct msm_ipc_router_smd_xprt_work),
319 GFP_ATOMIC);
320 if (!xprt_work) {
321 pr_err("%s: Couldn't notify %d event to IPC Router\n",
322 __func__, event);
323 return;
324 }
325 xprt_work->xprt = &smd_remote_xprt.xprt;
326 INIT_WORK(&xprt_work->work, smd_xprt_close_event);
327 queue_work(smd_xprt_workqueue, &xprt_work->work);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600328 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329 }
330}
331
332static int msm_ipc_router_smd_remote_probe(struct platform_device *pdev)
333{
334 int rc;
335
336 smd_xprt_workqueue = create_singlethread_workqueue("smd_xprt");
337 if (!smd_xprt_workqueue)
338 return -ENOMEM;
339
340 smd_remote_xprt.xprt.name = "msm_ipc_router_smd_xprt";
341 smd_remote_xprt.xprt.link_id = 1;
342 smd_remote_xprt.xprt.read_avail = NULL;
343 smd_remote_xprt.xprt.read = NULL;
344 smd_remote_xprt.xprt.write_avail =
345 msm_ipc_router_smd_remote_write_avail;
346 smd_remote_xprt.xprt.write = msm_ipc_router_smd_remote_write;
347 smd_remote_xprt.xprt.close = msm_ipc_router_smd_remote_close;
348 smd_remote_xprt.xprt.priv = NULL;
349
350 init_waitqueue_head(&write_avail_wait_q);
351
352 rc = smd_open("RPCRPY_CNTL", &smd_remote_xprt.channel, NULL,
353 msm_ipc_router_smd_remote_notify);
354 if (rc < 0) {
355 destroy_workqueue(smd_xprt_workqueue);
356 return rc;
357 }
358
359 smd_disable_read_intr(smd_remote_xprt.channel);
360
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361 smsm_change_state(SMSM_APPS_STATE, 0, SMSM_RPCINIT);
362
363 return 0;
364}
365
366static struct platform_driver msm_ipc_router_smd_remote_driver = {
367 .probe = msm_ipc_router_smd_remote_probe,
368 .driver = {
369 .name = "RPCRPY_CNTL",
370 .owner = THIS_MODULE,
371 },
372};
373
374static int __init msm_ipc_router_smd_init(void)
375{
376 return platform_driver_register(&msm_ipc_router_smd_remote_driver);
377}
378
379module_init(msm_ipc_router_smd_init);
380MODULE_DESCRIPTION("RPC Router SMD XPRT");
381MODULE_LICENSE("GPL v2");