xref: /haiku/src/apps/serialconnect/SerialApp.cpp (revision 41660cabf4f1cabafed672078cf3ee6d96eee073)
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 SerialApp::SerialApp()
22 	: BApplication(SerialApp::kApplicationSignature)
23 	, fLogFile(NULL)
24 {
25 	fWindow = new SerialWindow();
26 
27 	fSerialLock = create_sem(0, "Serial port lock");
28 	thread_id id = spawn_thread(PollSerial, "Serial port poller",
29 		B_LOW_PRIORITY, this);
30 	resume_thread(id);
31 }
32 
33 
34 SerialApp::~SerialApp()
35 {
36 	delete fLogFile;
37 }
38 
39 
40 void SerialApp::ReadyToRun()
41 {
42 	LoadSettings();
43 	fWindow->Show();
44 }
45 
46 
47 void SerialApp::MessageReceived(BMessage* message)
48 {
49 	switch (message->what) {
50 		case kMsgOpenPort:
51 		{
52 			if (message->FindString("port name", &fPortPath) == B_OK) {
53 				fSerialPort.Open(fPortPath);
54 				release_sem(fSerialLock);
55 			} else {
56 				fSerialPort.Close();
57 			}
58 			break;
59 		}
60 		case kMsgDataRead:
61 		{
62 			// forward the message to the window, which will display the
63 			// incoming data
64 			fWindow->PostMessage(message);
65 
66 			if (fLogFile) {
67 				const char* bytes;
68 				ssize_t length;
69 				message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
70 					&length);
71 				if (fLogFile->Write(bytes, length) != length)
72 				{
73 					// TODO error handling
74 				}
75 			}
76 
77 			break;
78 		}
79 		case kMsgDataWrite:
80 		{
81 			const char* bytes;
82 			ssize_t size;
83 
84 			if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
85 					&size) != B_OK)
86 				break;
87 
88 			fSerialPort.Write(bytes, size);
89 			break;
90 		}
91 		case kMsgLogfile:
92 		{
93 			entry_ref parent;
94 			const char* filename;
95 
96 			if (message->FindRef("directory", &parent) == B_OK
97 				&& message->FindString("name", &filename) == B_OK) {
98 				delete fLogFile;
99 				BDirectory directory(&parent);
100 				fLogFile = new BFile(&directory, filename,
101 					B_WRITE_ONLY | B_CREATE_FILE | B_OPEN_AT_END);
102 				status_t error = fLogFile->InitCheck();
103 				if (error != B_OK)
104 					puts(strerror(error));
105 			} else
106 				debugger("Invalid BMessage received");
107 			break;
108 		}
109 		case kMsgSettings:
110 		{
111 			int32 baudrate;
112 			stop_bits stopBits;
113 			data_bits dataBits;
114 			parity_mode parity;
115 			uint32 flowcontrol;
116 
117 			if (message->FindInt32("databits", (int32*)&dataBits) == B_OK)
118 				fSerialPort.SetDataBits(dataBits);
119 
120 			if (message->FindInt32("stopbits", (int32*)&stopBits) == B_OK)
121 				fSerialPort.SetStopBits(stopBits);
122 
123 			if (message->FindInt32("parity", (int32*)&parity) == B_OK)
124 				fSerialPort.SetParityMode(parity);
125 
126 			if (message->FindInt32("flowcontrol", (int32*)&flowcontrol) == B_OK)
127 				fSerialPort.SetFlowControl(flowcontrol);
128 
129 			if (message->FindInt32("baudrate", &baudrate) == B_OK) {
130 				data_rate rate = (data_rate)baudrate;
131 				fSerialPort.SetDataRate(rate);
132 			}
133 
134 			break;
135 		}
136 		default:
137 			BApplication::MessageReceived(message);
138 	}
139 }
140 
141 
142 bool SerialApp::QuitRequested()
143 {
144 	if (BApplication::QuitRequested()) {
145 		SaveSettings();
146 		return true;
147 	}
148 	return false;
149 }
150 
151 
152 const BString& SerialApp::GetPort()
153 {
154 	return fPortPath;
155 }
156 
157 
158 void SerialApp::LoadSettings()
159 {
160 	BPath path;
161 	find_directory(B_USER_SETTINGS_DIRECTORY, &path);
162 	path.Append("SerialConnect");
163 
164 	BFile file(path.Path(), B_READ_ONLY);
165 	BMessage message(kMsgSettings);
166 	if (message.Unflatten(&file) != B_OK) {
167 		message.AddInt32("parity", fSerialPort.ParityMode());
168 		message.AddInt32("databits", fSerialPort.DataBits());
169 		message.AddInt32("stopbits", fSerialPort.StopBits());
170 		message.AddInt32("baudrate", fSerialPort.DataRate());
171 		message.AddInt32("flowcontrol", fSerialPort.FlowControl());
172 	}
173 
174 	be_app->PostMessage(&message);
175 	fWindow->PostMessage(&message);
176 }
177 
178 
179 void SerialApp::SaveSettings()
180 {
181 	BMessage message(kMsgSettings);
182 	message.AddInt32("parity", fSerialPort.ParityMode());
183 	message.AddInt32("databits", fSerialPort.DataBits());
184 	message.AddInt32("stopbits", fSerialPort.StopBits());
185 	message.AddInt32("baudrate", fSerialPort.DataRate());
186 	message.AddInt32("flowcontrol", fSerialPort.FlowControl());
187 
188 	BPath path;
189 	find_directory(B_USER_SETTINGS_DIRECTORY, &path);
190 	path.Append("SerialConnect");
191 
192 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE);
193 	message.Flatten(&file);
194 }
195 
196 
197 /* static */
198 status_t SerialApp::PollSerial(void*)
199 {
200 	SerialApp* application = (SerialApp*)be_app;
201 	char buffer[256];
202 
203 	for (;;) {
204 		ssize_t bytesRead;
205 
206 		bytesRead = application->fSerialPort.Read(buffer, sizeof(buffer));
207 		if (bytesRead == B_FILE_ERROR) {
208 			// Port is not open - wait for it and start over
209 			acquire_sem(application->fSerialLock);
210 		} else if (bytesRead > 0) {
211 			// We read something, forward it to the app for handling
212 			BMessage* serialData = new BMessage(kMsgDataRead);
213 			serialData->AddData("data", B_RAW_TYPE, buffer, bytesRead);
214 			be_app_messenger.SendMessage(serialData);
215 		}
216 	}
217 
218 	// Should not reach this line anyway...
219 	return B_OK;
220 }
221 
222 
223 const char* SerialApp::kApplicationSignature
224 	= "application/x-vnd.haiku.SerialConnect";
225 
226 
227 int main(int argc, char** argv)
228 {
229 	SerialApp app;
230 	app.Run();
231 }
232