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