blob: 5987752854b3dae7ebd674d8c38defb003f00dcc [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
49static void smd_xprt_read_data(struct work_struct *work);
50static DECLARE_DELAYED_WORK(work_read_data, smd_xprt_read_data);
51static struct workqueue_struct *smd_xprt_workqueue;
52
53static wait_queue_head_t write_avail_wait_q;
54static struct rr_packet *in_pkt;
55static int is_partial_in_pkt;
56
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060057static DEFINE_SPINLOCK(modem_reset_lock);
58static int modem_reset;
59
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060static int msm_ipc_router_smd_remote_write_avail(void)
61{
62 return smd_write_avail(smd_remote_xprt.channel);
63}
64
65static int msm_ipc_router_smd_remote_write(void *data,
66 uint32_t len,
67 uint32_t type)
68{
69 struct rr_packet *pkt = (struct rr_packet *)data;
70 struct sk_buff *ipc_rtr_pkt;
71 int align_sz, align_data = 0;
72 int offset, sz_written = 0;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060073 int ret, num_retries = 0;
74 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075
76 if (!pkt)
77 return -EINVAL;
78
79 if (!len || pkt->length != len)
80 return -EINVAL;
81
82 align_sz = ALIGN_SIZE(pkt->length);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060083 while ((ret = smd_write_start(smd_remote_xprt.channel,
84 (len + align_sz))) < 0) {
85 spin_lock_irqsave(&modem_reset_lock, flags);
86 if (modem_reset) {
87 spin_unlock_irqrestore(&modem_reset_lock, flags);
88 pr_err("%s: Modem reset\n", __func__);
89 return -ENETRESET;
90 }
91 spin_unlock_irqrestore(&modem_reset_lock, flags);
92 if (num_retries >= 5) {
93 pr_err("%s: Error %d @ smd_write_start\n",
94 __func__, ret);
95 return ret;
96 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097 msleep(50);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060098 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099
100 D("%s: Ready to write\n", __func__);
101 skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) {
102 offset = 0;
103 while (offset < ipc_rtr_pkt->len) {
104 if (!smd_write_avail(smd_remote_xprt.channel))
105 smd_enable_read_intr(smd_remote_xprt.channel);
106
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600107 wait_event(write_avail_wait_q,
108 (smd_write_avail(smd_remote_xprt.channel) ||
109 modem_reset));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110 smd_disable_read_intr(smd_remote_xprt.channel);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600111 spin_lock_irqsave(&modem_reset_lock, flags);
112 if (modem_reset) {
113 spin_unlock_irqrestore(&modem_reset_lock,
114 flags);
115 pr_err("%s: Modem reset\n", __func__);
116 return -ENETRESET;
117 }
118 spin_unlock_irqrestore(&modem_reset_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119
120 sz_written = smd_write_segment(smd_remote_xprt.channel,
121 ipc_rtr_pkt->data + offset,
122 (ipc_rtr_pkt->len - offset), 0);
123 offset += sz_written;
124 sz_written = 0;
125 }
126 D("%s: Wrote %d bytes\n", __func__, offset);
127 }
128
129 if (align_sz) {
130 if (smd_write_avail(smd_remote_xprt.channel) < align_sz)
131 smd_enable_read_intr(smd_remote_xprt.channel);
132
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600133 wait_event(write_avail_wait_q,
134 ((smd_write_avail(smd_remote_xprt.channel) >=
135 align_sz) || modem_reset));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136 smd_disable_read_intr(smd_remote_xprt.channel);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600137 spin_lock_irqsave(&modem_reset_lock, flags);
138 if (modem_reset) {
139 spin_unlock_irqrestore(&modem_reset_lock, flags);
140 pr_err("%s: Modem reset\n", __func__);
141 return -ENETRESET;
142 }
143 spin_unlock_irqrestore(&modem_reset_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144
145 smd_write_segment(smd_remote_xprt.channel,
146 &align_data, align_sz, 0);
147 D("%s: Wrote %d align bytes\n", __func__, align_sz);
148 }
149 if (!smd_write_end(smd_remote_xprt.channel))
150 D("%s: Finished writing\n", __func__);
151 return len;
152}
153
154static int msm_ipc_router_smd_remote_close(void)
155{
156 smsm_change_state(SMSM_APPS_STATE, SMSM_RPCINIT, 0);
157 return smd_close(smd_remote_xprt.channel);
158}
159
160static void smd_xprt_read_data(struct work_struct *work)
161{
162 int pkt_size, sz_read, sz;
163 struct sk_buff *ipc_rtr_pkt;
164 void *data;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600165 unsigned long flags;
166
167 spin_lock_irqsave(&modem_reset_lock, flags);
168 if (modem_reset) {
169 spin_unlock_irqrestore(&modem_reset_lock, flags);
170 release_pkt(in_pkt);
171 is_partial_in_pkt = 0;
172 pr_err("%s: Modem reset\n", __func__);
173 return;
174 }
175 spin_unlock_irqrestore(&modem_reset_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176
177 D("%s pkt_size: %d, read_avail: %d\n", __func__,
178 smd_cur_packet_size(smd_remote_xprt.channel),
179 smd_read_avail(smd_remote_xprt.channel));
180 while ((pkt_size = smd_cur_packet_size(smd_remote_xprt.channel)) &&
181 smd_read_avail(smd_remote_xprt.channel)) {
182 if (!is_partial_in_pkt) {
183 in_pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
184 if (!in_pkt) {
185 pr_err("%s: Couldn't alloc rr_packet\n",
186 __func__);
187 return;
188 }
189
190 in_pkt->pkt_fragment_q = kmalloc(
191 sizeof(struct sk_buff_head),
192 GFP_KERNEL);
193 if (!in_pkt->pkt_fragment_q) {
194 pr_err("%s: Couldn't alloc pkt_fragment_q\n",
195 __func__);
196 kfree(in_pkt);
197 return;
198 }
199 skb_queue_head_init(in_pkt->pkt_fragment_q);
200 is_partial_in_pkt = 1;
201 D("%s: Allocated rr_packet\n", __func__);
202 }
203
204 if ((pkt_size >= MIN_FRAG_SZ) &&
205 (smd_read_avail(smd_remote_xprt.channel) < MIN_FRAG_SZ))
206 return;
207
208 sz = smd_read_avail(smd_remote_xprt.channel);
209 do {
210 ipc_rtr_pkt = alloc_skb(sz, GFP_KERNEL);
211 if (!ipc_rtr_pkt) {
212 if (sz <= (PAGE_SIZE/2)) {
213 queue_delayed_work(smd_xprt_workqueue,
214 &work_read_data,
215 msecs_to_jiffies(100));
216 return;
217 }
218 sz = sz / 2;
219 }
220 } while (!ipc_rtr_pkt);
221
222 D("%s: Allocated the sk_buff of size %d\n",
223 __func__, sz);
224 data = skb_put(ipc_rtr_pkt, sz);
225 sz_read = smd_read(smd_remote_xprt.channel, data, sz);
226 if (sz_read != sz) {
227 pr_err("%s: Couldn't read completely\n", __func__);
228 kfree_skb(ipc_rtr_pkt);
229 release_pkt(in_pkt);
230 is_partial_in_pkt = 0;
231 return;
232 }
233 skb_queue_tail(in_pkt->pkt_fragment_q, ipc_rtr_pkt);
234 in_pkt->length += sz_read;
235 if (sz_read != pkt_size)
236 is_partial_in_pkt = 1;
237 else
238 is_partial_in_pkt = 0;
239
240 if (!is_partial_in_pkt) {
241 D("%s: Packet size read %d\n",
242 __func__, in_pkt->length);
243 msm_ipc_router_xprt_notify(&smd_remote_xprt.xprt,
244 IPC_ROUTER_XPRT_EVENT_DATA,
245 (void *)in_pkt);
246 release_pkt(in_pkt);
247 in_pkt = NULL;
248 }
249 }
250}
251
252static void msm_ipc_router_smd_remote_notify(void *_dev, unsigned event)
253{
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600254 unsigned long flags;
255
256 switch (event) {
257 case SMD_EVENT_DATA:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258 if (smd_read_avail(smd_remote_xprt.channel))
259 queue_delayed_work(smd_xprt_workqueue,
260 &work_read_data, 0);
261 if (smd_write_avail(smd_remote_xprt.channel))
262 wake_up(&write_avail_wait_q);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600263 break;
264
265 case SMD_EVENT_OPEN:
266 spin_lock_irqsave(&modem_reset_lock, flags);
267 modem_reset = 0;
268 spin_unlock_irqrestore(&modem_reset_lock, flags);
269 msm_ipc_router_xprt_notify(&smd_remote_xprt.xprt,
270 IPC_ROUTER_XPRT_EVENT_OPEN, NULL);
271 D("%s: Notified IPC Router of OPEN Event\n", __func__);
272 break;
273
274 case SMD_EVENT_CLOSE:
275 spin_lock_irqsave(&modem_reset_lock, flags);
276 modem_reset = 1;
277 spin_unlock_irqrestore(&modem_reset_lock, flags);
278 wake_up(&write_avail_wait_q);
279 msm_ipc_router_xprt_notify(&smd_remote_xprt.xprt,
280 IPC_ROUTER_XPRT_EVENT_CLOSE, NULL);
281 D("%s: Notified IPC Router of CLOSE Event\n", __func__);
282 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700283 }
284}
285
286static int msm_ipc_router_smd_remote_probe(struct platform_device *pdev)
287{
288 int rc;
289
290 smd_xprt_workqueue = create_singlethread_workqueue("smd_xprt");
291 if (!smd_xprt_workqueue)
292 return -ENOMEM;
293
294 smd_remote_xprt.xprt.name = "msm_ipc_router_smd_xprt";
295 smd_remote_xprt.xprt.link_id = 1;
296 smd_remote_xprt.xprt.read_avail = NULL;
297 smd_remote_xprt.xprt.read = NULL;
298 smd_remote_xprt.xprt.write_avail =
299 msm_ipc_router_smd_remote_write_avail;
300 smd_remote_xprt.xprt.write = msm_ipc_router_smd_remote_write;
301 smd_remote_xprt.xprt.close = msm_ipc_router_smd_remote_close;
302 smd_remote_xprt.xprt.priv = NULL;
303
304 init_waitqueue_head(&write_avail_wait_q);
305
306 rc = smd_open("RPCRPY_CNTL", &smd_remote_xprt.channel, NULL,
307 msm_ipc_router_smd_remote_notify);
308 if (rc < 0) {
309 destroy_workqueue(smd_xprt_workqueue);
310 return rc;
311 }
312
313 smd_disable_read_intr(smd_remote_xprt.channel);
314
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700315 smsm_change_state(SMSM_APPS_STATE, 0, SMSM_RPCINIT);
316
317 return 0;
318}
319
320static struct platform_driver msm_ipc_router_smd_remote_driver = {
321 .probe = msm_ipc_router_smd_remote_probe,
322 .driver = {
323 .name = "RPCRPY_CNTL",
324 .owner = THIS_MODULE,
325 },
326};
327
328static int __init msm_ipc_router_smd_init(void)
329{
330 return platform_driver_register(&msm_ipc_router_smd_remote_driver);
331}
332
333module_init(msm_ipc_router_smd_init);
334MODULE_DESCRIPTION("RPC Router SMD XPRT");
335MODULE_LICENSE("GPL v2");