xref: /haiku/src/apps/serialconnect/SerialApp.cpp (revision f2df0cfe93a902842f6f4629ff614f5b3f9bf687)
1 /*
2  * Copyright 2012, Adrien Destugues, pulkomandy@gmail.com
3  * Distributed under the terms of the MIT licence.
4  */
5 
6 
7 #include "SerialApp.h"
8 
9 #include <stdio.h>
10 #include <string.h>
11 
12 #include <Directory.h>
13 #include <Entry.h>
14 #include <File.h>
15 #include <FindDirectory.h>
16 #include <Path.h>
17 
18 #include "CustomRateWindow.h"
19 #include "SerialWindow.h"
20 
21 
22 static property_info sProperties[] = {
23 	{ "baudrate",
24 		{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
25 		{ B_DIRECT_SPECIFIER, B_DIRECT_SPECIFIER, 0 },
26 		"get or set the baudrate",
27 		0, { B_INT32_TYPE }
28 	},
29 	{ "bits",
30 		{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
31 		{ B_DIRECT_SPECIFIER, B_DIRECT_SPECIFIER, 0 },
32 		"get or set the number of data bits (7 or 8)",
33 		0, { B_INT32_TYPE }
34 	},
35 	{ "stopbits",
36 		{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
37 		{ B_DIRECT_SPECIFIER, B_DIRECT_SPECIFIER, 0 },
38 		"get or set the number of stop bits (1 or 2)",
39 		0, { B_INT32_TYPE }
40 	},
41 	{ "parity",
42 		{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
43 		{ B_DIRECT_SPECIFIER, B_DIRECT_SPECIFIER, 0 },
44 		"get or set the parity (none, even or odd)",
45 		0, { B_STRING_TYPE }
46 	},
47 	{ "flowcontrol",
48 		{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
49 		{ B_DIRECT_SPECIFIER, B_DIRECT_SPECIFIER, 0 },
50 		"get or set the flow control (hardware, software, both, or none)",
51 		0, { B_STRING_TYPE }
52 	},
53 	{ "port",
54 		{ B_GET_PROPERTY, B_SET_PROPERTY, B_DELETE_PROPERTY, 0 },
55 		{ B_DIRECT_SPECIFIER, 0 },
56 		"get or set the port device",
57 		0, { B_STRING_TYPE }
58 	},
59 	{ NULL }
60 };
61 
62 const BPropertyInfo SerialApp::kScriptingProperties(sProperties);
63 
64 
65 SerialApp::SerialApp()
66 	: BApplication(SerialApp::kApplicationSignature)
67 	, fLogFile(NULL)
68 {
69 	fWindow = new SerialWindow();
70 
71 	fSerialLock = create_sem(0, "Serial port lock");
72 	thread_id id = spawn_thread(PollSerial, "Serial port poller",
73 		B_LOW_PRIORITY, this);
74 	resume_thread(id);
75 }
76 
77 
78 SerialApp::~SerialApp()
79 {
80 	delete fLogFile;
81 }
82 
83 
84 void SerialApp::ReadyToRun()
85 {
86 	LoadSettings();
87 	fWindow->Show();
88 }
89 
90 
91 void SerialApp::MessageReceived(BMessage* message)
92 {
93 	switch (message->what) {
94 		case kMsgOpenPort:
95 		{
96 			if (message->FindString("port name", &fPortPath) == B_OK) {
97 				fSerialPort.Open(fPortPath);
98 				release_sem(fSerialLock);
99 			} else {
100 				fSerialPort.Close();
101 			}
102 			return;
103 		}
104 		case kMsgDataRead:
105 		{
106 			// forward the message to the window, which will display the
107 			// incoming data
108 			fWindow->PostMessage(message);
109 
110 			if (fLogFile) {
111 				const char* bytes;
112 				ssize_t length;
113 				message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
114 					&length);
115 				if (fLogFile->Write(bytes, length) != length) {
116 					// TODO error handling
117 				}
118 			}
119 
120 			return;
121 		}
122 		case kMsgDataWrite:
123 		{
124 			const char* bytes;
125 			ssize_t size;
126 
127 			if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
128 					&size) == B_OK)
129 				fSerialPort.Write(bytes, size);
130 			return;
131 		}
132 		case kMsgLogfile:
133 		{
134 			entry_ref parent;
135 			const char* filename;
136 
137 			if (message->FindRef("directory", &parent) == B_OK
138 				&& message->FindString("name", &filename) == B_OK) {
139 				delete fLogFile;
140 				BDirectory directory(&parent);
141 				fLogFile = new BFile(&directory, filename,
142 					B_WRITE_ONLY | B_CREATE_FILE | B_OPEN_AT_END);
143 				status_t error = fLogFile->InitCheck();
144 				if (error != B_OK)
145 					puts(strerror(error));
146 			} else
147 				debugger("Invalid BMessage received");
148 			return;
149 		}
150 		case kMsgCustomBaudrate:
151 		{
152 			// open the custom baudrate selector window
153 			CustomRateWindow* window = new CustomRateWindow(fSerialPort.DataRate());
154 			window->Show();
155 			return;
156 		}
157 		case kMsgSettings:
158 		{
159 			int32 baudrate;
160 			stop_bits stopBits;
161 			data_bits dataBits;
162 			parity_mode parity;
163 			uint32 flowcontrol;
164 
165 			if (message->FindInt32("databits", (int32*)&dataBits) == B_OK)
166 				fSerialPort.SetDataBits(dataBits);
167 
168 			if (message->FindInt32("stopbits", (int32*)&stopBits) == B_OK)
169 				fSerialPort.SetStopBits(stopBits);
170 
171 			if (message->FindInt32("parity", (int32*)&parity) == B_OK)
172 				fSerialPort.SetParityMode(parity);
173 
174 			if (message->FindInt32("flowcontrol", (int32*)&flowcontrol) == B_OK)
175 				fSerialPort.SetFlowControl(flowcontrol);
176 
177 			if (message->FindInt32("baudrate", &baudrate) == B_OK) {
178 				data_rate rate = (data_rate)baudrate;
179 				fSerialPort.SetDataRate(rate);
180 			}
181 
182 			return;
183 		}
184 	}
185 
186 	// Handle scripting messages
187 	if (message->HasSpecifiers()) {
188 		BMessage specifier;
189 		int32 what;
190 		int32 index;
191 		const char* property;
192 
193 		BMessage reply(B_REPLY);
194 		BMessage settings(kMsgSettings);
195 		bool settingsChanged = false;
196 
197 		if (message->GetCurrentSpecifier(&index, &specifier, &what, &property)
198 			== B_OK) {
199 			switch (kScriptingProperties.FindMatch(message, index, &specifier,
200 				what, property)) {
201 				case 0: // baudrate
202 					if (message->what == B_GET_PROPERTY) {
203 						reply.AddInt32("result", fSerialPort.DataRate());
204 						message->SendReply(&reply);
205 						return;
206 					}
207 					if (message->what == B_SET_PROPERTY) {
208 						int32 rate = message->FindInt32("data");
209 						settingsChanged = true;
210 						settings.AddInt32("baudrate", rate);
211 					}
212 					break;
213 				case 1: // data bits
214 					if (message->what == B_GET_PROPERTY) {
215 						reply.AddInt32("result", fSerialPort.DataBits() + 7);
216 						message->SendReply(&reply);
217 						return;
218 					}
219 					if (message->what == B_SET_PROPERTY) {
220 						int32 bits = message->FindInt32("data");
221 						settingsChanged = true;
222 						settings.AddInt32("databits", bits - 7);
223 					}
224 					break;
225 				case 2: // stop bits
226 					if (message->what == B_GET_PROPERTY) {
227 						reply.AddInt32("result", fSerialPort.StopBits() + 1);
228 						message->SendReply(&reply);
229 						return;
230 					}
231 					if (message->what == B_SET_PROPERTY) {
232 						int32 bits = message->FindInt32("data");
233 						settingsChanged = true;
234 						settings.AddInt32("stopbits", bits - 1);
235 					}
236 					break;
237 				case 3: // parity
238 				{
239 					static const char* strings[] = {"none", "odd", "even"};
240 					if (message->what == B_GET_PROPERTY) {
241 						reply.AddString("result",
242 							strings[fSerialPort.ParityMode()]);
243 						message->SendReply(&reply);
244 						return;
245 					}
246 					if (message->what == B_SET_PROPERTY) {
247 						BString bits = message->FindString("data");
248 						int i;
249 						for (i = 0; i < 3; i++) {
250 							if (bits == strings[i])
251 								break;
252 						}
253 
254 						if (i < 3) {
255 							settingsChanged = true;
256 							settings.AddInt32("parity", i);
257 						}
258 					}
259 					break;
260 				}
261 				case 4: // flow control
262 				{
263 					static const char* strings[] = {"none", "hardware",
264 						"software", "both"};
265 					if (message->what == B_GET_PROPERTY) {
266 						reply.AddString("result",
267 							strings[fSerialPort.FlowControl()]);
268 						message->SendReply(&reply);
269 						return;
270 					}
271 					if (message->what == B_SET_PROPERTY) {
272 						BString bits = message->FindString("data");
273 						int i;
274 						for (i = 0; i < 4; i++) {
275 							if (bits == strings[i])
276 								break;
277 						}
278 
279 						if (i < 4) {
280 							settingsChanged = true;
281 							settings.AddInt32("flowcontrol", i);
282 						}
283 					}
284 					break;
285 				}
286 				case 5: // port
287 					if (message->what == B_GET_PROPERTY) {
288 						reply.AddString("port", GetPort());
289 						message->SendReply(&reply);
290 					} else if (message->what == B_DELETE_PROPERTY
291 						|| message->what == B_SET_PROPERTY) {
292 						BString path = message->FindString("data");
293 						BMessage openMessage(kMsgOpenPort);
294 						openMessage.AddString("port name", path);
295 						PostMessage(&openMessage);
296 						fWindow->PostMessage(&openMessage);
297 					}
298 					return;
299 			}
300 		}
301 
302 		if (settingsChanged) {
303 			PostMessage(&settings);
304 			fWindow->PostMessage(&settings);
305 			return;
306 		}
307 	}
308 
309 	BApplication::MessageReceived(message);
310 }
311 
312 
313 bool SerialApp::QuitRequested()
314 {
315 	if (BApplication::QuitRequested()) {
316 		SaveSettings();
317 		return true;
318 	}
319 	return false;
320 }
321 
322 
323 const BString& SerialApp::GetPort()
324 {
325 	return fPortPath;
326 }
327 
328 
329 void SerialApp::LoadSettings()
330 {
331 	BPath path;
332 	find_directory(B_USER_SETTINGS_DIRECTORY, &path);
333 	path.Append("SerialConnect");
334 
335 	BFile file(path.Path(), B_READ_ONLY);
336 	BMessage message(kMsgSettings);
337 	if (message.Unflatten(&file) != B_OK) {
338 		message.AddInt32("parity", fSerialPort.ParityMode());
339 		message.AddInt32("databits", fSerialPort.DataBits());
340 		message.AddInt32("stopbits", fSerialPort.StopBits());
341 		message.AddInt32("baudrate", fSerialPort.DataRate());
342 		message.AddInt32("flowcontrol", fSerialPort.FlowControl());
343 	}
344 
345 	be_app->PostMessage(&message);
346 	fWindow->PostMessage(&message);
347 }
348 
349 
350 void SerialApp::SaveSettings()
351 {
352 	BMessage message(kMsgSettings);
353 	message.AddInt32("parity", fSerialPort.ParityMode());
354 	message.AddInt32("databits", fSerialPort.DataBits());
355 	message.AddInt32("stopbits", fSerialPort.StopBits());
356 	message.AddInt32("baudrate", fSerialPort.DataRate());
357 	message.AddInt32("flowcontrol", fSerialPort.FlowControl());
358 
359 	BPath path;
360 	find_directory(B_USER_SETTINGS_DIRECTORY, &path);
361 	path.Append("SerialConnect");
362 
363 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE);
364 	message.Flatten(&file);
365 }
366 
367 
368 /* static */
369 status_t SerialApp::PollSerial(void*)
370 {
371 	SerialApp* application = (SerialApp*)be_app;
372 	char buffer[256];
373 
374 	for (;;) {
375 		ssize_t bytesRead;
376 
377 		bytesRead = application->fSerialPort.Read(buffer, sizeof(buffer));
378 		if (bytesRead == B_FILE_ERROR) {
379 			// Port is not open - wait for it and start over
380 			acquire_sem(application->fSerialLock);
381 		} else if (bytesRead > 0) {
382 			// We read something, forward it to the app for handling
383 			BMessage* serialData = new BMessage(kMsgDataRead);
384 			serialData->AddData("data", B_RAW_TYPE, buffer, bytesRead);
385 			be_app_messenger.SendMessage(serialData);
386 		}
387 	}
388 
389 	// Should not reach this line anyway...
390 	return B_OK;
391 }
392 
393 
394 const char* SerialApp::kApplicationSignature
395 	= "application/x-vnd.haiku.SerialConnect";
396 
397 
398 int main(int argc, char** argv)
399 {
400 	SerialApp app;
401 	app.Run();
402 }
403 
404 
405 status_t
406 SerialApp::GetSupportedSuites(BMessage* message)
407 {
408 	message->AddString("suites", "suite/vnd.Haiku-SerialPort");
409 	message->AddFlat("messages", &kScriptingProperties);
410 	return BApplication::GetSupportedSuites(message);
411 }
412 
413 
414 BHandler*
415 SerialApp::ResolveSpecifier(BMessage* message, int32 index,
416 	BMessage* specifier, int32 what, const char* property)
417 {
418 	if (kScriptingProperties.FindMatch(message, index, specifier, what,
419 		property) >= 0)
420 		return this;
421 
422 	return BApplication::ResolveSpecifier(message, index, specifier, what,
423 		property);
424 }
425