blob: bfc4150f0bb644b8960cd71d5347dc336d3e0585 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/smd_tty.c
2 *
3 * Copyright (C) 2007 Google, Inc.
4 * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
5 * Author: Brian Swetland <swetland@google.com>
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/module.h>
19#include <linux/fs.h>
20#include <linux/cdev.h>
21#include <linux/device.h>
22#include <linux/interrupt.h>
23#include <linux/delay.h>
24#include <linux/wakelock.h>
25#include <linux/platform_device.h>
26#include <linux/sched.h>
27
28#include <linux/tty.h>
29#include <linux/tty_driver.h>
30#include <linux/tty_flip.h>
31
32#include <mach/msm_smd.h>
33#include <mach/peripheral-loader.h>
Jeff Hugo4c0ba6c2011-07-15 11:47:13 -060034#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035
36#include "smd_private.h"
37
38#define MAX_SMD_TTYS 37
39#define MAX_TTY_BUF_SIZE 2048
40
41static DEFINE_MUTEX(smd_tty_lock);
42
43static uint smd_tty_modem_wait;
44module_param_named(modem_wait, smd_tty_modem_wait,
45 uint, S_IRUGO | S_IWUSR | S_IWGRP);
46
47struct smd_tty_info {
48 smd_channel_t *ch;
49 struct tty_struct *tty;
50 struct wake_lock wake_lock;
51 int open_count;
52 struct tasklet_struct tty_tsklt;
53 struct timer_list buf_req_timer;
54 struct completion ch_allocated;
55 struct platform_driver driver;
56 void *pil;
57 int in_reset;
58 int in_reset_updated;
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -060059 int is_open;
60 wait_queue_head_t ch_opened_wait_queue;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061 spinlock_t reset_lock;
62};
63
64#define LOOPBACK_IDX 36
65static char *smd_ch_name[] = {
66 [0] = "DS",
67 [1] = "APPS_FM",
68 [2] = "APPS_RIVA_BT_ACL",
69 [3] = "APPS_RIVA_BT_CMD",
70 [7] = "DATA1",
71 [21] = "DATA21",
72 [27] = "GPSNMEA",
73 [36] = "LOOPBACK",
74};
75
76static uint32_t smd_ch_edge[] = {
77 [0] = SMD_APPS_MODEM,
78 [1] = SMD_APPS_WCNSS,
79 [2] = SMD_APPS_WCNSS,
80 [3] = SMD_APPS_WCNSS,
81 [7] = SMD_APPS_MODEM,
82 [21] = SMD_APPS_MODEM,
83 [27] = SMD_APPS_MODEM,
84 [36] = SMD_APPS_MODEM,
85};
86
87
88static struct delayed_work loopback_work;
89static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
90
91static int is_in_reset(struct smd_tty_info *info)
92{
93 return info->in_reset;
94}
95
96static void buf_req_retry(unsigned long param)
97{
98 struct smd_tty_info *info = (struct smd_tty_info *)param;
99 tasklet_hi_schedule(&info->tty_tsklt);
100}
101
102static void smd_tty_read(unsigned long param)
103{
104 unsigned char *ptr;
105 int avail;
106 struct smd_tty_info *info = (struct smd_tty_info *)param;
107 struct tty_struct *tty = info->tty;
108
109 if (!tty)
110 return;
111
112 for (;;) {
113 if (is_in_reset(info)) {
114 /* signal TTY clients using TTY_BREAK */
115 tty_insert_flip_char(tty, 0x00, TTY_BREAK);
116 tty_flip_buffer_push(tty);
117 break;
118 }
119
120 if (test_bit(TTY_THROTTLED, &tty->flags)) break;
121 avail = smd_read_avail(info->ch);
122 if (avail == 0)
123 break;
124
125 if (avail > MAX_TTY_BUF_SIZE)
126 avail = MAX_TTY_BUF_SIZE;
127
128 avail = tty_prepare_flip_string(tty, &ptr, avail);
129 if (avail <= 0) {
130 if (!timer_pending(&info->buf_req_timer)) {
131 init_timer(&info->buf_req_timer);
132 info->buf_req_timer.expires = jiffies +
133 ((30 * HZ)/1000);
134 info->buf_req_timer.function = buf_req_retry;
135 info->buf_req_timer.data = param;
136 add_timer(&info->buf_req_timer);
137 }
138 return;
139 }
140
141 if (smd_read(info->ch, ptr, avail) != avail) {
142 /* shouldn't be possible since we're in interrupt
143 ** context here and nobody else could 'steal' our
144 ** characters.
145 */
146 printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
147 }
148
149 wake_lock_timeout(&info->wake_lock, HZ / 2);
150 tty_flip_buffer_push(tty);
151 }
152
153 /* XXX only when writable and necessary */
154 tty_wakeup(tty);
155}
156
157static void smd_tty_notify(void *priv, unsigned event)
158{
159 struct smd_tty_info *info = priv;
160 unsigned long flags;
161
162 switch (event) {
163 case SMD_EVENT_DATA:
164 /* There may be clients (tty framework) that are blocked
165 * waiting for space to write data, so if a possible read
166 * interrupt came in wake anyone waiting and disable the
167 * interrupts
168 */
169 if (smd_write_avail(info->ch)) {
170 smd_disable_read_intr(info->ch);
171 if (info->tty)
172 wake_up_interruptible(&info->tty->write_wait);
173 }
174 tasklet_hi_schedule(&info->tty_tsklt);
175 break;
176
177 case SMD_EVENT_OPEN:
178 spin_lock_irqsave(&info->reset_lock, flags);
179 info->in_reset = 0;
180 info->in_reset_updated = 1;
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600181 info->is_open = 1;
182 wake_up_interruptible(&info->ch_opened_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183 spin_unlock_irqrestore(&info->reset_lock, flags);
184 break;
185
186 case SMD_EVENT_CLOSE:
187 spin_lock_irqsave(&info->reset_lock, flags);
188 info->in_reset = 1;
189 info->in_reset_updated = 1;
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600190 info->is_open = 0;
191 wake_up_interruptible(&info->ch_opened_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192 spin_unlock_irqrestore(&info->reset_lock, flags);
193 /* schedule task to send TTY_BREAK */
194 tasklet_hi_schedule(&info->tty_tsklt);
195
196 if (info->tty->index == LOOPBACK_IDX)
197 schedule_delayed_work(&loopback_work,
198 msecs_to_jiffies(1000));
199 break;
200 }
201}
202
203static uint32_t is_modem_smsm_inited(void)
204{
205 uint32_t modem_state;
206 uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
207
208 modem_state = smsm_get_state(SMSM_MODEM_STATE);
209 return (modem_state & ready_state) == ready_state;
210}
211
212static int smd_tty_open(struct tty_struct *tty, struct file *f)
213{
214 int res = 0;
215 int n = tty->index;
216 struct smd_tty_info *info;
217 char *peripheral = NULL;
218
219
220 if (!smd_ch_name[n])
221 return -ENODEV;
222
223 info = smd_tty + n;
224
225 mutex_lock(&smd_tty_lock);
226 tty->driver_data = info;
227
228 if (info->open_count++ == 0) {
229 if (smd_ch_edge[n] == SMD_APPS_MODEM)
230 peripheral = "modem";
231
232 if (peripheral) {
233 info->pil = pil_get("modem");
234 if (IS_ERR(info->pil)) {
235 res = PTR_ERR(info->pil);
236 goto out;
237 }
238
239 /* Wait for the modem SMSM to be inited for the SMD
240 * Loopback channel to be allocated at the modem. Since
241 * the wait need to be done atmost once, using msleep
242 * doesn't degrade the performance.
243 */
244 if (n == 36) {
245 if (!is_modem_smsm_inited())
246 msleep(5000);
247 smsm_change_state(SMSM_APPS_STATE,
248 0, SMSM_SMD_LOOPBACK);
249 msleep(100);
250 }
251
252
253 /*
254 * Wait for a channel to be allocated so we know
255 * the modem is ready enough.
256 */
257 if (smd_tty_modem_wait) {
258 res = wait_for_completion_interruptible_timeout(
259 &info->ch_allocated,
260 msecs_to_jiffies(smd_tty_modem_wait *
261 1000));
262
263 if (res == 0) {
264 pr_err("Timed out waiting for SMD"
265 " channel\n");
266 res = -ETIMEDOUT;
267 goto release_pil;
268 } else if (res < 0) {
269 pr_err("Error waiting for SMD channel:"
270 " %d\n",
271 res);
272 goto release_pil;
273 }
274
275 res = 0;
276 }
277 }
278
279
280 info->tty = tty;
281 tasklet_init(&info->tty_tsklt, smd_tty_read,
282 (unsigned long)info);
283 wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND,
284 smd_ch_name[n]);
285 if (!info->ch) {
286 res = smd_named_open_on_edge(smd_ch_name[n],
287 smd_ch_edge[n],
288 &info->ch, info,
289 smd_tty_notify);
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600290 if (res < 0) {
291 pr_err("%s: %s open failed %d\n", __func__,
292 smd_ch_name[n], res);
293 goto release_pil;
294 }
295
296 res = wait_event_interruptible_timeout(
297 info->ch_opened_wait_queue,
298 info->is_open, (2 * HZ));
299 if (res == 0)
300 res = -ETIMEDOUT;
301 if (res < 0) {
302 pr_err("%s: wait for %s smd_open failed %d\n",
303 __func__, smd_ch_name[n], res);
304 goto release_pil;
305 }
306 res = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307 }
308 }
309
310release_pil:
311 if (res < 0)
312 pil_put(info->pil);
313 else
314 smd_disable_read_intr(info->ch);
315out:
316 mutex_unlock(&smd_tty_lock);
317
318 return res;
319}
320
321static void smd_tty_close(struct tty_struct *tty, struct file *f)
322{
323 struct smd_tty_info *info = tty->driver_data;
324
325 if (info == 0)
326 return;
327
328 mutex_lock(&smd_tty_lock);
329 if (--info->open_count == 0) {
330 if (info->tty) {
331 tasklet_kill(&info->tty_tsklt);
332 wake_lock_destroy(&info->wake_lock);
333 info->tty = 0;
334 }
335 tty->driver_data = 0;
336 del_timer(&info->buf_req_timer);
337 if (info->ch) {
338 smd_close(info->ch);
339 info->ch = 0;
340 pil_put(info->pil);
341 }
342 }
343 mutex_unlock(&smd_tty_lock);
344}
345
346static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len)
347{
348 struct smd_tty_info *info = tty->driver_data;
349 int avail;
350
351 /* if we're writing to a packet channel we will
352 ** never be able to write more data than there
353 ** is currently space for
354 */
355 if (is_in_reset(info))
356 return -ENETRESET;
357
358 avail = smd_write_avail(info->ch);
359 /* if no space, we'll have to setup a notification later to wake up the
360 * tty framework when space becomes avaliable
361 */
362 if (!avail) {
363 smd_enable_read_intr(info->ch);
364 return 0;
365 }
366 if (len > avail)
367 len = avail;
368
369 return smd_write(info->ch, buf, len);
370}
371
372static int smd_tty_write_room(struct tty_struct *tty)
373{
374 struct smd_tty_info *info = tty->driver_data;
375 return smd_write_avail(info->ch);
376}
377
378static int smd_tty_chars_in_buffer(struct tty_struct *tty)
379{
380 struct smd_tty_info *info = tty->driver_data;
381 return smd_read_avail(info->ch);
382}
383
384static void smd_tty_unthrottle(struct tty_struct *tty)
385{
386 struct smd_tty_info *info = tty->driver_data;
387 tasklet_hi_schedule(&info->tty_tsklt);
388 return;
389}
390
391/*
392 * Returns the current TIOCM status bits including:
393 * SMD Signals (DTR/DSR, CTS/RTS, CD, RI)
394 * TIOCM_OUT1 - reset state (1=in reset)
395 * TIOCM_OUT2 - reset state updated (1=updated)
396 */
397static int smd_tty_tiocmget(struct tty_struct *tty)
398{
399 struct smd_tty_info *info = tty->driver_data;
400 unsigned long flags;
401 int tiocm;
402
403 tiocm = smd_tiocmget(info->ch);
404
405 spin_lock_irqsave(&info->reset_lock, flags);
406 tiocm |= (info->in_reset ? TIOCM_OUT1 : 0);
407 if (info->in_reset_updated) {
408 tiocm |= TIOCM_OUT2;
409 info->in_reset_updated = 0;
410 }
411 spin_unlock_irqrestore(&info->reset_lock, flags);
412
413 return tiocm;
414}
415
416static int smd_tty_tiocmset(struct tty_struct *tty,
417 unsigned int set, unsigned int clear)
418{
419 struct smd_tty_info *info = tty->driver_data;
420
421 if (info->in_reset)
422 return -ENETRESET;
423
424 return smd_tiocmset(info->ch, set, clear);
425}
426
427static void loopback_probe_worker(struct work_struct *work)
428{
429 /* wait for modem to restart before requesting loopback server */
430 if (!is_modem_smsm_inited())
431 schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
432 else
433 smsm_change_state(SMSM_APPS_STATE,
434 0, SMSM_SMD_LOOPBACK);
435}
436
437static struct tty_operations smd_tty_ops = {
438 .open = smd_tty_open,
439 .close = smd_tty_close,
440 .write = smd_tty_write,
441 .write_room = smd_tty_write_room,
442 .chars_in_buffer = smd_tty_chars_in_buffer,
443 .unthrottle = smd_tty_unthrottle,
444 .tiocmget = smd_tty_tiocmget,
445 .tiocmset = smd_tty_tiocmset,
446};
447
448static int smd_tty_dummy_probe(struct platform_device *pdev)
449{
450 if (!strncmp(pdev->name, smd_ch_name[0],
451 strnlen(smd_ch_name[0], SMD_MAX_CH_NAME_LEN)))
452 complete_all(&smd_tty[0].ch_allocated);
453 else if (!strncmp(pdev->name, smd_ch_name[1],
454 strnlen(smd_ch_name[1], SMD_MAX_CH_NAME_LEN)))
455 complete_all(&smd_tty[1].ch_allocated);
456 else if (!strncmp(pdev->name, smd_ch_name[2],
457 strnlen(smd_ch_name[2], SMD_MAX_CH_NAME_LEN)))
458 complete_all(&smd_tty[2].ch_allocated);
459 else if (!strncmp(pdev->name, smd_ch_name[3],
460 strnlen(smd_ch_name[3], SMD_MAX_CH_NAME_LEN)))
461 complete_all(&smd_tty[3].ch_allocated);
462 else if (!strncmp(pdev->name, smd_ch_name[7],
463 strnlen(smd_ch_name[7], SMD_MAX_CH_NAME_LEN)))
464 complete_all(&smd_tty[7].ch_allocated);
465 else if (!strncmp(pdev->name, smd_ch_name[21],
466 strnlen(smd_ch_name[21], SMD_MAX_CH_NAME_LEN)))
467 complete_all(&smd_tty[21].ch_allocated);
468 else if (!strncmp(pdev->name, smd_ch_name[27],
469 strnlen(smd_ch_name[27], SMD_MAX_CH_NAME_LEN)))
470 complete_all(&smd_tty[27].ch_allocated);
471 else if (!strncmp(pdev->name, "LOOPBACK_TTY",
472 strnlen("LOOPBACK_TTY", SMD_MAX_CH_NAME_LEN)))
473 complete_all(&smd_tty[36].ch_allocated);
474
475 return 0;
476}
477
478static struct tty_driver *smd_tty_driver;
479
480static int __init smd_tty_init(void)
481{
482 int ret;
Jeff Hugo4c0ba6c2011-07-15 11:47:13 -0600483 int ds_registered = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700484
485 smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
486 if (smd_tty_driver == 0)
487 return -ENOMEM;
488
489 smd_tty_driver->owner = THIS_MODULE;
490 smd_tty_driver->driver_name = "smd_tty_driver";
491 smd_tty_driver->name = "smd";
492 smd_tty_driver->major = 0;
493 smd_tty_driver->minor_start = 0;
494 smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
495 smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
496 smd_tty_driver->init_termios = tty_std_termios;
497 smd_tty_driver->init_termios.c_iflag = 0;
498 smd_tty_driver->init_termios.c_oflag = 0;
499 smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
500 smd_tty_driver->init_termios.c_lflag = 0;
501 smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
502 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
503 tty_set_operations(smd_tty_driver, &smd_tty_ops);
504
505 ret = tty_register_driver(smd_tty_driver);
506 if (ret) return ret;
507
508 /* this should be dynamic */
509 tty_register_device(smd_tty_driver, 0, 0);
510 tty_register_device(smd_tty_driver, 1, 0);
511 tty_register_device(smd_tty_driver, 2, 0);
512 tty_register_device(smd_tty_driver, 3, 0);
513 tty_register_device(smd_tty_driver, 7, 0);
514 tty_register_device(smd_tty_driver, 21, 0);
515 tty_register_device(smd_tty_driver, 27, 0);
516 tty_register_device(smd_tty_driver, 36, 0);
517
518 init_completion(&smd_tty[0].ch_allocated);
519 init_completion(&smd_tty[1].ch_allocated);
520 init_completion(&smd_tty[2].ch_allocated);
521 init_completion(&smd_tty[3].ch_allocated);
522 init_completion(&smd_tty[7].ch_allocated);
523 init_completion(&smd_tty[21].ch_allocated);
524 init_completion(&smd_tty[27].ch_allocated);
525 init_completion(&smd_tty[36].ch_allocated);
526
527 smd_tty[0].driver.probe = smd_tty_dummy_probe;
528 smd_tty[0].driver.driver.name = smd_ch_name[0];
529 smd_tty[0].driver.driver.owner = THIS_MODULE;
530 spin_lock_init(&smd_tty[0].reset_lock);
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600531 smd_tty[0].is_open = 0;
532 init_waitqueue_head(&smd_tty[0].ch_opened_wait_queue);
Jeff Hugo4c0ba6c2011-07-15 11:47:13 -0600533 /*
534 * DS port is opened in the kernel starting with 8660 fusion.
535 * Only register the platform driver for targets older than that.
536 */
537 if (cpu_is_msm7x01() || cpu_is_msm7x25() || cpu_is_msm7x27() ||
538 cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
539 cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
540 cpu_is_msm7x30() || cpu_is_qsd8x50() ||
541 cpu_is_msm8x55() || (cpu_is_msm8x60() &&
542 socinfo_get_platform_subtype() == 0x1)) {
543 ret = platform_driver_register(&smd_tty[0].driver);
544 if (ret)
545 goto out;
546 ds_registered = 1;
547 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548 smd_tty[1].driver.probe = smd_tty_dummy_probe;
549 smd_tty[1].driver.driver.name = smd_ch_name[1];
550 smd_tty[1].driver.driver.owner = THIS_MODULE;
551 spin_lock_init(&smd_tty[1].reset_lock);
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600552 smd_tty[1].is_open = 0;
553 init_waitqueue_head(&smd_tty[1].ch_opened_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700554 ret = platform_driver_register(&smd_tty[1].driver);
555 if (ret)
556 goto unreg0;
557 smd_tty[2].driver.probe = smd_tty_dummy_probe;
558 smd_tty[2].driver.driver.name = smd_ch_name[2];
559 smd_tty[2].driver.driver.owner = THIS_MODULE;
560 spin_lock_init(&smd_tty[2].reset_lock);
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600561 smd_tty[2].is_open = 0;
562 init_waitqueue_head(&smd_tty[2].ch_opened_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563 ret = platform_driver_register(&smd_tty[2].driver);
564 if (ret)
565 goto unreg1;
566 smd_tty[3].driver.probe = smd_tty_dummy_probe;
567 smd_tty[3].driver.driver.name = smd_ch_name[3];
568 smd_tty[3].driver.driver.owner = THIS_MODULE;
569 spin_lock_init(&smd_tty[3].reset_lock);
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600570 smd_tty[3].is_open = 0;
571 init_waitqueue_head(&smd_tty[3].ch_opened_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572 ret = platform_driver_register(&smd_tty[3].driver);
573 if (ret)
574 goto unreg2;
575 smd_tty[7].driver.probe = smd_tty_dummy_probe;
576 smd_tty[7].driver.driver.name = smd_ch_name[7];
577 smd_tty[7].driver.driver.owner = THIS_MODULE;
578 spin_lock_init(&smd_tty[7].reset_lock);
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600579 smd_tty[7].is_open = 0;
580 init_waitqueue_head(&smd_tty[7].ch_opened_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700581 ret = platform_driver_register(&smd_tty[7].driver);
582 if (ret)
583 goto unreg3;
584 smd_tty[21].driver.probe = smd_tty_dummy_probe;
585 smd_tty[21].driver.driver.name = smd_ch_name[21];
586 smd_tty[21].driver.driver.owner = THIS_MODULE;
587 spin_lock_init(&smd_tty[21].reset_lock);
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600588 smd_tty[21].is_open = 0;
589 init_waitqueue_head(&smd_tty[21].ch_opened_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700590 ret = platform_driver_register(&smd_tty[21].driver);
591 if (ret)
592 goto unreg7;
593 smd_tty[27].driver.probe = smd_tty_dummy_probe;
594 smd_tty[27].driver.driver.name = smd_ch_name[27];
595 smd_tty[27].driver.driver.owner = THIS_MODULE;
596 spin_lock_init(&smd_tty[27].reset_lock);
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600597 smd_tty[27].is_open = 0;
598 init_waitqueue_head(&smd_tty[27].ch_opened_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700599 ret = platform_driver_register(&smd_tty[27].driver);
600 if (ret)
601 goto unreg21;
602 smd_tty[36].driver.probe = smd_tty_dummy_probe;
603 smd_tty[36].driver.driver.name = "LOOPBACK_TTY";
604 smd_tty[36].driver.driver.owner = THIS_MODULE;
605 spin_lock_init(&smd_tty[36].reset_lock);
Karthikeyan Ramasubramaniand5e63af2011-07-29 11:30:59 -0600606 smd_tty[36].is_open = 0;
607 init_waitqueue_head(&smd_tty[36].ch_opened_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700608 INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
609 ret = platform_driver_register(&smd_tty[36].driver);
610 if (ret)
611 goto unreg27;
612
613 return 0;
614
615unreg27:
616 platform_driver_unregister(&smd_tty[27].driver);
617unreg21:
618 platform_driver_unregister(&smd_tty[21].driver);
619unreg7:
620 platform_driver_unregister(&smd_tty[7].driver);
621unreg3:
622 platform_driver_unregister(&smd_tty[3].driver);
623unreg2:
624 platform_driver_unregister(&smd_tty[2].driver);
625unreg1:
626 platform_driver_unregister(&smd_tty[1].driver);
627unreg0:
Jeff Hugo4c0ba6c2011-07-15 11:47:13 -0600628 if (ds_registered)
629 platform_driver_unregister(&smd_tty[0].driver);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700630out:
631 tty_unregister_device(smd_tty_driver, 0);
632 tty_unregister_device(smd_tty_driver, 1);
633 tty_unregister_device(smd_tty_driver, 2);
634 tty_unregister_device(smd_tty_driver, 3);
635 tty_unregister_device(smd_tty_driver, 7);
636 tty_unregister_device(smd_tty_driver, 21);
637 tty_unregister_device(smd_tty_driver, 27);
638 tty_unregister_device(smd_tty_driver, 36);
639 tty_unregister_driver(smd_tty_driver);
640 put_tty_driver(smd_tty_driver);
641 return ret;
642}
643
644module_init(smd_tty_init);