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