2 * Copyright 2012-2017, Adrien Destugues, pulkomandy@gmail.com
3 * Distributed under the terms of the MIT licence.
12 #include <Directory.h>
15 #include <FindDirectory.h>
18 #include "CustomRateWindow.h"
19 #include "SerialWindow.h"
22 static property_info sProperties
[] = {
24 { B_GET_PROPERTY
, B_SET_PROPERTY
, 0 },
25 { B_DIRECT_SPECIFIER
, B_DIRECT_SPECIFIER
, 0 },
26 "get or set the baudrate",
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)",
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)",
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)",
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)",
54 { B_GET_PROPERTY
, B_SET_PROPERTY
, B_DELETE_PROPERTY
, 0 },
55 { B_DIRECT_SPECIFIER
, 0 },
56 "get or set the port device",
62 const BPropertyInfo
SerialApp::kScriptingProperties(sProperties
);
65 SerialApp::SerialApp()
66 : BApplication(SerialApp::kApplicationSignature
)
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);
79 SerialApp::~SerialApp()
86 void SerialApp::ReadyToRun()
93 void SerialApp::MessageReceived(BMessage
* message
)
95 switch (message
->what
) {
98 if (message
->FindString("port name", &fPortPath
) == B_OK
) {
99 fSerialPort
.Open(fPortPath
);
100 release_sem(fSerialLock
);
105 // Forward to the window so it can enable/disable menu items
106 fWindow
->PostMessage(message
);
111 const uint8_t* bytes
;
113 message
->FindData("data", B_RAW_TYPE
, (const void**)&bytes
,
116 if (fFileSender
!= NULL
) {
117 if (fFileSender
->BytesReceived(bytes
, length
)) {
122 // forward the message to the window, which will display the
124 fWindow
->PostMessage(message
);
127 if (fLogFile
->Write(bytes
, length
) != length
) {
128 // TODO error handling
137 // Do not allow sending if a file transfer is in progress.
138 if (fFileSender
!= NULL
)
144 if (message
->FindData("data", B_RAW_TYPE
, (const void**)&bytes
,
146 fSerialPort
.Write(bytes
, size
);
152 const char* filename
;
154 if (message
->FindRef("directory", &parent
) == B_OK
155 && message
->FindString("name", &filename
) == B_OK
) {
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();
162 puts(strerror(error
));
164 debugger("Invalid BMessage received");
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();
177 puts(strerror(error
));
180 if (protocol
== "xmodem")
181 fFileSender
= new XModemSender(file
, &fSerialPort
, fWindow
);
183 fFileSender
= new RawSender(file
, &fSerialPort
, fWindow
);
186 message
->PrintToStream();
187 debugger("Invalid BMessage received");
191 case kMsgCustomBaudrate
:
193 // open the custom baudrate selector window
194 CustomRateWindow
* window
= new CustomRateWindow(fSerialPort
.DataRate());
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
);
227 // Handle scripting messages
228 if (message
->HasSpecifiers()) {
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
)
240 switch (kScriptingProperties
.FindMatch(message
, index
, &specifier
,
243 if (message
->what
== B_GET_PROPERTY
) {
244 reply
.AddInt32("result", fSerialPort
.DataRate());
245 message
->SendReply(&reply
);
248 if (message
->what
== B_SET_PROPERTY
) {
249 int32 rate
= message
->FindInt32("data");
250 settingsChanged
= true;
251 settings
.AddInt32("baudrate", rate
);
255 if (message
->what
== B_GET_PROPERTY
) {
256 reply
.AddInt32("result", fSerialPort
.DataBits() + 7);
257 message
->SendReply(&reply
);
260 if (message
->what
== B_SET_PROPERTY
) {
261 int32 bits
= message
->FindInt32("data");
262 settingsChanged
= true;
263 settings
.AddInt32("databits", bits
- 7);
267 if (message
->what
== B_GET_PROPERTY
) {
268 reply
.AddInt32("result", fSerialPort
.StopBits() + 1);
269 message
->SendReply(&reply
);
272 if (message
->what
== B_SET_PROPERTY
) {
273 int32 bits
= message
->FindInt32("data");
274 settingsChanged
= true;
275 settings
.AddInt32("stopbits", bits
- 1);
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
);
287 if (message
->what
== B_SET_PROPERTY
) {
288 BString bits
= message
->FindString("data");
290 for (i
= 0; i
< 3; i
++) {
291 if (bits
== strings
[i
])
296 settingsChanged
= true;
297 settings
.AddInt32("parity", i
);
302 case 4: // flow control
304 static const char* strings
[] = {"none", "hardware",
306 if (message
->what
== B_GET_PROPERTY
) {
307 reply
.AddString("result",
308 strings
[fSerialPort
.FlowControl()]);
309 message
->SendReply(&reply
);
312 if (message
->what
== B_SET_PROPERTY
) {
313 BString bits
= message
->FindString("data");
315 for (i
= 0; i
< 4; i
++) {
316 if (bits
== strings
[i
])
321 settingsChanged
= true;
322 settings
.AddInt32("flowcontrol", i
);
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
);
343 if (settingsChanged
) {
344 PostMessage(&settings
);
345 fWindow
->PostMessage(&settings
);
350 BApplication::MessageReceived(message
);
354 bool SerialApp::QuitRequested()
356 if (BApplication::QuitRequested()) {
364 const BString
& SerialApp::GetPort()
370 void SerialApp::LoadSettings()
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());
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
);
410 status_t
SerialApp::PollSerial(void*)
412 SerialApp
* application
= (SerialApp
*)be_app
;
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...
435 const char* SerialApp::kApplicationSignature
436 = "application/x-vnd.haiku.SerialConnect";
439 int main(int argc
, char** argv
)
447 SerialApp::GetSupportedSuites(BMessage
* message
)
449 message
->AddString("suites", "suite/vnd.Haiku-SerialPort");
450 message
->AddFlat("messages", &kScriptingProperties
);
451 return BApplication::GetSupportedSuites(message
);
456 SerialApp::ResolveSpecifier(BMessage
* message
, int32 index
,
457 BMessage
* specifier
, int32 what
, const char* property
)
459 if (kScriptingProperties
.FindMatch(message
, index
, specifier
, what
,
463 return BApplication::ResolveSpecifier(message
, index
, specifier
, what
,