SerialConnect: add scripting support

It's now possible to configure the serial port through scripting:
hey SerialConnect set databits to 8
hey SerialConnect set parity to even
etc.

More useful is the ability to connect and disconnect from the port:
hey SerialConnect get port # returns "usb0"
hey SerialConnect set port to usb0
hey SerialConnect delete port

This allows a script to automatically disconnect SerialConnect, do
something with the serial port, and then reconnect SerialConnect. This
can be used for example to run a bootloader and update a firmware
through the same serial port used for debugging, something that's not
easily possible on other systems.
This commit is contained in:
Adrien Destugues 2014-11-09 14:18:50 +01:00
parent 600c0527d5
commit d9e931526c
4 changed files with 245 additions and 60 deletions

View File

@ -18,6 +18,49 @@
#include "SerialWindow.h" #include "SerialWindow.h"
static property_info sProperties[] = {
{ "baudrate",
{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
{ B_DIRECT_SPECIFIER, B_DIRECT_SPECIFIER, 0 },
"get or set the baudrate",
0, { B_INT32_TYPE }
},
{ "bits",
{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
{ B_DIRECT_SPECIFIER, B_DIRECT_SPECIFIER, 0 },
"get or set the number of data bits (7 or 8)",
0, { B_INT32_TYPE }
},
{ "stopbits",
{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
{ B_DIRECT_SPECIFIER, B_DIRECT_SPECIFIER, 0 },
"get or set the number of stop bits (1 or 2)",
0, { B_INT32_TYPE }
},
{ "parity",
{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
{ B_DIRECT_SPECIFIER, B_DIRECT_SPECIFIER, 0 },
"get or set the parity (none, even or odd)",
0, { B_STRING_TYPE }
},
{ "flowcontrol",
{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
{ B_DIRECT_SPECIFIER, B_DIRECT_SPECIFIER, 0 },
"get or set the flow control (hardware, software, both, or none)",
0, { B_STRING_TYPE }
},
{ "port",
{ B_GET_PROPERTY, B_SET_PROPERTY, B_DELETE_PROPERTY, 0 },
{ B_DIRECT_SPECIFIER, 0 },
"get or set the port device",
0, { B_STRING_TYPE }
},
{ NULL }
};
const BPropertyInfo SerialApp::kScriptingProperties(sProperties);
SerialApp::SerialApp() SerialApp::SerialApp()
: BApplication(SerialApp::kApplicationSignature) : BApplication(SerialApp::kApplicationSignature)
, fLogFile(NULL) , fLogFile(NULL)
@ -55,7 +98,7 @@ void SerialApp::MessageReceived(BMessage* message)
} else { } else {
fSerialPort.Close(); fSerialPort.Close();
} }
break; return;
} }
case kMsgDataRead: case kMsgDataRead:
{ {
@ -68,13 +111,12 @@ void SerialApp::MessageReceived(BMessage* message)
ssize_t length; ssize_t length;
message->FindData("data", B_RAW_TYPE, (const void**)&bytes, message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
&length); &length);
if (fLogFile->Write(bytes, length) != length) if (fLogFile->Write(bytes, length) != length) {
{
// TODO error handling // TODO error handling
} }
} }
break; return;
} }
case kMsgDataWrite: case kMsgDataWrite:
{ {
@ -82,11 +124,9 @@ void SerialApp::MessageReceived(BMessage* message)
ssize_t size; ssize_t size;
if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes, if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
&size) != B_OK) &size) == B_OK)
break; fSerialPort.Write(bytes, size);
return;
fSerialPort.Write(bytes, size);
break;
} }
case kMsgLogfile: case kMsgLogfile:
{ {
@ -104,7 +144,7 @@ void SerialApp::MessageReceived(BMessage* message)
puts(strerror(error)); puts(strerror(error));
} else } else
debugger("Invalid BMessage received"); debugger("Invalid BMessage received");
break; return;
} }
case kMsgSettings: case kMsgSettings:
{ {
@ -131,11 +171,134 @@ void SerialApp::MessageReceived(BMessage* message)
fSerialPort.SetDataRate(rate); fSerialPort.SetDataRate(rate);
} }
break; return;
} }
default:
BApplication::MessageReceived(message);
} }
// Handle scripting messages
if (message->HasSpecifiers()) {
BMessage specifier;
int32 what;
int32 index;
const char* property;
BMessage reply(B_REPLY);
BMessage settings(kMsgSettings);
bool settingsChanged = false;
if (message->GetCurrentSpecifier(&index, &specifier, &what, &property)
== B_OK) {
switch (kScriptingProperties.FindMatch(message, index, &specifier,
what, property)) {
case 0: // baudrate
if (message->what == B_GET_PROPERTY) {
reply.AddInt32("result", fSerialPort.DataRate());
message->SendReply(&reply);
return;
}
if (message->what == B_SET_PROPERTY) {
int32 rate = message->FindInt32("data");
settingsChanged = true;
settings.AddInt32("baudrate", rate);
}
break;
case 1: // data bits
if (message->what == B_GET_PROPERTY) {
reply.AddInt32("result", fSerialPort.DataBits() + 7);
message->SendReply(&reply);
return;
}
if (message->what == B_SET_PROPERTY) {
int32 bits = message->FindInt32("data");
settingsChanged = true;
settings.AddInt32("databits", bits - 7);
}
break;
case 2: // stop bits
if (message->what == B_GET_PROPERTY) {
reply.AddInt32("result", fSerialPort.StopBits() + 1);
message->SendReply(&reply);
return;
}
if (message->what == B_SET_PROPERTY) {
int32 bits = message->FindInt32("data");
settingsChanged = true;
settings.AddInt32("stopbits", bits - 1);
}
break;
case 3: // parity
{
static const char* strings[] = {"none", "odd", "even"};
if (message->what == B_GET_PROPERTY) {
reply.AddString("result",
strings[fSerialPort.ParityMode()]);
message->SendReply(&reply);
return;
}
if (message->what == B_SET_PROPERTY) {
BString bits = message->FindString("data");
int i;
for (i = 0; i < 3; i++) {
if (bits == strings[i])
break;
}
if (i < 3) {
settingsChanged = true;
settings.AddInt32("parity", i);
}
}
break;
}
case 4: // flow control
{
static const char* strings[] = {"none", "hardware",
"software", "both"};
if (message->what == B_GET_PROPERTY) {
reply.AddString("result",
strings[fSerialPort.FlowControl()]);
message->SendReply(&reply);
return;
}
if (message->what == B_SET_PROPERTY) {
BString bits = message->FindString("data");
int i;
for (i = 0; i < 4; i++) {
if (bits == strings[i])
break;
}
if (i < 4) {
settingsChanged = true;
settings.AddInt32("flowcontrol", i);
}
}
break;
}
case 5: // port
if (message->what == B_GET_PROPERTY) {
reply.AddString("port", GetPort());
message->SendReply(&reply);
} else if (message->what == B_DELETE_PROPERTY
|| message->what == B_SET_PROPERTY) {
BString path = message->FindString("data");
BMessage openMessage(kMsgOpenPort);
openMessage.AddString("port name", path);
PostMessage(&openMessage);
fWindow->PostMessage(&openMessage);
}
return;
}
}
if (settingsChanged) {
PostMessage(&settings);
fWindow->PostMessage(&settings);
return;
}
}
BApplication::MessageReceived(message);
} }
@ -229,3 +392,25 @@ int main(int argc, char** argv)
SerialApp app; SerialApp app;
app.Run(); app.Run();
} }
status_t
SerialApp::GetSupportedSuites(BMessage* message)
{
message->AddString("suites", "suite/vnd.Haiku-SerialPort");
message->AddFlat("messages", &kScriptingProperties);
return BApplication::GetSupportedSuites(message);
}
BHandler*
SerialApp::ResolveSpecifier(BMessage* message, int32 index,
BMessage* specifier, int32 what, const char* property)
{
if (kScriptingProperties.FindMatch(message, index, specifier, what,
property) >= 0)
return this;
return BApplication::ResolveSpecifier(message, index, specifier, what,
property);
}

