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/*
29 * @OSF_FREE_COPYRIGHT@
30 *
31 */
32
33/* Intercept mach console output and supply it to a user application */
34
35#include <mach_kdb.h>
36
37#include <types.h>
38#include <device/buf.h>
39#include <device/conf.h>
40#include <device/errno.h>
41#include <device/misc_protos.h>
42#include <device/ds_routines.h>
43#include <device/cirbuf.h>
44#include <ppc/console_feed_entries.h>
45#include <ppc/serial_io.h>
46
47#if	MACH_KDB
48#include <ppc/db_machdep.h>
49#endif	/* MACH_KDB */
50
51static struct cirbuf cons_feed_cb;
52static int cons_feed_count = 0;
53io_req_t   cons_feed_queued = 0;
54
55/* console feed lock should be taken at splhigh */
56decl_simple_lock_data(,cons_feed_lock)
57
58boolean_t cons_feed_read_done(io_req_t ior);
59
60io_return_t
61console_feed_open(
62	dev_t		dev,
63	dev_mode_t	flag,
64	io_req_t	ior)
65{
66	spl_t	s;
67
68        simple_lock_init(&cons_feed_lock, 0);
69#if	MACH_KDB
70	if (console_is_serial()) {
71		return D_DEVICE_DOWN;
72	}
73#endif	/* MACH_KDB */
74	cb_alloc(&cons_feed_cb, CONSOLE_FEED_BUFSIZE);
75	s = splhigh();
76	simple_lock(&cons_feed_lock);
77	cons_feed_count++;
78	simple_unlock(&cons_feed_lock);
79	splx(s);
80	return D_SUCCESS;
81}
82
83void
84console_feed_close(
85	dev_t		dev)
86{
87	spl_t	s;
88
89	s = splhigh();
90	simple_lock(&cons_feed_lock);
91	cons_feed_count--;
92	simple_unlock(&cons_feed_lock);
93	splx(s);
94
95	console_feed_cancel_and_flush();
96	cb_free(&cons_feed_cb);
97
98	return;
99}
100
101/* A routine that can be called from a panic or other problem
102 * situation. It switches off the console feed and dumps any
103 * remaining buffered information to the original console
104 * (usually the screen). It doesn't free up the buffer, since
105 * it tries to be as minimal as possible
106 */
107
108void console_feed_cancel_and_flush(void)
109{
110	int	c;
111	spl_t	s;
112
113#if	NCONSFEED > 0
114#if	MACH_KDB
115	if (console_is_serial()) {
116		return;
117	}
118#endif	/* MACH_KDB */
119
120	s = splhigh();
121	simple_lock(&cons_feed_lock);
122	if (cons_feed_count == 0) {
123		simple_unlock(&cons_feed_lock);
124		splx(s);
125		return;
126	}
127	cons_feed_count = 0;
128	simple_unlock(&cons_feed_lock);
129	splx(s);
130
131	do {
132		c = getc(&cons_feed_cb);
133		if (c == -1)
134			break;
135		cnputc(c);
136	} while (1);
137#endif /* NCONSFEED > 0 */
138}
139
140io_return_t
141console_feed_read(
142	dev_t		dev,
143	io_req_t 	ior)
144{
145	spl_t		s;
146	kern_return_t	rc;
147	int		count;
148
149	rc = device_read_alloc(ior, (vm_size_t) ior->io_count);
150	if (rc != KERN_SUCCESS)
151		return rc;
152
153	s = splhigh();
154	simple_lock(&cons_feed_lock);
155
156	ior->io_residual = ior->io_count;
157
158	count = q_to_b(&cons_feed_cb, (char *) ior->io_data, ior->io_count);
159	if (count == 0) {
160		if (ior->io_mode & D_NOWAIT) {
161			rc = D_WOULD_BLOCK;
162		}
163		if (cons_feed_queued == NULL) {
164			ior->io_done = cons_feed_read_done;
165			cons_feed_queued = ior;
166			rc = D_IO_QUEUED;
167		} else {
168			/* Can't queue multiple read requests yet */
169			rc = D_INVALID_OPERATION;
170		}
171		simple_unlock(&cons_feed_lock);
172		splx(s);
173		return rc;
174	}
175
176	simple_unlock(&cons_feed_lock);
177	splx(s);
178
179	ior->io_residual -= count;
180
181	iodone(ior);
182
183	if (ior->io_op & IO_SYNC) {
184		iowait(ior);
185	}
186
187	return D_SUCCESS;
188}
189
190/* Called when data is ready and there's a queued-up read waiting */
191boolean_t cons_feed_read_done(io_req_t ior)
192{
193	spl_t	s;
194	int	count;
195
196	s = splhigh();
197	simple_lock(&cons_feed_lock);
198
199	count = q_to_b(&cons_feed_cb, (char *) ior->io_data, ior->io_count);
200	if (count == 0) {
201		if (cons_feed_queued == NULL) {
202			ior->io_done = cons_feed_read_done;
203			cons_feed_queued = ior;
204		}
205		simple_unlock(&cons_feed_lock);
206		splx(s);
207		return FALSE;
208	}
209
210	simple_unlock(&cons_feed_lock);
211	splx(s);
212
213	ior->io_residual -= count;
214	ds_read_done(ior);
215
216	return TRUE;
217}
218
219/* This routine is called from putc() - it should return TRUE if
220 * the character should be passed on to a physical console, FALSE
221 * if the feed has intercepted the character. It may be called from
222 * under interrupt (even splhigh)
223 */
224
225boolean_t console_feed_putc(char c)
226{
227	spl_t 		s;
228	io_req_t	ior;
229	boolean_t	retval;
230
231#if	MACH_KDB
232	if (db_active) {
233		return TRUE;
234	}
235#endif	/* MACH_KDB */
236
237	retval=TRUE;	/* TRUE : character should be displayed now */
238	if (!cons_feed_count) {
239		return TRUE;
240	}
241	s = splhigh();
242	simple_lock(&cons_feed_lock);
243	if (!cons_feed_count) {
244		simple_unlock(&cons_feed_lock);
245		splx(s);
246		return TRUE;
247	}
248	/* queue up the data if we can */
249	if (!putc(c, &cons_feed_cb)) {
250		/* able to stock the character */
251		retval = FALSE;
252	}
253	if (cons_feed_queued != NULL) {
254		/* Queued up request - service it */
255		ior = cons_feed_queued;
256		cons_feed_queued = NULL;
257		simple_unlock(&cons_feed_lock);
258		splx(s);
259		iodone(ior);
260		retval=FALSE;
261	} else {
262		simple_unlock(&cons_feed_lock);
263		splx(s);
264	}
265	return retval;
266}
267