repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / serialconnect / SerialApp.cpp
blob896939a00fc9dc5fac79c55feab0a3c9466557e4
1 /*
2 * Copyright 2012-2017, Adrien Destugues, pulkomandy@gmail.com
3 * Distributed under the terms of the MIT licence.
4 */
7 #include "SerialApp.h"
9 #include <stdio.h>
10 #include <string.h>
12 #include <Directory.h>
13 #include <Entry.h>
14 #include <File.h>
15 #include <FindDirectory.h>
16 #include <Path.h>
18 #include "CustomRateWindow.h"
19 #include "SerialWindow.h"
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 }
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 }
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 }
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 }
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 }
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 }
59 { 0 }
62 const BPropertyInfo SerialApp::kScriptingProperties(sProperties);
65 SerialApp::SerialApp()
66 : BApplication(SerialApp::kApplicationSignature)
67 , fLogFile(NULL)
68 , fFileSender(NULL)
70 fWindow = new SerialWindow();
72 fSerialLock = create_sem(0, "Serial port lock");
73 thread_id id = spawn_thread(PollSerial, "Serial port poller",
74 B_LOW_PRIORITY, this);
75 resume_thread(id);
79 SerialApp::~SerialApp()
81 delete fLogFile;
82 delete fFileSender;
86 void SerialApp::ReadyToRun()
88 LoadSettings();
89 fWindow->Show();
93 void SerialApp::MessageReceived(BMessage* message)
95 switch (message->what) {
96 case kMsgOpenPort:
98 if (message->FindString("port name", &fPortPath) == B_OK) {
99 fSerialPort.Open(fPortPath);
100 release_sem(fSerialLock);
101 } else {
102 fSerialPort.Close();
105 // Forward to the window so it can enable/disable menu items
106 fWindow->PostMessage(message);
107 return;
109 case kMsgDataRead:
111 const uint8_t* bytes;
112 ssize_t length;
113 message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
114 &length);
116 if (fFileSender != NULL) {
117 if (fFileSender->BytesReceived(bytes, length)) {
118 delete fFileSender;
119 fFileSender = NULL;
121 } else {
122 // forward the message to the window, which will display the
123 // incoming data
124 fWindow->PostMessage(message);
126 if (fLogFile) {
127 if (fLogFile->Write(bytes, length) != length) {
128 // TODO error handling
133 return;
135 case kMsgDataWrite:
137 // Do not allow sending if a file transfer is in progress.
138 if (fFileSender != NULL)
139 return;
141 const char* bytes;
142 ssize_t size;
144 if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
145 &size) == B_OK)
146 fSerialPort.Write(bytes, size);
147 return;
149 case kMsgLogfile:
151 entry_ref parent;
152 const char* filename;
154 if (message->FindRef("directory", &parent) == B_OK
155 && message->FindString("name", &filename) == B_OK) {
156 delete fLogFile;
157 BDirectory directory(&parent);
158 fLogFile = new BFile(&directory, filename,
159 B_WRITE_ONLY | B_CREATE_FILE | B_OPEN_AT_END);
160 status_t error = fLogFile->InitCheck();
161 if (error != B_OK)
162 puts(strerror(error));
163 } else
164 debugger("Invalid BMessage received");
165 return;
167 case kMsgSendFile:
169 entry_ref ref;
171 BString protocol = message->FindString("protocol");
173 if (message->FindRef("refs", &ref) == B_OK) {
174 BFile* file = new BFile(&ref, B_READ_ONLY);
175 status_t error = file->InitCheck();
176 if (error != B_OK)
177 puts(strerror(error));
178 else {
179 delete fFileSender;
180 if (protocol == "xmodem")
181 fFileSender = new XModemSender(file, &fSerialPort, fWindow);
182 else
183 fFileSender = new RawSender(file, &fSerialPort, fWindow);
185 } else {
186 message->PrintToStream();
187 debugger("Invalid BMessage received");
189 return;
191 case kMsgCustomBaudrate:
193 // open the custom baudrate selector window
194 CustomRateWindow* window = new CustomRateWindow(fSerialPort.DataRate());
195 window->Show();
196 return;
198 case kMsgSettings:
200 int32 baudrate;
201 stop_bits stopBits;
202 data_bits dataBits;
203 parity_mode parity;
204 uint32 flowcontrol;
206 if (message->FindInt32("databits", (int32*)&dataBits) == B_OK)
207 fSerialPort.SetDataBits(dataBits);
209 if (message->FindInt32("stopbits", (int32*)&stopBits) == B_OK)
210 fSerialPort.SetStopBits(stopBits);
212 if (message->FindInt32("parity", (int32*)&parity) == B_OK)
213 fSerialPort.SetParityMode(parity);
215 if (message->FindInt32("flowcontrol", (int32*)&flowcontrol) == B_OK)
216 fSerialPort.SetFlowControl(flowcontrol);
218 if (message->FindInt32("baudrate", &baudrate) == B_OK) {
219 data_rate rate = (data_rate)baudrate;
220 fSerialPort.SetDataRate(rate);
223 return;
227 // Handle scripting messages
228 if (message->HasSpecifiers()) {
229 BMessage specifier;
230 int32 what;
231 int32 index;
232 const char* property;
234 BMessage reply(B_REPLY);
235 BMessage settings(kMsgSettings);
236 bool settingsChanged = false;
238 if (message->GetCurrentSpecifier(&index, &specifier, &what, &property)
239 == B_OK) {
240 switch (kScriptingProperties.FindMatch(message, index, &specifier,
241 what, property)) {
242 case 0: // baudrate
243 if (message->what == B_GET_PROPERTY) {
244 reply.AddInt32("result", fSerialPort.DataRate());
245 message->SendReply(&reply);
246 return;
248 if (message->what == B_SET_PROPERTY) {
249 int32 rate = message->FindInt32("data");
250 settingsChanged = true;
251 settings.AddInt32("baudrate", rate);
253 break;
254 case 1: // data bits
255 if (message->what == B_GET_PROPERTY) {
256 reply.AddInt32("result", fSerialPort.DataBits() + 7);
257 message->SendReply(&reply);
258 return;
260 if (message->what == B_SET_PROPERTY) {
261 int32 bits = message->FindInt32("data");
262 settingsChanged = true;
263 settings.AddInt32("databits", bits - 7);
265 break;
266 case 2: // stop bits
267 if (message->what == B_GET_PROPERTY) {
268 reply.AddInt32("result", fSerialPort.StopBits() + 1);
269 message->SendReply(&reply);
270 return;
272 if (message->what == B_SET_PROPERTY) {
273 int32 bits = message->FindInt32("data");
274 settingsChanged = true;
275 settings.AddInt32("stopbits", bits - 1);
277 break;
278 case 3: // parity
280 static const char* strings[] = {"none", "odd", "even"};
281 if (message->what == B_GET_PROPERTY) {
282 reply.AddString("result",
283 strings[fSerialPort.ParityMode()]);
284 message->SendReply(&reply);
285 return;
287 if (message->what == B_SET_PROPERTY) {
288 BString bits = message->FindString("data");
289 int i;
290 for (i = 0; i < 3; i++) {
291 if (bits == strings[i])
292 break;
295 if (i < 3) {
296 settingsChanged = true;
297 settings.AddInt32("parity", i);
300 break;
302 case 4: // flow control
304 static const char* strings[] = {"none", "hardware",
305 "software", "both"};
306 if (message->what == B_GET_PROPERTY) {
307 reply.AddString("result",
308 strings[fSerialPort.FlowControl()]);
309 message->SendReply(&reply);
310 return;
312 if (message->what == B_SET_PROPERTY) {
313 BString bits = message->FindString("data");
314 int i;
315 for (i = 0; i < 4; i++) {
316 if (bits == strings[i])
317 break;
320 if (i < 4) {
321 settingsChanged = true;
322 settings.AddInt32("flowcontrol", i);
325 break;
327 case 5: // port
328 if (message->what == B_GET_PROPERTY) {
329 reply.AddString("port", GetPort());
330 message->SendReply(&reply);
331 } else if (message->what == B_DELETE_PROPERTY
332 || message->what == B_SET_PROPERTY) {
333 BString path = message->FindString("data");
334 BMessage openMessage(kMsgOpenPort);
335 openMessage.AddString("port name", path);
336 PostMessage(&openMessage);
337 fWindow->PostMessage(&openMessage);
339 return;
343 if (settingsChanged) {
344 PostMessage(&settings);
345 fWindow->PostMessage(&settings);
346 return;
350 BApplication::MessageReceived(message);
354 bool SerialApp::QuitRequested()
356 if (BApplication::QuitRequested()) {
357 SaveSettings();
358 return true;
360 return false;
364 const BString& SerialApp::GetPort()
366 return fPortPath;
370 void SerialApp::LoadSettings()
372 BPath path;
373 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
374 path.Append("SerialConnect");
376 BFile file(path.Path(), B_READ_ONLY);
377 BMessage message(kMsgSettings);
378 if (message.Unflatten(&file) != B_OK) {
379 message.AddInt32("parity", fSerialPort.ParityMode());
380 message.AddInt32("databits", fSerialPort.DataBits());
381 message.AddInt32("stopbits", fSerialPort.StopBits());
382 message.AddInt32("baudrate", fSerialPort.DataRate());
383 message.AddInt32("flowcontrol", fSerialPort.FlowControl());
386 be_app->PostMessage(&message);
387 fWindow->PostMessage(&message);
391 void SerialApp::SaveSettings()
393 BMessage message(kMsgSettings);
394 message.AddInt32("parity", fSerialPort.ParityMode());
395 message.AddInt32("databits", fSerialPort.DataBits());
396 message.AddInt32("stopbits", fSerialPort.StopBits());
397 message.AddInt32("baudrate", fSerialPort.DataRate());
398 message.AddInt32("flowcontrol", fSerialPort.FlowControl());
400 BPath path;
401 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
402 path.Append("SerialConnect");
404 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE);
405 message.Flatten(&file);
409 /* static */
410 status_t SerialApp::PollSerial(void*)
412 SerialApp* application = (SerialApp*)be_app;
413 char buffer[256];
415 for (;;) {
416 ssize_t bytesRead;
418 bytesRead = application->fSerialPort.Read(buffer, sizeof(buffer));
419 if (bytesRead == B_FILE_ERROR) {
420 // Port is not open - wait for it and start over
421 acquire_sem(application->fSerialLock);
422 } else if (bytesRead > 0) {
423 // We read something, forward it to the app for handling
424 BMessage* serialData = new BMessage(kMsgDataRead);
425 serialData->AddData("data", B_RAW_TYPE, buffer, bytesRead);
426 be_app_messenger.SendMessage(serialData);
430 // Should not reach this line anyway...
431 return B_OK;
435 const char* SerialApp::kApplicationSignature
436 = "application/x-vnd.haiku.SerialConnect";
439 int main(int argc, char** argv)
441 SerialApp app;
442 app.Run();
446 status_t
447 SerialApp::GetSupportedSuites(BMessage* message)
449 message->AddString("suites", "suite/vnd.Haiku-SerialPort");
450 message->AddFlat("messages", &kScriptingProperties);
451 return BApplication::GetSupportedSuites(message);
455 BHandler*
456 SerialApp::ResolveSpecifier(BMessage* message, int32 index,
457 BMessage* specifier, int32 what, const char* property)
459 if (kScriptingProperties.FindMatch(message, index, specifier, what,
460 property) >= 0)
461 return this;
463 return BApplication::ResolveSpecifier(message, index, specifier, what,
464 property);