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