1/*
2** Copyright 2004, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
3** Distributed under the terms of the MIT License.
4*/
5
6
7#include "tty_private.h"
8
9#include <stdlib.h>
10#include <string.h>
11#include <util/AutoLock.h>
12
13
14//#define MASTER_TRACE
15#ifdef MASTER_TRACE
16#	define TRACE(x) dprintf x
17#else
18#	define TRACE(x) ;
19#endif
20
21
22struct master_cookie : tty_cookie {
23};
24
25
26struct tty gMasterTTYs[kNumTTYs];
27
28
29static status_t
30master_service(struct tty *tty, uint32 op)
31{
32	// nothing here yet
33	return B_OK;
34}
35
36
37static status_t
38create_master_cookie(master_cookie *&cookie, struct tty *master,
39	struct tty *slave, uint32 openMode)
40{
41	cookie = (master_cookie*)malloc(sizeof(struct master_cookie));
42	if (cookie == NULL)
43		return B_NO_MEMORY;
44
45	status_t error = init_tty_cookie(cookie, master, slave, openMode);
46	if (error != B_OK) {
47		free(cookie);
48		return error;
49	}
50
51	return B_OK;
52}
53
54
55//	#pragma mark -
56
57
58static status_t
59master_open(const char *name, uint32 flags, void **_cookie)
60{
61	bool findUnusedTTY = strcmp(name, "ptmx") == 0;
62
63	int32 index = -1;
64	if (!findUnusedTTY) {
65		index = get_tty_index(name);
66		if (index >= (int32)kNumTTYs)
67			return B_ERROR;
68	}
69
70	TRACE(("master_open: TTY index = %" B_PRId32 " (name = %s)\n", index,
71		name));
72
73	MutexLocker globalLocker(gGlobalTTYLock);
74
75	if (findUnusedTTY) {
76		for (index = 0; index < (int32)kNumTTYs; index++) {
77			if (gMasterTTYs[index].ref_count == 0)
78				break;
79		}
80		if (index >= (int32)kNumTTYs)
81			return ENOENT;
82	} else if (gMasterTTYs[index].ref_count > 0) {
83		// we're already open!
84		return B_BUSY;
85	}
86
87	gMasterTTYs[index].opened_count = 0;
88	gSlaveTTYs[index].opened_count = 0;
89	status_t status = tty_open(&gMasterTTYs[index], &master_service);
90	if (status < B_OK) {
91		// initializing TTY failed
92		return status;
93	}
94
95	master_cookie *cookie;
96	status = create_master_cookie(cookie, &gMasterTTYs[index],
97		&gSlaveTTYs[index], flags);
98	if (status != B_OK) {
99		tty_close(&gMasterTTYs[index]);
100		return status;
101	}
102
103	add_tty_cookie(cookie);
104
105	*_cookie = cookie;
106
107	return B_OK;
108}
109
110
111static status_t
112master_close(void *_cookie)
113{
114	master_cookie *cookie = (master_cookie *)_cookie;
115
116	TRACE(("master_close: cookie %p\n", _cookie));
117
118	MutexLocker globalLocker(gGlobalTTYLock);
119
120	// close all connected slave cookies first
121	while (tty_cookie *slave = cookie->other_tty->cookies.Head())
122		tty_close_cookie(slave);
123
124	// close the client cookie
125	tty_close_cookie(cookie);
126
127	return B_OK;
128}
129
130
131static status_t
132master_free_cookie(void *_cookie)
133{
134	// The TTY is already closed. We only have to free the cookie.
135	master_cookie *cookie = (master_cookie *)_cookie;
136
137	MutexLocker globalLocker(gGlobalTTYLock);
138	uninit_tty_cookie(cookie);
139	globalLocker.Unlock();
140
141	free(cookie);
142
143	return B_OK;
144}
145
146
147static status_t
148master_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
149{
150	master_cookie *cookie = (master_cookie *)_cookie;
151
152	TRACE(("master_ioctl: cookie %p, op %" B_PRIu32 ", buffer %p, length %lu"
153		"\n", _cookie, op, buffer, length));
154
155	return tty_ioctl(cookie, op, buffer, length);
156}
157
158
159static status_t
160master_read(void *_cookie, off_t offset, void *buffer, size_t *_length)
161{
162	master_cookie *cookie = (master_cookie *)_cookie;
163
164	TRACE(("master_read: cookie %p, offset %" B_PRIdOFF ", buffer %p, length "
165		"%lu\n", _cookie, offset, buffer, *_length));
166
167	status_t result = tty_input_read(cookie, buffer, _length);
168
169	TRACE(("master_read done: cookie %p, result: %" B_PRIx32 ", length %lu\n",
170		_cookie, result, *_length));
171
172	return result;
173}
174
175
176static status_t
177master_write(void *_cookie, off_t offset, const void *buffer, size_t *_length)
178{
179	master_cookie *cookie = (master_cookie *)_cookie;
180
181	TRACE(("master_write: cookie %p, offset %" B_PRIdOFF ", buffer %p, length "
182		"%lu\n", _cookie, offset, buffer, *_length));
183
184	status_t result = tty_write_to_tty_master(cookie, buffer, _length);
185
186	TRACE(("master_write done: cookie %p, result: %" B_PRIx32 ", length %lu\n",
187		_cookie, result, *_length));
188
189	return result;
190}
191
192
193static status_t
194master_select(void *_cookie, uint8 event, uint32 ref, selectsync *sync)
195{
196	master_cookie *cookie = (master_cookie *)_cookie;
197
198	return tty_select(cookie, event, ref, sync);
199}
200
201
202static status_t
203master_deselect(void *_cookie, uint8 event, selectsync *sync)
204{
205	master_cookie *cookie = (master_cookie *)_cookie;
206
207	return tty_deselect(cookie, event, sync);
208}
209
210
211device_hooks gMasterTTYHooks = {
212	&master_open,
213	&master_close,
214	&master_free_cookie,
215	&master_ioctl,
216	&master_read,
217	&master_write,
218	&master_select,
219	&master_deselect,
220	NULL,	// read_pages()
221	NULL	// write_pages()
222};
223