View File

@ -9,6 +9,7 @@
#include <Application.h> #include <Application.h>
#include <PropertyInfo.h>
#include <SerialPort.h> #include <SerialPort.h>
#include <String.h> #include <String.h>
@ -20,28 +21,33 @@ class SerialWindow;
class SerialApp: public BApplication class SerialApp: public BApplication
{ {
public: public:
SerialApp(); SerialApp();
~SerialApp(); ~SerialApp();
void ReadyToRun(); void ReadyToRun();
void MessageReceived(BMessage* message); void MessageReceived(BMessage* message);
bool QuitRequested(); bool QuitRequested();
const BString& GetPort(); status_t GetSupportedSuites(BMessage* message);
BHandler* ResolveSpecifier(BMessage*, int32, BMessage*, int32,
const char*);
const BString& GetPort();
private: private:
void LoadSettings(); void LoadSettings();
void SaveSettings(); void SaveSettings();
private: private:
BSerialPort fSerialPort; BSerialPort fSerialPort;
sem_id fSerialLock; sem_id fSerialLock;
SerialWindow* fWindow; SerialWindow* fWindow;
BFile* fLogFile; BFile* fLogFile;
BString fPortPath; BString fPortPath;
static status_t PollSerial(void*); static status_t PollSerial(void*);
static const char* kApplicationSignature; static const BPropertyInfo kScriptingProperties;
static const char* kApplicationSignature;
}; };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012, Adrien Destugues, pulkomandy@gmail.com * Copyright 2012-2014, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under the terms of the MIT licence. * Distributed under the terms of the MIT licence.
*/ */
@ -242,6 +242,7 @@ void SerialWindow::MenusBeginning()
new BMessage(kMsgOpenPort), 'Z', B_OPTION_KEY); new BMessage(kMsgOpenPort), 'Z', B_OPTION_KEY);
if (!connected) if (!connected)
disconnect->SetEnabled(false); disconnect->SetEnabled(false);
disconnect->SetTarget(be_app);
fConnectionMenu->AddItem(disconnect); fConnectionMenu->AddItem(disconnect);
} else { } else {
BMenuItem* noDevices = new BMenuItem("<no serial port available>", NULL); BMenuItem* noDevices = new BMenuItem("<no serial port available>", NULL);
@ -253,16 +254,8 @@ void SerialWindow::MenusBeginning()
void SerialWindow::MessageReceived(BMessage* message) void SerialWindow::MessageReceived(BMessage* message)
{ {
switch(message->what) switch (message->what)
{ {
case kMsgOpenPort:
{
BMenuItem* disconnectMenu;
if (message->FindPointer("source", (void**)&disconnectMenu) == B_OK)
disconnectMenu->SetMarked(false);
be_app->PostMessage(new BMessage(*message));
break;
}
case kMsgDataRead: case kMsgDataRead:
{ {
const char* bytes; const char* bytes;
@ -270,7 +263,7 @@ void SerialWindow::MessageReceived(BMessage* message)
if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes, if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
&length) == B_OK) &length) == B_OK)
fTermView->PushBytes(bytes, length); fTermView->PushBytes(bytes, length);
break; return;
} }
case kMsgLogfile: case kMsgLogfile:
{ {
@ -281,7 +274,7 @@ void SerialWindow::MessageReceived(BMessage* message)
fLogFilePanel->SetMessage(message); fLogFilePanel->SetMessage(message);
} }
fLogFilePanel->Show(); fLogFilePanel->Show();
break; return;
} }
case kMsgSettings: case kMsgSettings:
{ {
@ -348,9 +341,9 @@ void SerialWindow::MessageReceived(BMessage* message)
} }
} }
break; return;
} }
default:
BWindow::MessageReceived(message);
} }
BWindow::MessageReceived(message);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012, Adrien Destugues, pulkomandy@gmail.com * Copyright 2012-2014, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under the terms of the MIT licence. * Distributed under the terms of the MIT licence.
*/ */
@ -15,24 +15,25 @@ class TermView;
class SerialWindow: public BWindow class SerialWindow: public BWindow
{ {
public: public:
SerialWindow(); SerialWindow();
~SerialWindow(); ~SerialWindow();
void MenusBeginning();
void MessageReceived(BMessage* message);
void MenusBeginning();
void MessageReceived(BMessage* message);
private: private:
TermView* fTermView; TermView* fTermView;
BMenu* fConnectionMenu; BMenu* fConnectionMenu;
BMenu* fDatabitsMenu; BMenu* fDatabitsMenu;
BMenu* fStopbitsMenu; BMenu* fStopbitsMenu;
BMenu* fParityMenu; BMenu* fParityMenu;
BMenu* fFlowcontrolMenu; BMenu* fFlowcontrolMenu;
BMenu* fBaudrateMenu; BMenu* fBaudrateMenu;
BFilePanel* fLogFilePanel; BFilePanel* fLogFilePanel;
static const int kBaudrates[]; static const int kBaudrates[];
static const int kBaudrateConstants[]; static const int kBaudrateConstants[];
static const char* kWindowTitle; static const char* kWindowTitle;
}; };