1/* 2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* Copyright (c) 1992 NeXT Computer, Inc. All rights reserved. 29 * 30 * km.m - kernel keyboard/monitor module, procedural interface. 31 * 32 * HISTORY 33 */ 34 35#include <sys/param.h> 36#include <sys/tty.h> 37 38#include <machine/cons.h> 39#include <sys/conf.h> 40#include <sys/systm.h> 41#include <sys/uio.h> 42#include <sys/fcntl.h> /* for kmopen */ 43#include <sys/errno.h> 44#include <sys/proc.h> /* for kmopen */ 45#include <sys/msgbuf.h> 46#include <sys/time.h> 47#include <dev/kmreg_com.h> 48#include <pexpert/pexpert.h> 49#include <pexpert/arm/boot.h> 50 51extern int hz; 52 53extern void cnputcusr(char); 54extern int cngetc(void); 55 56void kminit(void); 57void cons_cinput(char ch); 58 59/* 60 * 'Global' variables, shared only by this file and conf.c. 61 */ 62struct tty *km_tty[1] = { 0 }; 63 64/* 65 * this works early on, after initialize_screen() but before autoconf (and thus 66 * before we have a kmDevice). 67 */ 68int disableConsoleOutput; 69 70/* 71 * 'Global' variables, shared only by this file and kmDevice.m. 72 */ 73int initialized = 0; 74 75static int kmoutput(struct tty *tp); 76static void kmstart(struct tty *tp); 77 78extern void KeyboardOpen(void); 79 80void kminit(void) 81{ 82 km_tty[0] = ttymalloc(); 83 km_tty[0]->t_dev = makedev(12, 0); 84 initialized = 1; 85} 86 87/* 88 * cdevsw interface to km driver. 89 */ 90int kmopen(dev_t dev, int flag, __unused int devtype, proc_t pp) 91{ 92 int unit; 93 struct tty *tp; 94 struct winsize *wp; 95 int ret; 96 97 unit = minor(dev); 98 if (unit >= 1) 99 return (ENXIO); 100 101 tp = km_tty[unit]; 102 103 tty_lock(tp); 104 105 tp->t_oproc = kmstart; 106 tp->t_param = NULL; 107 tp->t_dev = dev; 108 109 if (!(tp->t_state & TS_ISOPEN)) { 110 tp->t_iflag = TTYDEF_IFLAG; 111 tp->t_oflag = TTYDEF_OFLAG; 112 tp->t_cflag = (CREAD | CS8 | CLOCAL); 113 tp->t_lflag = TTYDEF_LFLAG; 114 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 115 termioschars(&tp->t_termios); 116 ttsetwater(tp); 117 } else if ((tp->t_state & TS_XCLUDE) && proc_suser(pp)) { 118 ret = EBUSY; 119 goto out; 120 } 121 122 tp->t_state |= TS_CARR_ON; /* lie and say carrier exists and is on. */ 123 124 ret = ((*linesw[tp->t_line].l_open) (dev, tp)); 125 { 126 PE_Video video; 127 wp = &tp->t_winsize; 128 /* 129 * Magic numbers. These are CHARWIDTH and CHARHEIGHT 130 * from pexpert/i386/video_console.c 131 */ 132 wp->ws_xpixel = 8; 133 wp->ws_ypixel = 16; 134 135 tty_unlock(tp); /* XXX race window */ 136 137 if (flag & O_POPUP) 138 PE_initialize_console(0, kPETextScreen); 139 140 bzero(&video, sizeof(video)); 141 PE_current_console(&video); 142 143 tty_lock(tp); 144 145 if (video.v_display == FB_TEXT_MODE && video.v_width != 0 && video.v_height != 0) { 146 wp->ws_col = video.v_width / wp->ws_xpixel; 147 wp->ws_row = video.v_height / wp->ws_ypixel; 148 } else { 149 wp->ws_col = 100; 150 wp->ws_row = 36; 151 } 152 } 153 154 out: 155 tty_unlock(tp); 156 157 return ret; 158} 159 160int kmclose(dev_t dev, int flag, __unused int mode, __unused proc_t p) 161{ 162 int ret; 163 struct tty *tp = km_tty[minor(dev)]; 164 165 tty_lock(tp); 166 ret = (*linesw[tp->t_line].l_close) (tp, flag); 167 ttyclose(tp); 168 tty_unlock(tp); 169 170 return (ret); 171} 172 173int kmread(dev_t dev, struct uio *uio, int ioflag) 174{ 175 int ret; 176 struct tty *tp = km_tty[minor(dev)]; 177 178 tty_lock(tp); 179 ret = (*linesw[tp->t_line].l_read) (tp, uio, ioflag); 180 tty_unlock(tp); 181 182 return (ret); 183} 184 185int kmwrite(dev_t dev, struct uio *uio, int ioflag) 186{ 187 int ret; 188 struct tty *tp = km_tty[minor(dev)]; 189 190 tty_lock(tp); 191 ret = (*linesw[tp->t_line].l_write) (tp, uio, ioflag); 192 tty_unlock(tp); 193 194 return (ret); 195} 196 197int kmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, proc_t p) 198{ 199 int error = 0; 200 struct tty *tp = km_tty[minor(dev)]; 201 struct winsize *wp; 202 203 tty_lock(tp); 204 205 switch (cmd) { 206 case KMIOCSIZE: 207 wp = (struct winsize *) data; 208 *wp = tp->t_winsize; 209 break; 210 211 case TIOCSWINSZ: 212 /* 213 * Prevent changing of console size -- 214 * * this ensures that login doesn't revert to the 215 * * termcap-defined size 216 */ 217 error = EINVAL; 218 break; 219 220 /* 221 * Bodge in the CLOCAL flag as the km device is always local 222 */ 223 case TIOCSETA_32: 224 case TIOCSETAW_32: 225 case TIOCSETAF_32: 226 { 227 struct termios32 *t = (struct termios32 *) data; 228 t->c_cflag |= CLOCAL; 229 /* 230 * No Break 231 */ 232 } 233 goto fallthrough; 234 case TIOCSETA_64: 235 case TIOCSETAW_64: 236 case TIOCSETAF_64: 237 { 238 struct user_termios *t = (struct user_termios *) data; 239 t->c_cflag |= CLOCAL; 240 /* 241 * No Break 242 */ 243 } 244 fallthrough: 245 default: 246 error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag, p); 247 if (ENOTTY != error) 248 break; 249 error = ttioctl_locked(tp, cmd, data, flag, p); 250 break; 251 } 252 253 tty_unlock(tp); 254 255 return (error); 256} 257 258/* 259 * kmputc 260 * 261 * Output a character to the serial console driver via cnputcusr(), 262 * which is exported by that driver. 263 * 264 * Locks: Assumes tp in the calling tty driver code is locked on 265 * entry, remains locked on exit 266 * 267 * Notes: Called from kmoutput(); giving the locking output 268 * assumptions here, this routine should be static (and 269 * inlined, given there is only one call site). 270 */ 271int kmputc(__unused dev_t dev, char c) 272{ 273 if (!disableConsoleOutput && initialized) { 274 /* 275 * OCRNL 276 */ 277 if (c == '\n') 278 cnputcusr('\r'); 279 cnputcusr(c); 280 } 281 282 return (0); 283} 284 285/* 286 * Callouts from linesw. 287 */ 288 289#define KM_LOWAT_DELAY ((ns_time_t)1000) 290 291/* 292 * t_oproc for this driver; called from within the line discipline 293 * 294 * Locks: Assumes tp is locked on entry, remains locked on exit 295 */ 296static void kmstart(struct tty *tp) 297{ 298 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) 299 goto out; 300 if (tp->t_outq.c_cc == 0) 301 goto out; 302 tp->t_state |= TS_BUSY; 303 kmoutput(tp); 304 return; 305 306 out: 307 (*linesw[tp->t_line].l_start) (tp); 308 return; 309} 310 311/* 312 * One-shot output retry timeout from kmoutput(); re-calls kmoutput() at 313 * intervals until the output queue for the tty is empty, at which point 314 * the timeout is not rescheduled by kmoutput() 315 * 316 * This function must take the tty_lock() around the kmoutput() call; it 317 * ignores the return value. 318 */ 319static void kmtimeout(void *arg) 320{ 321 struct tty *tp = (struct tty *) arg; 322 323 tty_lock(tp); 324 (void) kmoutput(tp); 325 tty_unlock(tp); 326} 327 328/* 329 * kmoutput 330 * 331 * Locks: Assumes tp is locked on entry, remains locked on exit 332 * 333 * Notes: Called from kmstart() and kmtimeout(); kmtimeout() is a 334 * timer initiated by this routine to deal with pending 335 * output not yet flushed (output is flushed at a maximum 336 * of sizeof(buf) charatcers at a time before dropping into 337 * the timeout code). 338 */ 339static int kmoutput(struct tty *tp) 340{ 341 unsigned char buf[80]; /* buffer; limits output per call */ 342 unsigned char *cp; 343 int cc = -1; 344 345 /* 346 * While there is data available to be output... 347 */ 348 while (tp->t_outq.c_cc > 0) { 349 cc = ndqb(&tp->t_outq, 0); 350 if (cc == 0) 351 break; 352 /* 353 * attempt to output as many characters as are available, 354 * up to the available transfer buffer size. 355 */ 356 cc = min(cc, sizeof(buf)); 357 /* 358 * copy the output queue contents to the buffer 359 */ 360 (void) q_to_b(&tp->t_outq, buf, cc); 361 for (cp = buf; cp < &buf[cc]; cp++) { 362 /* 363 * output the buffer one charatcer at a time 364 */ 365 kmputc(tp->t_dev, *cp & 0x7f); 366 } 367 } 368 /* 369 * XXX This is likely not necessary, as the tty output queue is not 370 * XXX writeable while we hold the tty_lock(). 371 */ 372 if (tp->t_outq.c_cc > 0) { 373 timeout(kmtimeout, tp, hz); 374 } 375 tp->t_state &= ~TS_BUSY; 376 /* 377 * Start the output processing for the line discipline 378 */ 379 (*linesw[tp->t_line].l_start) (tp); 380 381 return 0; 382} 383 384/* 385 * cons_cinput 386 * 387 * Driver character input from the polled mode serial console driver calls 388 * this routine to input a character from the serial driver into the tty 389 * line discipline specific input processing receiv interrupt routine, 390 * l_rint(). 391 * 392 * Locks: Assumes that the tty_lock() is NOT held on the tp, so a 393 * serial driver should NOT call this function as a result 394 * of being called from a function which already holds the 395 * lock; ECHOE will be handled at the line discipline, if 396 * output echo processing is going to occur. 397 */ 398void cons_cinput(char ch) 399{ 400 struct tty *tp = km_tty[0]; /* XXX */ 401 402 tty_lock(tp); 403 (*linesw[tp->t_line].l_rint) (ch, tp); 404 tty_unlock(tp); 405} 406