blob: 7e0e6f8d5207c7565176f57461c5bda9a9c4bcaa [file] [log] [blame]
Eric Holmberg9fff8872012-05-10 19:20:27 -06001/* Copyright (c) 2012, 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 * Serial Mux Control Driver -- Provides a binary serial muxed control
15 * port interface.
16 */
17
18#define DEBUG
19
20#include <linux/cdev.h>
21#include <linux/types.h>
22#include <linux/module.h>
23#include <linux/fs.h>
24#include <linux/device.h>
25#include <linux/delay.h>
26#include <linux/sched.h>
27#include <linux/spinlock.h>
28#include <linux/mutex.h>
29#include <linux/uaccess.h>
30#include <linux/workqueue.h>
31#include <linux/atomic.h>
32#include <linux/platform_device.h>
33#include <linux/smux.h>
34#include <linux/slab.h>
35#include <linux/debugfs.h>
Eric Holmberg58a677b2012-08-02 11:23:17 -060036#include <linux/poll.h>
Eric Holmberg9fff8872012-05-10 19:20:27 -060037
38#include <asm/ioctls.h>
39
40#define MAX_WRITE_RETRY 5
41#define MAGIC_NO_V1 0x33FC
42#define DEVICE_NAME "smuxctl"
43#define SMUX_CTL_MAX_BUF_SIZE 2048
44#define SMUX_CTL_MODULE_NAME "smux_ctl"
45#define DEBUG
Eric Holmberg9fff8872012-05-10 19:20:27 -060046
47static int msm_smux_ctl_debug_mask;
48module_param_named(debug_mask, msm_smux_ctl_debug_mask,
49 int, S_IRUGO | S_IWUSR | S_IWGRP);
50
51static uint32_t smux_ctl_ch_id[] = {
52 SMUX_DATA_CTL_0,
Eric Holmberg9fff8872012-05-10 19:20:27 -060053};
54
55#define SMUX_CTL_NUM_CHANNELS ARRAY_SIZE(smux_ctl_ch_id)
Angshuman Sarkar76db1412012-07-25 15:17:19 +053056#define DEFAULT_OPEN_TIMEOUT 5
Eric Holmberg9fff8872012-05-10 19:20:27 -060057
58struct smux_ctl_dev {
59 int id;
60 char name[10];
61 struct cdev cdev;
62 struct device *devicep;
63 struct mutex dev_lock;
64 atomic_t ref_count;
65 int state;
66 int is_channel_reset;
67 int is_high_wm;
68 int write_pending;
Angshuman Sarkar76db1412012-07-25 15:17:19 +053069 unsigned open_timeout_val;
Eric Holmberg9fff8872012-05-10 19:20:27 -060070
71 struct mutex rx_lock;
72 uint32_t read_avail;
73 struct list_head rx_list;
74
Eric Holmbergd6704e32012-06-19 08:53:02 -060075 int abort_wait;
Eric Holmberg9fff8872012-05-10 19:20:27 -060076 wait_queue_head_t read_wait_queue;
77 wait_queue_head_t write_wait_queue;
78
79 struct {
80 uint32_t bytes_tx;
81 uint32_t bytes_rx;
82 uint32_t pkts_tx;
83 uint32_t pkts_rx;
84 uint32_t cnt_ssr;
85 uint32_t cnt_read_fail;
86 uint32_t cnt_write_fail;
87 uint32_t cnt_high_wm_hit;
88 } stats;
89
90} *smux_ctl_devp[SMUX_CTL_NUM_CHANNELS];
91
92struct smux_ctl_pkt {
93 int data_size;
94 void *data;
95};
96
97struct smux_ctl_list_elem {
98 struct list_head list;
99 struct smux_ctl_pkt ctl_pkt;
100};
101
102struct class *smux_ctl_classp;
103static dev_t smux_ctl_number;
104static uint32_t smux_ctl_inited;
105
106enum {
107 MSM_SMUX_CTL_DEBUG = 1U << 0,
108 MSM_SMUX_CTL_DUMP_BUFFER = 1U << 1,
109};
110
111#if defined(DEBUG)
112
113static const char *smux_ctl_event_str[] = {
114 "SMUX_CONNECTED",
115 "SMUX_DISCONNECTED",
116 "SMUX_READ_DONE",
117 "SMUX_READ_FAIL",
118 "SMUX_WRITE_DONE",
119 "SMUX_WRITE_FAIL",
120 "SMUX_TIOCM_UPDATE",
121 "SMUX_LOW_WM_HIT",
122 "SMUX_HIGH_WM_HIT",
123};
124
125#define SMUXCTL_DUMP_BUFFER(prestr, cnt, buf) \
126do { \
127 if (msm_smux_ctl_debug_mask & MSM_SMUX_CTL_DUMP_BUFFER) { \
128 int i; \
129 pr_err("%s", prestr); \
130 for (i = 0; i < cnt; i++) \
131 pr_err("%.2x", buf[i]); \
132 pr_err("\n"); \
133 } \
134} while (0)
135
136#define SMUXCTL_DBG(x...) \
137do { \
138 if (msm_smux_ctl_debug_mask & MSM_SMUX_CTL_DEBUG) \
139 pr_err(x); \
140} while (0)
141
142
143#else
144#define SMUXCTL_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
145#define SMUXCTL_DBG(x...) do {} while (0)
146#endif
147
148#if defined(DEBUG_LOOPBACK)
149#define SMUXCTL_SET_LOOPBACK(lcid) \
150 msm_smux_set_ch_option(lcid, SMUX_CH_OPTION_LOCAL_LOOPBACK, 0)
151#else
152#define SMUXCTL_SET_LOOPBACK(lcid) do {} while (0)
153#endif
154
Angshuman Sarkar76db1412012-07-25 15:17:19 +0530155static ssize_t open_timeout_store(struct device *d,
156 struct device_attribute *attr,
157 const char *buf,
158 size_t n)
159{
160 int i;
161 unsigned long tmp;
162 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
163 if (smux_ctl_devp[i]->devicep == d)
164 break;
165 }
166 if (i >= SMUX_CTL_NUM_CHANNELS) {
167 pr_err("%s: unable to match device to valid smux ctl port\n",
168 __func__);
169 return -EINVAL;
170 }
171 if (!kstrtoul(buf, 10, &tmp)) {
172 smux_ctl_devp[i]->open_timeout_val = tmp;
173 return n;
174 } else {
175 pr_err("%s: unable to convert: %s to an int\n", __func__,
176 buf);
177 return -EINVAL;
178 }
179}
180
181static ssize_t open_timeout_show(struct device *d,
182 struct device_attribute *attr,
183 char *buf)
184{
185 int i;
186 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
187 if (smux_ctl_devp[i]->devicep == d)
188 break;
189 }
190 if (i >= SMUX_CTL_NUM_CHANNELS) {
191 pr_err("%s: unable to match device to valid smux ctl port\n",
192 __func__);
193 return -EINVAL;
194 }
195 return snprintf(buf, PAGE_SIZE, "%d\n",
196 smux_ctl_devp[i]->open_timeout_val);
197}
198
199static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
200
Eric Holmberg9fff8872012-05-10 19:20:27 -0600201static int get_ctl_dev_index(int id)
202{
203 int dev_index;
204 for (dev_index = 0; dev_index < SMUX_CTL_NUM_CHANNELS; dev_index++) {
205 if (smux_ctl_ch_id[dev_index] == id)
206 return dev_index;
207 }
208 return -ENODEV;
209}
210
211static int smux_ctl_get_rx_buf_cb(void *priv, void **pkt_priv,
212 void **buffer, int size)
213{
214 void *buf = NULL;
215 int id = ((struct smux_ctl_dev *)(priv))->id;
216 int dev_index;
217
218 if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
219 return -ENODEV;
220
221 if (!buffer || 0 >= size)
222 return -EINVAL;
223
224 dev_index = get_ctl_dev_index(id);
225 if (dev_index < 0) {
226 pr_err(SMUX_CTL_MODULE_NAME ": %s: Ch%d is not "
227 "exported to user-space\n",
228 __func__, id);
229 return -ENODEV;
230 }
231
232 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: Allocating Rx buf size %d "
233 "for ch%d\n",
234 __func__, size, smux_ctl_devp[dev_index]->id);
235
236 buf = kmalloc(size, GFP_KERNEL);
237 if (!buf) {
238 pr_err(SMUX_CTL_MODULE_NAME ": %s: buffer allocation failed: "
239 "Ch%d, size %d ", __func__, id, size);
240 return -ENOMEM;
241 }
242
243 *buffer = buf;
244 *pkt_priv = NULL;
245 return 0;
246
247}
248
249void smux_ctl_notify_cb(void *priv, int event_type, const void *metadata)
250{
251 int id = ((struct smux_ctl_dev *)(priv))->id;
252 struct smux_ctl_list_elem *list_elem = NULL;
253 int dev_index;
254 void *data;
255 int len;
256
257 if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
258 return;
259
260 dev_index = get_ctl_dev_index(id);
261 if (dev_index < 0) {
262 pr_err(SMUX_CTL_MODULE_NAME ": %s: Ch%d is not exported "
263 "to user-space\n", __func__, id);
264 return;
265 }
266
267 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: Ch%d, Event %d (%s)\n",
268 __func__, smux_ctl_devp[dev_index]->id,
269 event_type, smux_ctl_event_str[event_type]);
270
271
272 switch (event_type) {
273 case SMUX_CONNECTED:
274 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
275 smux_ctl_devp[dev_index]->state = SMUX_CONNECTED;
276 smux_ctl_devp[dev_index]->is_high_wm = 0;
277 smux_ctl_devp[dev_index]->is_channel_reset = 0;
278 smux_ctl_devp[dev_index]->read_avail = 0;
279 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
280 wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
281 break;
282
283 case SMUX_DISCONNECTED:
284 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
285 smux_ctl_devp[dev_index]->state = SMUX_DISCONNECTED;
286 smux_ctl_devp[dev_index]->is_channel_reset =
287 ((struct smux_meta_disconnected *)metadata)->is_ssr;
288 if (smux_ctl_devp[dev_index]->is_channel_reset)
289 smux_ctl_devp[dev_index]->stats.cnt_ssr++;
290 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
291 wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
292 wake_up(&smux_ctl_devp[dev_index]->read_wait_queue);
293 break;
294
295 case SMUX_READ_FAIL:
296 data = ((struct smux_meta_read *)metadata)->buffer;
297 kfree(data);
298 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
299 smux_ctl_devp[dev_index]->stats.cnt_read_fail++;
300 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
301 wake_up(&smux_ctl_devp[dev_index]->read_wait_queue);
302 break;
303
304 case SMUX_READ_DONE:
305 data = ((struct smux_meta_read *)metadata)->buffer;
306 len = ((struct smux_meta_read *)metadata)->len;
307
308 if (data && len > 0) {
309 list_elem = kmalloc(sizeof(struct smux_ctl_list_elem),
310 GFP_KERNEL);
311 if (list_elem) {
312 list_elem->ctl_pkt.data = data;
313 list_elem->ctl_pkt.data_size = len;
314
315 mutex_lock(&smux_ctl_devp[dev_index]->rx_lock);
316 list_add_tail(&list_elem->list,
317 &smux_ctl_devp[dev_index]->rx_list);
318 smux_ctl_devp[dev_index]->read_avail += len;
319 mutex_unlock(
320 &smux_ctl_devp[dev_index]->rx_lock);
321 } else {
322 kfree(data);
323 }
324 }
325
326 wake_up(&smux_ctl_devp[dev_index]->read_wait_queue);
327 break;
328
329 case SMUX_WRITE_DONE:
330 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
331 smux_ctl_devp[dev_index]->write_pending = 0;
332 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
333 data = ((struct smux_meta_write *)metadata)->buffer;
334 kfree(data);
335 wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
336 break;
337
338 case SMUX_WRITE_FAIL:
339 data = ((struct smux_meta_write *)metadata)->buffer;
340 kfree(data);
341 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
342 smux_ctl_devp[dev_index]->stats.cnt_write_fail++;
343 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
344 wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
345 break;
346
347 case SMUX_LOW_WM_HIT:
348 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
349 smux_ctl_devp[dev_index]->is_high_wm = 0;
350 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
351 wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
352 break;
353
354 case SMUX_HIGH_WM_HIT:
355 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
356 smux_ctl_devp[dev_index]->is_high_wm = 1;
357 smux_ctl_devp[dev_index]->stats.cnt_high_wm_hit++;
358 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
359 break;
360
361 case SMUX_TIOCM_UPDATE:
362 default:
363 pr_err(SMUX_CTL_MODULE_NAME ": %s: Event %d not supported\n",
364 __func__, event_type);
365 break;
366
367 }
368
369}
370
371int smux_ctl_open(struct inode *inode, struct file *file)
372{
373 int r = 0;
374 struct smux_ctl_dev *devp;
Angshuman Sarkar76db1412012-07-25 15:17:19 +0530375 unsigned wait_time = DEFAULT_OPEN_TIMEOUT * HZ;
Eric Holmberg9fff8872012-05-10 19:20:27 -0600376
377 if (!smux_ctl_inited)
378 return -EIO;
379
380 devp = container_of(inode->i_cdev, struct smux_ctl_dev, cdev);
381 if (!devp)
382 return -ENODEV;
383
384 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d device\n",
385 __func__, devp->id);
386
387 if (1 == atomic_add_return(1, &devp->ref_count)) {
388
389 SMUXCTL_SET_LOOPBACK(devp->id);
390 r = msm_smux_open(devp->id,
391 devp,
392 smux_ctl_notify_cb,
393 smux_ctl_get_rx_buf_cb);
394 if (r < 0) {
395 pr_err(SMUX_CTL_MODULE_NAME ": %s: smux_open failed "
396 "for smuxctl%d with rc %d\n",
397 __func__, devp->id, r);
398 atomic_dec(&devp->ref_count);
399 return r;
400 }
401
Angshuman Sarkar76db1412012-07-25 15:17:19 +0530402 if (devp->open_timeout_val)
403 wait_time = devp->open_timeout_val * HZ;
404
Eric Holmberg9fff8872012-05-10 19:20:27 -0600405 r = wait_event_interruptible_timeout(
406 devp->write_wait_queue,
Eric Holmbergd6704e32012-06-19 08:53:02 -0600407 (devp->state == SMUX_CONNECTED ||
Angshuman Sarkar76db1412012-07-25 15:17:19 +0530408 devp->abort_wait),
409 wait_time);
Eric Holmberg9fff8872012-05-10 19:20:27 -0600410 if (r == 0)
411 r = -ETIMEDOUT;
412
413 if (r < 0) {
414 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
415 "SMUX open timed out: %d, LCID %d\n",
416 __func__, r, devp->id);
417 atomic_dec(&devp->ref_count);
418 msm_smux_close(devp->id);
419 return r;
420
Eric Holmbergd6704e32012-06-19 08:53:02 -0600421 } else if (devp->abort_wait) {
422 pr_err("%s: %s: Open command aborted\n",
423 SMUX_CTL_MODULE_NAME, __func__);
424 r = -EIO;
425 atomic_dec(&devp->ref_count);
426 msm_smux_close(devp->id);
427 return r;
Eric Holmberg9fff8872012-05-10 19:20:27 -0600428 } else if (devp->state != SMUX_CONNECTED) {
429 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
430 "Invalid open notification\n", __func__);
431 r = -ENODEV;
432 atomic_dec(&devp->ref_count);
433 msm_smux_close(devp->id);
434 return r;
435 }
436 }
437
438 file->private_data = devp;
439 return 0;
440}
441
442int smux_ctl_release(struct inode *inode, struct file *file)
443{
444 struct smux_ctl_dev *devp;
445 struct smux_ctl_list_elem *list_elem = NULL;
446
447 devp = file->private_data;
448 if (!devp)
449 return -EINVAL;
450
451 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d device\n",
452 __func__, devp->id);
453
454 mutex_lock(&devp->dev_lock);
455 if (atomic_dec_and_test(&devp->ref_count)) {
456 mutex_lock(&devp->rx_lock);
457 while (!list_empty(&devp->rx_list)) {
458 list_elem = list_first_entry(
459 &devp->rx_list,
460 struct smux_ctl_list_elem,
461 list);
462 list_del(&list_elem->list);
463 kfree(list_elem->ctl_pkt.data);
464 kfree(list_elem);
465 }
466 devp->read_avail = 0;
467 mutex_unlock(&devp->rx_lock);
468 msm_smux_close(devp->id);
469 }
470 mutex_unlock(&devp->dev_lock);
471 file->private_data = NULL;
472
473 return 0;
474}
475
476static int smux_ctl_readable(int id)
477{
478 int r;
479 int dev_index;
480
481 if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
482 return -ENODEV;
483
484 dev_index = get_ctl_dev_index(id);
485 if (dev_index < 0) {
486 pr_err(SMUX_CTL_MODULE_NAME ": %s: Ch%d "
487 "is not exported to user-space\n",
488 __func__, id);
489 return -ENODEV;
490 }
491
492 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
493
494 if (signal_pending(current))
495 r = -ERESTARTSYS;
Eric Holmbergd6704e32012-06-19 08:53:02 -0600496 else if (smux_ctl_devp[dev_index]->abort_wait)
497 r = -ENETRESET;
498 else if (smux_ctl_devp[dev_index]->state == SMUX_DISCONNECTED &&
Eric Holmberg9fff8872012-05-10 19:20:27 -0600499 smux_ctl_devp[dev_index]->is_channel_reset != 0)
500 r = -ENETRESET;
501
502 else if (smux_ctl_devp[dev_index]->state != SMUX_CONNECTED)
503 r = -ENODEV;
504
505 else
506 r = smux_ctl_devp[dev_index]->read_avail;
507
508
509 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
510
511 return r;
512
513}
514
515ssize_t smux_ctl_read(struct file *file,
516 char __user *buf,
517 size_t count,
518 loff_t *ppos)
519{
520 int r = 0, id, bytes_to_read, read_err;
521 struct smux_ctl_dev *devp;
522 struct smux_ctl_list_elem *list_elem = NULL;
523
524 devp = file->private_data;
525
526 if (!devp)
527 return -ENODEV;
528
529 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: read from ch%d\n",
530 __func__, devp->id);
531
532 id = devp->id;
533 mutex_lock(&devp->rx_lock);
534 while (devp->read_avail <= 0) {
535 mutex_unlock(&devp->rx_lock);
536 r = wait_event_interruptible(devp->read_wait_queue,
537 0 != (read_err = smux_ctl_readable(id)));
538
539 if (r < 0) {
540 pr_err(SMUX_CTL_MODULE_NAME ": %s:"
541 "wait_event_interruptible "
542 "ret %i\n", __func__, r);
543 return r;
544 }
545
546 if (read_err < 0) {
547 pr_err(SMUX_CTL_MODULE_NAME ": %s:"
548 " Read block failed for Ch%d, err %d\n",
549 __func__, devp->id, read_err);
550 return read_err;
551 }
552
553 mutex_lock(&devp->rx_lock);
554 }
555
556 if (list_empty(&devp->rx_list)) {
557 mutex_unlock(&devp->rx_lock);
558 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: "
559 "Nothing in ch%d's rx_list\n", __func__,
560 devp->id);
561 return -EAGAIN;
562 }
563
564 list_elem = list_first_entry(&devp->rx_list,
565 struct smux_ctl_list_elem, list);
566 bytes_to_read = (uint32_t)(list_elem->ctl_pkt.data_size);
567 if (bytes_to_read > count) {
568 mutex_unlock(&devp->rx_lock);
569 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
570 "Packet size %d > buf size %d\n", __func__,
571 bytes_to_read, count);
572 return -ENOMEM;
573 }
574
575 if (copy_to_user(buf, list_elem->ctl_pkt.data, bytes_to_read)) {
576 mutex_unlock(&devp->rx_lock);
577 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
578 "copy_to_user failed for ch%d\n", __func__,
579 devp->id);
580 return -EFAULT;
581 }
582
583 devp->read_avail -= bytes_to_read;
584 list_del(&list_elem->list);
585 kfree(list_elem->ctl_pkt.data);
586 kfree(list_elem);
587 devp->stats.pkts_rx++;
588 devp->stats.bytes_rx += bytes_to_read;
589 mutex_unlock(&devp->rx_lock);
590
591 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: "
592 "Returning %d bytes to ch%d\n", __func__,
593 bytes_to_read, devp->id);
594 return bytes_to_read;
595}
596
597static int smux_ctl_writeable(int id)
598{
599 int r;
600 int dev_index;
601
602 if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
603 return -ENODEV;
604
605 dev_index = get_ctl_dev_index(id);
606 if (dev_index < 0) {
607 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
608 "Ch%d is not exported to user-space\n",
609 __func__, id);
610 return -ENODEV;
611 }
612
613 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
614
615 if (signal_pending(current))
616 r = -ERESTARTSYS;
Eric Holmbergd6704e32012-06-19 08:53:02 -0600617
618 else if (smux_ctl_devp[dev_index]->abort_wait)
619 r = -ENETRESET;
Eric Holmberg9fff8872012-05-10 19:20:27 -0600620 else if (smux_ctl_devp[dev_index]->state == SMUX_DISCONNECTED &&
621 smux_ctl_devp[dev_index]->is_channel_reset != 0)
622 r = -ENETRESET;
623 else if (smux_ctl_devp[dev_index]->state != SMUX_CONNECTED)
624 r = -ENODEV;
625 else if (smux_ctl_devp[dev_index]->is_high_wm ||
626 smux_ctl_devp[dev_index]->write_pending)
627 r = 0;
628 else
629 r = SMUX_CTL_MAX_BUF_SIZE;
630
631 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
632
633 return r;
634
635}
636
637ssize_t smux_ctl_write(struct file *file,
638 const char __user *buf,
639 size_t count,
640 loff_t *ppos)
641{
642 int r = 0, id, write_err;
643 char *temp_buf;
644 struct smux_ctl_dev *devp;
645
646 if (count <= 0)
647 return -EINVAL;
648
649 devp = file->private_data;
650 if (!devp)
651 return -ENODEV;
652
653 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: writing %i bytes on ch%d\n",
654 __func__, count, devp->id);
655
656 id = devp->id;
657 r = wait_event_interruptible(devp->write_wait_queue,
658 0 != (write_err = smux_ctl_writeable(id)));
659
660 if (r < 0) {
661 pr_err(SMUX_CTL_MODULE_NAME
662 ": %s: wait_event_interruptible "
663 "ret %i\n", __func__, r);
664 return r;
665 }
666
667 if (write_err < 0) {
668 pr_err(SMUX_CTL_MODULE_NAME ": %s:"
669 "Write block failed for Ch%d, err %d\n",
670 __func__, devp->id, write_err);
671 return write_err;
672 }
673
674 temp_buf = kmalloc(count, GFP_KERNEL);
675 if (!temp_buf) {
676 pr_err(SMUX_CTL_MODULE_NAME
677 ": %s: temp_buf alloc failed\n", __func__);
678 return -ENOMEM;
679 }
680
681 if (copy_from_user(temp_buf, buf, count)) {
682 pr_err(SMUX_CTL_MODULE_NAME
683 ": %s: copy_from_user failed\n", __func__);
684 kfree(temp_buf);
685 return -EFAULT;
686 }
687
688 mutex_lock(&devp->dev_lock);
689 devp->write_pending = 1;
690 mutex_unlock(&devp->dev_lock);
691
692 r = msm_smux_write(id, NULL, (void *)temp_buf, count);
693 if (r < 0) {
694 pr_err(SMUX_CTL_MODULE_NAME
695 ": %s: smux_write on Ch%dfailed, err %d\n",
696 __func__, id, r);
697 mutex_lock(&devp->dev_lock);
698 devp->write_pending = 0;
699 mutex_unlock(&devp->dev_lock);
700 return r;
701 }
702
703 r = wait_event_interruptible(devp->write_wait_queue,
704 0 != (write_err = smux_ctl_writeable(id)));
Eric Holmbergd6704e32012-06-19 08:53:02 -0600705
706 if (-EIO == r) {
707 pr_err("%s: %s: wait_event_interruptible ret %i\n",
708 SMUX_CTL_MODULE_NAME, __func__, r);
709 return -EIO;
710 }
711
Eric Holmberg9fff8872012-05-10 19:20:27 -0600712 if (r < 0) {
713 pr_err(SMUX_CTL_MODULE_NAME " :%s: wait_event_interruptible "
714 "ret %i\n", __func__, r);
715 mutex_lock(&devp->dev_lock);
716 devp->write_pending = 0;
717 mutex_unlock(&devp->dev_lock);
718 return r;
719 }
720
721 mutex_lock(&devp->dev_lock);
722 devp->write_pending = 0;
723 devp->stats.pkts_tx++;
724 devp->stats.bytes_tx += count;
725 mutex_unlock(&devp->dev_lock);
726 return count;
727}
728
729static long smux_ctl_ioctl(struct file *file, unsigned int cmd,
730 unsigned long arg)
731{
732 int ret;
733 struct smux_ctl_dev *devp;
734
735 devp = file->private_data;
736 if (!devp)
737 return -ENODEV;
738
739 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d device\n",
740 __func__, devp->id);
741
742 switch (cmd) {
743 case TIOCMGET:
744 ret = msm_smux_tiocm_get(devp->id);
745 break;
746 case TIOCMSET:
747 ret = msm_smux_tiocm_set(devp->id, arg, ~arg);
748 break;
749 default:
750 ret = -EINVAL;
751 break;
752 }
753
754 return ret;
755}
756
Eric Holmberg58a677b2012-08-02 11:23:17 -0600757static unsigned int smux_ctl_poll(struct file *file, poll_table *wait)
758{
759 struct smux_ctl_dev *devp;
760 unsigned int mask = 0;
761 int readable;
762
763 devp = file->private_data;
764 if (!devp)
765 return -ENODEV;
766
767 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d\n",
768 __func__, devp->id);
769
770 poll_wait(file, &devp->read_wait_queue, wait);
771
772 readable = smux_ctl_readable(devp->id);
773 if (readable < 0) {
774 pr_err(SMUX_CTL_MODULE_NAME ": %s err%d during poll for smuxctl%d\n",
775 __func__, readable, devp->id);
776 mask = POLLERR;
777 } else if (readable) {
778 mask = POLLIN | POLLRDNORM;
779 }
780
781 return mask;
782}
783
Eric Holmberg9fff8872012-05-10 19:20:27 -0600784static const struct file_operations smux_ctl_fops = {
785 .owner = THIS_MODULE,
786 .open = smux_ctl_open,
787 .release = smux_ctl_release,
788 .read = smux_ctl_read,
789 .write = smux_ctl_write,
790 .unlocked_ioctl = smux_ctl_ioctl,
Eric Holmberg58a677b2012-08-02 11:23:17 -0600791 .poll = smux_ctl_poll,
Eric Holmberg9fff8872012-05-10 19:20:27 -0600792};
793
Eric Holmbergd6704e32012-06-19 08:53:02 -0600794static void smux_ctl_reset_channel(struct smux_ctl_dev *devp)
795{
796 devp->is_high_wm = 0;
797 devp->write_pending = 0;
798 devp->is_channel_reset = 0;
799 devp->state = SMUX_DISCONNECTED;
800 devp->read_avail = 0;
801
802 devp->stats.bytes_tx = 0;
803 devp->stats.bytes_rx = 0;
804 devp->stats.pkts_tx = 0;
805 devp->stats.pkts_rx = 0;
806 devp->stats.cnt_ssr = 0;
807 devp->stats.cnt_read_fail = 0;
808 devp->stats.cnt_write_fail = 0;
809 devp->stats.cnt_high_wm_hit = 0;
810 devp->abort_wait = 0;
811}
812
Eric Holmberg9fff8872012-05-10 19:20:27 -0600813static int smux_ctl_probe(struct platform_device *pdev)
814{
815 int i;
816 int r;
817
818 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Begins\n", __func__);
819
Eric Holmbergd6704e32012-06-19 08:53:02 -0600820 if (smux_ctl_inited) {
821 /* Already loaded once - reinitialize channels */
822 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
823 struct smux_ctl_dev *devp = smux_ctl_devp[i];
824
825 smux_ctl_reset_channel(devp);
826
827 if (atomic_read(&devp->ref_count)) {
828 r = msm_smux_open(devp->id,
829 devp,
830 smux_ctl_notify_cb,
831 smux_ctl_get_rx_buf_cb);
832 if (r)
833 pr_err("%s: unable to reopen ch %d, ret %d\n",
834 __func__, devp->id, r);
835 }
836 }
837 return 0;
838 }
839
840 /* Create character devices */
Eric Holmberg9fff8872012-05-10 19:20:27 -0600841 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
842 smux_ctl_devp[i] = kzalloc(sizeof(struct smux_ctl_dev),
843 GFP_KERNEL);
844 if (IS_ERR(smux_ctl_devp[i])) {
845 pr_err(SMUX_CTL_MODULE_NAME
846 ": %s kmalloc() ENOMEM\n", __func__);
847 r = -ENOMEM;
848 goto error0;
849 }
850
851 smux_ctl_devp[i]->id = smux_ctl_ch_id[i];
852 atomic_set(&smux_ctl_devp[i]->ref_count, 0);
Eric Holmberg9fff8872012-05-10 19:20:27 -0600853
854 mutex_init(&smux_ctl_devp[i]->dev_lock);
855 init_waitqueue_head(&smux_ctl_devp[i]->read_wait_queue);
856 init_waitqueue_head(&smux_ctl_devp[i]->write_wait_queue);
857 mutex_init(&smux_ctl_devp[i]->rx_lock);
858 INIT_LIST_HEAD(&smux_ctl_devp[i]->rx_list);
Eric Holmbergd6704e32012-06-19 08:53:02 -0600859 smux_ctl_reset_channel(smux_ctl_devp[i]);
Eric Holmberg9fff8872012-05-10 19:20:27 -0600860 }
861
862 r = alloc_chrdev_region(&smux_ctl_number, 0, SMUX_CTL_NUM_CHANNELS,
863 DEVICE_NAME);
864 if (IS_ERR_VALUE(r)) {
865 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
866 "alloc_chrdev_region() ret %i.\n",
867 __func__, r);
868 goto error0;
869 }
870
871 smux_ctl_classp = class_create(THIS_MODULE, DEVICE_NAME);
872 if (IS_ERR(smux_ctl_classp)) {
873 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
874 "class_create() ENOMEM\n", __func__);
875 r = -ENOMEM;
876 goto error1;
877 }
878
879 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
880 cdev_init(&smux_ctl_devp[i]->cdev, &smux_ctl_fops);
881 smux_ctl_devp[i]->cdev.owner = THIS_MODULE;
882
Eric Holmbergd6704e32012-06-19 08:53:02 -0600883 r = cdev_add(&smux_ctl_devp[i]->cdev,
884 (smux_ctl_number + i), 1);
Eric Holmberg9fff8872012-05-10 19:20:27 -0600885
886 if (IS_ERR_VALUE(r)) {
887 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
888 "cdev_add() ret %i\n", __func__, r);
889 kfree(smux_ctl_devp[i]);
890 goto error2;
891 }
892
893 smux_ctl_devp[i]->devicep =
894 device_create(smux_ctl_classp, NULL,
895 (smux_ctl_number + i), NULL,
896 DEVICE_NAME "%d", smux_ctl_ch_id[i]);
897
898 if (IS_ERR(smux_ctl_devp[i]->devicep)) {
899 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
900 "device_create() ENOMEM\n", __func__);
901 r = -ENOMEM;
902 cdev_del(&smux_ctl_devp[i]->cdev);
903 kfree(smux_ctl_devp[i]);
904 goto error2;
905 }
Angshuman Sarkar76db1412012-07-25 15:17:19 +0530906 if (device_create_file(smux_ctl_devp[i]->devicep,
907 &dev_attr_open_timeout))
908 pr_err("%s: unable to create device attr for" \
909 " smux ctl dev id:%d\n", __func__, i);
910
Eric Holmberg9fff8872012-05-10 19:20:27 -0600911 }
912
913 smux_ctl_inited = 1;
914 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: "
915 "SMUX Control Port Driver Initialized.\n", __func__);
916 return 0;
917
918error2:
919 while (--i >= 0) {
920 cdev_del(&smux_ctl_devp[i]->cdev);
921 device_destroy(smux_ctl_classp,
922 MKDEV(MAJOR(smux_ctl_number), i));
923 }
924
925 class_destroy(smux_ctl_classp);
926 i = SMUX_CTL_NUM_CHANNELS;
927
928error1:
929 unregister_chrdev_region(MAJOR(smux_ctl_number),
930 SMUX_CTL_NUM_CHANNELS);
931
932error0:
933 while (--i >= 0)
934 kfree(smux_ctl_devp[i]);
935
936 return r;
937}
938
939static int smux_ctl_remove(struct platform_device *pdev)
940{
941 int i;
942
943 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Begins\n", __func__);
944
945 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
Eric Holmbergd6704e32012-06-19 08:53:02 -0600946 struct smux_ctl_dev *devp = smux_ctl_devp[i];
Eric Holmberg9fff8872012-05-10 19:20:27 -0600947
Eric Holmbergd6704e32012-06-19 08:53:02 -0600948 mutex_lock(&devp->dev_lock);
949 devp->abort_wait = 1;
950 wake_up(&devp->write_wait_queue);
951 wake_up(&devp->read_wait_queue);
952 mutex_unlock(&devp->dev_lock);
953
954 /* Empty RX queue */
955 mutex_lock(&devp->rx_lock);
956 while (!list_empty(&devp->rx_list)) {
957 struct smux_ctl_list_elem *list_elem;
958
959 list_elem = list_first_entry(
960 &devp->rx_list,
961 struct smux_ctl_list_elem,
962 list);
963 list_del(&list_elem->list);
964 kfree(list_elem->ctl_pkt.data);
965 kfree(list_elem);
966 }
967 devp->read_avail = 0;
968 mutex_unlock(&devp->rx_lock);
969 }
970
971 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Ends\n", __func__);
Eric Holmberg9fff8872012-05-10 19:20:27 -0600972 return 0;
973}
974
975static struct platform_driver smux_ctl_driver = {
976 .probe = smux_ctl_probe,
977 .remove = smux_ctl_remove,
978 .driver = {
979 .name = "SMUX_CTL",
980 .owner = THIS_MODULE,
981 },
982};
983
984static int __init smux_ctl_init(void)
985{
Eric Holmberg9fff8872012-05-10 19:20:27 -0600986 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Begins\n", __func__);
987 return platform_driver_register(&smux_ctl_driver);
988}
989
990
991#if defined(CONFIG_DEBUG_FS)
992
993#define DEBUG_BUFMAX 4096
994static char debug_buffer[DEBUG_BUFMAX];
995
996static ssize_t debug_read(struct file *file, char __user *buf,
997 size_t count, loff_t *ppos)
998{
999 int bsize = 0;
1000 int i;
1001 if (!smux_ctl_inited) {
1002 pr_err(SMUX_CTL_MODULE_NAME ": %s: SMUX_CTL not yet inited\n",
1003 __func__);
1004 return -EIO;
1005 }
1006
1007 bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
1008 "SMUX_CTL Channel States:\n");
1009
1010 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
1011 bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
1012 "Ch%02d %s RefCnt=%01d State=%02d "
1013 "SSR=%02d HighWM=%02d ReadAvail=%04d WritePending=%02d\n",
1014 smux_ctl_devp[i]->id,
1015 smux_ctl_devp[i]->name,
1016 atomic_read(&smux_ctl_devp[i]->ref_count),
1017 smux_ctl_devp[i]->state,
1018 smux_ctl_devp[i]->is_channel_reset,
1019 smux_ctl_devp[i]->is_high_wm,
1020 smux_ctl_devp[i]->read_avail,
1021 smux_ctl_devp[i]->write_pending);
1022 }
1023
1024 bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
1025 "\nSMUX_CTL Channel Statistics:\n");
1026 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
1027 bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
1028 "Ch%02d %s BytesTX=%08d "
1029 "BytesRx=%08d PktsTx=%04d PktsRx=%04d"
1030 "CntSSR=%02d CntHighWM=%02d "
1031 "CntReadFail%02d CntWriteFailed=%02d\n",
1032 smux_ctl_devp[i]->id,
1033 smux_ctl_devp[i]->name,
1034 smux_ctl_devp[i]->stats.bytes_tx,
1035 smux_ctl_devp[i]->stats.bytes_rx,
1036 smux_ctl_devp[i]->stats.pkts_tx,
1037 smux_ctl_devp[i]->stats.pkts_rx,
1038 smux_ctl_devp[i]->stats.cnt_ssr,
1039 smux_ctl_devp[i]->stats.cnt_high_wm_hit,
1040 smux_ctl_devp[i]->stats.cnt_read_fail,
1041 smux_ctl_devp[i]->stats.cnt_write_fail);
1042 }
1043
1044 return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
1045}
1046
1047static int debug_open(struct inode *inode, struct file *file)
1048{
1049 file->private_data = inode->i_private;
1050 return 0;
1051}
1052
1053static const struct file_operations debug_ops = {
1054 .read = debug_read,
1055 .open = debug_open,
1056};
1057
1058static int __init smux_debugfs_init(void)
1059{
1060 struct dentry *dent;
1061
1062 dent = debugfs_create_dir("smux_ctl", 0);
1063 if (!IS_ERR(dent))
1064 debugfs_create_file("smux_ctl_state", 0444, dent,
1065 NULL, &debug_ops);
1066
1067 return 0;
1068}
1069
1070late_initcall(smux_debugfs_init);
1071#endif
1072
1073module_init(smux_ctl_init);
1074MODULE_DESCRIPTION("MSM SMUX Control Port");
1075MODULE_LICENSE("GPL v2");
1076
1077