1/*
2** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3** Distributed under the terms of the Haiku 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 = %ld (name = %s)\n", index, name));
71
72	MutexLocker globalLocker(gGlobalTTYLock);
73
74	if (findUnusedTTY) {
75		for (index = 0; index < (int32)kNumTTYs; index++) {
76			if (gMasterTTYs[index].ref_count == 0)
77				break;
78		}
79		if (index >= (int32)kNumTTYs)
80			return ENOENT;
81	} else if (gMasterTTYs[index].ref_count > 0) {
82		// we're already open!
83		return B_BUSY;
84	}
85
86	status_t status = tty_open(&gMasterTTYs[index], &master_service);
87	if (status < B_OK) {
88		// initializing TTY failed
89		return status;
90	}
91
92	master_cookie *cookie;
93	status = create_master_cookie(cookie, &gMasterTTYs[index],
94		&gSlaveTTYs[index], flags);
95	if (status != B_OK) {
96		tty_close(&gMasterTTYs[index]);
97		return status;
98	}
99
100	add_tty_cookie(cookie);
101
102	*_cookie = cookie;
103
104	return B_OK;
105}
106
107
108static status_t
109master_close(void *_cookie)
110{
111	master_cookie *cookie = (master_cookie *)_cookie;
112
113	TRACE(("master_close: cookie %p\n", _cookie));
114
115	MutexLocker globalLocker(gGlobalTTYLock);
116
117	// close all connected slave cookies first
118	while (tty_cookie *slave = cookie->other_tty->cookies.Head())
119		tty_close_cookie(slave);
120
121	// close the client cookie
122	tty_close_cookie(cookie);
123
124	return B_OK;
125}
126
127
128static status_t
129master_free_cookie(void *_cookie)
130{
131	// The TTY is already closed. We only have to free the cookie.
132	master_cookie *cookie = (master_cookie *)_cookie;
133
134	MutexLocker globalLocker(gGlobalTTYLock);
135	uninit_tty_cookie(cookie);
136	globalLocker.Unlock();
137
138	free(cookie);
139
140	return B_OK;
141}
142
143
144static status_t
145master_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
146{
147	master_cookie *cookie = (master_cookie *)_cookie;
148
149	TRACE(("master_ioctl: cookie %p, op %lu, buffer %p, length %lu\n", _cookie, op, buffer, length));
150
151	return tty_ioctl(cookie, op, buffer, length);
152}
153
154
155static status_t
156master_read(void *_cookie, off_t offset, void *buffer, size_t *_length)
157{
158	master_cookie *cookie = (master_cookie *)_cookie;
159
160	TRACE(("master_read: cookie %p, offset %Ld, buffer %p, length %lu\n",
161		_cookie, offset, buffer, *_length));
162
163	status_t result = tty_input_read(cookie, buffer, _length);
164
165	TRACE(("master_read done: cookie %p, result: %lx, length %lu\n", _cookie,
166		result, *_length));
167
168	return result;
169}
170
171
172static status_t
173master_write(void *_cookie, off_t offset, const void *buffer, size_t *_length)
174{
175	master_cookie *cookie = (master_cookie *)_cookie;
176
177	TRACE(("master_write: cookie %p, offset %Ld, buffer %p, length %lu\n",
178		_cookie, offset, buffer, *_length));
179
180	status_t result = tty_write_to_tty_master(cookie, buffer, _length);
181
182	TRACE(("master_write done: cookie %p, result: %lx, length %lu\n", _cookie,
183		result, *_length));
184
185	return result;
186}
187
188
189static status_t
190master_select(void *_cookie, uint8 event, uint32 ref, selectsync *sync)
191{
192	master_cookie *cookie = (master_cookie *)_cookie;
193
194	return tty_select(cookie, event, ref, sync);
195}
196
197
198static status_t
199master_deselect(void *_cookie, uint8 event, selectsync *sync)
200{
201	master_cookie *cookie = (master_cookie *)_cookie;
202
203	return tty_deselect(cookie, event, sync);
204}
205
206
207device_hooks gMasterTTYHooks = {
208	&master_open,
209	&master_close,
210	&master_free_cookie,
211	&master_ioctl,
212	&master_read,
213	&master_write,
214	&master_select,
215	&master_deselect,
216	NULL,	// read_pages()
217	NULL	// write_pages()
218};
219