1 /* drivers/char/dcc_tty.c
3 * Copyright (C) 2007 Google, Inc.
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/delay.h>
19 #include <linux/console.h>
20 #include <linux/hrtimer.h>
21 #include <linux/tty.h>
22 #include <linux/tty_driver.h>
23 #include <linux/tty_flip.h>
25 MODULE_DESCRIPTION("DCC TTY Driver");
26 MODULE_LICENSE("GPL");
27 MODULE_VERSION("1.0");
29 static spinlock_t g_dcc_tty_lock
= SPIN_LOCK_UNLOCKED
;
30 static struct hrtimer g_dcc_timer
;
31 static char g_dcc_buffer
[16];
32 static int g_dcc_buffer_head
;
33 static int g_dcc_buffer_count
;
34 static unsigned g_dcc_write_delay_usecs
= 1;
35 static struct tty_driver
*g_dcc_tty_driver
;
36 static struct tty_struct
*g_dcc_tty
;
37 static int g_dcc_tty_open_count
;
39 static void dcc_poll_locked(void)
45 while (g_dcc_buffer_count
) {
46 ch
= g_dcc_buffer
[g_dcc_buffer_head
];
48 "mrc 14, 0, r15, c0, c1, 0\n"
49 "mcrcc 14, 0, %1, c0, c5, 0\n"
57 g_dcc_buffer
[g_dcc_buffer_head
] = '\r';
59 g_dcc_buffer_head
= (g_dcc_buffer_head
+ 1) % ARRAY_SIZE(g_dcc_buffer
);
62 tty_wakeup(g_dcc_tty
);
64 g_dcc_write_delay_usecs
= 1;
66 if (g_dcc_write_delay_usecs
> 0x100)
68 g_dcc_write_delay_usecs
<<= 1;
69 udelay(g_dcc_write_delay_usecs
);
73 if (g_dcc_tty
&& !test_bit(TTY_THROTTLED
, &g_dcc_tty
->flags
)) {
75 "mrc 14, 0, %0, c0, c1, 0\n"
76 "tst %0, #(1 << 30)\n"
78 "mrcne 14, 0, %0, c0, c5, 0\n"
83 tty_insert_flip_string(g_dcc_tty
, &ch
, 1);
84 tty_flip_buffer_push(g_dcc_tty
);
89 if (g_dcc_buffer_count
)
90 hrtimer_start(&g_dcc_timer
, ktime_set(0, g_dcc_write_delay_usecs
* NSEC_PER_USEC
), HRTIMER_MODE_REL
);
92 hrtimer_start(&g_dcc_timer
, ktime_set(0, 20 * NSEC_PER_MSEC
), HRTIMER_MODE_REL
);
95 static int dcc_tty_open(struct tty_struct
* tty
, struct file
* filp
)
98 unsigned long irq_flags
;
100 spin_lock_irqsave(&g_dcc_tty_lock
, irq_flags
);
101 if (g_dcc_tty
== NULL
|| g_dcc_tty
== tty
) {
103 g_dcc_tty_open_count
++;
107 spin_unlock_irqrestore(&g_dcc_tty_lock
, irq_flags
);
109 printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty
, filp
->f_flags
, ret
);
114 static void dcc_tty_close(struct tty_struct
* tty
, struct file
* filp
)
116 printk("dcc_tty_close, tty %p, f_flags %x\n", tty
, filp
->f_flags
);
117 if (g_dcc_tty
== tty
) {
118 if (--g_dcc_tty_open_count
== 0)
123 static int dcc_write(const unsigned char *buf_start
, int count
)
125 const unsigned char *buf
= buf_start
;
126 unsigned long irq_flags
;
134 spin_lock_irqsave(&g_dcc_tty_lock
, irq_flags
);
136 tail
= (g_dcc_buffer_head
+ g_dcc_buffer_count
) % ARRAY_SIZE(g_dcc_buffer
);
137 copy_len
= ARRAY_SIZE(g_dcc_buffer
) - tail
;
138 space_left
= ARRAY_SIZE(g_dcc_buffer
) - g_dcc_buffer_count
;
139 if (copy_len
> space_left
)
140 copy_len
= space_left
;
141 if (copy_len
> count
)
143 memcpy(&g_dcc_buffer
[tail
], buf
, copy_len
);
144 g_dcc_buffer_count
+= copy_len
;
147 if (copy_len
< count
&& copy_len
< space_left
) {
148 space_left
-= copy_len
;
150 if (copy_len
> space_left
) {
151 copy_len
= space_left
;
153 memcpy(g_dcc_buffer
, buf
, copy_len
);
156 g_dcc_buffer_count
+= copy_len
;
159 space_left
= ARRAY_SIZE(g_dcc_buffer
) - g_dcc_buffer_count
;
160 } while(count
&& space_left
);
161 spin_unlock_irqrestore(&g_dcc_tty_lock
, irq_flags
);
162 return buf
- buf_start
;
165 static int dcc_tty_write(struct tty_struct
* tty
, const unsigned char *buf
, int count
)
168 /* printk("dcc_tty_write %p, %d\n", buf, count); */
169 ret
= dcc_write(buf
, count
);
171 printk("dcc_tty_write %p, %d, returned %d\n", buf
, count
, ret
);
175 static int dcc_tty_write_room(struct tty_struct
*tty
)
178 unsigned long irq_flags
;
180 spin_lock_irqsave(&g_dcc_tty_lock
, irq_flags
);
181 space_left
= ARRAY_SIZE(g_dcc_buffer
) - g_dcc_buffer_count
;
182 spin_unlock_irqrestore(&g_dcc_tty_lock
, irq_flags
);
186 static int dcc_tty_chars_in_buffer(struct tty_struct
*tty
)
190 "mrc 14, 0, %0, c0, c1, 0\n"
191 "mov %0, %0, LSR #30\n"
198 static void dcc_tty_unthrottle(struct tty_struct
* tty
)
200 unsigned long irq_flags
;
202 spin_lock_irqsave(&g_dcc_tty_lock
, irq_flags
);
204 spin_unlock_irqrestore(&g_dcc_tty_lock
, irq_flags
);
207 static enum hrtimer_restart
dcc_tty_timer_func(struct hrtimer
*timer
)
209 unsigned long irq_flags
;
211 spin_lock_irqsave(&g_dcc_tty_lock
, irq_flags
);
213 spin_unlock_irqrestore(&g_dcc_tty_lock
, irq_flags
);
214 return HRTIMER_NORESTART
;
217 void dcc_console_write(struct console
*co
, const char *b
, unsigned count
)
222 /* blocking printk */
225 written
= dcc_write(b
, count
);
234 static struct tty_driver
*dcc_console_device(struct console
*c
, int *index
)
237 return g_dcc_tty_driver
;
240 static int __init
dcc_console_setup(struct console
*co
, char *options
)
248 static struct console dcc_console
=
251 .write
= dcc_console_write
,
252 .device
= dcc_console_device
,
253 .setup
= dcc_console_setup
,
254 .flags
= CON_PRINTBUFFER
,
258 static struct tty_operations dcc_tty_ops
= {
259 .open
= dcc_tty_open
,
260 .close
= dcc_tty_close
,
261 .write
= dcc_tty_write
,
262 .write_room
= dcc_tty_write_room
,
263 .chars_in_buffer
= dcc_tty_chars_in_buffer
,
264 .unthrottle
= dcc_tty_unthrottle
,
267 static int __init
dcc_tty_init(void)
271 hrtimer_init(&g_dcc_timer
, CLOCK_MONOTONIC
, HRTIMER_MODE_REL
);
272 g_dcc_timer
.function
= dcc_tty_timer_func
;
274 g_dcc_tty_driver
= alloc_tty_driver(1);
275 if (!g_dcc_tty_driver
) {
276 printk(KERN_ERR
"dcc_tty_probe: alloc_tty_driver failed\n");
278 goto err_alloc_tty_driver_failed
;
280 g_dcc_tty_driver
->owner
= THIS_MODULE
;
281 g_dcc_tty_driver
->driver_name
= "dcc";
282 g_dcc_tty_driver
->name
= "ttyDCC";
283 g_dcc_tty_driver
->major
= 0; // auto assign
284 g_dcc_tty_driver
->minor_start
= 0;
285 g_dcc_tty_driver
->type
= TTY_DRIVER_TYPE_SERIAL
;
286 g_dcc_tty_driver
->subtype
= SERIAL_TYPE_NORMAL
;
287 g_dcc_tty_driver
->init_termios
= tty_std_termios
;
288 g_dcc_tty_driver
->flags
= TTY_DRIVER_RESET_TERMIOS
| TTY_DRIVER_REAL_RAW
| TTY_DRIVER_DYNAMIC_DEV
;
289 tty_set_operations(g_dcc_tty_driver
, &dcc_tty_ops
);
290 ret
= tty_register_driver(g_dcc_tty_driver
);
292 printk(KERN_ERR
"dcc_tty_probe: tty_register_driver failed, %d\n", ret
);
293 goto err_tty_register_driver_failed
;
295 tty_register_device(g_dcc_tty_driver
, 0, NULL
);
297 register_console(&dcc_console
);
298 hrtimer_start(&g_dcc_timer
, ktime_set(0, 0), HRTIMER_MODE_REL
);
302 err_tty_register_driver_failed
:
303 put_tty_driver(g_dcc_tty_driver
);
304 g_dcc_tty_driver
= NULL
;
305 err_alloc_tty_driver_failed
:
309 static void __exit
dcc_tty_exit(void)
313 tty_unregister_device(g_dcc_tty_driver
, 0);
314 ret
= tty_unregister_driver(g_dcc_tty_driver
);
316 printk(KERN_ERR
"dcc_tty_remove: tty_unregister_driver failed, %d\n", ret
);
318 put_tty_driver(g_dcc_tty_driver
);
320 g_dcc_tty_driver
= NULL
;
323 module_init(dcc_tty_init
);
324 module_exit(dcc_tty_exit
);