1/*
2 * Copyright 2022, Gerasim Troeglazov <3dEyes@gmail.com>
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "WinChipHead.h"
7
8WCHDevice::WCHDevice(usb_device device, uint16 vendorID, uint16 productID,
9	const char *description)
10	:	SerialDevice(device, vendorID, productID, description),
11		fChipVersion(0),
12		fStatusMCR(0),
13		fStatusLCR(CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX | CH34X_LCR_CS8),
14		fDataRate(CH34X_SIO_9600)
15{
16}
17
18
19// Called for each configuration of the device. Return B_OK if the given
20// configuration sounds like it is the usb serial one.
21status_t
22WCHDevice::AddDevice(const usb_configuration_info *config)
23{
24	TRACE_FUNCALLS("> WCHDevice::AddDevice(%08x, %08x)\n", this, config);
25
26	status_t status = ENODEV;
27	if (config->interface_count > 0) {
28		int32 pipesSet = 0;
29		usb_interface_info *interface = config->interface[0].active;
30		for (size_t i = 0; i < interface->endpoint_count; i++) {
31			usb_endpoint_info *endpoint = &interface->endpoint[i];
32			if (endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) {
33				if (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) {
34					SetReadPipe(endpoint->handle);
35					if (++pipesSet >= 3)
36						break;
37				} else {
38					if (endpoint->descr->endpoint_address) {
39						SetControlPipe(endpoint->handle);
40						SetWritePipe(endpoint->handle);
41						pipesSet += 2;
42						if (pipesSet >= 3)
43							break;
44					}
45				}
46			}
47		}
48
49		if (pipesSet >= 3) {
50			status = B_OK;
51		}
52	}
53
54	TRACE_FUNCRET("< WCHDevice::AddDevice() returns: 0x%08x\n", status);
55
56	return status;
57}
58
59
60status_t
61WCHDevice::ResetDevice()
62{
63	TRACE_FUNCALLS("> WCHDevice::ResetDevice(0x%08x)\n", this);
64	size_t length = 0;
65
66	uint8 inputBuffer[CH34X_INPUT_BUF_SIZE];
67	status_t status = gUSBModule->send_request(Device(),
68		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
69		CH34X_REQ_READ_VERSION, 0, 0, sizeof(inputBuffer), inputBuffer, &length);
70
71	if (status == B_OK) {
72		fChipVersion = inputBuffer[0];
73		TRACE_ALWAYS("= WCHDevice::ResetDevice(): Chip version: 0x%02x\n", fChipVersion);
74	} else {
75		TRACE_ALWAYS("= WCHDevice::ResetDevice(): Can't get chip version: 0x%08x\n",
76			status);
77		return status;
78	}
79
80	status = gUSBModule->send_request(Device(),
81		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
82		CH34X_REQ_SERIAL_INIT, 0, 0, 0, NULL, &length);
83
84	if (status != B_OK) {
85		TRACE_ALWAYS("= WCHDevice::ResetDevice(): init failed\n");
86		return status;
87	}
88
89	status = WriteConfig(fDataRate, fStatusLCR, fStatusMCR);
90
91	TRACE_FUNCRET("< WCHDevice::ResetDevice() returns:%08x\n", status);
92	return status;
93}
94
95status_t
96WCHDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
97{
98	TRACE_FUNCALLS("> WCHDevice::SetLineCoding(0x%08x, {%d, 0x%02x, 0x%02x, 0x%02x})\n",
99		this, lineCoding->speed, lineCoding->stopbits, lineCoding->parity,
100		lineCoding->databits);
101
102	fStatusLCR = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
103
104	switch (lineCoding->stopbits) {
105		case USB_CDC_LINE_CODING_1_STOPBIT:
106			break;
107		case USB_CDC_LINE_CODING_2_STOPBITS:
108			fStatusLCR |= CH34X_LCR_STOP_BITS_2;
109			break;
110		default:
111			TRACE_ALWAYS("= WCHDevice::SetLineCoding(): Wrong stopbits param: %d\n",
112				lineCoding->stopbits);
113			break;
114	}
115
116	switch (lineCoding->parity) {
117		case USB_CDC_LINE_CODING_NO_PARITY:
118			break;
119		case USB_CDC_LINE_CODING_EVEN_PARITY:
120			fStatusLCR |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_PAR_EVEN;
121			break;
122		case USB_CDC_LINE_CODING_ODD_PARITY:
123			fStatusLCR |= CH34X_LCR_ENABLE_PAR;
124			break;
125		default:
126			TRACE_ALWAYS("= WCHDevice::SetLineCoding(): Wrong parity param: %d\n",
127				lineCoding->parity);
128			break;
129	}
130
131	switch (lineCoding->databits) {
132		case 5:
133			fStatusLCR |= CH34X_LCR_CS5;
134			break;
135		case 6:
136			fStatusLCR |= CH34X_LCR_CS6;
137			break;
138		case 7:
139			fStatusLCR |= CH34X_LCR_CS7;
140			break;
141		case 8:
142			fStatusLCR |= CH34X_LCR_CS8;
143			break;
144		default:
145			fStatusLCR |= CH34X_LCR_CS8;
146			TRACE_ALWAYS("= WCHDevice::SetLineCoding(): Wrong databits param: %d\n",
147				lineCoding->databits);
148			break;
149	}
150
151	switch (lineCoding->speed) {
152		case 600: fDataRate = CH34X_SIO_600; break;
153		case 1200: fDataRate = CH34X_SIO_1200; break;
154		case 1800: fDataRate = CH34X_SIO_1800; break;
155		case 2400: fDataRate = CH34X_SIO_2400; break;
156		case 4800: fDataRate = CH34X_SIO_4800; break;
157		case 9600: fDataRate = CH34X_SIO_9600; break;
158		case 19200: fDataRate = CH34X_SIO_19200; break;
159		case 31250: fDataRate = CH34X_SIO_31250; break;
160		case 38400: fDataRate = CH34X_SIO_38400; break;
161		case 57600: fDataRate = CH34X_SIO_57600; break;
162		case 115200: fDataRate = CH34X_SIO_115200; break;
163		case 230400: fDataRate = CH34X_SIO_230400; break;
164		default:
165			fDataRate = CH34X_SIO_9600;
166			TRACE_ALWAYS("= WCHDevice::SetLineCoding(): Datarate: %d is not "
167				"supported by this hardware. Defaulted to %d\n",
168				lineCoding->speed, CH34X_DEFAULT_BAUD_RATE);
169			break;
170	}
171
172	status_t status = WriteConfig(fDataRate, fStatusLCR, fStatusMCR);
173
174	if (status != B_OK)
175		TRACE_ALWAYS("= WCHDevice::SetLineCoding(): WriteConfig failed\n");
176
177	TRACE_FUNCRET("< WCHDevice::SetLineCoding() returns: 0x%08x\n", status);
178
179	return status;
180}
181
182
183status_t
184WCHDevice::SetControlLineState(uint16 state)
185{
186	TRACE_FUNCALLS("> WCHDevice::SetControlLineState(0x%08x, 0x%04x)\n",
187		this, state);
188
189	fStatusMCR = 0;
190
191	if (state & USB_CDC_CONTROL_SIGNAL_STATE_RTS)
192		fStatusMCR |= CH34X_BIT_RTS;
193
194	if (state & USB_CDC_CONTROL_SIGNAL_STATE_DTR)
195		fStatusMCR |= CH34X_BIT_DTR;
196
197	size_t length = 0;
198	status_t status = 0;
199
200	if (fChipVersion < CH34X_VER_20) {
201		status = gUSBModule->send_request(Device(),
202			USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
203			CH34X_REQ_WRITE_REG, CH34X_REG_STAT1 | (CH34X_REG_STAT1 << 8),
204			~fStatusMCR | (~fStatusMCR << 8), 0, NULL, &length);
205	} else {
206		status = gUSBModule->send_request(Device(),
207			USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
208			CH34X_REQ_MODEM_CTRL, ~fStatusMCR, 0, 0, NULL, &length);
209	}
210
211	TRACE_FUNCRET("< WCHDevice::SetControlLineState() returns: 0x%08x\n",
212		status);
213
214	return status;
215}
216
217status_t
218WCHDevice::WriteConfig(uint16 dataRate, uint8 lcr, uint8 mcr)
219{
220	size_t length = 0;
221	status_t status = gUSBModule->send_request(Device(),
222		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
223		CH34X_REQ_WRITE_REG, CH34X_REG_BPS_PRE | (CH34X_REG_BPS_DIV << 8),
224		dataRate | CH34X_BPS_PRE_IMM, 0, NULL, &length);
225
226	if (status != B_OK) {
227		TRACE_ALWAYS("= WCHDevice::WriteConfig(): datarate request failed: 0x%08x\n",
228			status);
229		return status;
230	}
231
232	status = gUSBModule->send_request(Device(),
233		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
234		CH34X_REQ_WRITE_REG, CH34X_REG_LCR | (CH34X_REG_LCR2 << 8),
235		lcr, 0, NULL, &length);
236
237	if (status != B_OK) {
238		TRACE_ALWAYS("= WCHDevice::WriteConfig(): LCR request failed: 0x%08x\n",
239			status);
240		return status;
241	}
242
243	status = gUSBModule->send_request(Device(),
244		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
245		CH34X_REQ_MODEM_CTRL, ~mcr, 0, 0, NULL, &length);
246
247	if (status != B_OK)
248		TRACE_ALWAYS("= WCHDevice::WriteConfig(): handshake failed: 0x%08x\n", status);
249
250	return status;
251}
252