1/*
2 * Copyright 2009 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5#ifndef PORTLISTENER_H_
6#define PORTLISTENER_H_
7
8#include <OS.h>
9
10template <
11	typename TYPE,
12	ssize_t MAX_MESSAGE_SIZE = 256,
13	size_t MAX_MESSAGE_DEEP	= 16,
14	uint32 PRIORITY	= B_URGENT_DISPLAY_PRIORITY>
15class PortListener {
16public:
17	typedef status_t (*port_listener_func)(TYPE*, int32, size_t);
18
19	PortListener(const char* name, port_listener_func handler)
20	{
21		fInformation.func = handler;
22		fInformation.port = &fPort;
23
24		fPortName = strdup(name);
25
26		fThreadName = (char*)malloc(strlen(name) + strlen(" thread") + 1);
27		fThreadName = strcpy(fThreadName, fPortName);
28		fThreadName = strcat(fThreadName, " thread");
29
30		InitCheck();
31	}
32
33
34	~PortListener()
35	{
36		status_t status;
37
38		close_port(fPort);
39		// Closing the port	should provoke the thread to finish
40		wait_for_thread(fThread, &status);
41
42		free(fThreadName);
43		free(fPortName);
44	}
45
46
47	status_t Trigger(int32 code)
48	{
49			return write_port(fPort, code, NULL, 0);
50	}
51
52
53	status_t Trigger(int32 code, TYPE* buffer, size_t size)
54	{
55		if (buffer == NULL)
56			return B_ERROR;
57
58		return write_port(fPort, code, buffer, size);
59	}
60
61
62	status_t InitCheck()
63	{
64		// Create Port
65		fPort = find_port(fPortName);
66		if (fPort == B_NAME_NOT_FOUND) {
67			fPort = create_port(MAX_MESSAGE_DEEP, fPortName);
68		}
69
70		if (fPort < B_OK)
71			return fPort;
72
73		#ifdef KERNEL_LAND
74		// if this is the case you better stay with kernel
75		set_port_owner(fPort, B_SYSTEM_TEAM);
76		#endif
77
78		// Create Thread
79		fThread = find_thread(fThreadName);
80		if (fThread < B_OK) {
81#ifdef KERNEL_LAND
82			fThread = spawn_kernel_thread((thread_func)&PortListener<TYPE,
83				MAX_MESSAGE_SIZE, MAX_MESSAGE_DEEP, PRIORITY>::threadFunction,
84				fThreadName, PRIORITY, &fInformation);
85#else
86			fThread = spawn_thread((thread_func)&PortListener<TYPE,
87				MAX_MESSAGE_SIZE, MAX_MESSAGE_DEEP, PRIORITY>::threadFunction,
88				fThreadName, PRIORITY, &fInformation);
89#endif
90		}
91
92		if (fThread < B_OK)
93			return fThread;
94
95		return B_OK;
96	}
97
98
99	status_t Launch()
100	{
101		status_t check = InitCheck();
102
103		if (check < B_OK)
104			return check;
105
106		return resume_thread(fThread);
107	}
108
109
110	status_t Stop()
111	{
112		status_t status;
113
114		close_port(fPort);
115		// Closing the port	should provoke the thread to finish
116		wait_for_thread(fThread, &status);
117
118		return status;
119	}
120
121
122private:
123
124	struct PortListenerInfo {
125		port_id* port;
126		port_listener_func func;
127	} fInformation;
128
129	port_id fPort;
130	thread_id fThread;
131	char* fThreadName;
132	char* fPortName;
133
134	static int32 threadFunction(void* data)
135	{
136		ssize_t	ssizePort;
137		ssize_t	ssizeRead;
138		status_t status = B_OK;
139		int32 code;
140
141		port_id* port = ((struct PortListenerInfo*)data)->port;
142		port_listener_func handler = ((struct PortListenerInfo*)data)->func;
143
144
145		TYPE* buffer = (TYPE*)malloc(MAX_MESSAGE_SIZE);
146
147		while ((ssizePort = port_buffer_size(*port)) != B_BAD_PORT_ID) {
148
149			if (ssizePort <= 0) {
150				snooze(500 * 1000);
151				continue;
152			}
153
154			if (ssizePort >	MAX_MESSAGE_SIZE) {
155				snooze(500 * 1000);
156				continue;
157			}
158
159			ssizeRead = read_port(*port, &code, (void*)buffer, ssizePort);
160
161			if (ssizeRead != ssizePort)
162				continue;
163
164			status = handler(buffer, code,	ssizePort);
165
166			if (status != B_OK)
167				break;
168
169		}
170
171		#ifdef DEBUG_PORTLISTENER
172		#ifdef KERNEL_LAND
173		dprintf("Error in PortListener handler=%s port=%s\n", strerror(status),
174			strerror(ssizePort));
175		#else
176		printf("Error in PortListener handler=%s port=%s\n", strerror(status),
177			strerror(ssizePort));
178		#endif
179		#endif
180
181		free(buffer);
182
183		if (ssizePort == B_BAD_PORT_ID) // the port disappeared
184			return ssizePort;
185
186		return status;
187	}
188
189}; // PortListener
190
191#endif // PORTLISTENER_H_
192