blob: b9cba8c89ae1b23f8d9aa940a20f03f499b89801 [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;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079} *smd_pkt_devp[NUM_SMD_PKT_PORTS];
80
81struct class *smd_pkt_classp;
82static dev_t smd_pkt_number;
83static struct delayed_work loopback_work;
84static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp);
85static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp);
86static uint32_t is_modem_smsm_inited(void);
87
88static int msm_smd_pkt_debug_mask;
89module_param_named(debug_mask, msm_smd_pkt_debug_mask,
90 int, S_IRUGO | S_IWUSR | S_IWGRP);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -060091
92enum {
93 SMD_PKT_STATUS = 1U << 0,
94 SMD_PKT_READ = 1U << 1,
95 SMD_PKT_WRITE = 1U << 2,
96 SMD_PKT_READ_DUMP_BUFFER = 1U << 3,
97 SMD_PKT_WRITE_DUMP_BUFFER = 1U << 4,
98 SMD_PKT_POLL = 1U << 5,
99};
100
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101#define DEBUG
102
103#ifdef DEBUG
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600104#define D_STATUS(x...) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105do { \
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600106 if (msm_smd_pkt_debug_mask & SMD_PKT_STATUS) \
107 pr_info("Status: "x); \
108} while (0)
109
110#define D_READ(x...) \
111do { \
112 if (msm_smd_pkt_debug_mask & SMD_PKT_READ) \
113 pr_info("Read: "x); \
114} while (0)
115
116#define D_WRITE(x...) \
117do { \
118 if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE) \
119 pr_info("Write: "x); \
120} while (0)
121
122#define D_READ_DUMP_BUFFER(prestr, cnt, buf) \
123do { \
124 if (msm_smd_pkt_debug_mask & SMD_PKT_READ_DUMP_BUFFER) \
125 print_hex_dump(KERN_INFO, prestr, \
126 DUMP_PREFIX_NONE, 16, 1, \
127 buf, cnt, 1); \
128} while (0)
129
130#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) \
131do { \
132 if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE_DUMP_BUFFER) \
133 print_hex_dump(KERN_INFO, prestr, \
134 DUMP_PREFIX_NONE, 16, 1, \
135 buf, cnt, 1); \
136} while (0)
137
138#define D_POLL(x...) \
139do { \
140 if (msm_smd_pkt_debug_mask & SMD_PKT_POLL) \
141 pr_info("Poll: "x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142} while (0)
143#else
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600144#define D_STATUS(x...) do {} while (0)
145#define D_READ(x...) do {} while (0)
146#define D_WRITE(x...) do {} while (0)
147#define D_READ_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
148#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
149#define D_POLL(x...) do {} while (0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150#endif
151
152static ssize_t open_timeout_store(struct device *d,
153 struct device_attribute *attr,
154 const char *buf,
155 size_t n)
156{
157 int i;
158 unsigned long tmp;
159 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
160 if (smd_pkt_devp[i]->devicep == d)
161 break;
162 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700163 if (i >= NUM_SMD_PKT_PORTS) {
164 pr_err("%s: unable to match device to valid smd_pkt port\n",
165 __func__);
166 return -EINVAL;
167 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 if (!strict_strtoul(buf, 10, &tmp)) {
169 smd_pkt_devp[i]->open_modem_wait = tmp;
170 return n;
171 } else {
172 pr_err("%s: unable to convert: %s to an int\n", __func__,
173 buf);
174 return -EINVAL;
175 }
176}
177
178static ssize_t open_timeout_show(struct device *d,
179 struct device_attribute *attr,
180 char *buf)
181{
182 int i;
183 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
184 if (smd_pkt_devp[i]->devicep == d)
185 break;
186 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700187 if (i >= NUM_SMD_PKT_PORTS) {
188 pr_err("%s: unable to match device to valid smd_pkt port\n",
189 __func__);
190 return -EINVAL;
191 }
Karthikeyan Ramasubramanian63fa3d32011-09-29 17:06:26 -0600192 return snprintf(buf, PAGE_SIZE, "%d\n",
193 smd_pkt_devp[i]->open_modem_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194}
195
196static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
197
198static int notify_reset(struct smd_pkt_dev *smd_pkt_devp)
199{
200 smd_pkt_devp->do_reset_notification = 0;
201
202 return -ENETRESET;
203}
204
205static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp)
206{
207 smd_pkt_devp->do_reset_notification = 1;
208 smd_pkt_devp->has_reset = 1;
209
210 smd_pkt_devp->is_open = 0;
211
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700212 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700213 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600215 D_STATUS("%s smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216}
217
218static void loopback_probe_worker(struct work_struct *work)
219{
220
221 /* Wait for the modem SMSM to be inited for the SMD
222 ** Loopback channel to be allocated at the modem. Since
223 ** the wait need to be done atmost once, using msleep
224 ** doesn't degrade the performance. */
225 if (!is_modem_smsm_inited())
226 schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
227 else
228 smsm_change_state(SMSM_APPS_STATE,
229 0, SMSM_SMD_LOOPBACK);
230
231}
232
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700233static void packet_arrival_worker(struct work_struct *work)
234{
235 struct smd_pkt_dev *smd_pkt_devp;
236
237 smd_pkt_devp = container_of(work, struct smd_pkt_dev,
238 packet_arrival_work);
239 mutex_lock(&smd_pkt_devp->ch_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600240 if (smd_pkt_devp->ch) {
241 D_READ("%s locking smd_pkt_dev id:%d wakelock\n",
242 __func__, smd_pkt_devp->i);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700243 wake_lock_timeout(&smd_pkt_devp->pa_wake_lock,
244 WAKELOCK_TIMEOUT);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600245 }
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700246 mutex_unlock(&smd_pkt_devp->ch_lock);
247}
248
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700249static long smd_pkt_ioctl(struct file *file, unsigned int cmd,
250 unsigned long arg)
251{
252 int ret;
253 struct smd_pkt_dev *smd_pkt_devp;
254
255 smd_pkt_devp = file->private_data;
256 if (!smd_pkt_devp)
257 return -EINVAL;
258
259 switch (cmd) {
260 case TIOCMGET:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600261 D_STATUS("%s TIOCMGET command on smd_pkt_dev id:%d\n",
262 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263 ret = smd_tiocmget(smd_pkt_devp->ch);
264 break;
265 case TIOCMSET:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600266 D_STATUS("%s TIOCSET command on smd_pkt_dev id:%d\n",
267 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268 ret = smd_tiocmset(smd_pkt_devp->ch, arg, ~arg);
269 break;
270 case SMD_PKT_IOCTL_BLOCKING_WRITE:
271 ret = get_user(smd_pkt_devp->blocking_write, (int *)arg);
272 break;
273 default:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600274 pr_err("%s: Unrecognized ioctl command %d\n", __func__, cmd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 ret = -1;
276 }
277
278 return ret;
279}
280
281ssize_t smd_pkt_read(struct file *file,
282 char __user *buf,
283 size_t count,
284 loff_t *ppos)
285{
286 int r;
287 int bytes_read;
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700288 int pkt_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700289 struct smd_pkt_dev *smd_pkt_devp;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700290 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700292 smd_pkt_devp = file->private_data;
293
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600294 if (!smd_pkt_devp) {
295 pr_err("%s on NULL smd_pkt_dev\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600297 }
298
299 if (!smd_pkt_devp->ch) {
300 pr_err("%s on a closed smd_pkt_dev id:%d\n",
301 __func__, smd_pkt_devp->i);
302 return -EINVAL;
303 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304
305 if (smd_pkt_devp->do_reset_notification) {
306 /* notify client that a reset occurred */
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600307 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
308 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700309 return notify_reset(smd_pkt_devp);
310 }
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600311 D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %d\n",
312 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700314wait_for_packet:
315 r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600316 !smd_pkt_devp->ch ||
317 (smd_cur_packet_size(smd_pkt_devp->ch) > 0
318 && smd_read_avail(smd_pkt_devp->ch)) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319 smd_pkt_devp->has_reset);
320
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600321 mutex_lock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600322 if (smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600323 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600324 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
325 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600327 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700328
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600329 if (!smd_pkt_devp->ch) {
330 mutex_unlock(&smd_pkt_devp->rx_lock);
331 pr_err("%s on a closed smd_pkt_dev id:%d\n",
332 __func__, smd_pkt_devp->i);
333 return -EINVAL;
334 }
335
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700336 if (r < 0) {
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600337 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700338 /* qualify error message */
339 if (r != -ERESTARTSYS) {
340 /* we get this anytime a signal comes in */
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600341 pr_err("%s: wait_event_interruptible on smd_pkt_dev"
342 " id:%d ret %i\n",
343 __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700344 }
345 return r;
346 }
347
348 /* Here we have a whole packet waiting for us */
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700349 pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700350
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700351 if (!pkt_size) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600352 pr_err("%s: No data on smd_pkt_dev id:%d, False wakeup\n",
353 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700354 mutex_unlock(&smd_pkt_devp->rx_lock);
355 goto wait_for_packet;
356 }
357
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700358 if (pkt_size > count) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600359 pr_err("%s: failure on smd_pkt_dev id: %d - packet size %d"
360 " > buffer size %d,", __func__, smd_pkt_devp->i,
361 pkt_size, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700363 return -ETOOSMALL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364 }
365
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700366 bytes_read = 0;
367 do {
368 r = smd_read_user_buffer(smd_pkt_devp->ch,
369 (buf + bytes_read),
370 (pkt_size - bytes_read));
371 if (r < 0) {
372 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600373 if (smd_pkt_devp->has_reset) {
374 pr_err("%s notifying reset for smd_pkt_dev"
375 " id:%d\n", __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700376 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600377 }
378 pr_err("%s Error while reading %d\n", __func__, r);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700379 return r;
380 }
381 bytes_read += r;
382 if (pkt_size != bytes_read)
383 wait_event(smd_pkt_devp->ch_read_wait_queue,
384 smd_read_avail(smd_pkt_devp->ch) ||
385 smd_pkt_devp->has_reset);
386 if (smd_pkt_devp->has_reset) {
387 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600388 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
389 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700391 }
392 } while (pkt_size != bytes_read);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600393 D_READ_DUMP_BUFFER("Read: ", (bytes_read > 16 ? 16 : bytes_read), buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 mutex_unlock(&smd_pkt_devp->rx_lock);
395
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700396 mutex_lock(&smd_pkt_devp->ch_lock);
397 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
398 if (smd_pkt_devp->poll_mode &&
399 !smd_cur_packet_size(smd_pkt_devp->ch)) {
400 wake_unlock(&smd_pkt_devp->pa_wake_lock);
401 smd_pkt_devp->poll_mode = 0;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600402 D_READ("%s unlocked smd_pkt_dev id:%d wakelock\n",
403 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700404 }
405 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
406 mutex_unlock(&smd_pkt_devp->ch_lock);
407
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600408 D_READ("Finished %s on smd_pkt_dev id:%d %d bytes\n",
409 __func__, smd_pkt_devp->i, bytes_read);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700410
411 /* check and wakeup read threads waiting on this device */
412 check_and_wakeup_reader(smd_pkt_devp);
413
414 return bytes_read;
415}
416
417ssize_t smd_pkt_write(struct file *file,
418 const char __user *buf,
419 size_t count,
420 loff_t *ppos)
421{
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700422 int r = 0, bytes_written;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423 struct smd_pkt_dev *smd_pkt_devp;
424 DEFINE_WAIT(write_wait);
425
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426 smd_pkt_devp = file->private_data;
427
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600428 if (!smd_pkt_devp) {
429 pr_err("%s on NULL smd_pkt_dev\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700430 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600431 }
432
433 if (!smd_pkt_devp->ch) {
434 pr_err("%s on a closed smd_pkt_dev id:%d\n",
435 __func__, smd_pkt_devp->i);
436 return -EINVAL;
437 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700438
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600439 if (smd_pkt_devp->do_reset_notification || smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600440 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
441 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442 /* notify client that a reset occurred */
443 return notify_reset(smd_pkt_devp);
444 }
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600445 D_WRITE("Begin %s on smd_pkt_dev id:%d data_size %d\n",
446 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700447
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700448 mutex_lock(&smd_pkt_devp->tx_lock);
449 if (!smd_pkt_devp->blocking_write) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700450 if (smd_write_avail(smd_pkt_devp->ch) < count) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600451 pr_err("%s: Not enough space in smd_pkt_dev id:%d\n",
452 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453 mutex_unlock(&smd_pkt_devp->tx_lock);
454 return -ENOMEM;
455 }
456 }
457
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700458 r = smd_write_start(smd_pkt_devp->ch, count);
459 if (r < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700460 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600461 pr_err("%s: Error:%d in smd_pkt_dev id:%d @ smd_write_start\n",
462 __func__, r, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463 return r;
464 }
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700465
466 bytes_written = 0;
467 do {
468 prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue,
469 &write_wait, TASK_UNINTERRUPTIBLE);
470 if (!smd_write_avail(smd_pkt_devp->ch) &&
471 !smd_pkt_devp->has_reset) {
472 smd_enable_read_intr(smd_pkt_devp->ch);
473 schedule();
474 }
475 finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait);
476 smd_disable_read_intr(smd_pkt_devp->ch);
477
478 if (smd_pkt_devp->has_reset) {
479 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600480 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
481 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700482 return notify_reset(smd_pkt_devp);
483 } else {
484 r = smd_write_segment(smd_pkt_devp->ch,
485 (void *)(buf + bytes_written),
486 (count - bytes_written), 1);
487 if (r < 0) {
488 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600489 if (smd_pkt_devp->has_reset) {
490 pr_err("%s notifying reset for"
491 " smd_pkt_dev id:%d\n",
492 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700493 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600494 }
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600495 pr_err("%s on smd_pkt_dev id:%d failed r:%d\n",
496 __func__, smd_pkt_devp->i, r);
497 return r;
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700498 }
499 bytes_written += r;
500 }
501 } while (bytes_written != count);
502 smd_write_end(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600504 D_WRITE_DUMP_BUFFER("Write: ",
505 (bytes_written > 16 ? 16 : bytes_written), buf);
506 D_WRITE("Finished %s on smd_pkt_dev id:%d %d bytes\n",
507 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700508
509 return count;
510}
511
512static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
513{
514 struct smd_pkt_dev *smd_pkt_devp;
515 unsigned int mask = 0;
516
517 smd_pkt_devp = file->private_data;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600518 if (!smd_pkt_devp) {
519 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700520 return POLLERR;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600521 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700522
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700523 smd_pkt_devp->poll_mode = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700524 poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600525 mutex_lock(&smd_pkt_devp->ch_lock);
526 if (smd_pkt_devp->has_reset || !smd_pkt_devp->ch) {
527 mutex_unlock(&smd_pkt_devp->ch_lock);
528 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
529 __func__, smd_pkt_devp->i);
530 return POLLERR;
531 }
532
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600533 if (smd_read_avail(smd_pkt_devp->ch)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700534 mask |= POLLIN | POLLRDNORM;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600535 D_POLL("%s sets POLLIN for smd_pkt_dev id: %d\n",
536 __func__, smd_pkt_devp->i);
537 }
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600538 mutex_unlock(&smd_pkt_devp->ch_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700539
540 return mask;
541}
542
543static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
544{
545 int sz;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700546 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600548 if (!smd_pkt_devp) {
549 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700550 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600551 }
552
553 if (!smd_pkt_devp->ch) {
554 pr_err("%s on a closed smd_pkt_dev id:%d\n",
555 __func__, smd_pkt_devp->i);
556 return;
557 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700558
559 sz = smd_cur_packet_size(smd_pkt_devp->ch);
560 if (sz == 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600561 D_READ("%s: No packet in smd_pkt_dev id:%d\n",
562 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563 return;
564 }
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700565 if (!smd_read_avail(smd_pkt_devp->ch)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600566 D_READ("%s: packet size is %d in smd_pkt_dev id:%d -"
567 " but the data isn't here\n",
568 __func__, sz, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700569 return;
570 }
571
572 /* here we have a packet of size sz ready */
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700573 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700574 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
575 wake_lock(&smd_pkt_devp->pa_wake_lock);
576 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700577 schedule_work(&smd_pkt_devp->packet_arrival_work);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600578 D_READ("%s: wake_up smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579}
580
581static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp)
582{
583 int sz;
584
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600585 if (!smd_pkt_devp) {
586 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600588 }
589
590 if (!smd_pkt_devp->ch) {
591 pr_err("%s on a closed smd_pkt_dev id:%d\n",
592 __func__, smd_pkt_devp->i);
593 return;
594 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700595
596 sz = smd_write_avail(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700597 if (sz) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600598 D_WRITE("%s: %d bytes write space in smd_pkt_dev id:%d\n",
599 __func__, sz, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700600 smd_disable_read_intr(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700601 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700602 }
603}
604
605static void ch_notify(void *priv, unsigned event)
606{
607 struct smd_pkt_dev *smd_pkt_devp = priv;
608
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600609 if (smd_pkt_devp->ch == 0) {
610 pr_err("%s on a closed smd_pkt_dev id:%d\n",
611 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700612 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600613 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700614
615 switch (event) {
616 case SMD_EVENT_DATA: {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600617 D_STATUS("%s: DATA event in smd_pkt_dev id:%d\n",
618 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700619 check_and_wakeup_reader(smd_pkt_devp);
620 if (smd_pkt_devp->blocking_write)
621 check_and_wakeup_writer(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700622 break;
623 }
624 case SMD_EVENT_OPEN:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600625 D_STATUS("%s: OPEN event in smd_pkt_dev id:%d\n",
626 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700627 smd_pkt_devp->has_reset = 0;
628 smd_pkt_devp->is_open = 1;
629 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
630 break;
631 case SMD_EVENT_CLOSE:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600632 D_STATUS("%s: CLOSE event in smd_pkt_dev id:%d\n",
633 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700634 smd_pkt_devp->is_open = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700635 /* put port into reset state */
636 clean_and_signal(smd_pkt_devp);
637 if (smd_pkt_devp->i == LOOPBACK_INX)
638 schedule_delayed_work(&loopback_work,
639 msecs_to_jiffies(1000));
640 break;
641 }
642}
643
644#ifdef CONFIG_ARCH_FSM9XXX
645static char *smd_pkt_dev_name[] = {
646 "smdcntl1",
647 "smdcntl2",
648 "smd22",
649 "smd_pkt_loopback",
650};
651
652static char *smd_ch_name[] = {
653 "DATA6_CNTL",
654 "DATA7_CNTL",
655 "DATA22",
656 "LOOPBACK",
657};
658
659static uint32_t smd_ch_edge[] = {
660 SMD_APPS_QDSP,
661 SMD_APPS_QDSP,
662 SMD_APPS_QDSP,
663 SMD_APPS_QDSP
664};
665#else
666static char *smd_pkt_dev_name[] = {
667 "smdcntl0",
668 "smdcntl1",
669 "smdcntl2",
670 "smdcntl3",
671 "smdcntl4",
672 "smdcntl5",
673 "smdcntl6",
674 "smdcntl7",
675 "smd22",
676 "smd_sns_dsps",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530677 "apr_apps2",
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700678 "smdcntl8",
Jeff Hugodf5ee322012-04-16 11:56:12 -0600679 "smd_sns_adsp",
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600680 "smd_cxm_qmi",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700681 "smd_pkt_loopback",
682};
683
684static char *smd_ch_name[] = {
685 "DATA5_CNTL",
686 "DATA6_CNTL",
687 "DATA7_CNTL",
688 "DATA8_CNTL",
689 "DATA9_CNTL",
690 "DATA12_CNTL",
691 "DATA13_CNTL",
692 "DATA14_CNTL",
693 "DATA22",
694 "SENSOR",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530695 "apr_apps2",
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700696 "DATA40_CNTL",
Jeff Hugodf5ee322012-04-16 11:56:12 -0600697 "SENSOR",
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600698 "CXM_QMI_PORT_8064",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700699 "LOOPBACK",
700};
701
702static uint32_t smd_ch_edge[] = {
703 SMD_APPS_MODEM,
704 SMD_APPS_MODEM,
705 SMD_APPS_MODEM,
706 SMD_APPS_MODEM,
707 SMD_APPS_MODEM,
708 SMD_APPS_MODEM,
709 SMD_APPS_MODEM,
710 SMD_APPS_MODEM,
711 SMD_APPS_MODEM,
712 SMD_APPS_DSPS,
713 SMD_APPS_QDSP,
714 SMD_APPS_MODEM,
Jeff Hugodf5ee322012-04-16 11:56:12 -0600715 SMD_APPS_QDSP,
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600716 SMD_APPS_WCNSS,
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700717 SMD_APPS_MODEM,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700718};
719#endif
720
721static int smd_pkt_dummy_probe(struct platform_device *pdev)
722{
723 int i;
724
725 for (i = 0; i < NUM_SMD_PKT_PORTS; i++) {
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600726 if (!strncmp(pdev->name, smd_ch_name[i], SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700727 complete_all(&smd_pkt_devp[i]->ch_allocated);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600728 D_STATUS("%s allocated SMD ch for smd_pkt_dev id:%d\n",
729 __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700730 break;
731 }
732 }
733 return 0;
734}
735
736static uint32_t is_modem_smsm_inited(void)
737{
738 uint32_t modem_state;
739 uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
740
741 modem_state = smsm_get_state(SMSM_MODEM_STATE);
742 return (modem_state & ready_state) == ready_state;
743}
744
745int smd_pkt_open(struct inode *inode, struct file *file)
746{
747 int r = 0;
748 struct smd_pkt_dev *smd_pkt_devp;
Eric Holmbergcce6daf2012-02-27 14:34:02 -0700749 const char *peripheral = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700750
751 smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
752
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600753 if (!smd_pkt_devp) {
754 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700755 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600756 }
757 D_STATUS("Begin %s on smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700758
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700759 wake_lock_init(&smd_pkt_devp->pa_wake_lock, WAKE_LOCK_SUSPEND,
760 smd_pkt_dev_name[smd_pkt_devp->i]);
761 INIT_WORK(&smd_pkt_devp->packet_arrival_work, packet_arrival_worker);
762
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700763 file->private_data = smd_pkt_devp;
764
765 mutex_lock(&smd_pkt_devp->ch_lock);
766 if (smd_pkt_devp->ch == 0) {
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700767 init_completion(&smd_pkt_devp->ch_allocated);
768 smd_pkt_devp->driver.probe = smd_pkt_dummy_probe;
769 smd_pkt_devp->driver.driver.name =
770 smd_ch_name[smd_pkt_devp->i];
771 smd_pkt_devp->driver.driver.owner = THIS_MODULE;
772 r = platform_driver_register(&smd_pkt_devp->driver);
773 if (r) {
774 pr_err("%s: %s Platform driver reg. failed\n",
775 __func__, smd_ch_name[smd_pkt_devp->i]);
776 goto out;
777 }
778
Eric Holmbergcce6daf2012-02-27 14:34:02 -0700779 peripheral = smd_edge_to_subsystem(
780 smd_ch_edge[smd_pkt_devp->i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700781 if (peripheral) {
782 smd_pkt_devp->pil = pil_get(peripheral);
783 if (IS_ERR(smd_pkt_devp->pil)) {
784 r = PTR_ERR(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600785 pr_err("%s failed on smd_pkt_dev id:%d -"
786 " pil_get failed for %s\n", __func__,
787 smd_pkt_devp->i, peripheral);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700788 goto release_pd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700789 }
790
791 /* Wait for the modem SMSM to be inited for the SMD
792 ** Loopback channel to be allocated at the modem. Since
793 ** the wait need to be done atmost once, using msleep
794 ** doesn't degrade the performance. */
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600795 if (!strncmp(smd_ch_name[smd_pkt_devp->i], "LOOPBACK",
796 SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700797 if (!is_modem_smsm_inited())
798 msleep(5000);
799 smsm_change_state(SMSM_APPS_STATE,
800 0, SMSM_SMD_LOOPBACK);
801 msleep(100);
802 }
803
804 /*
805 * Wait for a packet channel to be allocated so we know
806 * the modem is ready enough.
807 */
808 if (smd_pkt_devp->open_modem_wait) {
809 r = wait_for_completion_interruptible_timeout(
810 &smd_pkt_devp->ch_allocated,
811 msecs_to_jiffies(
812 smd_pkt_devp->open_modem_wait
813 * 1000));
814 if (r == 0)
815 r = -ETIMEDOUT;
816 if (r < 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600817 pr_err("%s: wait on smd_pkt_dev id:%d"
818 " allocation failed rc:%d\n",
819 __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700820 goto release_pil;
821 }
822 }
823 }
824
825 r = smd_named_open_on_edge(smd_ch_name[smd_pkt_devp->i],
826 smd_ch_edge[smd_pkt_devp->i],
827 &smd_pkt_devp->ch,
828 smd_pkt_devp,
829 ch_notify);
830 if (r < 0) {
831 pr_err("%s: %s open failed %d\n", __func__,
832 smd_ch_name[smd_pkt_devp->i], r);
833 goto release_pil;
834 }
835
836 r = wait_event_interruptible_timeout(
837 smd_pkt_devp->ch_opened_wait_queue,
838 smd_pkt_devp->is_open, (2 * HZ));
Jeff Hugo4d9fdf32012-04-30 11:10:54 -0600839 if (r == 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700840 r = -ETIMEDOUT;
Jeff Hugo4d9fdf32012-04-30 11:10:54 -0600841 /* close the ch to sync smd's state with smd_pkt */
842 smd_close(smd_pkt_devp->ch);
843 smd_pkt_devp->ch = NULL;
844 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700845
846 if (r < 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600847 pr_err("%s: wait on smd_pkt_dev id:%d OPEN event failed"
848 " rc:%d\n", __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700849 } else if (!smd_pkt_devp->is_open) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600850 pr_err("%s: Invalid OPEN event on smd_pkt_dev id:%d\n",
851 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700852 r = -ENODEV;
853 } else {
854 smd_disable_read_intr(smd_pkt_devp->ch);
855 smd_pkt_devp->ch_size =
856 smd_write_avail(smd_pkt_devp->ch);
857 r = 0;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600858 D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
859 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700860 }
861 }
862release_pil:
863 if (peripheral && (r < 0))
864 pil_put(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700865
866release_pd:
867 if (r < 0)
868 platform_driver_unregister(&smd_pkt_devp->driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700869out:
870 mutex_unlock(&smd_pkt_devp->ch_lock);
871
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700872 if (r < 0)
873 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
874
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700875 return r;
876}
877
878int smd_pkt_release(struct inode *inode, struct file *file)
879{
880 int r = 0;
881 struct smd_pkt_dev *smd_pkt_devp = file->private_data;
882
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600883 if (!smd_pkt_devp) {
884 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700885 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600886 }
887 D_STATUS("Begin %s on smd_pkt_dev id:%d\n",
888 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700889
890 clean_and_signal(smd_pkt_devp);
891
892 mutex_lock(&smd_pkt_devp->ch_lock);
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600893 mutex_lock(&smd_pkt_devp->rx_lock);
894 mutex_lock(&smd_pkt_devp->tx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895 if (smd_pkt_devp->ch != 0) {
896 r = smd_close(smd_pkt_devp->ch);
897 smd_pkt_devp->ch = 0;
898 smd_pkt_devp->blocking_write = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700899 smd_pkt_devp->poll_mode = 0;
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700900 platform_driver_unregister(&smd_pkt_devp->driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700901 if (smd_pkt_devp->pil)
902 pil_put(smd_pkt_devp->pil);
903 }
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600904 mutex_unlock(&smd_pkt_devp->tx_lock);
905 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700906 mutex_unlock(&smd_pkt_devp->ch_lock);
907
908 smd_pkt_devp->has_reset = 0;
909 smd_pkt_devp->do_reset_notification = 0;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700910 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600911 D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
912 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700913
914 return r;
915}
916
917static const struct file_operations smd_pkt_fops = {
918 .owner = THIS_MODULE,
919 .open = smd_pkt_open,
920 .release = smd_pkt_release,
921 .read = smd_pkt_read,
922 .write = smd_pkt_write,
923 .poll = smd_pkt_poll,
924 .unlocked_ioctl = smd_pkt_ioctl,
925};
926
927static int __init smd_pkt_init(void)
928{
929 int i;
930 int r;
931
932 r = alloc_chrdev_region(&smd_pkt_number,
933 0,
934 NUM_SMD_PKT_PORTS,
935 DEVICE_NAME);
936 if (IS_ERR_VALUE(r)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600937 pr_err("%s: alloc_chrdev_region() failed ret:%i\n",
938 __func__, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700939 goto error0;
940 }
941
942 smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
943 if (IS_ERR(smd_pkt_classp)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600944 pr_err("%s: class_create() failed ENOMEM\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700945 r = -ENOMEM;
946 goto error1;
947 }
948
949 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
950 smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
951 GFP_KERNEL);
952 if (IS_ERR(smd_pkt_devp[i])) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600953 pr_err("%s: kzalloc() failed for smd_pkt_dev id:%d\n",
954 __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700955 r = -ENOMEM;
956 goto error2;
957 }
958
959 smd_pkt_devp[i]->i = i;
960
961 init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue);
962 init_waitqueue_head(&smd_pkt_devp[i]->ch_write_wait_queue);
963 smd_pkt_devp[i]->is_open = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700964 smd_pkt_devp[i]->poll_mode = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700965 init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue);
966
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700967 spin_lock_init(&smd_pkt_devp[i]->pa_spinlock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700968 mutex_init(&smd_pkt_devp[i]->ch_lock);
969 mutex_init(&smd_pkt_devp[i]->rx_lock);
970 mutex_init(&smd_pkt_devp[i]->tx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700971
972 cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops);
973 smd_pkt_devp[i]->cdev.owner = THIS_MODULE;
974
975 r = cdev_add(&smd_pkt_devp[i]->cdev,
976 (smd_pkt_number + i),
977 1);
978
979 if (IS_ERR_VALUE(r)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600980 pr_err("%s: cdev_add() failed for smd_pkt_dev id:%d"
981 " ret:%i\n", __func__, i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700982 kfree(smd_pkt_devp[i]);
983 goto error2;
984 }
985
986 smd_pkt_devp[i]->devicep =
987 device_create(smd_pkt_classp,
988 NULL,
989 (smd_pkt_number + i),
990 NULL,
991 smd_pkt_dev_name[i]);
992
993 if (IS_ERR(smd_pkt_devp[i]->devicep)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600994 pr_err("%s: device_create() failed for smd_pkt_dev"
995 " id:%d\n", __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700996 r = -ENOMEM;
997 cdev_del(&smd_pkt_devp[i]->cdev);
998 kfree(smd_pkt_devp[i]);
999 goto error2;
1000 }
1001 if (device_create_file(smd_pkt_devp[i]->devicep,
1002 &dev_attr_open_timeout))
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001003 pr_err("%s: unable to create device attr for"
1004 " smd_pkt_dev id:%d\n", __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001005 }
1006
1007 INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
1008
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001009 D_STATUS("SMD Packet Port Driver Initialized.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001010 return 0;
1011
1012 error2:
1013 if (i > 0) {
1014 while (--i >= 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001015 cdev_del(&smd_pkt_devp[i]->cdev);
1016 kfree(smd_pkt_devp[i]);
1017 device_destroy(smd_pkt_classp,
1018 MKDEV(MAJOR(smd_pkt_number), i));
1019 }
1020 }
1021
1022 class_destroy(smd_pkt_classp);
1023 error1:
1024 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
1025 error0:
1026 return r;
1027}
1028
1029static void __exit smd_pkt_cleanup(void)
1030{
1031 int i;
1032
1033 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001034 cdev_del(&smd_pkt_devp[i]->cdev);
1035 kfree(smd_pkt_devp[i]);
1036 device_destroy(smd_pkt_classp,
1037 MKDEV(MAJOR(smd_pkt_number), i));
1038 }
1039
1040 class_destroy(smd_pkt_classp);
1041
1042 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
1043}
1044
1045module_init(smd_pkt_init);
1046module_exit(smd_pkt_cleanup);
1047
1048MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
1049MODULE_LICENSE("GPL v2");