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"
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()
: BApplication(SerialApp::kApplicationSignature)
, fLogFile(NULL)
@ -55,7 +98,7 @@ void SerialApp::MessageReceived(BMessage* message)
} else {
fSerialPort.Close();
}
break;
return;
}
case kMsgDataRead:
{
@ -68,13 +111,12 @@ void SerialApp::MessageReceived(BMessage* message)
ssize_t length;
message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
&length);
if (fLogFile->Write(bytes, length) != length)
{
if (fLogFile->Write(bytes, length) != length) {
// TODO error handling
}
}
break;
return;
}
case kMsgDataWrite:
{
@ -82,11 +124,9 @@ void SerialApp::MessageReceived(BMessage* message)
ssize_t size;
if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
&size) != B_OK)
break;
fSerialPort.Write(bytes, size);
break;
&size) == B_OK)
fSerialPort.Write(bytes, size);
return;
}
case kMsgLogfile:
{
@ -104,7 +144,7 @@ void SerialApp::MessageReceived(BMessage* message)
puts(strerror(error));
} else
debugger("Invalid BMessage received");
break;
return;
}
case kMsgSettings:
{
@ -131,11 +171,134 @@ void SerialApp::MessageReceived(BMessage* message)
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;
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 <PropertyInfo.h>
#include <SerialPort.h>
#include <String.h>
@ -20,28 +21,33 @@ class SerialWindow;
class SerialApp: public BApplication
{
public:
SerialApp();
~SerialApp();
void ReadyToRun();
void MessageReceived(BMessage* message);
bool QuitRequested();
SerialApp();
~SerialApp();
void ReadyToRun();
void MessageReceived(BMessage* message);
bool QuitRequested();
const BString& GetPort();
status_t GetSupportedSuites(BMessage* message);
BHandler* ResolveSpecifier(BMessage*, int32, BMessage*, int32,
const char*);
const BString& GetPort();
private:
void LoadSettings();
void SaveSettings();
void LoadSettings();
void SaveSettings();
private:
BSerialPort fSerialPort;
sem_id fSerialLock;
SerialWindow* fWindow;
BFile* fLogFile;
BString fPortPath;
BSerialPort fSerialPort;
sem_id fSerialLock;
SerialWindow* fWindow;
BFile* fLogFile;
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.
*/
@ -242,6 +242,7 @@ void SerialWindow::MenusBeginning()
new BMessage(kMsgOpenPort), 'Z', B_OPTION_KEY);
if (!connected)
disconnect->SetEnabled(false);
disconnect->SetTarget(be_app);
fConnectionMenu->AddItem(disconnect);
} else {
BMenuItem* noDevices = new BMenuItem("<no serial port available>", NULL);
@ -253,16 +254,8 @@ void SerialWindow::MenusBeginning()
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:
{
const char* bytes;
@ -270,7 +263,7 @@ void SerialWindow::MessageReceived(BMessage* message)
if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
&length) == B_OK)
fTermView->PushBytes(bytes, length);
break;
return;
}
case kMsgLogfile:
{
@ -281,7 +274,7 @@ void SerialWindow::MessageReceived(BMessage* message)
fLogFilePanel->SetMessage(message);
}
fLogFilePanel->Show();
break;
return;
}
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.
*/
@ -15,24 +15,25 @@ class TermView;
class SerialWindow: public BWindow
{
public:
SerialWindow();
~SerialWindow();
SerialWindow();
~SerialWindow();
void MenusBeginning();
void MessageReceived(BMessage* message);
void MenusBeginning();
void MessageReceived(BMessage* message);
private:
TermView* fTermView;
TermView* fTermView;
BMenu* fConnectionMenu;
BMenu* fDatabitsMenu;
BMenu* fStopbitsMenu;
BMenu* fParityMenu;
BMenu* fFlowcontrolMenu;
BMenu* fBaudrateMenu;
BFilePanel* fLogFilePanel;
BMenu* fConnectionMenu;
BMenu* fDatabitsMenu;
BMenu* fStopbitsMenu;
BMenu* fParityMenu;
BMenu* fFlowcontrolMenu;
BMenu* fBaudrateMenu;
BFilePanel* fLogFilePanel;
static const int kBaudrates[];
static const int kBaudrateConstants[];
static const char* kWindowTitle;
static const int kBaudrates[];
static const int kBaudrateConstants[];
static const char* kWindowTitle;
};