1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * linux/arch/alpha/kernel/srmcons.c 4 * 5 * Callback based driver for SRM Console console device. 6 * (TTY driver and console driver) 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/init.h> 11 #include <linux/console.h> 12 #include <linux/delay.h> 13 #include <linux/mm.h> 14 #include <linux/slab.h> 15 #include <linux/spinlock.h> 16 #include <linux/timer.h> 17 #include <linux/tty.h> 18 #include <linux/tty_driver.h> 19 #include <linux/tty_flip.h> 20 21 #include <asm/console.h> 22 #include <linux/uaccess.h> 23 24 25 static DEFINE_SPINLOCK(srmcons_callback_lock); 26 static int srm_is_registered_console = 0; 27 28 /* 29 * The TTY driver 30 */ 31 #define MAX_SRM_CONSOLE_DEVICES 1 /* only support 1 console device */ 32 33 struct srmcons_private { 34 struct tty_port port; 35 struct timer_list timer; 36 } srmcons_singleton; 37 38 typedef union _srmcons_result { 39 struct { 40 unsigned long c :61; 41 unsigned long status :3; 42 } bits; 43 long as_long; 44 } srmcons_result; 45 46 /* called with callback_lock held */ 47 static int 48 srmcons_do_receive_chars(struct tty_port *port) 49 { 50 srmcons_result result; 51 int count = 0, loops = 0; 52 53 do { 54 result.as_long = callback_getc(0); 55 if (result.bits.status < 2) { 56 tty_insert_flip_char(port, (u8)result.bits.c, 0); 57 count++; 58 } 59 } while((result.bits.status & 1) && (++loops < 10)); 60 61 if (count) 62 tty_flip_buffer_push(port); 63 64 return count; 65 } 66 67 static void 68 srmcons_receive_chars(struct timer_list *t) 69 { 70 struct srmcons_private *srmconsp = from_timer(srmconsp, t, timer); 71 struct tty_port *port = &srmconsp->port; 72 unsigned long flags; 73 int incr = 10; 74 75 local_irq_save(flags); 76 if (spin_trylock(&srmcons_callback_lock)) { 77 if (!srmcons_do_receive_chars(port)) 78 incr = 100; 79 spin_unlock(&srmcons_callback_lock); 80 } 81 82 spin_lock(&port->lock); 83 if (port->tty) 84 mod_timer(&srmconsp->timer, jiffies + incr); 85 spin_unlock(&port->lock); 86 87 local_irq_restore(flags); 88 } 89 90 /* called with callback_lock held */ 91 static void 92 srmcons_do_write(struct tty_port *port, const u8 *buf, size_t count) 93 { 94 size_t c; 95 srmcons_result result; 96 97 while (count > 0) { 98 bool need_cr = false; 99 /* 100 * Break it up into reasonable size chunks to allow a chance 101 * for input to get in 102 */ 103 for (c = 0; c < min_t(size_t, 128U, count) && !need_cr; c++) 104 if (buf[c] == '\n') 105 need_cr = true; 106 107 while (c > 0) { 108 result.as_long = callback_puts(0, buf, c); 109 c -= result.bits.c; 110 count -= result.bits.c; 111 buf += result.bits.c; 112 113 /* 114 * Check for pending input iff a tty port was provided 115 */ 116 if (port) 117 srmcons_do_receive_chars(port); 118 } 119 120 while (need_cr) { 121 result.as_long = callback_puts(0, "\r", 1); 122 if (result.bits.c > 0) 123 need_cr = false; 124 } 125 } 126 } 127 128 static ssize_t 129 srmcons_write(struct tty_struct *tty, const u8 *buf, size_t count) 130 { 131 unsigned long flags; 132 133 spin_lock_irqsave(&srmcons_callback_lock, flags); 134 srmcons_do_write(tty->port, buf, count); 135 spin_unlock_irqrestore(&srmcons_callback_lock, flags); 136 137 return count; 138 } 139 140 static unsigned int 141 srmcons_write_room(struct tty_struct *tty) 142 { 143 return 512; 144 } 145 146 static int 147 srmcons_open(struct tty_struct *tty, struct file *filp) 148 { 149 struct srmcons_private *srmconsp = &srmcons_singleton; 150 struct tty_port *port = &srmconsp->port; 151 unsigned long flags; 152 153 spin_lock_irqsave(&port->lock, flags); 154 155 if (!port->tty) { 156 tty->driver_data = srmconsp; 157 tty->port = port; 158 port->tty = tty; /* XXX proper refcounting */ 159 mod_timer(&srmconsp->timer, jiffies + 10); 160 } 161 162 spin_unlock_irqrestore(&port->lock, flags); 163 164 return 0; 165 } 166 167 static void 168 srmcons_close(struct tty_struct *tty, struct file *filp) 169 { 170 struct srmcons_private *srmconsp = tty->driver_data; 171 struct tty_port *port = &srmconsp->port; 172 unsigned long flags; 173 174 spin_lock_irqsave(&port->lock, flags); 175 176 if (tty->count == 1) { 177 port->tty = NULL; 178 del_timer(&srmconsp->timer); 179 } 180 181 spin_unlock_irqrestore(&port->lock, flags); 182 } 183 184 185 static struct tty_driver *srmcons_driver; 186 187 static const struct tty_operations srmcons_ops = { 188 .open = srmcons_open, 189 .close = srmcons_close, 190 .write = srmcons_write, 191 .write_room = srmcons_write_room, 192 }; 193 194 static int __init 195 srmcons_init(void) 196 { 197 timer_setup(&srmcons_singleton.timer, srmcons_receive_chars, 0); 198 if (srm_is_registered_console) { 199 struct tty_driver *driver; 200 int err; 201 202 driver = tty_alloc_driver(MAX_SRM_CONSOLE_DEVICES, 0); 203 if (IS_ERR(driver)) 204 return PTR_ERR(driver); 205 206 tty_port_init(&srmcons_singleton.port); 207 208 driver->driver_name = "srm"; 209 driver->name = "srm"; 210 driver->major = 0; /* dynamic */ 211 driver->minor_start = 0; 212 driver->type = TTY_DRIVER_TYPE_SYSTEM; 213 driver->subtype = SYSTEM_TYPE_SYSCONS; 214 driver->init_termios = tty_std_termios; 215 tty_set_operations(driver, &srmcons_ops); 216 tty_port_link_device(&srmcons_singleton.port, driver, 0); 217 err = tty_register_driver(driver); 218 if (err) { 219 tty_driver_kref_put(driver); 220 tty_port_destroy(&srmcons_singleton.port); 221 return err; 222 } 223 srmcons_driver = driver; 224 } 225 226 return -ENODEV; 227 } 228 device_initcall(srmcons_init); 229 230 231 /* 232 * The console driver 233 */ 234 static void 235 srm_console_write(struct console *co, const char *s, unsigned count) 236 { 237 unsigned long flags; 238 239 spin_lock_irqsave(&srmcons_callback_lock, flags); 240 srmcons_do_write(NULL, s, count); 241 spin_unlock_irqrestore(&srmcons_callback_lock, flags); 242 } 243 244 static struct tty_driver * 245 srm_console_device(struct console *co, int *index) 246 { 247 *index = co->index; 248 return srmcons_driver; 249 } 250 251 static int 252 srm_console_setup(struct console *co, char *options) 253 { 254 return 0; 255 } 256 257 static struct console srmcons = { 258 .name = "srm", 259 .write = srm_console_write, 260 .device = srm_console_device, 261 .setup = srm_console_setup, 262 .flags = CON_PRINTBUFFER | CON_BOOT, 263 .index = -1, 264 }; 265 266 void __init 267 register_srm_console(void) 268 { 269 if (!srm_is_registered_console) { 270 callback_open_console(); 271 register_console(&srmcons); 272 srm_is_registered_console = 1; 273 } 274 } 275 276 void __init 277 unregister_srm_console(void) 278 { 279 if (srm_is_registered_console) { 280 callback_close_console(); 281 unregister_console(&srmcons); 282 srm_is_registered_console = 0; 283 } 284 } 285