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