1 /* 2 * Copyright 2012-2017, 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 { 0 } 60 }; 61 62 const BPropertyInfo SerialApp::kScriptingProperties(sProperties); 63 64 65 SerialApp::SerialApp() 66 : BApplication(SerialApp::kApplicationSignature) 67 , fLogFile(NULL) 68 , fFileSender(NULL) 69 { 70 fWindow = new SerialWindow(); 71 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); 76 } 77 78 79 SerialApp::~SerialApp() 80 { 81 delete fLogFile; 82 delete fFileSender; 83 } 84 85 86 void SerialApp::ReadyToRun() 87 { 88 LoadSettings(); 89 fWindow->Show(); 90 } 91 92 93 void SerialApp::MessageReceived(BMessage* message) 94 { 95 switch (message->what) { 96 case kMsgOpenPort: 97 { 98 if (message->FindString("port name", &fPortPath) == B_OK) { 99 fSerialPort.Open(fPortPath); 100 release_sem(fSerialLock); 101 } else { 102 fSerialPort.Close(); 103 } 104 105 // Forward to the window so it can enable/disable menu items 106 fWindow->PostMessage(message); 107 return; 108 } 109 case kMsgDataRead: 110 { 111 const uint8_t* bytes; 112 ssize_t length; 113 message->FindData("data", B_RAW_TYPE, (const void**)&bytes, 114 &length); 115 116 if (fFileSender != NULL) { 117 if (fFileSender->BytesReceived(bytes, length)) { 118 delete fFileSender; 119 fFileSender = NULL; 120 } 121 } else { 122 // forward the message to the window, which will display the 123 // incoming data 124 fWindow->PostMessage(message); 125 126 if (fLogFile) { 127 if (fLogFile->Write(bytes, length) != length) { 128 // TODO error handling 129 } 130 } 131 } 132 133 return; 134 } 135 case kMsgDataWrite: 136 { 137 // Do not allow sending if a file transfer is in progress. 138 if (fFileSender != NULL) 139 return; 140 141 const char* bytes; 142 ssize_t size; 143 144 if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes, 145 &size) == B_OK) 146 fSerialPort.Write(bytes, size); 147 return; 148 } 149 case kMsgLogfile: 150 { 151 entry_ref parent; 152 const char* filename; 153 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; 166 } 167 case kMsgSendFile: 168 { 169 entry_ref ref; 170 171 BString protocol = message->FindString("protocol"); 172 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); 184 } 185 } else { 186 message->PrintToStream(); 187 debugger("Invalid BMessage received"); 188 } 189 return; 190 } 191 case kMsgCustomBaudrate: 192 { 193 // open the custom baudrate selector window 194 CustomRateWindow* window = new CustomRateWindow(fSerialPort.DataRate()); 195 window->Show(); 196 return; 197 } 198 case kMsgSettings: 199 { 200 int32 baudrate; 201 stop_bits stopBits; 202 data_bits dataBits; 203 parity_mode parity; 204 uint32 flowcontrol; 205 206 if (message->FindInt32("databits", (int32*)&dataBits) == B_OK) 207 fSerialPort.SetDataBits(dataBits); 208 209 if (message->FindInt32("stopbits", (int32*)&stopBits) == B_OK) 210 fSerialPort.SetStopBits(stopBits); 211 212 if (message->FindInt32("parity", (int32*)&parity) == B_OK) 213 fSerialPort.SetParityMode(parity); 214 215 if (message->FindInt32("flowcontrol", (int32*)&flowcontrol) == B_OK) 216 fSerialPort.SetFlowControl(flowcontrol); 217 218 if (message->FindInt32("baudrate", &baudrate) == B_OK) { 219 data_rate rate = (data_rate)baudrate; 220 fSerialPort.SetDataRate(rate); 221 } 222 223 return; 224 } 225 } 226 227 // Handle scripting messages 228 if (message->HasSpecifiers()) { 229 BMessage specifier; 230 int32 what; 231 int32 index; 232 const char* property; 233 234 BMessage reply(B_REPLY); 235 BMessage settings(kMsgSettings); 236 bool settingsChanged = false; 237 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; 247 } 248 if (message->what == B_SET_PROPERTY) { 249 int32 rate = message->FindInt32("data"); 250 settingsChanged = true; 251 settings.AddInt32("baudrate", rate); 252 } 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; 259 } 260 if (message->what == B_SET_PROPERTY) { 261 int32 bits = message->FindInt32("data"); 262 settingsChanged = true; 263 settings.AddInt32("databits", bits - 7); 264 } 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; 271 } 272 if (message->what == B_SET_PROPERTY) { 273 int32 bits = message->FindInt32("data"); 274 settingsChanged = true; 275 settings.AddInt32("stopbits", bits - 1); 276 } 277 break; 278 case 3: // parity 279 { 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; 286 } 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; 293 } 294 295 if (i < 3) { 296 settingsChanged = true; 297 settings.AddInt32("parity", i); 298 } 299 } 300 break; 301 } 302 case 4: // flow control 303 { 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; 311 } 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; 318 } 319 320 if (i < 4) { 321 settingsChanged = true; 322 settings.AddInt32("flowcontrol", i); 323 } 324 } 325 break; 326 } 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); 338 } 339 return; 340 } 341 } 342 343 if (settingsChanged) { 344 PostMessage(&settings); 345 fWindow->PostMessage(&settings); 346 return; 347 } 348 } 349 350 BApplication::MessageReceived(message); 351 } 352 353 354 bool SerialApp::QuitRequested() 355 { 356 if (BApplication::QuitRequested()) { 357 SaveSettings(); 358 return true; 359 } 360 return false; 361 } 362 363 364 const BString& SerialApp::GetPort() 365 { 366 return fPortPath; 367 } 368 369 370 void SerialApp::LoadSettings() 371 { 372 BPath path; 373 find_directory(B_USER_SETTINGS_DIRECTORY, &path); 374 path.Append("SerialConnect"); 375 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()); 384 } 385 386 be_app->PostMessage(&message); 387 fWindow->PostMessage(&message); 388 } 389 390 391 void SerialApp::SaveSettings() 392 { 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()); 399 400 BPath path; 401 find_directory(B_USER_SETTINGS_DIRECTORY, &path); 402 path.Append("SerialConnect"); 403 404 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE); 405 message.Flatten(&file); 406 } 407 408 409 /* static */ 410 status_t SerialApp::PollSerial(void*) 411 { 412 SerialApp* application = (SerialApp*)be_app; 413 char buffer[256]; 414 415 for (;;) { 416 ssize_t bytesRead; 417 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); 427 } 428 } 429 430 // Should not reach this line anyway... 431 return B_OK; 432 } 433 434 435 const char* SerialApp::kApplicationSignature 436 = "application/x-vnd.haiku.SerialConnect"; 437 438 439 int main(int argc, char** argv) 440 { 441 SerialApp app; 442 app.Run(); 443 } 444 445 446 status_t 447 SerialApp::GetSupportedSuites(BMessage* message) 448 { 449 message->AddString("suites", "suite/vnd.Haiku-SerialPort"); 450 message->AddFlat("messages", &kScriptingProperties); 451 return BApplication::GetSupportedSuites(message); 452 } 453 454 455 BHandler* 456 SerialApp::ResolveSpecifier(BMessage* message, int32 index, 457 BMessage* specifier, int32 what, const char* property) 458 { 459 if (kScriptingProperties.FindMatch(message, index, specifier, what, 460 property) >= 0) 461 return this; 462 463 return BApplication::ResolveSpecifier(message, index, specifier, what, 464 property); 465 } 466