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
16#include "SerialWindow.h"
17
18
19SerialApp::SerialApp()
20	: BApplication(SerialApp::kApplicationSignature)
21	, fLogFile(NULL)
22{
23	fWindow = new SerialWindow();
24
25	fSerialLock = create_sem(0, "Serial port lock");
26	thread_id id = spawn_thread(PollSerial, "Serial port poller",
27		B_LOW_PRIORITY, this);
28	resume_thread(id);
29}
30
31
32SerialApp::~SerialApp()
33{
34	delete fLogFile;
35}
36
37
38void SerialApp::ReadyToRun()
39{
40	fWindow->Show();
41}
42
43
44void SerialApp::MessageReceived(BMessage* message)
45{
46	switch(message->what)
47	{
48		case kMsgOpenPort:
49		{
50			const char* portName;
51			if(message->FindString("port name", &portName) == B_OK)
52			{
53				fSerialPort.Open(portName);
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			{
68				const char* bytes;
69				ssize_t length;
70				message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
71					&length);
72				if(fLogFile->Write(bytes, length) != length)
73				{
74					// TODO error handling
75				}
76			}
77
78			break;
79		}
80		case kMsgDataWrite:
81		{
82			const char* bytes;
83			ssize_t size;
84
85			message->FindData("data", B_RAW_TYPE, (const void**)&bytes, &size);
86
87			if (bytes[0] == '\n') {
88				size = 2;
89				bytes = "\r\n";
90			}
91			fSerialPort.Write(bytes, size);
92			break;
93		}
94		case kMsgLogfile:
95		{
96			entry_ref parent;
97			const char* filename;
98
99			if (message->FindRef("directory", &parent) == B_OK
100				&& message->FindString("name", &filename) == B_OK)
101			{
102				delete fLogFile;
103				BDirectory directory(&parent);
104				fLogFile = new BFile(&directory, filename,
105					B_WRITE_ONLY | B_CREATE_FILE | B_OPEN_AT_END);
106				status_t error = fLogFile->InitCheck();
107				if(error != B_OK)
108				{
109					puts(strerror(error));
110				}
111			} else {
112				debugger("Invalid BMessage received");
113			}
114		}
115		case kMsgSettings:
116		{
117			int32 baudrate;
118			stop_bits stopBits;
119			data_bits dataBits;
120			parity_mode parity;
121			uint32 flowcontrol;
122
123			if(message->FindInt32("databits", (int32*)&dataBits) == B_OK)
124				fSerialPort.SetDataBits(dataBits);
125
126			if(message->FindInt32("stopbits", (int32*)&stopBits) == B_OK)
127				fSerialPort.SetStopBits(stopBits);
128
129			if(message->FindInt32("parity", (int32*)&parity) == B_OK)
130				fSerialPort.SetParityMode(parity);
131
132			if(message->FindInt32("flowcontrol", (int32*)&flowcontrol) == B_OK)
133				fSerialPort.SetFlowControl(flowcontrol);
134
135			if(message->FindInt32("baudrate", &baudrate) == B_OK) {
136				data_rate rate;
137				switch(baudrate) {
138					case 50:
139						rate = B_50_BPS;
140						break;
141					case 75:
142						rate = B_75_BPS;
143						break;
144					case 110:
145						rate = B_110_BPS;
146						break;
147					case 134:
148						rate = B_134_BPS;
149						break;
150					case 150:
151						rate = B_150_BPS;
152						break;
153					case 200:
154						rate = B_200_BPS;
155						break;
156					case 300:
157						rate = B_300_BPS;
158						break;
159					case 600:
160						rate = B_600_BPS;
161						break;
162					case 1200:
163						rate = B_1200_BPS;
164						break;
165					case 1800:
166						rate = B_1800_BPS;
167						break;
168					case 2400:
169						rate = B_2400_BPS;
170						break;
171					case 4800:
172						rate = B_4800_BPS;
173						break;
174					case 9600:
175						rate = B_9600_BPS;
176						break;
177					case 19200:
178						rate = B_19200_BPS;
179						break;
180					case 31250:
181						rate = B_31250_BPS;
182						break;
183					case 38400:
184						rate = B_38400_BPS;
185						break;
186					case 57600:
187						rate = B_57600_BPS;
188						break;
189					case 115200:
190						rate = B_115200_BPS;
191						break;
192					case 230400:
193						rate = B_230400_BPS;
194						break;
195					default:
196						rate = B_0_BPS;
197						break;
198				}
199				fSerialPort.SetDataRate(rate);
200			}
201
202			break;
203		}
204		default:
205			BApplication::MessageReceived(message);
206	}
207}
208
209
210/* static */
211status_t SerialApp::PollSerial(void*)
212{
213	SerialApp* application = (SerialApp*)be_app;
214	char buffer[256];
215
216	for(;;)
217	{
218		ssize_t bytesRead;
219
220		bytesRead = application->fSerialPort.Read(buffer, 256);
221		if (bytesRead == B_FILE_ERROR)
222		{
223			// Port is not open - wait for it and start over
224			acquire_sem(application->fSerialLock);
225		} else if (bytesRead > 0) {
226			// We read something, forward it to the app for handling
227			BMessage* serialData = new BMessage(kMsgDataRead);
228			serialData->AddData("data", B_RAW_TYPE, buffer, bytesRead);
229			be_app_messenger.SendMessage(serialData);
230		}
231	}
232
233	// Should not reach this line anyway...
234	return B_OK;
235}
236
237const char* SerialApp::kApplicationSignature
238	= "application/x-vnd.haiku.SerialConnect";
239
240
241int main(int argc, char** argv)
242{
243	SerialApp app;
244	app.Run();
245}
246