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