xref: /haiku/src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 
8 WCHDevice::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.
21 status_t
22 WCHDevice::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 
60 status_t
61 WCHDevice::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 
95 status_t
96 WCHDevice::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 
183 status_t
184 WCHDevice::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 
217 status_t
218 WCHDevice::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