xref: /xnu-10002.61.3/bsd/dev/arm/km.c (revision 0f4c859e951fba394238ab619495c4e1d54d0f34)
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
kminit(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
kmopen(dev_t dev,__unused int flag,__unused int devtype,proc_t pp)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(&tp->t_termios);
90 		ttsetwater(tp);
91 	} else if ((tp->t_state & TS_XCLUDE) && proc_suser(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(&video, sizeof(video));
112 		PE_current_console(&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
kmclose(dev_t dev,int flag,__unused int mode,__unused proc_t p)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
kmread(dev_t dev,struct uio * uio,int ioflag)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
kmwrite(dev_t dev,struct uio * uio,int ioflag)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
kmioctl(dev_t dev,u_long cmd,caddr_t data,int flag,proc_t p)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, 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
kmputc(__unused dev_t dev,char c)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
kmstart(struct tty * tp)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
kmtimeout(void * arg)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
kmoutput(struct tty * tp)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(&tp->t_outq, 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(cc, sizeof(buf));
355 		/* copy the output queue contents to the buffer */
356 		(void) q_to_b(&tp->t_outq, 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(tp->t_dev, *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, tp, 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
cons_cinput(char ch)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