1/*
2 * Copyright 2001-2017 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *   Michael Pfeiffer
7 *   Adrien Destugues <pulkomandy@pulkomandy.tk>
8 */
9
10
11#include <unistd.h>
12#include <stdio.h>
13#include <termios.h>
14
15#include <StorageKit.h>
16#include <SupportKit.h>
17
18#include "PrintTransportAddOn.h"
19
20
21class SerialTransport : public BDataIO {
22public:
23	SerialTransport(BDirectory* printer, BMessage* msg);
24	~SerialTransport();
25
26	status_t InitCheck() { return fFile > -1 ? B_OK : B_ERROR; }
27
28	ssize_t Read(void* buffer, size_t size);
29	ssize_t Write(const void* buffer, size_t size);
30
31private:
32	int fFile;
33};
34
35
36// Impelmentation of SerialTransport
37SerialTransport::SerialTransport(BDirectory* printer, BMessage* msg)
38	: fFile(-1)
39{
40	char address[80];
41	char device[B_PATH_NAME_LENGTH];
42	bool bidirectional = true;
43
44	unsigned int size = printer->ReadAttr("transport_address", B_STRING_TYPE, 0,
45		address, sizeof(address));
46	if (size <= 0 || size >= sizeof(address)) return;
47	address[size] = 0; // make sure string is 0-terminated
48
49	strcat(strcpy(device, "/dev/ports/"), address);
50	fFile = open(device, O_RDWR | O_EXCL, 0);
51	if (fFile < 0) {
52		// Try unidirectional access mode
53		bidirectional = false;
54		fFile = open(device, O_WRONLY | O_EXCL, 0);
55	}
56
57	if (fFile < 0)
58		return;
59
60	int32 baudrate;
61	size = printer->ReadAttr("transport_baudrate", B_INT32_TYPE, 0,
62		&baudrate, sizeof(baudrate));
63
64	struct termios options;
65	tcgetattr(fFile, &options);
66
67	cfmakeraw(&options);
68	options.c_cc[VTIME] = 10; // wait for data at most for 1 second
69	options.c_cc[VMIN] = 0; // allow to return 0 chars
70
71	if (size == sizeof(baudrate)) {
72		// Printer driver asked for a specific baudrate, configure it
73		cfsetispeed(&options, baudrate);
74		cfsetospeed(&options, baudrate);
75	}
76
77	tcsetattr(fFile, TCSANOW, &options);
78
79	if (msg == NULL) {
80		// Caller don't care about transport init message output content...
81		return;
82	}
83
84	msg->what = 'okok';
85	msg->AddBool("bidirectional", bidirectional);
86	msg->AddString("_serial/DeviceName", device);
87
88}
89
90
91SerialTransport::~SerialTransport()
92{
93	if (InitCheck() == B_OK)
94		close(fFile);
95}
96
97
98ssize_t
99SerialTransport::Read(void* buffer, size_t size)
100{
101	return read(fFile, buffer, size);
102}
103
104
105ssize_t
106SerialTransport::Write(const void* buffer, size_t size)
107{
108	return write(fFile, buffer, size);
109}
110
111
112BDataIO*
113instantiate_transport(BDirectory* printer, BMessage* msg)
114{
115	SerialTransport* transport = new SerialTransport(printer, msg);
116	if (transport->InitCheck() == B_OK)
117		return transport;
118
119	delete transport;
120	return NULL;
121}
122
123
124status_t
125list_transport_ports(BMessage* msg)
126{
127	BDirectory dir("/dev/ports");
128	status_t rc;
129
130	if ((rc=dir.InitCheck()) != B_OK)
131		return rc;
132
133	if ((rc=dir.Rewind()) != B_OK)
134		return rc;
135
136	entry_ref ref;
137	while (dir.GetNextRef(&ref) == B_OK)
138		msg->AddString("port_id", ref.name);
139
140	return B_OK;
141}
142