xref: /haiku/src/system/kernel/port.cpp (revision 5aee691ed843815dab1736dc5eb77d9cd20ea1ef)
1224aee3fSIngo Weinhold /*
2224aee3fSIngo Weinhold  * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de.
3224aee3fSIngo Weinhold  * Distributed under the terms of the MIT License.
4224aee3fSIngo Weinhold  *
5224aee3fSIngo Weinhold  * Copyright 2001, Mark-Jan Bastian. All rights reserved.
6224aee3fSIngo Weinhold  * Distributed under the terms of the NewOS License.
7224aee3fSIngo Weinhold  */
8224aee3fSIngo Weinhold 
9224aee3fSIngo Weinhold /*!	Ports for IPC */
10224aee3fSIngo Weinhold 
11224aee3fSIngo Weinhold 
12224aee3fSIngo Weinhold #include <OS.h>
13224aee3fSIngo Weinhold 
14224aee3fSIngo Weinhold #include <port.h>
15224aee3fSIngo Weinhold #include <kernel.h>
16224aee3fSIngo Weinhold #include <sem.h>
17224aee3fSIngo Weinhold #include <team.h>
18224aee3fSIngo Weinhold #include <util/list.h>
19224aee3fSIngo Weinhold #include <arch/int.h>
20224aee3fSIngo Weinhold #include <cbuf.h>
21224aee3fSIngo Weinhold #include <wait_for_objects.h>
22224aee3fSIngo Weinhold 
23224aee3fSIngo Weinhold #include <iovec.h>
24224aee3fSIngo Weinhold #include <string.h>
25224aee3fSIngo Weinhold #include <stdlib.h>
26224aee3fSIngo Weinhold #include <ctype.h>
27224aee3fSIngo Weinhold 
28224aee3fSIngo Weinhold 
29224aee3fSIngo Weinhold //#define TRACE_PORTS
30224aee3fSIngo Weinhold #ifdef TRACE_PORTS
31224aee3fSIngo Weinhold #	define TRACE(x) dprintf x
32224aee3fSIngo Weinhold #else
33224aee3fSIngo Weinhold #	define TRACE(x)
34224aee3fSIngo Weinhold #endif
35224aee3fSIngo Weinhold 
36224aee3fSIngo Weinhold 
37224aee3fSIngo Weinhold typedef struct port_msg {
38224aee3fSIngo Weinhold 	list_link	link;
39224aee3fSIngo Weinhold 	int32		code;
40224aee3fSIngo Weinhold 	cbuf		*buffer_chain;
41224aee3fSIngo Weinhold 	size_t		size;
42224aee3fSIngo Weinhold } port_msg;
43224aee3fSIngo Weinhold 
44224aee3fSIngo Weinhold struct port_entry {
45224aee3fSIngo Weinhold 	port_id 	id;
46224aee3fSIngo Weinhold 	team_id 	owner;
47224aee3fSIngo Weinhold 	int32 		capacity;
48224aee3fSIngo Weinhold 	spinlock	lock;
49224aee3fSIngo Weinhold 	const char	*name;
50224aee3fSIngo Weinhold 	sem_id		read_sem;
51224aee3fSIngo Weinhold 	sem_id		write_sem;
52224aee3fSIngo Weinhold 	int32		total_count;	// messages read from port since creation
53224aee3fSIngo Weinhold 	select_info	*select_infos;
54224aee3fSIngo Weinhold 	struct list	msg_queue;
55224aee3fSIngo Weinhold };
56224aee3fSIngo Weinhold 
57224aee3fSIngo Weinhold 
58224aee3fSIngo Weinhold #define MAX_QUEUE_LENGTH 4096
59224aee3fSIngo Weinhold #define PORT_MAX_MESSAGE_SIZE 65536
60224aee3fSIngo Weinhold 
61224aee3fSIngo Weinhold // sMaxPorts must be power of 2
62224aee3fSIngo Weinhold static int32 sMaxPorts = 4096;
63224aee3fSIngo Weinhold static int32 sUsedPorts = 0;
64224aee3fSIngo Weinhold 
65224aee3fSIngo Weinhold static struct port_entry *sPorts = NULL;
66224aee3fSIngo Weinhold static area_id sPortArea = 0;
67224aee3fSIngo Weinhold static bool sPortsActive = false;
68224aee3fSIngo Weinhold static port_id sNextPort = 1;
69224aee3fSIngo Weinhold static int32 sFirstFreeSlot = 1;
70224aee3fSIngo Weinhold 
71224aee3fSIngo Weinhold static spinlock sPortSpinlock = 0;
72224aee3fSIngo Weinhold 
73224aee3fSIngo Weinhold #define GRAB_PORT_LIST_LOCK() acquire_spinlock(&sPortSpinlock)
74224aee3fSIngo Weinhold #define RELEASE_PORT_LIST_LOCK() release_spinlock(&sPortSpinlock)
75224aee3fSIngo Weinhold #define GRAB_PORT_LOCK(s) acquire_spinlock(&(s).lock)
76224aee3fSIngo Weinhold #define RELEASE_PORT_LOCK(s) release_spinlock(&(s).lock)
77224aee3fSIngo Weinhold 
78224aee3fSIngo Weinhold 
79224aee3fSIngo Weinhold static int
80224aee3fSIngo Weinhold dump_port_list(int argc, char **argv)
81224aee3fSIngo Weinhold {
82224aee3fSIngo Weinhold 	const char *name = NULL;
83224aee3fSIngo Weinhold 	team_id owner = -1;
84224aee3fSIngo Weinhold 	int32 i;
85224aee3fSIngo Weinhold 
86224aee3fSIngo Weinhold 	if (argc > 2) {
87224aee3fSIngo Weinhold 		if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner"))
88224aee3fSIngo Weinhold 			owner = strtoul(argv[2], NULL, 0);
89224aee3fSIngo Weinhold 		else if (!strcmp(argv[1], "name"))
90224aee3fSIngo Weinhold 			name = argv[2];
91224aee3fSIngo Weinhold 	} else if (argc > 1)
92224aee3fSIngo Weinhold 		owner = strtoul(argv[1], NULL, 0);
93224aee3fSIngo Weinhold 
94224aee3fSIngo Weinhold 	kprintf("port           id  cap  r-sem  w-sem   team  name\n");
95224aee3fSIngo Weinhold 
96224aee3fSIngo Weinhold 	for (i = 0; i < sMaxPorts; i++) {
97224aee3fSIngo Weinhold 		struct port_entry *port = &sPorts[i];
98224aee3fSIngo Weinhold 		if (port->id < 0
99224aee3fSIngo Weinhold 			|| (owner != -1 && port->owner != owner)
100224aee3fSIngo Weinhold 			|| (name != NULL && strstr(port->name, name) == NULL))
101224aee3fSIngo Weinhold 			continue;
102224aee3fSIngo Weinhold 
103224aee3fSIngo Weinhold 		kprintf("%p %6ld %4ld %6ld %6ld %6ld  %s\n", port, port->id,
104224aee3fSIngo Weinhold 			port->capacity, port->read_sem, port->write_sem, port->owner,
105224aee3fSIngo Weinhold 			port->name);
106224aee3fSIngo Weinhold 	}
107224aee3fSIngo Weinhold 	return 0;
108224aee3fSIngo Weinhold }
109224aee3fSIngo Weinhold 
110224aee3fSIngo Weinhold 
111224aee3fSIngo Weinhold static void
112224aee3fSIngo Weinhold _dump_port_info(struct port_entry *port)
113224aee3fSIngo Weinhold {
114224aee3fSIngo Weinhold 	int32 count;
115224aee3fSIngo Weinhold 
116224aee3fSIngo Weinhold 	kprintf("PORT: %p\n", port);
117224aee3fSIngo Weinhold 	kprintf(" id:              %#lx\n", port->id);
118224aee3fSIngo Weinhold 	kprintf(" name:            \"%s\"\n", port->name);
119224aee3fSIngo Weinhold 	kprintf(" owner:           %#lx\n", port->owner);
120224aee3fSIngo Weinhold 	kprintf(" capacity:        %ld\n", port->capacity);
121224aee3fSIngo Weinhold 	kprintf(" read_sem:        %#lx\n", port->read_sem);
122224aee3fSIngo Weinhold 	kprintf(" write_sem:       %#lx\n", port->write_sem);
123224aee3fSIngo Weinhold  	get_sem_count(port->read_sem, &count);
124224aee3fSIngo Weinhold  	kprintf(" read_sem count:  %ld\n", count);
125224aee3fSIngo Weinhold  	get_sem_count(port->write_sem, &count);
126224aee3fSIngo Weinhold 	kprintf(" write_sem count: %ld\n", count);
127224aee3fSIngo Weinhold 	kprintf(" total count:     %ld\n", port->total_count);
128*5aee691eSIngo Weinhold 
129*5aee691eSIngo Weinhold 	set_debug_variable("_port", (addr_t)port);
130*5aee691eSIngo Weinhold 	set_debug_variable("_portID", port->id);
131*5aee691eSIngo Weinhold 	set_debug_variable("_owner", port->owner);
132*5aee691eSIngo Weinhold 	set_debug_variable("_readSem", port->read_sem);
133*5aee691eSIngo Weinhold 	set_debug_variable("_writeSem", port->write_sem);
134224aee3fSIngo Weinhold }
135224aee3fSIngo Weinhold 
136224aee3fSIngo Weinhold 
137224aee3fSIngo Weinhold static int
138224aee3fSIngo Weinhold dump_port_info(int argc, char **argv)
139224aee3fSIngo Weinhold {
140224aee3fSIngo Weinhold 	const char *name = NULL;
141224aee3fSIngo Weinhold 	sem_id sem = -1;
142224aee3fSIngo Weinhold 	int i;
143224aee3fSIngo Weinhold 
144224aee3fSIngo Weinhold 	if (argc < 2) {
145ce637fdaSIngo Weinhold 		print_debugger_command_usage(argv[0]);
146224aee3fSIngo Weinhold 		return 0;
147224aee3fSIngo Weinhold 	}
148224aee3fSIngo Weinhold 
149224aee3fSIngo Weinhold 	if (argc > 2) {
150224aee3fSIngo Weinhold 		if (!strcmp(argv[1], "address")) {
151224aee3fSIngo Weinhold 			_dump_port_info((struct port_entry *)strtoul(argv[2], NULL, 0));
152224aee3fSIngo Weinhold 			return 0;
153224aee3fSIngo Weinhold 		} else if (!strcmp(argv[1], "sem"))
154224aee3fSIngo Weinhold 			sem = strtoul(argv[2], NULL, 0);
155224aee3fSIngo Weinhold 		else if (!strcmp(argv[1], "name"))
156224aee3fSIngo Weinhold 			name = argv[2];
157224aee3fSIngo Weinhold 	} else if (isdigit(argv[1][0])) {
158224aee3fSIngo Weinhold 		// if the argument looks like a number, treat it as such
159224aee3fSIngo Weinhold 		uint32 num = strtoul(argv[1], NULL, 0);
160224aee3fSIngo Weinhold 		uint32 slot = num % sMaxPorts;
161224aee3fSIngo Weinhold 		if (sPorts[slot].id != (int)num) {
162224aee3fSIngo Weinhold 			kprintf("port %ld (%#lx) doesn't exist!\n", num, num);
163224aee3fSIngo Weinhold 			return 0;
164224aee3fSIngo Weinhold 		}
165224aee3fSIngo Weinhold 		_dump_port_info(&sPorts[slot]);
166224aee3fSIngo Weinhold 		return 0;
167224aee3fSIngo Weinhold 	} else
168224aee3fSIngo Weinhold 		name = argv[1];
169224aee3fSIngo Weinhold 
170224aee3fSIngo Weinhold 	// walk through the ports list, trying to match name
171224aee3fSIngo Weinhold 	for (i = 0; i < sMaxPorts; i++) {
172224aee3fSIngo Weinhold 		if ((name != NULL && sPorts[i].name != NULL
173224aee3fSIngo Weinhold 				&& !strcmp(name, sPorts[i].name))
174224aee3fSIngo Weinhold 			|| (sem != -1 && (sPorts[i].read_sem == sem
175224aee3fSIngo Weinhold 				|| sPorts[i].write_sem == sem))) {
176224aee3fSIngo Weinhold 			_dump_port_info(&sPorts[i]);
177224aee3fSIngo Weinhold 			return 0;
178224aee3fSIngo Weinhold 		}
179224aee3fSIngo Weinhold 	}
180224aee3fSIngo Weinhold 
181224aee3fSIngo Weinhold 	return 0;
182224aee3fSIngo Weinhold }
183224aee3fSIngo Weinhold 
184224aee3fSIngo Weinhold 
185224aee3fSIngo Weinhold static void
186224aee3fSIngo Weinhold notify_port_select_events(int slot, uint16 events)
187224aee3fSIngo Weinhold {
188224aee3fSIngo Weinhold 	if (sPorts[slot].select_infos)
189224aee3fSIngo Weinhold 		notify_select_events_list(sPorts[slot].select_infos, events);
190224aee3fSIngo Weinhold }
191224aee3fSIngo Weinhold 
192224aee3fSIngo Weinhold 
193224aee3fSIngo Weinhold static void
194224aee3fSIngo Weinhold put_port_msg(port_msg *msg)
195224aee3fSIngo Weinhold {
196224aee3fSIngo Weinhold 	cbuf_free_chain(msg->buffer_chain);
197224aee3fSIngo Weinhold 	free(msg);
198224aee3fSIngo Weinhold }
199224aee3fSIngo Weinhold 
200224aee3fSIngo Weinhold 
201224aee3fSIngo Weinhold static port_msg *
202224aee3fSIngo Weinhold get_port_msg(int32 code, size_t bufferSize)
203224aee3fSIngo Weinhold {
204224aee3fSIngo Weinhold 	// ToDo: investigate preallocation of port_msgs (or use a slab allocator)
205224aee3fSIngo Weinhold 	cbuf *bufferChain = NULL;
206224aee3fSIngo Weinhold 
207224aee3fSIngo Weinhold 	port_msg *msg = (port_msg *)malloc(sizeof(port_msg));
208224aee3fSIngo Weinhold 	if (msg == NULL)
209224aee3fSIngo Weinhold 		return NULL;
210224aee3fSIngo Weinhold 
211224aee3fSIngo Weinhold 	if (bufferSize > 0) {
212224aee3fSIngo Weinhold 		bufferChain = cbuf_get_chain(bufferSize);
213224aee3fSIngo Weinhold 		if (bufferChain == NULL) {
214224aee3fSIngo Weinhold 			free(msg);
215224aee3fSIngo Weinhold 			return NULL;
216224aee3fSIngo Weinhold 		}
217224aee3fSIngo Weinhold 	}
218224aee3fSIngo Weinhold 
219224aee3fSIngo Weinhold 	msg->code = code;
220224aee3fSIngo Weinhold 	msg->buffer_chain = bufferChain;
221224aee3fSIngo Weinhold 	msg->size = bufferSize;
222224aee3fSIngo Weinhold 	return msg;
223224aee3fSIngo Weinhold }
224224aee3fSIngo Weinhold 
225224aee3fSIngo Weinhold 
226224aee3fSIngo Weinhold /*!	You need to own the port's lock when calling this function */
227224aee3fSIngo Weinhold static bool
228224aee3fSIngo Weinhold is_port_closed(int32 slot)
229224aee3fSIngo Weinhold {
230224aee3fSIngo Weinhold 	return sPorts[slot].capacity == 0;
231224aee3fSIngo Weinhold }
232224aee3fSIngo Weinhold 
233224aee3fSIngo Weinhold 
234224aee3fSIngo Weinhold /*!	Fills the port_info structure with information from the specified
235224aee3fSIngo Weinhold 	port.
236224aee3fSIngo Weinhold 	The port lock must be held when called.
237224aee3fSIngo Weinhold */
238224aee3fSIngo Weinhold static void
239224aee3fSIngo Weinhold fill_port_info(struct port_entry *port, port_info *info, size_t size)
240224aee3fSIngo Weinhold {
241224aee3fSIngo Weinhold 	int32 count;
242224aee3fSIngo Weinhold 
243224aee3fSIngo Weinhold 	info->port = port->id;
244224aee3fSIngo Weinhold 	info->team = port->owner;
245224aee3fSIngo Weinhold 	info->capacity = port->capacity;
246224aee3fSIngo Weinhold 
247224aee3fSIngo Weinhold 	get_sem_count(port->read_sem, &count);
248224aee3fSIngo Weinhold 	if (count < 0)
249224aee3fSIngo Weinhold 		count = 0;
250224aee3fSIngo Weinhold 
251224aee3fSIngo Weinhold 	info->queue_count = count;
252224aee3fSIngo Weinhold 	info->total_count = port->total_count;
253224aee3fSIngo Weinhold 
254224aee3fSIngo Weinhold 	strlcpy(info->name, port->name, B_OS_NAME_LENGTH);
255224aee3fSIngo Weinhold }
256224aee3fSIngo Weinhold 
257224aee3fSIngo Weinhold 
258224aee3fSIngo Weinhold //	#pragma mark - private kernel API
259224aee3fSIngo Weinhold 
260224aee3fSIngo Weinhold 
261224aee3fSIngo Weinhold /*! This function cycles through the ports table, deleting all
262224aee3fSIngo Weinhold 	the ports that are owned by the passed team_id
263224aee3fSIngo Weinhold */
264224aee3fSIngo Weinhold int
265224aee3fSIngo Weinhold delete_owned_ports(team_id owner)
266224aee3fSIngo Weinhold {
267224aee3fSIngo Weinhold 	// ToDo: investigate maintaining a list of ports in the team
268224aee3fSIngo Weinhold 	//	to make this simpler and more efficient.
269224aee3fSIngo Weinhold 	cpu_status state;
270224aee3fSIngo Weinhold 	int i;
271224aee3fSIngo Weinhold 	int count = 0;
272224aee3fSIngo Weinhold 
273224aee3fSIngo Weinhold 	TRACE(("delete_owned_ports(owner = %ld)\n", owner));
274224aee3fSIngo Weinhold 
275224aee3fSIngo Weinhold 	if (!sPortsActive)
276224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
277224aee3fSIngo Weinhold 
278224aee3fSIngo Weinhold 	state = disable_interrupts();
279224aee3fSIngo Weinhold 	GRAB_PORT_LIST_LOCK();
280224aee3fSIngo Weinhold 
281224aee3fSIngo Weinhold 	for (i = 0; i < sMaxPorts; i++) {
282224aee3fSIngo Weinhold 		if (sPorts[i].id != -1 && sPorts[i].owner == owner) {
283224aee3fSIngo Weinhold 			port_id id = sPorts[i].id;
284224aee3fSIngo Weinhold 
285224aee3fSIngo Weinhold 			RELEASE_PORT_LIST_LOCK();
286224aee3fSIngo Weinhold 			restore_interrupts(state);
287224aee3fSIngo Weinhold 
288224aee3fSIngo Weinhold 			delete_port(id);
289224aee3fSIngo Weinhold 			count++;
290224aee3fSIngo Weinhold 
291224aee3fSIngo Weinhold 			state = disable_interrupts();
292224aee3fSIngo Weinhold 			GRAB_PORT_LIST_LOCK();
293224aee3fSIngo Weinhold 		}
294224aee3fSIngo Weinhold 	}
295224aee3fSIngo Weinhold 
296224aee3fSIngo Weinhold 	RELEASE_PORT_LIST_LOCK();
297224aee3fSIngo Weinhold 	restore_interrupts(state);
298224aee3fSIngo Weinhold 
299224aee3fSIngo Weinhold 	return count;
300224aee3fSIngo Weinhold }
301224aee3fSIngo Weinhold 
302224aee3fSIngo Weinhold 
303224aee3fSIngo Weinhold int32
304224aee3fSIngo Weinhold port_max_ports(void)
305224aee3fSIngo Weinhold {
306224aee3fSIngo Weinhold 	return sMaxPorts;
307224aee3fSIngo Weinhold }
308224aee3fSIngo Weinhold 
309224aee3fSIngo Weinhold 
310224aee3fSIngo Weinhold int32
311224aee3fSIngo Weinhold port_used_ports(void)
312224aee3fSIngo Weinhold {
313224aee3fSIngo Weinhold 	return sUsedPorts;
314224aee3fSIngo Weinhold }
315224aee3fSIngo Weinhold 
316224aee3fSIngo Weinhold 
317224aee3fSIngo Weinhold status_t
318224aee3fSIngo Weinhold port_init(kernel_args *args)
319224aee3fSIngo Weinhold {
320224aee3fSIngo Weinhold 	size_t size = sizeof(struct port_entry) * sMaxPorts;
321224aee3fSIngo Weinhold 	int32 i;
322224aee3fSIngo Weinhold 
323224aee3fSIngo Weinhold 	// create and initialize ports table
324224aee3fSIngo Weinhold 	sPortArea = create_area("port_table", (void **)&sPorts, B_ANY_KERNEL_ADDRESS,
325224aee3fSIngo Weinhold 		size, B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
326224aee3fSIngo Weinhold 	if (sPortArea < 0) {
327224aee3fSIngo Weinhold 		panic("unable to allocate kernel port table!\n");
328224aee3fSIngo Weinhold 		return sPortArea;
329224aee3fSIngo Weinhold 	}
330224aee3fSIngo Weinhold 
331224aee3fSIngo Weinhold 	// ToDo: investigate preallocating a list of port_msgs to
332224aee3fSIngo Weinhold 	//	speed up actual message sending/receiving, a slab allocator
333224aee3fSIngo Weinhold 	//	might do it as well, though :-)
334224aee3fSIngo Weinhold 
335224aee3fSIngo Weinhold 	memset(sPorts, 0, size);
336224aee3fSIngo Weinhold 	for (i = 0; i < sMaxPorts; i++)
337224aee3fSIngo Weinhold 		sPorts[i].id = -1;
338224aee3fSIngo Weinhold 
339224aee3fSIngo Weinhold 	// add debugger commands
340ce637fdaSIngo Weinhold 	add_debugger_command_etc("ports", &dump_port_list,
341ce637fdaSIngo Weinhold 		"Dump a list of all active ports (for team, with name, etc.)",
342ce637fdaSIngo Weinhold 		"[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]\n"
343ce637fdaSIngo Weinhold 		"Prints a list of all active ports meeting the given\n"
344ce637fdaSIngo Weinhold 		"requirement. If no argument is given, all ports are listed.\n"
345ce637fdaSIngo Weinhold 		"  <team>             - The team owning the ports.\n"
346ce637fdaSIngo Weinhold 		"  <name>             - Part of the name of the ports.\n", 0);
347ce637fdaSIngo Weinhold 	add_debugger_command_etc("port", &dump_port_info,
348ce637fdaSIngo Weinhold 		"Dump info about a particular port",
349ce637fdaSIngo Weinhold 		"([ \"address\" ] <address>) | ([ \"name\" ] <name>) "
350ce637fdaSIngo Weinhold 			"| (\"sem\" <sem>)\n"
351ce637fdaSIngo Weinhold 		"Prints info about the specified port.\n"
352ce637fdaSIngo Weinhold 		"  <address>  - Pointer to the port structure.\n"
353ce637fdaSIngo Weinhold 		"  <name>     - Name of the port.\n"
354ce637fdaSIngo Weinhold 		"  <sem>      - ID of the port's read or write semaphore.\n", 0);
355224aee3fSIngo Weinhold 
356224aee3fSIngo Weinhold 	sPortsActive = true;
357224aee3fSIngo Weinhold 	return B_OK;
358224aee3fSIngo Weinhold }
359224aee3fSIngo Weinhold 
360224aee3fSIngo Weinhold 
361224aee3fSIngo Weinhold //	#pragma mark - public kernel API
362224aee3fSIngo Weinhold 
363224aee3fSIngo Weinhold 
364224aee3fSIngo Weinhold port_id
365224aee3fSIngo Weinhold create_port(int32 queueLength, const char *name)
366224aee3fSIngo Weinhold {
367224aee3fSIngo Weinhold 	cpu_status state;
368224aee3fSIngo Weinhold 	char nameBuffer[B_OS_NAME_LENGTH];
369224aee3fSIngo Weinhold 	sem_id readSem, writeSem;
370224aee3fSIngo Weinhold 	status_t status;
371224aee3fSIngo Weinhold 	team_id	owner;
372224aee3fSIngo Weinhold 	int32 slot;
373224aee3fSIngo Weinhold 
374224aee3fSIngo Weinhold 	TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength, name));
375224aee3fSIngo Weinhold 
376224aee3fSIngo Weinhold 	if (!sPortsActive)
377224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
378224aee3fSIngo Weinhold 
379224aee3fSIngo Weinhold 	// check queue length
380224aee3fSIngo Weinhold 	if (queueLength < 1
381224aee3fSIngo Weinhold 		|| queueLength > MAX_QUEUE_LENGTH)
382224aee3fSIngo Weinhold 		return B_BAD_VALUE;
383224aee3fSIngo Weinhold 
384224aee3fSIngo Weinhold 	// check early on if there are any free port slots to use
385224aee3fSIngo Weinhold 	if (atomic_add(&sUsedPorts, 1) >= sMaxPorts) {
386224aee3fSIngo Weinhold 		status = B_NO_MORE_PORTS;
387224aee3fSIngo Weinhold 		goto err1;
388224aee3fSIngo Weinhold 	}
389224aee3fSIngo Weinhold 
390224aee3fSIngo Weinhold 	// check & dup name
391224aee3fSIngo Weinhold 	if (name == NULL)
392224aee3fSIngo Weinhold 		name = "unnamed port";
393224aee3fSIngo Weinhold 
394224aee3fSIngo Weinhold 	// ToDo: we could save the memory and use the semaphore name only instead
395224aee3fSIngo Weinhold 	strlcpy(nameBuffer, name, B_OS_NAME_LENGTH);
396224aee3fSIngo Weinhold 	name = strdup(nameBuffer);
397224aee3fSIngo Weinhold 	if (name == NULL) {
398224aee3fSIngo Weinhold 		status = B_NO_MEMORY;
399224aee3fSIngo Weinhold 		goto err1;
400224aee3fSIngo Weinhold 	}
401224aee3fSIngo Weinhold 
402224aee3fSIngo Weinhold 	// create read sem with owner set to -1
403224aee3fSIngo Weinhold 	// ToDo: should be B_SYSTEM_TEAM
404224aee3fSIngo Weinhold 	readSem = create_sem_etc(0, name, -1);
405224aee3fSIngo Weinhold 	if (readSem < B_OK) {
406224aee3fSIngo Weinhold 		status = readSem;
407224aee3fSIngo Weinhold 		goto err2;
408224aee3fSIngo Weinhold 	}
409224aee3fSIngo Weinhold 
410224aee3fSIngo Weinhold 	// create write sem
411224aee3fSIngo Weinhold 	writeSem = create_sem_etc(queueLength, name, -1);
412224aee3fSIngo Weinhold 	if (writeSem < B_OK) {
413224aee3fSIngo Weinhold 		status = writeSem;
414224aee3fSIngo Weinhold 		goto err3;
415224aee3fSIngo Weinhold 	}
416224aee3fSIngo Weinhold 
417224aee3fSIngo Weinhold 	owner = team_get_current_team_id();
418224aee3fSIngo Weinhold 
419224aee3fSIngo Weinhold 	state = disable_interrupts();
420224aee3fSIngo Weinhold 	GRAB_PORT_LIST_LOCK();
421224aee3fSIngo Weinhold 
422224aee3fSIngo Weinhold 	// find the first empty spot
423224aee3fSIngo Weinhold 	for (slot = 0; slot < sMaxPorts; slot++) {
424224aee3fSIngo Weinhold 		int32 i = (slot + sFirstFreeSlot) % sMaxPorts;
425224aee3fSIngo Weinhold 
426224aee3fSIngo Weinhold 		if (sPorts[i].id == -1) {
427224aee3fSIngo Weinhold 			port_id id;
428224aee3fSIngo Weinhold 
429224aee3fSIngo Weinhold 			// make the port_id be a multiple of the slot it's in
430224aee3fSIngo Weinhold 			if (i >= sNextPort % sMaxPorts)
431224aee3fSIngo Weinhold 				sNextPort += i - sNextPort % sMaxPorts;
432224aee3fSIngo Weinhold 			else
433224aee3fSIngo Weinhold 				sNextPort += sMaxPorts - (sNextPort % sMaxPorts - i);
434224aee3fSIngo Weinhold 			sFirstFreeSlot = slot + 1;
435224aee3fSIngo Weinhold 
436224aee3fSIngo Weinhold 			GRAB_PORT_LOCK(sPorts[i]);
437224aee3fSIngo Weinhold 			sPorts[i].id = sNextPort++;
438224aee3fSIngo Weinhold 			RELEASE_PORT_LIST_LOCK();
439224aee3fSIngo Weinhold 
440224aee3fSIngo Weinhold 			sPorts[i].capacity = queueLength;
441224aee3fSIngo Weinhold 			sPorts[i].owner = owner;
442224aee3fSIngo Weinhold 			sPorts[i].name = name;
443224aee3fSIngo Weinhold 
444224aee3fSIngo Weinhold 			sPorts[i].read_sem	= readSem;
445224aee3fSIngo Weinhold 			sPorts[i].write_sem	= writeSem;
446224aee3fSIngo Weinhold 
447224aee3fSIngo Weinhold 			list_init(&sPorts[i].msg_queue);
448224aee3fSIngo Weinhold 			sPorts[i].total_count = 0;
449224aee3fSIngo Weinhold 			sPorts[i].select_infos = NULL;
450224aee3fSIngo Weinhold 			id = sPorts[i].id;
451224aee3fSIngo Weinhold 
452224aee3fSIngo Weinhold 			RELEASE_PORT_LOCK(sPorts[i]);
453224aee3fSIngo Weinhold 			restore_interrupts(state);
454224aee3fSIngo Weinhold 
455224aee3fSIngo Weinhold 			TRACE(("create_port() done: port created %ld\n", id));
456224aee3fSIngo Weinhold 
457224aee3fSIngo Weinhold 			return id;
458224aee3fSIngo Weinhold 		}
459224aee3fSIngo Weinhold 	}
460224aee3fSIngo Weinhold 
461224aee3fSIngo Weinhold 	// not enough ports...
462224aee3fSIngo Weinhold 
463224aee3fSIngo Weinhold 	// ToDo: due to sUsedPorts, this cannot happen anymore - as
464224aee3fSIngo Weinhold 	//		long as sMaxPorts stays constant over the kernel run
465224aee3fSIngo Weinhold 	//		time (which it should be). IOW we could simply panic()
466224aee3fSIngo Weinhold 	//		here.
467224aee3fSIngo Weinhold 
468224aee3fSIngo Weinhold 	RELEASE_PORT_LIST_LOCK();
469224aee3fSIngo Weinhold 	restore_interrupts(state);
470224aee3fSIngo Weinhold 
471224aee3fSIngo Weinhold 	status = B_NO_MORE_PORTS;
472224aee3fSIngo Weinhold 
473224aee3fSIngo Weinhold 	delete_sem(writeSem);
474224aee3fSIngo Weinhold err3:
475224aee3fSIngo Weinhold 	delete_sem(readSem);
476224aee3fSIngo Weinhold err2:
477224aee3fSIngo Weinhold 	free((char *)name);
478224aee3fSIngo Weinhold err1:
479224aee3fSIngo Weinhold 	atomic_add(&sUsedPorts, -1);
480224aee3fSIngo Weinhold 
481224aee3fSIngo Weinhold 	return status;
482224aee3fSIngo Weinhold }
483224aee3fSIngo Weinhold 
484224aee3fSIngo Weinhold 
485224aee3fSIngo Weinhold status_t
486224aee3fSIngo Weinhold close_port(port_id id)
487224aee3fSIngo Weinhold {
488224aee3fSIngo Weinhold 	sem_id readSem, writeSem;
489224aee3fSIngo Weinhold 	cpu_status state;
490224aee3fSIngo Weinhold 	int32 slot;
491224aee3fSIngo Weinhold 
492224aee3fSIngo Weinhold 	TRACE(("close_port(id = %ld)\n", id));
493224aee3fSIngo Weinhold 
494224aee3fSIngo Weinhold 	if (!sPortsActive || id < 0)
495224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
496224aee3fSIngo Weinhold 
497224aee3fSIngo Weinhold 	slot = id % sMaxPorts;
498224aee3fSIngo Weinhold 
499224aee3fSIngo Weinhold 	// walk through the sem list, trying to match name
500224aee3fSIngo Weinhold 	state = disable_interrupts();
501224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
502224aee3fSIngo Weinhold 
503224aee3fSIngo Weinhold 	if (sPorts[slot].id != id) {
504224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
505224aee3fSIngo Weinhold 		restore_interrupts(state);
506224aee3fSIngo Weinhold 		TRACE(("close_port: invalid port_id %ld\n", id));
507224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
508224aee3fSIngo Weinhold 	}
509224aee3fSIngo Weinhold 
510224aee3fSIngo Weinhold 	// mark port to disable writing - deleting the semaphores will
511224aee3fSIngo Weinhold 	// wake up waiting read/writes
512224aee3fSIngo Weinhold 	sPorts[slot].capacity = 0;
513224aee3fSIngo Weinhold 	readSem = sPorts[slot].read_sem;
514224aee3fSIngo Weinhold 	writeSem = sPorts[slot].write_sem;
515224aee3fSIngo Weinhold 
516224aee3fSIngo Weinhold 	notify_port_select_events(slot, B_EVENT_INVALID);
517224aee3fSIngo Weinhold 	sPorts[slot].select_infos = NULL;
518224aee3fSIngo Weinhold 
519224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
520224aee3fSIngo Weinhold 	restore_interrupts(state);
521224aee3fSIngo Weinhold 
522224aee3fSIngo Weinhold 	delete_sem(readSem);
523224aee3fSIngo Weinhold 	delete_sem(writeSem);
524224aee3fSIngo Weinhold 
525224aee3fSIngo Weinhold 	return B_NO_ERROR;
526224aee3fSIngo Weinhold }
527224aee3fSIngo Weinhold 
528224aee3fSIngo Weinhold 
529224aee3fSIngo Weinhold status_t
530224aee3fSIngo Weinhold delete_port(port_id id)
531224aee3fSIngo Weinhold {
532224aee3fSIngo Weinhold 	cpu_status state;
533224aee3fSIngo Weinhold 	sem_id readSem, writeSem;
534224aee3fSIngo Weinhold 	const char *name;
535224aee3fSIngo Weinhold 	struct list list;
536224aee3fSIngo Weinhold 	port_msg *msg;
537224aee3fSIngo Weinhold 	int32 slot;
538224aee3fSIngo Weinhold 
539224aee3fSIngo Weinhold 	TRACE(("delete_port(id = %ld)\n", id));
540224aee3fSIngo Weinhold 
541224aee3fSIngo Weinhold 	if (!sPortsActive || id < 0)
542224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
543224aee3fSIngo Weinhold 
544224aee3fSIngo Weinhold 	slot = id % sMaxPorts;
545224aee3fSIngo Weinhold 
546224aee3fSIngo Weinhold 	state = disable_interrupts();
547224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
548224aee3fSIngo Weinhold 
549224aee3fSIngo Weinhold 	if (sPorts[slot].id != id) {
550224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
551224aee3fSIngo Weinhold 		restore_interrupts(state);
552224aee3fSIngo Weinhold 
553224aee3fSIngo Weinhold 		TRACE(("delete_port: invalid port_id %ld\n", id));
554224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
555224aee3fSIngo Weinhold 	}
556224aee3fSIngo Weinhold 
557224aee3fSIngo Weinhold 	/* mark port as invalid */
558224aee3fSIngo Weinhold 	sPorts[slot].id	= -1;
559224aee3fSIngo Weinhold 	name = sPorts[slot].name;
560224aee3fSIngo Weinhold 	readSem = sPorts[slot].read_sem;
561224aee3fSIngo Weinhold 	writeSem = sPorts[slot].write_sem;
562224aee3fSIngo Weinhold 	sPorts[slot].name = NULL;
563224aee3fSIngo Weinhold 	list_move_to_list(&sPorts[slot].msg_queue, &list);
564224aee3fSIngo Weinhold 
565224aee3fSIngo Weinhold 	notify_port_select_events(slot, B_EVENT_INVALID);
566224aee3fSIngo Weinhold 	sPorts[slot].select_infos = NULL;
567224aee3fSIngo Weinhold 
568224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
569224aee3fSIngo Weinhold 
570224aee3fSIngo Weinhold 	// update the first free slot hint in the array
571224aee3fSIngo Weinhold 	GRAB_PORT_LIST_LOCK();
572224aee3fSIngo Weinhold 	if (slot < sFirstFreeSlot)
573224aee3fSIngo Weinhold 		sFirstFreeSlot = slot;
574224aee3fSIngo Weinhold 	RELEASE_PORT_LIST_LOCK();
575224aee3fSIngo Weinhold 
576224aee3fSIngo Weinhold 	restore_interrupts(state);
577224aee3fSIngo Weinhold 
578224aee3fSIngo Weinhold 	atomic_add(&sUsedPorts, -1);
579224aee3fSIngo Weinhold 
580224aee3fSIngo Weinhold 	// free the queue
581224aee3fSIngo Weinhold 	while ((msg = (port_msg *)list_remove_head_item(&list)) != NULL) {
582224aee3fSIngo Weinhold 		put_port_msg(msg);
583224aee3fSIngo Weinhold 	}
584224aee3fSIngo Weinhold 
585224aee3fSIngo Weinhold 	free((char *)name);
586224aee3fSIngo Weinhold 
587224aee3fSIngo Weinhold 	// release the threads that were blocking on this port by deleting the sem
588224aee3fSIngo Weinhold 	// read_port() will see the B_BAD_SEM_ID acq_sem() return value, and act accordingly
589224aee3fSIngo Weinhold 	delete_sem(readSem);
590224aee3fSIngo Weinhold 	delete_sem(writeSem);
591224aee3fSIngo Weinhold 
592224aee3fSIngo Weinhold 	return B_OK;
593224aee3fSIngo Weinhold }
594224aee3fSIngo Weinhold 
595224aee3fSIngo Weinhold 
596224aee3fSIngo Weinhold status_t
597224aee3fSIngo Weinhold select_port(int32 id, struct select_info *info, bool kernel)
598224aee3fSIngo Weinhold {
599224aee3fSIngo Weinhold 	cpu_status state;
600224aee3fSIngo Weinhold 	int32 slot;
601224aee3fSIngo Weinhold 	status_t error = B_OK;
602224aee3fSIngo Weinhold 
603224aee3fSIngo Weinhold 	if (id < 0)
604224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
605224aee3fSIngo Weinhold 
606224aee3fSIngo Weinhold 	slot = id % sMaxPorts;
607224aee3fSIngo Weinhold 
608224aee3fSIngo Weinhold 	state = disable_interrupts();
609224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
610224aee3fSIngo Weinhold 
611224aee3fSIngo Weinhold 	if (sPorts[slot].id != id || is_port_closed(slot)) {
612224aee3fSIngo Weinhold 		// bad port ID
613224aee3fSIngo Weinhold 		error = B_BAD_SEM_ID;
614224aee3fSIngo Weinhold 	} else if (!kernel && sPorts[slot].owner == team_get_kernel_team_id()) {
615224aee3fSIngo Weinhold 		// kernel port, but call from userland
616224aee3fSIngo Weinhold 		error = B_NOT_ALLOWED;
617224aee3fSIngo Weinhold 	} else {
618224aee3fSIngo Weinhold 		info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID;
619224aee3fSIngo Weinhold 
620224aee3fSIngo Weinhold 		if (info->selected_events != 0) {
621224aee3fSIngo Weinhold 			uint16 events = 0;
622224aee3fSIngo Weinhold 			int32 writeCount = 0;
623224aee3fSIngo Weinhold 
624224aee3fSIngo Weinhold 			info->next = sPorts[slot].select_infos;
625224aee3fSIngo Weinhold 			sPorts[slot].select_infos = info;
626224aee3fSIngo Weinhold 
627224aee3fSIngo Weinhold 			// check for events
628224aee3fSIngo Weinhold 			if ((info->selected_events & B_EVENT_READ) != 0
629224aee3fSIngo Weinhold 				&& !list_is_empty(&sPorts[slot].msg_queue)) {
630224aee3fSIngo Weinhold 				events |= B_EVENT_READ;
631224aee3fSIngo Weinhold 			}
632224aee3fSIngo Weinhold 
633224aee3fSIngo Weinhold 			if (get_sem_count(sPorts[slot].write_sem, &writeCount) == B_OK
634224aee3fSIngo Weinhold 				&& writeCount > 0) {
635224aee3fSIngo Weinhold 				events |= B_EVENT_WRITE;
636224aee3fSIngo Weinhold 			}
637224aee3fSIngo Weinhold 
638224aee3fSIngo Weinhold 			if (events != 0)
639224aee3fSIngo Weinhold 				notify_select_events(info, events);
640224aee3fSIngo Weinhold 		}
641224aee3fSIngo Weinhold 	}
642224aee3fSIngo Weinhold 
643224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
644224aee3fSIngo Weinhold 	restore_interrupts(state);
645224aee3fSIngo Weinhold 
646224aee3fSIngo Weinhold 	return error;
647224aee3fSIngo Weinhold }
648224aee3fSIngo Weinhold 
649224aee3fSIngo Weinhold 
650224aee3fSIngo Weinhold status_t
651224aee3fSIngo Weinhold deselect_port(int32 id, struct select_info *info, bool kernel)
652224aee3fSIngo Weinhold {
653224aee3fSIngo Weinhold 	cpu_status state;
654224aee3fSIngo Weinhold 	int32 slot;
655224aee3fSIngo Weinhold 
656224aee3fSIngo Weinhold 	if (id < 0)
657224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
658224aee3fSIngo Weinhold 
659224aee3fSIngo Weinhold 	if (info->selected_events == 0)
660224aee3fSIngo Weinhold 		return B_OK;
661224aee3fSIngo Weinhold 
662224aee3fSIngo Weinhold 	slot = id % sMaxPorts;
663224aee3fSIngo Weinhold 
664224aee3fSIngo Weinhold 	state = disable_interrupts();
665224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
666224aee3fSIngo Weinhold 
667224aee3fSIngo Weinhold 	if (sPorts[slot].id == id) {
668224aee3fSIngo Weinhold 		select_info** infoLocation = &sPorts[slot].select_infos;
669224aee3fSIngo Weinhold 		while (*infoLocation != NULL && *infoLocation != info)
670224aee3fSIngo Weinhold 			infoLocation = &(*infoLocation)->next;
671224aee3fSIngo Weinhold 
672224aee3fSIngo Weinhold 		if (*infoLocation == info)
673224aee3fSIngo Weinhold 			*infoLocation = info->next;
674224aee3fSIngo Weinhold 	}
675224aee3fSIngo Weinhold 
676224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
677224aee3fSIngo Weinhold 	restore_interrupts(state);
678224aee3fSIngo Weinhold 
679224aee3fSIngo Weinhold 	return B_OK;
680224aee3fSIngo Weinhold }
681224aee3fSIngo Weinhold 
682224aee3fSIngo Weinhold 
683224aee3fSIngo Weinhold port_id
684224aee3fSIngo Weinhold find_port(const char *name)
685224aee3fSIngo Weinhold {
686224aee3fSIngo Weinhold 	port_id portFound = B_NAME_NOT_FOUND;
687224aee3fSIngo Weinhold 	cpu_status state;
688224aee3fSIngo Weinhold 	int32 i;
689224aee3fSIngo Weinhold 
690224aee3fSIngo Weinhold 	TRACE(("find_port(name = \"%s\")\n", name));
691224aee3fSIngo Weinhold 
692224aee3fSIngo Weinhold 	if (!sPortsActive)
693224aee3fSIngo Weinhold 		return B_NAME_NOT_FOUND;
694224aee3fSIngo Weinhold 	if (name == NULL)
695224aee3fSIngo Weinhold 		return B_BAD_VALUE;
696224aee3fSIngo Weinhold 
697224aee3fSIngo Weinhold 	// Since we have to check every single port, and we don't
698224aee3fSIngo Weinhold 	// care if it goes away at any point, we're only grabbing
699224aee3fSIngo Weinhold 	// the port lock in question, not the port list lock
700224aee3fSIngo Weinhold 
701224aee3fSIngo Weinhold 	// loop over list
702224aee3fSIngo Weinhold 	for (i = 0; i < sMaxPorts && portFound < B_OK; i++) {
703224aee3fSIngo Weinhold 		// lock every individual port before comparing
704224aee3fSIngo Weinhold 		state = disable_interrupts();
705224aee3fSIngo Weinhold 		GRAB_PORT_LOCK(sPorts[i]);
706224aee3fSIngo Weinhold 
707224aee3fSIngo Weinhold 		if (sPorts[i].id >= 0 && !strcmp(name, sPorts[i].name))
708224aee3fSIngo Weinhold 			portFound = sPorts[i].id;
709224aee3fSIngo Weinhold 
710224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[i]);
711224aee3fSIngo Weinhold 		restore_interrupts(state);
712224aee3fSIngo Weinhold 	}
713224aee3fSIngo Weinhold 
714224aee3fSIngo Weinhold 	return portFound;
715224aee3fSIngo Weinhold }
716224aee3fSIngo Weinhold 
717224aee3fSIngo Weinhold 
718224aee3fSIngo Weinhold status_t
719224aee3fSIngo Weinhold _get_port_info(port_id id, port_info *info, size_t size)
720224aee3fSIngo Weinhold {
721224aee3fSIngo Weinhold 	cpu_status state;
722224aee3fSIngo Weinhold 	int slot;
723224aee3fSIngo Weinhold 
724224aee3fSIngo Weinhold 	TRACE(("get_port_info(id = %ld)\n", id));
725224aee3fSIngo Weinhold 
726224aee3fSIngo Weinhold 	if (info == NULL || size != sizeof(port_info))
727224aee3fSIngo Weinhold 		return B_BAD_VALUE;
728224aee3fSIngo Weinhold 	if (!sPortsActive || id < 0)
729224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
730224aee3fSIngo Weinhold 
731224aee3fSIngo Weinhold 	slot = id % sMaxPorts;
732224aee3fSIngo Weinhold 
733224aee3fSIngo Weinhold 	state = disable_interrupts();
734224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
735224aee3fSIngo Weinhold 
736224aee3fSIngo Weinhold 	if (sPorts[slot].id != id || sPorts[slot].capacity == 0) {
737224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
738224aee3fSIngo Weinhold 		restore_interrupts(state);
739224aee3fSIngo Weinhold 		TRACE(("get_port_info: invalid port_id %ld\n", id));
740224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
741224aee3fSIngo Weinhold 	}
742224aee3fSIngo Weinhold 
743224aee3fSIngo Weinhold 	// fill a port_info struct with info
744224aee3fSIngo Weinhold 	fill_port_info(&sPorts[slot], info, size);
745224aee3fSIngo Weinhold 
746224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
747224aee3fSIngo Weinhold 	restore_interrupts(state);
748224aee3fSIngo Weinhold 
749224aee3fSIngo Weinhold 	return B_OK;
750224aee3fSIngo Weinhold }
751224aee3fSIngo Weinhold 
752224aee3fSIngo Weinhold 
753224aee3fSIngo Weinhold status_t
754224aee3fSIngo Weinhold _get_next_port_info(team_id team, int32 *_cookie, struct port_info *info, size_t size)
755224aee3fSIngo Weinhold {
756224aee3fSIngo Weinhold 	cpu_status state;
757224aee3fSIngo Weinhold 	int slot;
758224aee3fSIngo Weinhold 
759224aee3fSIngo Weinhold 	TRACE(("get_next_port_info(team = %ld)\n", team));
760224aee3fSIngo Weinhold 
761224aee3fSIngo Weinhold 	if (info == NULL || size != sizeof(port_info) || _cookie == NULL || team < B_OK)
762224aee3fSIngo Weinhold 		return B_BAD_VALUE;
763224aee3fSIngo Weinhold 	if (!sPortsActive)
764224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
765224aee3fSIngo Weinhold 
766224aee3fSIngo Weinhold 	slot = *_cookie;
767224aee3fSIngo Weinhold 	if (slot >= sMaxPorts)
768224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
769224aee3fSIngo Weinhold 
770224aee3fSIngo Weinhold 	if (team == B_CURRENT_TEAM)
771224aee3fSIngo Weinhold 		team = team_get_current_team_id();
772224aee3fSIngo Weinhold 
773224aee3fSIngo Weinhold 	info->port = -1; // used as found flag
774224aee3fSIngo Weinhold 
775224aee3fSIngo Weinhold 	// spinlock
776224aee3fSIngo Weinhold 	state = disable_interrupts();
777224aee3fSIngo Weinhold 	GRAB_PORT_LIST_LOCK();
778224aee3fSIngo Weinhold 
779224aee3fSIngo Weinhold 	while (slot < sMaxPorts) {
780224aee3fSIngo Weinhold 		GRAB_PORT_LOCK(sPorts[slot]);
781224aee3fSIngo Weinhold 		if (sPorts[slot].id != -1 && sPorts[slot].capacity != 0 && sPorts[slot].owner == team) {
782224aee3fSIngo Weinhold 			// found one!
783224aee3fSIngo Weinhold 			fill_port_info(&sPorts[slot], info, size);
784224aee3fSIngo Weinhold 
785224aee3fSIngo Weinhold 			RELEASE_PORT_LOCK(sPorts[slot]);
786224aee3fSIngo Weinhold 			slot++;
787224aee3fSIngo Weinhold 			break;
788224aee3fSIngo Weinhold 		}
789224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
790224aee3fSIngo Weinhold 		slot++;
791224aee3fSIngo Weinhold 	}
792224aee3fSIngo Weinhold 	RELEASE_PORT_LIST_LOCK();
793224aee3fSIngo Weinhold 	restore_interrupts(state);
794224aee3fSIngo Weinhold 
795224aee3fSIngo Weinhold 	if (info->port == -1)
796224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
797224aee3fSIngo Weinhold 
798224aee3fSIngo Weinhold 	*_cookie = slot;
799224aee3fSIngo Weinhold 	return B_NO_ERROR;
800224aee3fSIngo Weinhold }
801224aee3fSIngo Weinhold 
802224aee3fSIngo Weinhold 
803224aee3fSIngo Weinhold ssize_t
804224aee3fSIngo Weinhold port_buffer_size(port_id id)
805224aee3fSIngo Weinhold {
806224aee3fSIngo Weinhold 	return port_buffer_size_etc(id, 0, 0);
807224aee3fSIngo Weinhold }
808224aee3fSIngo Weinhold 
809224aee3fSIngo Weinhold 
810224aee3fSIngo Weinhold ssize_t
811224aee3fSIngo Weinhold port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout)
812224aee3fSIngo Weinhold {
813224aee3fSIngo Weinhold 	cpu_status state;
814224aee3fSIngo Weinhold 	sem_id cachedSem;
815224aee3fSIngo Weinhold 	status_t status;
816224aee3fSIngo Weinhold 	port_msg *msg;
817224aee3fSIngo Weinhold 	ssize_t size;
818224aee3fSIngo Weinhold 	int32 slot;
819224aee3fSIngo Weinhold 
820224aee3fSIngo Weinhold 	if (!sPortsActive || id < 0)
821224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
822224aee3fSIngo Weinhold 
823224aee3fSIngo Weinhold 	slot = id % sMaxPorts;
824224aee3fSIngo Weinhold 
825224aee3fSIngo Weinhold 	state = disable_interrupts();
826224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
827224aee3fSIngo Weinhold 
828224aee3fSIngo Weinhold 	if (sPorts[slot].id != id
829224aee3fSIngo Weinhold 		|| (is_port_closed(slot) && list_is_empty(&sPorts[slot].msg_queue))) {
830224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
831224aee3fSIngo Weinhold 		restore_interrupts(state);
832224aee3fSIngo Weinhold 		TRACE(("port_buffer_size_etc(): %s port %ld\n",
833224aee3fSIngo Weinhold 			sPorts[slot].id == id ? "closed" : "invalid", id));
834224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
835224aee3fSIngo Weinhold 	}
836224aee3fSIngo Weinhold 
837224aee3fSIngo Weinhold 	cachedSem = sPorts[slot].read_sem;
838224aee3fSIngo Weinhold 
839224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
840224aee3fSIngo Weinhold 	restore_interrupts(state);
841224aee3fSIngo Weinhold 
842224aee3fSIngo Weinhold 	// block if no message, or, if B_TIMEOUT flag set, block with timeout
843224aee3fSIngo Weinhold 
844224aee3fSIngo Weinhold 	status = acquire_sem_etc(cachedSem, 1, flags, timeout);
845224aee3fSIngo Weinhold 	if (status != B_OK && status != B_BAD_SEM_ID)
846224aee3fSIngo Weinhold 		return status;
847224aee3fSIngo Weinhold 
848224aee3fSIngo Weinhold 	// in case of B_BAD_SEM_ID, the port might have been closed but not yet
849224aee3fSIngo Weinhold 	// deleted, ie. there could still be messages waiting for us
850224aee3fSIngo Weinhold 
851224aee3fSIngo Weinhold 	state = disable_interrupts();
852224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
853224aee3fSIngo Weinhold 
854224aee3fSIngo Weinhold 	if (sPorts[slot].id != id) {
855224aee3fSIngo Weinhold 		// the port is no longer there
856224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
857224aee3fSIngo Weinhold 		restore_interrupts(state);
858224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
859224aee3fSIngo Weinhold 	}
860224aee3fSIngo Weinhold 
861224aee3fSIngo Weinhold 	// determine tail & get the length of the message
862224aee3fSIngo Weinhold 	msg = (port_msg*)list_get_first_item(&sPorts[slot].msg_queue);
863224aee3fSIngo Weinhold 	if (msg == NULL) {
864224aee3fSIngo Weinhold 		if (status == B_OK)
865224aee3fSIngo Weinhold 			panic("port %ld: no messages found\n", sPorts[slot].id);
866224aee3fSIngo Weinhold 
867224aee3fSIngo Weinhold 		size = B_BAD_PORT_ID;
868224aee3fSIngo Weinhold 	} else
869224aee3fSIngo Weinhold 		size = msg->size;
870224aee3fSIngo Weinhold 
871224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
872224aee3fSIngo Weinhold 	restore_interrupts(state);
873224aee3fSIngo Weinhold 
874224aee3fSIngo Weinhold 	// restore read_sem, as we haven't read from the port
875224aee3fSIngo Weinhold 	release_sem(cachedSem);
876224aee3fSIngo Weinhold 
877224aee3fSIngo Weinhold 	// return length of item at end of queue
878224aee3fSIngo Weinhold 	return size;
879224aee3fSIngo Weinhold }
880224aee3fSIngo Weinhold 
881224aee3fSIngo Weinhold 
882224aee3fSIngo Weinhold ssize_t
883224aee3fSIngo Weinhold port_count(port_id id)
884224aee3fSIngo Weinhold {
885224aee3fSIngo Weinhold 	cpu_status state;
886224aee3fSIngo Weinhold 	int32 count = 0;
887224aee3fSIngo Weinhold 	int32 slot;
888224aee3fSIngo Weinhold 
889224aee3fSIngo Weinhold 	if (!sPortsActive || id < 0)
890224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
891224aee3fSIngo Weinhold 
892224aee3fSIngo Weinhold 	slot = id % sMaxPorts;
893224aee3fSIngo Weinhold 
894224aee3fSIngo Weinhold 	state = disable_interrupts();
895224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
896224aee3fSIngo Weinhold 
897224aee3fSIngo Weinhold 	if (sPorts[slot].id != id) {
898224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
899224aee3fSIngo Weinhold 		restore_interrupts(state);
900224aee3fSIngo Weinhold 		TRACE(("port_count: invalid port_id %ld\n", id));
901224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
902224aee3fSIngo Weinhold 	}
903224aee3fSIngo Weinhold 
904224aee3fSIngo Weinhold 	if (get_sem_count(sPorts[slot].read_sem, &count) == B_OK) {
905224aee3fSIngo Weinhold 		// do not return negative numbers
906224aee3fSIngo Weinhold 		if (count < 0)
907224aee3fSIngo Weinhold 			count = 0;
908224aee3fSIngo Weinhold 	} else {
909224aee3fSIngo Weinhold 		// the port might have been closed - we need to actually count the messages
910224aee3fSIngo Weinhold 		void *message = NULL;
911224aee3fSIngo Weinhold 		while ((message = list_get_next_item(&sPorts[slot].msg_queue, message)) != NULL) {
912224aee3fSIngo Weinhold 			count++;
913224aee3fSIngo Weinhold 		}
914224aee3fSIngo Weinhold 	}
915224aee3fSIngo Weinhold 
916224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
917224aee3fSIngo Weinhold 	restore_interrupts(state);
918224aee3fSIngo Weinhold 
919224aee3fSIngo Weinhold 	// return count of messages
920224aee3fSIngo Weinhold 	return count;
921224aee3fSIngo Weinhold }
922224aee3fSIngo Weinhold 
923224aee3fSIngo Weinhold 
924224aee3fSIngo Weinhold ssize_t
925224aee3fSIngo Weinhold read_port(port_id port, int32 *msgCode, void *msgBuffer, size_t bufferSize)
926224aee3fSIngo Weinhold {
927224aee3fSIngo Weinhold 	return read_port_etc(port, msgCode, msgBuffer, bufferSize, 0, 0);
928224aee3fSIngo Weinhold }
929224aee3fSIngo Weinhold 
930224aee3fSIngo Weinhold 
931224aee3fSIngo Weinhold ssize_t
932224aee3fSIngo Weinhold read_port_etc(port_id id, int32 *_msgCode, void *msgBuffer, size_t bufferSize,
933224aee3fSIngo Weinhold 	uint32 flags, bigtime_t timeout)
934224aee3fSIngo Weinhold {
935224aee3fSIngo Weinhold 	cpu_status state;
936224aee3fSIngo Weinhold 	sem_id cachedSem;
937224aee3fSIngo Weinhold 	status_t status;
938224aee3fSIngo Weinhold 	bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0;
939224aee3fSIngo Weinhold 	port_msg *msg;
940224aee3fSIngo Weinhold 	size_t size;
941224aee3fSIngo Weinhold 	int slot;
942224aee3fSIngo Weinhold 
943224aee3fSIngo Weinhold 	if (!sPortsActive || id < 0)
944224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
945224aee3fSIngo Weinhold 
946224aee3fSIngo Weinhold 	if ((msgBuffer == NULL && bufferSize > 0)
947224aee3fSIngo Weinhold 		|| timeout < 0)
948224aee3fSIngo Weinhold 		return B_BAD_VALUE;
949224aee3fSIngo Weinhold 
950224aee3fSIngo Weinhold 	flags = flags & (B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT
951224aee3fSIngo Weinhold 				| B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT);
952224aee3fSIngo Weinhold 	slot = id % sMaxPorts;
953224aee3fSIngo Weinhold 
954224aee3fSIngo Weinhold 	state = disable_interrupts();
955224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
956224aee3fSIngo Weinhold 
957224aee3fSIngo Weinhold 	if (sPorts[slot].id != id
958224aee3fSIngo Weinhold 		|| (is_port_closed(slot) && list_is_empty(&sPorts[slot].msg_queue))) {
959224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
960224aee3fSIngo Weinhold 		restore_interrupts(state);
961224aee3fSIngo Weinhold 		TRACE(("read_port_etc(): %s port %ld\n",
962224aee3fSIngo Weinhold 			sPorts[slot].id == id ? "closed" : "invalid", id));
963224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
964224aee3fSIngo Weinhold 	}
965224aee3fSIngo Weinhold 	// store sem_id in local variable
966224aee3fSIngo Weinhold 	cachedSem = sPorts[slot].read_sem;
967224aee3fSIngo Weinhold 
968224aee3fSIngo Weinhold 	// unlock port && enable ints/
969224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
970224aee3fSIngo Weinhold 	restore_interrupts(state);
971224aee3fSIngo Weinhold 
972224aee3fSIngo Weinhold 	status = acquire_sem_etc(cachedSem, 1, flags, timeout);
973224aee3fSIngo Weinhold 		// get 1 entry from the queue, block if needed
974224aee3fSIngo Weinhold 
975224aee3fSIngo Weinhold 	if (status != B_OK && status != B_BAD_SEM_ID)
976224aee3fSIngo Weinhold 		return status;
977224aee3fSIngo Weinhold 
978224aee3fSIngo Weinhold 	// in case of B_BAD_SEM_ID, the port might have been closed but not yet
979224aee3fSIngo Weinhold 	// deleted, ie. there could still be messages waiting for us
980224aee3fSIngo Weinhold 
981224aee3fSIngo Weinhold 	state = disable_interrupts();
982224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
983224aee3fSIngo Weinhold 
984224aee3fSIngo Weinhold 	// first, let's check if the port is still alive
985224aee3fSIngo Weinhold 	if (sPorts[slot].id == -1) {
986224aee3fSIngo Weinhold 		// the port has been deleted in the meantime
987224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
988224aee3fSIngo Weinhold 		restore_interrupts(state);
989224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
990224aee3fSIngo Weinhold 	}
991224aee3fSIngo Weinhold 
992224aee3fSIngo Weinhold 	msg = (port_msg*)list_get_first_item(&sPorts[slot].msg_queue);
993224aee3fSIngo Weinhold 	if (msg == NULL) {
994224aee3fSIngo Weinhold 		if (status == B_OK)
995224aee3fSIngo Weinhold 			panic("port %ld: no messages found", sPorts[slot].id);
996224aee3fSIngo Weinhold 
997224aee3fSIngo Weinhold 		// the port has obviously been closed, but no messages are left anymore
998224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
999224aee3fSIngo Weinhold 		restore_interrupts(state);
1000224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
1001224aee3fSIngo Weinhold 	}
1002224aee3fSIngo Weinhold 
1003224aee3fSIngo Weinhold 	list_remove_link(msg);
1004224aee3fSIngo Weinhold 
1005224aee3fSIngo Weinhold 	sPorts[slot].total_count++;
1006224aee3fSIngo Weinhold 
1007224aee3fSIngo Weinhold 	notify_port_select_events(slot, B_EVENT_WRITE);
1008224aee3fSIngo Weinhold 
1009224aee3fSIngo Weinhold 	cachedSem = sPorts[slot].write_sem;
1010224aee3fSIngo Weinhold 
1011224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
1012224aee3fSIngo Weinhold 	restore_interrupts(state);
1013224aee3fSIngo Weinhold 
1014224aee3fSIngo Weinhold 	// check output buffer size
1015224aee3fSIngo Weinhold 	size = min_c(bufferSize, msg->size);
1016224aee3fSIngo Weinhold 
1017224aee3fSIngo Weinhold 	// copy message
1018224aee3fSIngo Weinhold 	if (_msgCode != NULL)
1019224aee3fSIngo Weinhold 		*_msgCode = msg->code;
1020224aee3fSIngo Weinhold 	if (size > 0) {
1021224aee3fSIngo Weinhold 		if (userCopy) {
1022224aee3fSIngo Weinhold 			if ((status = cbuf_user_memcpy_from_chain(msgBuffer, msg->buffer_chain, 0, size) < B_OK)) {
1023224aee3fSIngo Weinhold 				// leave the port intact, for other threads that might not crash
1024224aee3fSIngo Weinhold 				put_port_msg(msg);
1025224aee3fSIngo Weinhold 				release_sem(cachedSem);
1026224aee3fSIngo Weinhold 				return status;
1027224aee3fSIngo Weinhold 			}
1028224aee3fSIngo Weinhold 		} else
1029224aee3fSIngo Weinhold 			cbuf_memcpy_from_chain(msgBuffer, msg->buffer_chain, 0, size);
1030224aee3fSIngo Weinhold 	}
1031224aee3fSIngo Weinhold 	put_port_msg(msg);
1032224aee3fSIngo Weinhold 
1033224aee3fSIngo Weinhold 	// make one spot in queue available again for write
1034224aee3fSIngo Weinhold 	release_sem(cachedSem);
1035224aee3fSIngo Weinhold 		// ToDo: we might think about setting B_NO_RESCHEDULE here
1036224aee3fSIngo Weinhold 		//	from time to time (always?)
1037224aee3fSIngo Weinhold 
1038224aee3fSIngo Weinhold 	return size;
1039224aee3fSIngo Weinhold }
1040224aee3fSIngo Weinhold 
1041224aee3fSIngo Weinhold 
1042224aee3fSIngo Weinhold status_t
1043224aee3fSIngo Weinhold write_port(port_id id, int32 msgCode, const void *msgBuffer, size_t bufferSize)
1044224aee3fSIngo Weinhold {
1045224aee3fSIngo Weinhold 	iovec vec = { (void *)msgBuffer, bufferSize };
1046224aee3fSIngo Weinhold 
1047224aee3fSIngo Weinhold 	return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0);
1048224aee3fSIngo Weinhold }
1049224aee3fSIngo Weinhold 
1050224aee3fSIngo Weinhold 
1051224aee3fSIngo Weinhold status_t
1052224aee3fSIngo Weinhold write_port_etc(port_id id, int32 msgCode, const void *msgBuffer,
1053224aee3fSIngo Weinhold 	size_t bufferSize, uint32 flags, bigtime_t timeout)
1054224aee3fSIngo Weinhold {
1055224aee3fSIngo Weinhold 	iovec vec = { (void *)msgBuffer, bufferSize };
1056224aee3fSIngo Weinhold 
1057224aee3fSIngo Weinhold 	return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout);
1058224aee3fSIngo Weinhold }
1059224aee3fSIngo Weinhold 
1060224aee3fSIngo Weinhold 
1061224aee3fSIngo Weinhold status_t
1062224aee3fSIngo Weinhold writev_port_etc(port_id id, int32 msgCode, const iovec *msgVecs,
1063224aee3fSIngo Weinhold 	size_t vecCount, size_t bufferSize, uint32 flags,
1064224aee3fSIngo Weinhold 	bigtime_t timeout)
1065224aee3fSIngo Weinhold {
1066224aee3fSIngo Weinhold 	cpu_status state;
1067224aee3fSIngo Weinhold 	sem_id cachedSem;
1068224aee3fSIngo Weinhold 	status_t status;
1069224aee3fSIngo Weinhold 	port_msg *msg;
1070224aee3fSIngo Weinhold 	bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0;
1071224aee3fSIngo Weinhold 	int slot;
1072224aee3fSIngo Weinhold 
1073224aee3fSIngo Weinhold 	if (!sPortsActive || id < 0)
1074224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
1075224aee3fSIngo Weinhold 
1076224aee3fSIngo Weinhold 	// mask irrelevant flags (for acquire_sem() usage)
1077224aee3fSIngo Weinhold 	flags = flags & (B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT
1078224aee3fSIngo Weinhold 				| B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT);
1079224aee3fSIngo Weinhold 	slot = id % sMaxPorts;
1080224aee3fSIngo Weinhold 
1081224aee3fSIngo Weinhold 	if (bufferSize > PORT_MAX_MESSAGE_SIZE)
1082224aee3fSIngo Weinhold 		return B_BAD_VALUE;
1083224aee3fSIngo Weinhold 
1084224aee3fSIngo Weinhold 	state = disable_interrupts();
1085224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
1086224aee3fSIngo Weinhold 
1087224aee3fSIngo Weinhold 	if (sPorts[slot].id != id) {
1088224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
1089224aee3fSIngo Weinhold 		restore_interrupts(state);
1090224aee3fSIngo Weinhold 		TRACE(("write_port_etc: invalid port_id %ld\n", id));
1091224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
1092224aee3fSIngo Weinhold 	}
1093224aee3fSIngo Weinhold 
1094224aee3fSIngo Weinhold 	if (is_port_closed(slot)) {
1095224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
1096224aee3fSIngo Weinhold 		restore_interrupts(state);
1097224aee3fSIngo Weinhold 		TRACE(("write_port_etc: port %ld closed\n", id));
1098224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
1099224aee3fSIngo Weinhold 	}
1100224aee3fSIngo Weinhold 
1101224aee3fSIngo Weinhold 	// store sem_id in local variable
1102224aee3fSIngo Weinhold 	cachedSem = sPorts[slot].write_sem;
1103224aee3fSIngo Weinhold 
1104224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
1105224aee3fSIngo Weinhold 	restore_interrupts(state);
1106224aee3fSIngo Weinhold 
1107224aee3fSIngo Weinhold 	status = acquire_sem_etc(cachedSem, 1, flags, timeout);
1108224aee3fSIngo Weinhold 		// get 1 entry from the queue, block if needed
1109224aee3fSIngo Weinhold 
1110224aee3fSIngo Weinhold 	if (status == B_BAD_SEM_ID) {
1111224aee3fSIngo Weinhold 		// somebody deleted or closed the port
1112224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
1113224aee3fSIngo Weinhold 	}
1114224aee3fSIngo Weinhold 	if (status != B_OK)
1115224aee3fSIngo Weinhold 		return status;
1116224aee3fSIngo Weinhold 
1117224aee3fSIngo Weinhold 	msg = get_port_msg(msgCode, bufferSize);
1118224aee3fSIngo Weinhold 	if (msg == NULL)
1119224aee3fSIngo Weinhold 		return B_NO_MEMORY;
1120224aee3fSIngo Weinhold 
1121224aee3fSIngo Weinhold 	if (bufferSize > 0) {
1122224aee3fSIngo Weinhold 		uint32 i;
1123224aee3fSIngo Weinhold 		if (userCopy) {
1124224aee3fSIngo Weinhold 			// copy from user memory
1125224aee3fSIngo Weinhold 			for (i = 0; i < vecCount; i++) {
1126224aee3fSIngo Weinhold 				size_t bytes = msgVecs[i].iov_len;
1127224aee3fSIngo Weinhold 				if (bytes > bufferSize)
1128224aee3fSIngo Weinhold 					bytes = bufferSize;
1129224aee3fSIngo Weinhold 
1130224aee3fSIngo Weinhold 				if ((status = cbuf_user_memcpy_to_chain(msg->buffer_chain,
1131224aee3fSIngo Weinhold 						0, msgVecs[i].iov_base, bytes)) < B_OK)
1132224aee3fSIngo Weinhold 					return status;
1133224aee3fSIngo Weinhold 
1134224aee3fSIngo Weinhold 				bufferSize -= bytes;
1135224aee3fSIngo Weinhold 				if (bufferSize == 0)
1136224aee3fSIngo Weinhold 					break;
1137224aee3fSIngo Weinhold 			}
1138224aee3fSIngo Weinhold 		} else {
1139224aee3fSIngo Weinhold 			// copy from kernel memory
1140224aee3fSIngo Weinhold 			for (i = 0; i < vecCount; i++) {
1141224aee3fSIngo Weinhold 				size_t bytes = msgVecs[i].iov_len;
1142224aee3fSIngo Weinhold 				if (bytes > bufferSize)
1143224aee3fSIngo Weinhold 					bytes = bufferSize;
1144224aee3fSIngo Weinhold 
1145224aee3fSIngo Weinhold 				if ((status = cbuf_memcpy_to_chain(msg->buffer_chain,
1146224aee3fSIngo Weinhold 						0, msgVecs[i].iov_base, bytes)) < 0)
1147224aee3fSIngo Weinhold 					return status;
1148224aee3fSIngo Weinhold 
1149224aee3fSIngo Weinhold 				bufferSize -= bytes;
1150224aee3fSIngo Weinhold 				if (bufferSize == 0)
1151224aee3fSIngo Weinhold 					break;
1152224aee3fSIngo Weinhold 			}
1153224aee3fSIngo Weinhold 		}
1154224aee3fSIngo Weinhold 	}
1155224aee3fSIngo Weinhold 
1156224aee3fSIngo Weinhold 	// attach message to queue
1157224aee3fSIngo Weinhold 	state = disable_interrupts();
1158224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
1159224aee3fSIngo Weinhold 
1160224aee3fSIngo Weinhold 	// first, let's check if the port is still alive
1161224aee3fSIngo Weinhold 	if (sPorts[slot].id == -1) {
1162224aee3fSIngo Weinhold 		// the port has been deleted in the meantime
1163224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
1164224aee3fSIngo Weinhold 		restore_interrupts(state);
1165224aee3fSIngo Weinhold 
1166224aee3fSIngo Weinhold 		put_port_msg(msg);
1167224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
1168224aee3fSIngo Weinhold 	}
1169224aee3fSIngo Weinhold 
1170224aee3fSIngo Weinhold 	list_add_item(&sPorts[slot].msg_queue, msg);
1171224aee3fSIngo Weinhold 
1172224aee3fSIngo Weinhold 	notify_port_select_events(slot, B_EVENT_READ);
1173224aee3fSIngo Weinhold 
1174224aee3fSIngo Weinhold 	// store sem_id in local variable
1175224aee3fSIngo Weinhold 	cachedSem = sPorts[slot].read_sem;
1176224aee3fSIngo Weinhold 
1177224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
1178224aee3fSIngo Weinhold 	restore_interrupts(state);
1179224aee3fSIngo Weinhold 
1180224aee3fSIngo Weinhold 	// release sem, allowing read (might reschedule)
1181224aee3fSIngo Weinhold 	release_sem(cachedSem);
1182224aee3fSIngo Weinhold 
1183224aee3fSIngo Weinhold 	return B_NO_ERROR;
1184224aee3fSIngo Weinhold }
1185224aee3fSIngo Weinhold 
1186224aee3fSIngo Weinhold 
1187224aee3fSIngo Weinhold status_t
1188224aee3fSIngo Weinhold set_port_owner(port_id id, team_id team)
1189224aee3fSIngo Weinhold {
1190224aee3fSIngo Weinhold 	cpu_status state;
1191224aee3fSIngo Weinhold 	int slot;
1192224aee3fSIngo Weinhold // ToDo: Shouldn't we at least check, whether the team exists?
1193224aee3fSIngo Weinhold 
1194224aee3fSIngo Weinhold 	TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, team));
1195224aee3fSIngo Weinhold 
1196224aee3fSIngo Weinhold 	if (!sPortsActive || id < 0)
1197224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
1198224aee3fSIngo Weinhold 
1199224aee3fSIngo Weinhold 	slot = id % sMaxPorts;
1200224aee3fSIngo Weinhold 
1201224aee3fSIngo Weinhold 	state = disable_interrupts();
1202224aee3fSIngo Weinhold 	GRAB_PORT_LOCK(sPorts[slot]);
1203224aee3fSIngo Weinhold 
1204224aee3fSIngo Weinhold 	if (sPorts[slot].id != id) {
1205224aee3fSIngo Weinhold 		RELEASE_PORT_LOCK(sPorts[slot]);
1206224aee3fSIngo Weinhold 		restore_interrupts(state);
1207224aee3fSIngo Weinhold 		TRACE(("set_port_owner: invalid port_id %ld\n", id));
1208224aee3fSIngo Weinhold 		return B_BAD_PORT_ID;
1209224aee3fSIngo Weinhold 	}
1210224aee3fSIngo Weinhold 
1211224aee3fSIngo Weinhold 	// transfer ownership to other team
1212224aee3fSIngo Weinhold 	sPorts[slot].owner = team;
1213224aee3fSIngo Weinhold 
1214224aee3fSIngo Weinhold 	// unlock port
1215224aee3fSIngo Weinhold 	RELEASE_PORT_LOCK(sPorts[slot]);
1216224aee3fSIngo Weinhold 	restore_interrupts(state);
1217224aee3fSIngo Weinhold 
1218224aee3fSIngo Weinhold 	return B_NO_ERROR;
1219224aee3fSIngo Weinhold }
1220224aee3fSIngo Weinhold 
1221224aee3fSIngo Weinhold 
1222224aee3fSIngo Weinhold //	#pragma mark - syscalls
1223224aee3fSIngo Weinhold 
1224224aee3fSIngo Weinhold 
1225224aee3fSIngo Weinhold port_id
1226224aee3fSIngo Weinhold _user_create_port(int32 queueLength, const char *userName)
1227224aee3fSIngo Weinhold {
1228224aee3fSIngo Weinhold 	char name[B_OS_NAME_LENGTH];
1229224aee3fSIngo Weinhold 
1230224aee3fSIngo Weinhold 	if (userName == NULL)
1231224aee3fSIngo Weinhold 		return create_port(queueLength, NULL);
1232224aee3fSIngo Weinhold 
1233224aee3fSIngo Weinhold 	if (!IS_USER_ADDRESS(userName)
1234224aee3fSIngo Weinhold 		|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1235224aee3fSIngo Weinhold 		return B_BAD_ADDRESS;
1236224aee3fSIngo Weinhold 
1237224aee3fSIngo Weinhold 	return create_port(queueLength, name);
1238224aee3fSIngo Weinhold }
1239224aee3fSIngo Weinhold 
1240224aee3fSIngo Weinhold 
1241224aee3fSIngo Weinhold status_t
1242224aee3fSIngo Weinhold _user_close_port(port_id id)
1243224aee3fSIngo Weinhold {
1244224aee3fSIngo Weinhold 	return close_port(id);
1245224aee3fSIngo Weinhold }
1246224aee3fSIngo Weinhold 
1247224aee3fSIngo Weinhold 
1248224aee3fSIngo Weinhold status_t
1249224aee3fSIngo Weinhold _user_delete_port(port_id id)
1250224aee3fSIngo Weinhold {
1251224aee3fSIngo Weinhold 	return delete_port(id);
1252224aee3fSIngo Weinhold }
1253224aee3fSIngo Weinhold 
1254224aee3fSIngo Weinhold 
1255224aee3fSIngo Weinhold port_id
1256224aee3fSIngo Weinhold _user_find_port(const char *userName)
1257224aee3fSIngo Weinhold {
1258224aee3fSIngo Weinhold 	char name[B_OS_NAME_LENGTH];
1259224aee3fSIngo Weinhold 
1260224aee3fSIngo Weinhold 	if (userName == NULL)
1261224aee3fSIngo Weinhold 		return B_BAD_VALUE;
1262224aee3fSIngo Weinhold 	if (!IS_USER_ADDRESS(userName)
1263224aee3fSIngo Weinhold 		|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
1264224aee3fSIngo Weinhold 		return B_BAD_ADDRESS;
1265224aee3fSIngo Weinhold 
1266224aee3fSIngo Weinhold 	return find_port(name);
1267224aee3fSIngo Weinhold }
1268224aee3fSIngo Weinhold 
1269224aee3fSIngo Weinhold 
1270224aee3fSIngo Weinhold status_t
1271224aee3fSIngo Weinhold _user_get_port_info(port_id id, struct port_info *userInfo)
1272224aee3fSIngo Weinhold {
1273224aee3fSIngo Weinhold 	struct port_info info;
1274224aee3fSIngo Weinhold 	status_t status;
1275224aee3fSIngo Weinhold 
1276224aee3fSIngo Weinhold 	if (userInfo == NULL)
1277224aee3fSIngo Weinhold 		return B_BAD_VALUE;
1278224aee3fSIngo Weinhold 	if (!IS_USER_ADDRESS(userInfo))
1279224aee3fSIngo Weinhold 		return B_BAD_ADDRESS;
1280224aee3fSIngo Weinhold 
1281224aee3fSIngo Weinhold 	status = get_port_info(id, &info);
1282224aee3fSIngo Weinhold 
1283224aee3fSIngo Weinhold 	// copy back to user space
1284224aee3fSIngo Weinhold 	if (status == B_OK && user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK)
1285224aee3fSIngo Weinhold 		return B_BAD_ADDRESS;
1286224aee3fSIngo Weinhold 
1287224aee3fSIngo Weinhold 	return status;
1288224aee3fSIngo Weinhold }
1289224aee3fSIngo Weinhold 
1290224aee3fSIngo Weinhold 
1291224aee3fSIngo Weinhold status_t
1292224aee3fSIngo Weinhold _user_get_next_port_info(team_id team, int32 *userCookie, struct port_info *userInfo)
1293224aee3fSIngo Weinhold {
1294224aee3fSIngo Weinhold 	struct port_info info;
1295224aee3fSIngo Weinhold 	status_t status;
1296224aee3fSIngo Weinhold 	int32 cookie;
1297224aee3fSIngo Weinhold 
1298224aee3fSIngo Weinhold 	if (userCookie == NULL || userInfo == NULL)
1299224aee3fSIngo Weinhold 		return B_BAD_VALUE;
1300224aee3fSIngo Weinhold 	if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo)
1301224aee3fSIngo Weinhold 		|| user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
1302224aee3fSIngo Weinhold 		return B_BAD_ADDRESS;
1303224aee3fSIngo Weinhold 
1304224aee3fSIngo Weinhold 	status = get_next_port_info(team, &cookie, &info);
1305224aee3fSIngo Weinhold 
1306224aee3fSIngo Weinhold 	// copy back to user space
1307224aee3fSIngo Weinhold 	if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK
1308224aee3fSIngo Weinhold 		|| (status == B_OK && user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK))
1309224aee3fSIngo Weinhold 		return B_BAD_ADDRESS;
1310224aee3fSIngo Weinhold 
1311224aee3fSIngo Weinhold 	return status;
1312224aee3fSIngo Weinhold }
1313224aee3fSIngo Weinhold 
1314224aee3fSIngo Weinhold 
1315224aee3fSIngo Weinhold ssize_t
1316224aee3fSIngo Weinhold _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout)
1317224aee3fSIngo Weinhold {
1318224aee3fSIngo Weinhold 	return port_buffer_size_etc(port, flags | B_CAN_INTERRUPT, timeout);
1319224aee3fSIngo Weinhold }
1320224aee3fSIngo Weinhold 
1321224aee3fSIngo Weinhold 
1322224aee3fSIngo Weinhold ssize_t
1323224aee3fSIngo Weinhold _user_port_count(port_id port)
1324224aee3fSIngo Weinhold {
1325224aee3fSIngo Weinhold 	return port_count(port);
1326224aee3fSIngo Weinhold }
1327224aee3fSIngo Weinhold 
1328224aee3fSIngo Weinhold 
1329224aee3fSIngo Weinhold status_t
1330224aee3fSIngo Weinhold _user_set_port_owner(port_id port, team_id team)
1331224aee3fSIngo Weinhold {
1332224aee3fSIngo Weinhold 	return set_port_owner(port, team);
1333224aee3fSIngo Weinhold }
1334224aee3fSIngo Weinhold 
1335224aee3fSIngo Weinhold 
1336224aee3fSIngo Weinhold ssize_t
1337224aee3fSIngo Weinhold _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer,
1338224aee3fSIngo Weinhold 	size_t bufferSize, uint32 flags, bigtime_t timeout)
1339224aee3fSIngo Weinhold {
1340224aee3fSIngo Weinhold 	int32 messageCode;
1341224aee3fSIngo Weinhold 	ssize_t	bytesRead;
1342224aee3fSIngo Weinhold 
1343224aee3fSIngo Weinhold 	if (userBuffer == NULL && bufferSize != 0)
1344224aee3fSIngo Weinhold 		return B_BAD_VALUE;
1345224aee3fSIngo Weinhold 	if ((userCode != NULL && !IS_USER_ADDRESS(userCode))
1346224aee3fSIngo Weinhold 		|| (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)))
1347224aee3fSIngo Weinhold 		return B_BAD_ADDRESS;
1348224aee3fSIngo Weinhold 
1349224aee3fSIngo Weinhold 	bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize,
1350224aee3fSIngo Weinhold 		flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1351224aee3fSIngo Weinhold 
1352224aee3fSIngo Weinhold 	if (bytesRead >= 0 && userCode != NULL
1353224aee3fSIngo Weinhold 		&& user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK)
1354224aee3fSIngo Weinhold 		return B_BAD_ADDRESS;
1355224aee3fSIngo Weinhold 
1356224aee3fSIngo Weinhold 	return bytesRead;
1357224aee3fSIngo Weinhold }
1358224aee3fSIngo Weinhold 
1359224aee3fSIngo Weinhold 
1360224aee3fSIngo Weinhold status_t
1361224aee3fSIngo Weinhold _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer,
1362224aee3fSIngo Weinhold 	size_t bufferSize, uint32 flags, bigtime_t timeout)
1363224aee3fSIngo Weinhold {
1364224aee3fSIngo Weinhold 	iovec vec = { (void *)userBuffer, bufferSize };
1365224aee3fSIngo Weinhold 
1366224aee3fSIngo Weinhold 	if (userBuffer == NULL && bufferSize != 0)
1367224aee3fSIngo Weinhold 		return B_BAD_VALUE;
1368224aee3fSIngo Weinhold 	if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))
1369224aee3fSIngo Weinhold 		return B_BAD_ADDRESS;
1370224aee3fSIngo Weinhold 
1371224aee3fSIngo Weinhold 	return writev_port_etc(port, messageCode, &vec, 1, bufferSize,
1372224aee3fSIngo Weinhold 				flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1373224aee3fSIngo Weinhold }
1374224aee3fSIngo Weinhold 
1375224aee3fSIngo Weinhold 
1376224aee3fSIngo Weinhold status_t
1377224aee3fSIngo Weinhold _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs,
1378224aee3fSIngo Weinhold 	size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout)
1379224aee3fSIngo Weinhold {
1380224aee3fSIngo Weinhold 	iovec *vecs = NULL;
1381224aee3fSIngo Weinhold 	status_t status;
1382224aee3fSIngo Weinhold 
1383224aee3fSIngo Weinhold 	if (userVecs == NULL && bufferSize != 0)
1384224aee3fSIngo Weinhold 		return B_BAD_VALUE;
1385224aee3fSIngo Weinhold 	if (userVecs != NULL && !IS_USER_ADDRESS(userVecs))
1386224aee3fSIngo Weinhold 		return B_BAD_ADDRESS;
1387224aee3fSIngo Weinhold 
1388224aee3fSIngo Weinhold 	if (userVecs && vecCount != 0) {
1389224aee3fSIngo Weinhold 		vecs = (iovec*)malloc(sizeof(iovec) * vecCount);
1390224aee3fSIngo Weinhold 		if (vecs == NULL)
1391224aee3fSIngo Weinhold 			return B_NO_MEMORY;
1392224aee3fSIngo Weinhold 
1393224aee3fSIngo Weinhold 		if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) {
1394224aee3fSIngo Weinhold 			free(vecs);
1395224aee3fSIngo Weinhold 			return B_BAD_ADDRESS;
1396224aee3fSIngo Weinhold 		}
1397224aee3fSIngo Weinhold 	}
1398224aee3fSIngo Weinhold 	status = writev_port_etc(port, messageCode, vecs, vecCount, bufferSize,
1399224aee3fSIngo Weinhold 				flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
1400224aee3fSIngo Weinhold 
1401224aee3fSIngo Weinhold 	free(vecs);
1402224aee3fSIngo Weinhold 	return status;
1403224aee3fSIngo Weinhold }
1404