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