blob: 8d567f814d890641889421a3b642d1b004d1d9c8 [file] [log] [blame]
Jeff Hugo31f83b42012-01-25 15:15:26 -07001/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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 * SMD Packet Driver -- Provides a binary SMD non-muxed packet port
15 * interface.
16 */
17
18#include <linux/slab.h>
19#include <linux/cdev.h>
20#include <linux/module.h>
21#include <linux/fs.h>
22#include <linux/device.h>
23#include <linux/sched.h>
24#include <linux/spinlock.h>
25#include <linux/mutex.h>
26#include <linux/delay.h>
27#include <linux/uaccess.h>
28#include <linux/workqueue.h>
29#include <linux/platform_device.h>
30#include <linux/completion.h>
31#include <linux/msm_smd_pkt.h>
32#include <linux/poll.h>
33#include <asm/ioctls.h>
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070034#include <linux/wakelock.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035
36#include <mach/msm_smd.h>
37#include <mach/peripheral-loader.h>
38
39#include "smd_private.h"
40#ifdef CONFIG_ARCH_FSM9XXX
41#define NUM_SMD_PKT_PORTS 4
42#else
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -060043#define NUM_SMD_PKT_PORTS 15
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044#endif
45
46#define LOOPBACK_INX (NUM_SMD_PKT_PORTS - 1)
47
48#define DEVICE_NAME "smdpkt"
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070049#define WAKELOCK_TIMEOUT (2*HZ)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050
51struct smd_pkt_dev {
52 struct cdev cdev;
53 struct device *devicep;
54 void *pil;
55 struct platform_driver driver;
56
57 struct smd_channel *ch;
58 struct mutex ch_lock;
59 struct mutex rx_lock;
60 struct mutex tx_lock;
61 wait_queue_head_t ch_read_wait_queue;
62 wait_queue_head_t ch_write_wait_queue;
63 wait_queue_head_t ch_opened_wait_queue;
64
65 int i;
66
67 int blocking_write;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068 int is_open;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -070069 int poll_mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070 unsigned ch_size;
71 uint open_modem_wait;
72
73 int has_reset;
74 int do_reset_notification;
75 struct completion ch_allocated;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070076 struct wake_lock pa_wake_lock; /* Packet Arrival Wake lock*/
77 struct work_struct packet_arrival_work;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -070078 struct spinlock pa_spinlock;
Jeff Hugo93476a32012-06-01 14:32:49 -060079 int wakelock_locked;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080} *smd_pkt_devp[NUM_SMD_PKT_PORTS];
81
82struct class *smd_pkt_classp;
83static dev_t smd_pkt_number;
84static struct delayed_work loopback_work;
85static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp);
86static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp);
87static uint32_t is_modem_smsm_inited(void);
88
89static int msm_smd_pkt_debug_mask;
90module_param_named(debug_mask, msm_smd_pkt_debug_mask,
91 int, S_IRUGO | S_IWUSR | S_IWGRP);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -060092
93enum {
94 SMD_PKT_STATUS = 1U << 0,
95 SMD_PKT_READ = 1U << 1,
96 SMD_PKT_WRITE = 1U << 2,
97 SMD_PKT_READ_DUMP_BUFFER = 1U << 3,
98 SMD_PKT_WRITE_DUMP_BUFFER = 1U << 4,
99 SMD_PKT_POLL = 1U << 5,
100};
101
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102#define DEBUG
103
104#ifdef DEBUG
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600105#define D_STATUS(x...) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106do { \
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600107 if (msm_smd_pkt_debug_mask & SMD_PKT_STATUS) \
108 pr_info("Status: "x); \
109} while (0)
110
111#define D_READ(x...) \
112do { \
113 if (msm_smd_pkt_debug_mask & SMD_PKT_READ) \
114 pr_info("Read: "x); \
115} while (0)
116
117#define D_WRITE(x...) \
118do { \
119 if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE) \
120 pr_info("Write: "x); \
121} while (0)
122
123#define D_READ_DUMP_BUFFER(prestr, cnt, buf) \
124do { \
125 if (msm_smd_pkt_debug_mask & SMD_PKT_READ_DUMP_BUFFER) \
126 print_hex_dump(KERN_INFO, prestr, \
127 DUMP_PREFIX_NONE, 16, 1, \
128 buf, cnt, 1); \
129} while (0)
130
131#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) \
132do { \
133 if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE_DUMP_BUFFER) \
134 print_hex_dump(KERN_INFO, prestr, \
135 DUMP_PREFIX_NONE, 16, 1, \
136 buf, cnt, 1); \
137} while (0)
138
139#define D_POLL(x...) \
140do { \
141 if (msm_smd_pkt_debug_mask & SMD_PKT_POLL) \
142 pr_info("Poll: "x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143} while (0)
144#else
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600145#define D_STATUS(x...) do {} while (0)
146#define D_READ(x...) do {} while (0)
147#define D_WRITE(x...) do {} while (0)
148#define D_READ_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
149#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
150#define D_POLL(x...) do {} while (0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151#endif
152
153static ssize_t open_timeout_store(struct device *d,
154 struct device_attribute *attr,
155 const char *buf,
156 size_t n)
157{
158 int i;
159 unsigned long tmp;
160 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
161 if (smd_pkt_devp[i]->devicep == d)
162 break;
163 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700164 if (i >= NUM_SMD_PKT_PORTS) {
165 pr_err("%s: unable to match device to valid smd_pkt port\n",
166 __func__);
167 return -EINVAL;
168 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169 if (!strict_strtoul(buf, 10, &tmp)) {
170 smd_pkt_devp[i]->open_modem_wait = tmp;
171 return n;
172 } else {
173 pr_err("%s: unable to convert: %s to an int\n", __func__,
174 buf);
175 return -EINVAL;
176 }
177}
178
179static ssize_t open_timeout_show(struct device *d,
180 struct device_attribute *attr,
181 char *buf)
182{
183 int i;
184 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
185 if (smd_pkt_devp[i]->devicep == d)
186 break;
187 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700188 if (i >= NUM_SMD_PKT_PORTS) {
189 pr_err("%s: unable to match device to valid smd_pkt port\n",
190 __func__);
191 return -EINVAL;
192 }
Karthikeyan Ramasubramanian63fa3d32011-09-29 17:06:26 -0600193 return snprintf(buf, PAGE_SIZE, "%d\n",
194 smd_pkt_devp[i]->open_modem_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195}
196
197static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
198
199static int notify_reset(struct smd_pkt_dev *smd_pkt_devp)
200{
201 smd_pkt_devp->do_reset_notification = 0;
202
203 return -ENETRESET;
204}
205
206static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp)
207{
208 smd_pkt_devp->do_reset_notification = 1;
209 smd_pkt_devp->has_reset = 1;
210
211 smd_pkt_devp->is_open = 0;
212
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700213 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700214 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600216 D_STATUS("%s smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217}
218
219static void loopback_probe_worker(struct work_struct *work)
220{
221
222 /* Wait for the modem SMSM to be inited for the SMD
223 ** Loopback channel to be allocated at the modem. Since
224 ** the wait need to be done atmost once, using msleep
225 ** doesn't degrade the performance. */
226 if (!is_modem_smsm_inited())
227 schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
228 else
229 smsm_change_state(SMSM_APPS_STATE,
230 0, SMSM_SMD_LOOPBACK);
231
232}
233
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700234static void packet_arrival_worker(struct work_struct *work)
235{
236 struct smd_pkt_dev *smd_pkt_devp;
Jeff Hugo93476a32012-06-01 14:32:49 -0600237 unsigned long flags;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700238
239 smd_pkt_devp = container_of(work, struct smd_pkt_dev,
240 packet_arrival_work);
241 mutex_lock(&smd_pkt_devp->ch_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600242 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
243 if (smd_pkt_devp->ch && smd_pkt_devp->wakelock_locked) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600244 D_READ("%s locking smd_pkt_dev id:%d wakelock\n",
245 __func__, smd_pkt_devp->i);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700246 wake_lock_timeout(&smd_pkt_devp->pa_wake_lock,
247 WAKELOCK_TIMEOUT);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600248 }
Jeff Hugo93476a32012-06-01 14:32:49 -0600249 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700250 mutex_unlock(&smd_pkt_devp->ch_lock);
251}
252
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253static long smd_pkt_ioctl(struct file *file, unsigned int cmd,
254 unsigned long arg)
255{
256 int ret;
257 struct smd_pkt_dev *smd_pkt_devp;
258
259 smd_pkt_devp = file->private_data;
260 if (!smd_pkt_devp)
261 return -EINVAL;
262
263 switch (cmd) {
264 case TIOCMGET:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600265 D_STATUS("%s TIOCMGET command on smd_pkt_dev id:%d\n",
266 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267 ret = smd_tiocmget(smd_pkt_devp->ch);
268 break;
269 case TIOCMSET:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600270 D_STATUS("%s TIOCSET command on smd_pkt_dev id:%d\n",
271 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 ret = smd_tiocmset(smd_pkt_devp->ch, arg, ~arg);
273 break;
274 case SMD_PKT_IOCTL_BLOCKING_WRITE:
275 ret = get_user(smd_pkt_devp->blocking_write, (int *)arg);
276 break;
277 default:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600278 pr_err("%s: Unrecognized ioctl command %d\n", __func__, cmd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279 ret = -1;
280 }
281
282 return ret;
283}
284
285ssize_t smd_pkt_read(struct file *file,
286 char __user *buf,
287 size_t count,
288 loff_t *ppos)
289{
290 int r;
291 int bytes_read;
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700292 int pkt_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 struct smd_pkt_dev *smd_pkt_devp;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700294 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296 smd_pkt_devp = file->private_data;
297
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600298 if (!smd_pkt_devp) {
299 pr_err("%s on NULL smd_pkt_dev\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700300 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600301 }
302
303 if (!smd_pkt_devp->ch) {
304 pr_err("%s on a closed smd_pkt_dev id:%d\n",
305 __func__, smd_pkt_devp->i);
306 return -EINVAL;
307 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308
309 if (smd_pkt_devp->do_reset_notification) {
310 /* notify client that a reset occurred */
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600311 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
312 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313 return notify_reset(smd_pkt_devp);
314 }
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600315 D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %d\n",
316 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700317
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318wait_for_packet:
319 r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600320 !smd_pkt_devp->ch ||
321 (smd_cur_packet_size(smd_pkt_devp->ch) > 0
322 && smd_read_avail(smd_pkt_devp->ch)) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700323 smd_pkt_devp->has_reset);
324
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600325 mutex_lock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600326 if (smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600327 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600328 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
329 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700330 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600331 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600333 if (!smd_pkt_devp->ch) {
334 mutex_unlock(&smd_pkt_devp->rx_lock);
335 pr_err("%s on a closed smd_pkt_dev id:%d\n",
336 __func__, smd_pkt_devp->i);
337 return -EINVAL;
338 }
339
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340 if (r < 0) {
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600341 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342 /* qualify error message */
343 if (r != -ERESTARTSYS) {
344 /* we get this anytime a signal comes in */
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600345 pr_err("%s: wait_event_interruptible on smd_pkt_dev"
346 " id:%d ret %i\n",
347 __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348 }
349 return r;
350 }
351
352 /* Here we have a whole packet waiting for us */
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700353 pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700354
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700355 if (!pkt_size) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600356 pr_err("%s: No data on smd_pkt_dev id:%d, False wakeup\n",
357 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358 mutex_unlock(&smd_pkt_devp->rx_lock);
359 goto wait_for_packet;
360 }
361
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700362 if (pkt_size > count) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600363 pr_err("%s: failure on smd_pkt_dev id: %d - packet size %d"
364 " > buffer size %d,", __func__, smd_pkt_devp->i,
365 pkt_size, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700366 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700367 return -ETOOSMALL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368 }
369
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700370 bytes_read = 0;
371 do {
372 r = smd_read_user_buffer(smd_pkt_devp->ch,
373 (buf + bytes_read),
374 (pkt_size - bytes_read));
375 if (r < 0) {
376 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600377 if (smd_pkt_devp->has_reset) {
378 pr_err("%s notifying reset for smd_pkt_dev"
379 " id:%d\n", __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700380 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600381 }
382 pr_err("%s Error while reading %d\n", __func__, r);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700383 return r;
384 }
385 bytes_read += r;
386 if (pkt_size != bytes_read)
387 wait_event(smd_pkt_devp->ch_read_wait_queue,
388 smd_read_avail(smd_pkt_devp->ch) ||
389 smd_pkt_devp->has_reset);
390 if (smd_pkt_devp->has_reset) {
391 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600392 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
393 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700395 }
396 } while (pkt_size != bytes_read);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600397 D_READ_DUMP_BUFFER("Read: ", (bytes_read > 16 ? 16 : bytes_read), buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398 mutex_unlock(&smd_pkt_devp->rx_lock);
399
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700400 mutex_lock(&smd_pkt_devp->ch_lock);
401 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
402 if (smd_pkt_devp->poll_mode &&
403 !smd_cur_packet_size(smd_pkt_devp->ch)) {
404 wake_unlock(&smd_pkt_devp->pa_wake_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600405 smd_pkt_devp->wakelock_locked = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700406 smd_pkt_devp->poll_mode = 0;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600407 D_READ("%s unlocked smd_pkt_dev id:%d wakelock\n",
408 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700409 }
410 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
411 mutex_unlock(&smd_pkt_devp->ch_lock);
412
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600413 D_READ("Finished %s on smd_pkt_dev id:%d %d bytes\n",
414 __func__, smd_pkt_devp->i, bytes_read);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415
416 /* check and wakeup read threads waiting on this device */
417 check_and_wakeup_reader(smd_pkt_devp);
418
419 return bytes_read;
420}
421
422ssize_t smd_pkt_write(struct file *file,
423 const char __user *buf,
424 size_t count,
425 loff_t *ppos)
426{
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700427 int r = 0, bytes_written;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700428 struct smd_pkt_dev *smd_pkt_devp;
429 DEFINE_WAIT(write_wait);
430
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431 smd_pkt_devp = file->private_data;
432
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600433 if (!smd_pkt_devp) {
434 pr_err("%s on NULL smd_pkt_dev\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600436 }
437
438 if (!smd_pkt_devp->ch) {
439 pr_err("%s on a closed smd_pkt_dev id:%d\n",
440 __func__, smd_pkt_devp->i);
441 return -EINVAL;
442 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700443
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600444 if (smd_pkt_devp->do_reset_notification || smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600445 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
446 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700447 /* notify client that a reset occurred */
448 return notify_reset(smd_pkt_devp);
449 }
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600450 D_WRITE("Begin %s on smd_pkt_dev id:%d data_size %d\n",
451 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700453 mutex_lock(&smd_pkt_devp->tx_lock);
454 if (!smd_pkt_devp->blocking_write) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700455 if (smd_write_avail(smd_pkt_devp->ch) < count) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600456 pr_err("%s: Not enough space in smd_pkt_dev id:%d\n",
457 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458 mutex_unlock(&smd_pkt_devp->tx_lock);
459 return -ENOMEM;
460 }
461 }
462
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700463 r = smd_write_start(smd_pkt_devp->ch, count);
464 if (r < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700465 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600466 pr_err("%s: Error:%d in smd_pkt_dev id:%d @ smd_write_start\n",
467 __func__, r, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700468 return r;
469 }
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700470
471 bytes_written = 0;
472 do {
473 prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue,
474 &write_wait, TASK_UNINTERRUPTIBLE);
475 if (!smd_write_avail(smd_pkt_devp->ch) &&
476 !smd_pkt_devp->has_reset) {
477 smd_enable_read_intr(smd_pkt_devp->ch);
478 schedule();
479 }
480 finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait);
481 smd_disable_read_intr(smd_pkt_devp->ch);
482
483 if (smd_pkt_devp->has_reset) {
484 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600485 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
486 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700487 return notify_reset(smd_pkt_devp);
488 } else {
489 r = smd_write_segment(smd_pkt_devp->ch,
490 (void *)(buf + bytes_written),
491 (count - bytes_written), 1);
492 if (r < 0) {
493 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600494 if (smd_pkt_devp->has_reset) {
495 pr_err("%s notifying reset for"
496 " smd_pkt_dev id:%d\n",
497 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700498 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600499 }
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600500 pr_err("%s on smd_pkt_dev id:%d failed r:%d\n",
501 __func__, smd_pkt_devp->i, r);
502 return r;
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700503 }
504 bytes_written += r;
505 }
506 } while (bytes_written != count);
507 smd_write_end(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700508 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600509 D_WRITE_DUMP_BUFFER("Write: ",
510 (bytes_written > 16 ? 16 : bytes_written), buf);
511 D_WRITE("Finished %s on smd_pkt_dev id:%d %d bytes\n",
512 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700513
514 return count;
515}
516
517static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
518{
519 struct smd_pkt_dev *smd_pkt_devp;
520 unsigned int mask = 0;
521
522 smd_pkt_devp = file->private_data;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600523 if (!smd_pkt_devp) {
524 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700525 return POLLERR;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600526 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700528 smd_pkt_devp->poll_mode = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700529 poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600530 mutex_lock(&smd_pkt_devp->ch_lock);
531 if (smd_pkt_devp->has_reset || !smd_pkt_devp->ch) {
532 mutex_unlock(&smd_pkt_devp->ch_lock);
533 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
534 __func__, smd_pkt_devp->i);
535 return POLLERR;
536 }
537
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600538 if (smd_read_avail(smd_pkt_devp->ch)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700539 mask |= POLLIN | POLLRDNORM;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600540 D_POLL("%s sets POLLIN for smd_pkt_dev id: %d\n",
541 __func__, smd_pkt_devp->i);
542 }
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600543 mutex_unlock(&smd_pkt_devp->ch_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700544
545 return mask;
546}
547
548static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
549{
550 int sz;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700551 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600553 if (!smd_pkt_devp) {
554 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600556 }
557
558 if (!smd_pkt_devp->ch) {
559 pr_err("%s on a closed smd_pkt_dev id:%d\n",
560 __func__, smd_pkt_devp->i);
561 return;
562 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563
564 sz = smd_cur_packet_size(smd_pkt_devp->ch);
565 if (sz == 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600566 D_READ("%s: No packet in smd_pkt_dev id:%d\n",
567 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568 return;
569 }
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700570 if (!smd_read_avail(smd_pkt_devp->ch)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600571 D_READ("%s: packet size is %d in smd_pkt_dev id:%d -"
572 " but the data isn't here\n",
573 __func__, sz, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700574 return;
575 }
576
577 /* here we have a packet of size sz ready */
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700578 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
579 wake_lock(&smd_pkt_devp->pa_wake_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600580 smd_pkt_devp->wakelock_locked = 1;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700581 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
Jeff Hugo93476a32012-06-01 14:32:49 -0600582 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700583 schedule_work(&smd_pkt_devp->packet_arrival_work);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600584 D_READ("%s: wake_up smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700585}
586
587static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp)
588{
589 int sz;
590
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600591 if (!smd_pkt_devp) {
592 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700593 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600594 }
595
596 if (!smd_pkt_devp->ch) {
597 pr_err("%s on a closed smd_pkt_dev id:%d\n",
598 __func__, smd_pkt_devp->i);
599 return;
600 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601
602 sz = smd_write_avail(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700603 if (sz) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600604 D_WRITE("%s: %d bytes write space in smd_pkt_dev id:%d\n",
605 __func__, sz, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700606 smd_disable_read_intr(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700607 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700608 }
609}
610
611static void ch_notify(void *priv, unsigned event)
612{
613 struct smd_pkt_dev *smd_pkt_devp = priv;
614
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600615 if (smd_pkt_devp->ch == 0) {
616 pr_err("%s on a closed smd_pkt_dev id:%d\n",
617 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700618 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600619 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700620
621 switch (event) {
622 case SMD_EVENT_DATA: {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600623 D_STATUS("%s: DATA event in smd_pkt_dev id:%d\n",
624 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700625 check_and_wakeup_reader(smd_pkt_devp);
626 if (smd_pkt_devp->blocking_write)
627 check_and_wakeup_writer(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700628 break;
629 }
630 case SMD_EVENT_OPEN:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600631 D_STATUS("%s: OPEN event in smd_pkt_dev id:%d\n",
632 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700633 smd_pkt_devp->has_reset = 0;
634 smd_pkt_devp->is_open = 1;
635 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
636 break;
637 case SMD_EVENT_CLOSE:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600638 D_STATUS("%s: CLOSE event in smd_pkt_dev id:%d\n",
639 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700640 smd_pkt_devp->is_open = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700641 /* put port into reset state */
642 clean_and_signal(smd_pkt_devp);
643 if (smd_pkt_devp->i == LOOPBACK_INX)
644 schedule_delayed_work(&loopback_work,
645 msecs_to_jiffies(1000));
646 break;
647 }
648}
649
650#ifdef CONFIG_ARCH_FSM9XXX
651static char *smd_pkt_dev_name[] = {
652 "smdcntl1",
653 "smdcntl2",
654 "smd22",
655 "smd_pkt_loopback",
656};
657
658static char *smd_ch_name[] = {
659 "DATA6_CNTL",
660 "DATA7_CNTL",
661 "DATA22",
662 "LOOPBACK",
663};
664
665static uint32_t smd_ch_edge[] = {
666 SMD_APPS_QDSP,
667 SMD_APPS_QDSP,
668 SMD_APPS_QDSP,
669 SMD_APPS_QDSP
670};
671#else
672static char *smd_pkt_dev_name[] = {
673 "smdcntl0",
674 "smdcntl1",
675 "smdcntl2",
676 "smdcntl3",
677 "smdcntl4",
678 "smdcntl5",
679 "smdcntl6",
680 "smdcntl7",
681 "smd22",
682 "smd_sns_dsps",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530683 "apr_apps2",
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700684 "smdcntl8",
Jeff Hugodf5ee322012-04-16 11:56:12 -0600685 "smd_sns_adsp",
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600686 "smd_cxm_qmi",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700687 "smd_pkt_loopback",
688};
689
690static char *smd_ch_name[] = {
691 "DATA5_CNTL",
692 "DATA6_CNTL",
693 "DATA7_CNTL",
694 "DATA8_CNTL",
695 "DATA9_CNTL",
696 "DATA12_CNTL",
697 "DATA13_CNTL",
698 "DATA14_CNTL",
699 "DATA22",
700 "SENSOR",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530701 "apr_apps2",
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700702 "DATA40_CNTL",
Jeff Hugodf5ee322012-04-16 11:56:12 -0600703 "SENSOR",
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600704 "CXM_QMI_PORT_8064",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700705 "LOOPBACK",
706};
707
708static uint32_t smd_ch_edge[] = {
709 SMD_APPS_MODEM,
710 SMD_APPS_MODEM,
711 SMD_APPS_MODEM,
712 SMD_APPS_MODEM,
713 SMD_APPS_MODEM,
714 SMD_APPS_MODEM,
715 SMD_APPS_MODEM,
716 SMD_APPS_MODEM,
717 SMD_APPS_MODEM,
718 SMD_APPS_DSPS,
719 SMD_APPS_QDSP,
720 SMD_APPS_MODEM,
Jeff Hugodf5ee322012-04-16 11:56:12 -0600721 SMD_APPS_QDSP,
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600722 SMD_APPS_WCNSS,
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700723 SMD_APPS_MODEM,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700724};
725#endif
726
727static int smd_pkt_dummy_probe(struct platform_device *pdev)
728{
729 int i;
730
731 for (i = 0; i < NUM_SMD_PKT_PORTS; i++) {
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600732 if (!strncmp(pdev->name, smd_ch_name[i], SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700733 complete_all(&smd_pkt_devp[i]->ch_allocated);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600734 D_STATUS("%s allocated SMD ch for smd_pkt_dev id:%d\n",
735 __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700736 break;
737 }
738 }
739 return 0;
740}
741
742static uint32_t is_modem_smsm_inited(void)
743{
744 uint32_t modem_state;
745 uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
746
747 modem_state = smsm_get_state(SMSM_MODEM_STATE);
748 return (modem_state & ready_state) == ready_state;
749}
750
751int smd_pkt_open(struct inode *inode, struct file *file)
752{
753 int r = 0;
754 struct smd_pkt_dev *smd_pkt_devp;
Eric Holmbergcce6daf2012-02-27 14:34:02 -0700755 const char *peripheral = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700756
757 smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
758
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600759 if (!smd_pkt_devp) {
760 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700761 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600762 }
763 D_STATUS("Begin %s on smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700764
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700765 wake_lock_init(&smd_pkt_devp->pa_wake_lock, WAKE_LOCK_SUSPEND,
766 smd_pkt_dev_name[smd_pkt_devp->i]);
767 INIT_WORK(&smd_pkt_devp->packet_arrival_work, packet_arrival_worker);
768
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700769 file->private_data = smd_pkt_devp;
770
771 mutex_lock(&smd_pkt_devp->ch_lock);
772 if (smd_pkt_devp->ch == 0) {
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700773 init_completion(&smd_pkt_devp->ch_allocated);
774 smd_pkt_devp->driver.probe = smd_pkt_dummy_probe;
775 smd_pkt_devp->driver.driver.name =
776 smd_ch_name[smd_pkt_devp->i];
777 smd_pkt_devp->driver.driver.owner = THIS_MODULE;
778 r = platform_driver_register(&smd_pkt_devp->driver);
779 if (r) {
780 pr_err("%s: %s Platform driver reg. failed\n",
781 __func__, smd_ch_name[smd_pkt_devp->i]);
782 goto out;
783 }
784
Eric Holmbergcce6daf2012-02-27 14:34:02 -0700785 peripheral = smd_edge_to_subsystem(
786 smd_ch_edge[smd_pkt_devp->i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700787 if (peripheral) {
788 smd_pkt_devp->pil = pil_get(peripheral);
789 if (IS_ERR(smd_pkt_devp->pil)) {
790 r = PTR_ERR(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600791 pr_err("%s failed on smd_pkt_dev id:%d -"
792 " pil_get failed for %s\n", __func__,
793 smd_pkt_devp->i, peripheral);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700794 goto release_pd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700795 }
796
797 /* Wait for the modem SMSM to be inited for the SMD
798 ** Loopback channel to be allocated at the modem. Since
799 ** the wait need to be done atmost once, using msleep
800 ** doesn't degrade the performance. */
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600801 if (!strncmp(smd_ch_name[smd_pkt_devp->i], "LOOPBACK",
802 SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803 if (!is_modem_smsm_inited())
804 msleep(5000);
805 smsm_change_state(SMSM_APPS_STATE,
806 0, SMSM_SMD_LOOPBACK);
807 msleep(100);
808 }
809
810 /*
811 * Wait for a packet channel to be allocated so we know
812 * the modem is ready enough.
813 */
814 if (smd_pkt_devp->open_modem_wait) {
815 r = wait_for_completion_interruptible_timeout(
816 &smd_pkt_devp->ch_allocated,
817 msecs_to_jiffies(
818 smd_pkt_devp->open_modem_wait
819 * 1000));
820 if (r == 0)
821 r = -ETIMEDOUT;
822 if (r < 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600823 pr_err("%s: wait on smd_pkt_dev id:%d"
824 " allocation failed rc:%d\n",
825 __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700826 goto release_pil;
827 }
828 }
829 }
830
831 r = smd_named_open_on_edge(smd_ch_name[smd_pkt_devp->i],
832 smd_ch_edge[smd_pkt_devp->i],
833 &smd_pkt_devp->ch,
834 smd_pkt_devp,
835 ch_notify);
836 if (r < 0) {
837 pr_err("%s: %s open failed %d\n", __func__,
838 smd_ch_name[smd_pkt_devp->i], r);
839 goto release_pil;
840 }
841
842 r = wait_event_interruptible_timeout(
843 smd_pkt_devp->ch_opened_wait_queue,
844 smd_pkt_devp->is_open, (2 * HZ));
Jeff Hugo4d9fdf32012-04-30 11:10:54 -0600845 if (r == 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700846 r = -ETIMEDOUT;
Jeff Hugo4d9fdf32012-04-30 11:10:54 -0600847 /* close the ch to sync smd's state with smd_pkt */
848 smd_close(smd_pkt_devp->ch);
849 smd_pkt_devp->ch = NULL;
850 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700851
852 if (r < 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600853 pr_err("%s: wait on smd_pkt_dev id:%d OPEN event failed"
854 " rc:%d\n", __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700855 } else if (!smd_pkt_devp->is_open) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600856 pr_err("%s: Invalid OPEN event on smd_pkt_dev id:%d\n",
857 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700858 r = -ENODEV;
859 } else {
860 smd_disable_read_intr(smd_pkt_devp->ch);
861 smd_pkt_devp->ch_size =
862 smd_write_avail(smd_pkt_devp->ch);
863 r = 0;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600864 D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
865 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700866 }
867 }
868release_pil:
869 if (peripheral && (r < 0))
870 pil_put(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700871
872release_pd:
873 if (r < 0)
874 platform_driver_unregister(&smd_pkt_devp->driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700875out:
876 mutex_unlock(&smd_pkt_devp->ch_lock);
877
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700878 if (r < 0)
879 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
880
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700881 return r;
882}
883
884int smd_pkt_release(struct inode *inode, struct file *file)
885{
886 int r = 0;
887 struct smd_pkt_dev *smd_pkt_devp = file->private_data;
888
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600889 if (!smd_pkt_devp) {
890 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700891 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600892 }
893 D_STATUS("Begin %s on smd_pkt_dev id:%d\n",
894 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895
896 clean_and_signal(smd_pkt_devp);
897
898 mutex_lock(&smd_pkt_devp->ch_lock);
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600899 mutex_lock(&smd_pkt_devp->rx_lock);
900 mutex_lock(&smd_pkt_devp->tx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700901 if (smd_pkt_devp->ch != 0) {
902 r = smd_close(smd_pkt_devp->ch);
903 smd_pkt_devp->ch = 0;
904 smd_pkt_devp->blocking_write = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700905 smd_pkt_devp->poll_mode = 0;
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700906 platform_driver_unregister(&smd_pkt_devp->driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700907 if (smd_pkt_devp->pil)
908 pil_put(smd_pkt_devp->pil);
909 }
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600910 mutex_unlock(&smd_pkt_devp->tx_lock);
911 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700912 mutex_unlock(&smd_pkt_devp->ch_lock);
913
914 smd_pkt_devp->has_reset = 0;
915 smd_pkt_devp->do_reset_notification = 0;
Jeff Hugo93476a32012-06-01 14:32:49 -0600916 smd_pkt_devp->wakelock_locked = 0;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700917 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600918 D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
919 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700920
921 return r;
922}
923
924static const struct file_operations smd_pkt_fops = {
925 .owner = THIS_MODULE,
926 .open = smd_pkt_open,
927 .release = smd_pkt_release,
928 .read = smd_pkt_read,
929 .write = smd_pkt_write,
930 .poll = smd_pkt_poll,
931 .unlocked_ioctl = smd_pkt_ioctl,
932};
933
934static int __init smd_pkt_init(void)
935{
936 int i;
937 int r;
938
939 r = alloc_chrdev_region(&smd_pkt_number,
940 0,
941 NUM_SMD_PKT_PORTS,
942 DEVICE_NAME);
943 if (IS_ERR_VALUE(r)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600944 pr_err("%s: alloc_chrdev_region() failed ret:%i\n",
945 __func__, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700946 goto error0;
947 }
948
949 smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
950 if (IS_ERR(smd_pkt_classp)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600951 pr_err("%s: class_create() failed ENOMEM\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700952 r = -ENOMEM;
953 goto error1;
954 }
955
956 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
957 smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
958 GFP_KERNEL);
959 if (IS_ERR(smd_pkt_devp[i])) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600960 pr_err("%s: kzalloc() failed for smd_pkt_dev id:%d\n",
961 __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700962 r = -ENOMEM;
963 goto error2;
964 }
965
966 smd_pkt_devp[i]->i = i;
967
968 init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue);
969 init_waitqueue_head(&smd_pkt_devp[i]->ch_write_wait_queue);
970 smd_pkt_devp[i]->is_open = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700971 smd_pkt_devp[i]->poll_mode = 0;
Jeff Hugo93476a32012-06-01 14:32:49 -0600972 smd_pkt_devp[i]->wakelock_locked = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700973 init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue);
974
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700975 spin_lock_init(&smd_pkt_devp[i]->pa_spinlock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700976 mutex_init(&smd_pkt_devp[i]->ch_lock);
977 mutex_init(&smd_pkt_devp[i]->rx_lock);
978 mutex_init(&smd_pkt_devp[i]->tx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700979
980 cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops);
981 smd_pkt_devp[i]->cdev.owner = THIS_MODULE;
982
983 r = cdev_add(&smd_pkt_devp[i]->cdev,
984 (smd_pkt_number + i),
985 1);
986
987 if (IS_ERR_VALUE(r)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600988 pr_err("%s: cdev_add() failed for smd_pkt_dev id:%d"
989 " ret:%i\n", __func__, i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700990 kfree(smd_pkt_devp[i]);
991 goto error2;
992 }
993
994 smd_pkt_devp[i]->devicep =
995 device_create(smd_pkt_classp,
996 NULL,
997 (smd_pkt_number + i),
998 NULL,
999 smd_pkt_dev_name[i]);
1000
1001 if (IS_ERR(smd_pkt_devp[i]->devicep)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001002 pr_err("%s: device_create() failed for smd_pkt_dev"
1003 " id:%d\n", __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001004 r = -ENOMEM;
1005 cdev_del(&smd_pkt_devp[i]->cdev);
1006 kfree(smd_pkt_devp[i]);
1007 goto error2;
1008 }
1009 if (device_create_file(smd_pkt_devp[i]->devicep,
1010 &dev_attr_open_timeout))
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001011 pr_err("%s: unable to create device attr for"
1012 " smd_pkt_dev id:%d\n", __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001013 }
1014
1015 INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
1016
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001017 D_STATUS("SMD Packet Port Driver Initialized.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001018 return 0;
1019
1020 error2:
1021 if (i > 0) {
1022 while (--i >= 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001023 cdev_del(&smd_pkt_devp[i]->cdev);
1024 kfree(smd_pkt_devp[i]);
1025 device_destroy(smd_pkt_classp,
1026 MKDEV(MAJOR(smd_pkt_number), i));
1027 }
1028 }
1029
1030 class_destroy(smd_pkt_classp);
1031 error1:
1032 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
1033 error0:
1034 return r;
1035}
1036
1037static void __exit smd_pkt_cleanup(void)
1038{
1039 int i;
1040
1041 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001042 cdev_del(&smd_pkt_devp[i]->cdev);
1043 kfree(smd_pkt_devp[i]);
1044 device_destroy(smd_pkt_classp,
1045 MKDEV(MAJOR(smd_pkt_number), i));
1046 }
1047
1048 class_destroy(smd_pkt_classp);
1049
1050 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
1051}
1052
1053module_init(smd_pkt_init);
1054module_exit(smd_pkt_cleanup);
1055
1056MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
1057MODULE_LICENSE("GPL v2");