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/i386/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 81kminit(void) 82{ 83 km_tty[0] = ttymalloc(); 84 km_tty[0]->t_dev = makedev(12, 0); 85 initialized = 1; 86} 87 88/* 89 * cdevsw interface to km driver. 90 */ 91int 92kmopen(dev_t dev, int flag, __unused int devtype, proc_t pp) 93{ 94 int unit; 95 struct tty *tp; 96 struct winsize *wp; 97 int ret; 98 99 unit = minor(dev); 100 if(unit >= 1) 101 return (ENXIO); 102 103 tp = km_tty[unit]; 104 105 tty_lock(tp); 106 107 tp->t_oproc = kmstart; 108 tp->t_param = NULL; 109 tp->t_dev = dev; 110 111 if ( !(tp->t_state & TS_ISOPEN) ) { 112 tp->t_iflag = TTYDEF_IFLAG; 113 tp->t_oflag = TTYDEF_OFLAG; 114 tp->t_cflag = (CREAD | CS8 | CLOCAL); 115 tp->t_lflag = TTYDEF_LFLAG; 116 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 117 termioschars(&tp->t_termios); 118 ttsetwater(tp); 119 } else if ((tp->t_state & TS_XCLUDE) && proc_suser(pp)) { 120 ret = EBUSY; 121 goto out; 122 } 123 124 tp->t_state |= TS_CARR_ON; /* lie and say carrier exists and is on. */ 125 126 ret = ((*linesw[tp->t_line].l_open)(dev, tp)); 127 { 128 PE_Video video; 129 wp = &tp->t_winsize; 130 /* 131 * Magic numbers. These are CHARWIDTH and CHARHEIGHT 132 * from pexpert/i386/video_console.c 133 */ 134 wp->ws_xpixel = 8; 135 wp->ws_ypixel = 16; 136 137 tty_unlock(tp); /* XXX race window */ 138 139 if (flag & O_POPUP) 140 PE_initialize_console(0, kPETextScreen); 141 142 bzero(&video, sizeof(video)); 143 PE_current_console(&video); 144 145 tty_lock(tp); 146 147 if( video.v_display == FB_TEXT_MODE && video.v_width != 0 && video.v_height != 0 ) { 148 wp->ws_col = video.v_width / wp->ws_xpixel; 149 wp->ws_row = video.v_height / wp->ws_ypixel; 150 } else { 151 wp->ws_col = 100; 152 wp->ws_row = 36; 153 } 154 } 155 156out: 157 tty_unlock(tp); 158 159 return ret; 160} 161 162int 163kmclose(dev_t dev, int flag, __unused int mode, __unused proc_t p) 164{ 165 int ret; 166 struct tty *tp = km_tty[minor(dev)]; 167 168 tty_lock(tp); 169 ret = (*linesw[tp->t_line].l_close)(tp,flag); 170 ttyclose(tp); 171 tty_unlock(tp); 172 173 return (ret); 174} 175 176int 177kmread(dev_t dev, struct uio *uio, int ioflag) 178{ 179 int ret; 180 struct tty *tp = km_tty[minor(dev)]; 181 182 tty_lock(tp); 183 ret = (*linesw[tp->t_line].l_read)(tp, uio, ioflag); 184 tty_unlock(tp); 185 186 return (ret); 187} 188 189int 190kmwrite(dev_t dev, struct uio *uio, int ioflag) 191{ 192 int ret; 193 struct tty *tp = km_tty[minor(dev)]; 194 195 tty_lock(tp); 196 ret = (*linesw[tp->t_line].l_write)(tp, uio, ioflag); 197 tty_unlock(tp); 198 199 return (ret); 200} 201 202int 203kmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, proc_t p) 204{ 205 int error = 0; 206 struct tty *tp = km_tty[minor(dev)]; 207 struct winsize *wp; 208 209 tty_lock(tp); 210 211 switch (cmd) { 212 case KMIOCSIZE: 213 wp = (struct winsize *)data; 214 *wp = tp->t_winsize; 215 break; 216 217 case TIOCSWINSZ: 218 /* Prevent changing of console size -- 219 * this ensures that login doesn't revert to the 220 * termcap-defined size 221 */ 222 error = EINVAL; 223 break; 224 225 /* Bodge in the CLOCAL flag as the km device is always local */ 226 case TIOCSETA_32: 227 case TIOCSETAW_32: 228 case TIOCSETAF_32: 229 { 230 struct termios32 *t = (struct termios32 *)data; 231 t->c_cflag |= CLOCAL; 232 /* No Break */ 233 } 234 goto fallthrough; 235 case TIOCSETA_64: 236 case TIOCSETAW_64: 237 case TIOCSETAF_64: 238 { 239 struct user_termios *t = (struct user_termios *)data; 240 t->c_cflag |= CLOCAL; 241 /* No Break */ 242 } 243fallthrough: 244 default: 245 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 246 if (ENOTTY != error) 247 break; 248 error = ttioctl_locked(tp, cmd, data, flag, p); 249 break; 250 } 251 252 tty_unlock(tp); 253 254 return (error); 255} 256 257/* 258 * kmputc 259 * 260 * Output a character to the serial console driver via cnputcusr(), 261 * which is exported by that driver. 262 * 263 * Locks: Assumes tp in the calling tty driver code is locked on 264 * entry, remains locked on exit 265 * 266 * Notes: Called from kmoutput(); giving the locking output 267 * assumptions here, this routine should be static (and 268 * inlined, given there is only one call site). 269 */ 270int 271kmputc(__unused dev_t dev, char c) 272{ 273 if(!disableConsoleOutput && initialized) { 274 /* OCRNL */ 275 if(c == '\n') 276 cnputcusr('\r'); 277 cnputcusr(c); 278 } 279 280 return (0); 281} 282 283 284/* 285 * Callouts from linesw. 286 */ 287 288#define KM_LOWAT_DELAY ((ns_time_t)1000) 289 290/* 291 * t_oproc for this driver; called from within the line discipline 292 * 293 * Locks: Assumes tp is locked on entry, remains locked on exit 294 */ 295static void 296kmstart(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 306out: 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 320kmtimeout(void *arg) 321{ 322 struct tty *tp = (struct tty *)arg; 323 324 tty_lock(tp); 325 (void)kmoutput(tp); 326 tty_unlock(tp); 327} 328 329/* 330 * kmoutput 331 * 332 * Locks: Assumes tp is locked on entry, remains locked on exit 333 * 334 * Notes: Called from kmstart() and kmtimeout(); kmtimeout() is a 335 * timer initiated by this routine to deal with pending 336 * output not yet flushed (output is flushed at a maximum 337 * of sizeof(buf) charatcers at a time before dropping into 338 * the timeout code). 339 */ 340static int 341kmoutput(struct tty *tp) 342{ 343 unsigned char buf[80]; /* buffer; limits output per call */ 344 unsigned char *cp; 345 int cc = -1; 346 347 348 /* While there is data available to be output... */ 349 while (tp->t_outq.c_cc > 0) { 350 cc = ndqb(&tp->t_outq, 0); 351 if (cc == 0) 352 break; 353 /* 354 * attempt to output as many characters as are available, 355 * up to the available transfer buffer size. 356 */ 357 cc = min(cc, sizeof(buf)); 358 /* copy the output queue contents to the buffer */ 359 (void) q_to_b(&tp->t_outq, buf, cc); 360 for (cp = buf; cp < &buf[cc]; cp++) { 361 /* output the buffer one charatcer at a time */ 362 kmputc(tp->t_dev, *cp & 0x7f); 363 } 364 } 365 /* 366 * XXX This is likely not necessary, as the tty output queue is not 367 * XXX writeable while we hold the tty_lock(). 368 */ 369 if (tp->t_outq.c_cc > 0) { 370 timeout(kmtimeout, tp, hz); 371 } 372 tp->t_state &= ~TS_BUSY; 373 /* Start the output processing for the line discipline */ 374 (*linesw[tp->t_line].l_start)(tp); 375 376 return 0; 377} 378 379 380/* 381 * cons_cinput 382 * 383 * Driver character input from the polled mode serial console driver calls 384 * this routine to input a character from the serial driver into the tty 385 * line discipline specific input processing receiv interrupt routine, 386 * l_rint(). 387 * 388 * Locks: Assumes that the tty_lock() is NOT held on the tp, so a 389 * serial driver should NOT call this function as a result 390 * of being called from a function which already holds the 391 * lock; ECHOE will be handled at the line discipline, if 392 * output echo processing is going to occur. 393 */ 394void 395cons_cinput(char ch) 396{ 397 struct tty *tp = km_tty[0]; /* XXX */ 398 399 tty_lock(tp); 400 (*linesw[tp->t_line].l_rint) (ch, tp); 401 tty_unlock(tp); 402} 403