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