xref: /haiku/src/add-ons/kernel/drivers/ports/usb_serial/FTDI.cpp (revision c42868a015daa160e093679b2637b1cf9f0b26ba)
1 /*
2  * Copyright (c) 2007-2008 by Michael Lotz
3  * Heavily based on the original usb_serial driver which is:
4  *
5  * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li>
6  * Distributed under the terms of the MIT License.
7  *
8  * Authors:
9  *		Alexander von Gluck IV, kallisti5@unixzen.com
10  */
11 
12 
13 #include "FTDI.h"
14 #include "FTDIRegs.h"
15 
16 
17 FTDIDevice::FTDIDevice(usb_device device, uint16 vendorID, uint16 productID,
18 	const char *description)
19 	:	SerialDevice(device, vendorID, productID, description),
20 		fHeaderLength(0),
21 		fStatusMSR(0),
22 		fStatusLSR(0)
23 {
24 }
25 
26 
27 status_t
28 FTDIDevice::AddDevice(const usb_configuration_info *config)
29 {
30 	TRACE_FUNCALLS("> FTDIDevice::AddDevice(%08x, %08x)\n", this, config);
31 	status_t status = ENODEV;
32 	if (config->interface_count > 0) {
33 		int32 pipesSet = 0;
34 		usb_interface_info *interface = config->interface[0].active;
35 		for (size_t i = 0; i < interface->endpoint_count; i++) {
36 			usb_endpoint_info *endpoint = &interface->endpoint[i];
37 			if (endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) {
38 				if (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) {
39 					SetReadPipe(endpoint->handle);
40 					if (++pipesSet >= 3)
41 						break;
42 				} else {
43 					if (endpoint->descr->endpoint_address) {
44 						SetControlPipe(endpoint->handle);
45 						SetWritePipe(endpoint->handle);
46 						pipesSet += 2;
47 						if (pipesSet >= 3)
48 							break;
49 					}
50 				}
51 			}
52 		}
53 
54 		if (pipesSet >= 3) {
55 			if (ProductID() == 0x8372) // AU100AX
56 				fHeaderLength = 1;
57 			else
58 				fHeaderLength = 0;
59 
60 			status = B_OK;
61 		}
62 	}
63 
64 	TRACE_FUNCRET("< FTDIDevice::AddDevice() returns: 0x%08x\n", status);
65 	return status;
66 }
67 
68 
69 status_t
70 FTDIDevice::ResetDevice()
71 {
72 	TRACE_FUNCALLS("> FTDIDevice::ResetDevice(0x%08x)\n", this);
73 
74 	size_t length = 0;
75 	status_t status = gUSBModule->send_request(Device(),
76 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
77 		FTDI_SIO_RESET, FTDI_SIO_RESET_SIO,
78 		FTDI_PIT_DEFAULT, 0, NULL, &length);
79 
80 	TRACE_FUNCRET("< FTDIDevice::ResetDevice() returns:%08x\n", status);
81 	return status;
82 }
83 
84 
85 status_t
86 FTDIDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
87 {
88 	TRACE_FUNCALLS("> FTDIDevice::SetLineCoding(0x%08x, {%d, 0x%02x, 0x%02x, 0x%02x})\n",
89 		this, lineCoding->speed, lineCoding->stopbits, lineCoding->parity,
90 		lineCoding->databits);
91 
92 	int32 rate = 0;
93 	if (ProductID() == 0x8372) {
94 		// AU100AX
95 		switch (lineCoding->speed) {
96 			case 300: rate = ftdi_sio_b300; break;
97 			case 600: rate = ftdi_sio_b600; break;
98 			case 1200: rate = ftdi_sio_b1200; break;
99 			case 2400: rate = ftdi_sio_b2400; break;
100 			case 4800: rate = ftdi_sio_b4800; break;
101 			case 9600: rate = ftdi_sio_b9600; break;
102 			case 19200: rate = ftdi_sio_b19200; break;
103 			case 38400: rate = ftdi_sio_b38400; break;
104 			case 57600: rate = ftdi_sio_b57600; break;
105 			case 115200: rate = ftdi_sio_b115200; break;
106 			default:
107 				rate = ftdi_sio_b19200;
108 				TRACE_ALWAYS("= FTDIDevice::SetLineCoding(): Datarate: %d is "
109 					"not supported by this hardware. Defaulted to %d\n",
110 					lineCoding->speed, rate);
111 			break;
112 		}
113 	} else {
114 		/* Compute baudrate register value as documented in AN232B-05 from FTDI.
115 		 Bits 13-0 are the integer divider, and bits 16-14 are the fractional
116 		 divider setting. 3Mbaud and 2Mbaud are special values, and at such
117 		 high speeds the use of the fractional divider is not possible. */
118 		if (lineCoding->speed == 3000000)
119 			rate = 0;
120 		else if (lineCoding->speed == 2000000)
121 			rate = 1;
122 		else {
123 			if (lineCoding->speed > 1500000) {
124 				TRACE_ALWAYS("= FTDIDevice::SetLineCoding(): Datarate: %d is "
125 					"not supported by this hardware. Defaulted to %d\n",
126 					lineCoding->speed, 19200);
127 				lineCoding->speed = 19200;
128 			}
129 			rate = 3000000 * 8 / lineCoding->speed;
130 			int frac = ftdi_8u232am_frac[rate & 0x7];
131 			rate = (rate >> 3) | frac;
132 		}
133 	}
134 
135 	size_t length = 0;
136 	status_t status = gUSBModule->send_request(Device(),
137 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
138 		FTDI_SIO_SET_BAUD_RATE, rate,
139 		FTDI_PIT_DEFAULT, 0, NULL, &length);
140 	if (status != B_OK)
141 		TRACE_ALWAYS("= FTDIDevice::SetLineCoding(): datarate set request failed: 0x%08x\n", status);
142 
143 	int32 data = 0;
144 	switch (lineCoding->stopbits) {
145 		case USB_CDC_LINE_CODING_1_STOPBIT: data = FTDI_SIO_SET_DATA_STOP_BITS_2; break;
146 		case USB_CDC_LINE_CODING_2_STOPBITS: data = FTDI_SIO_SET_DATA_STOP_BITS_1; break;
147 		default:
148 			TRACE_ALWAYS("= FTDIDevice::SetLineCoding(): Wrong stopbits param: %d\n",
149 				lineCoding->stopbits);
150 			break;
151 	}
152 
153 	switch (lineCoding->parity) {
154 		case USB_CDC_LINE_CODING_NO_PARITY: data |= FTDI_SIO_SET_DATA_PARITY_NONE; break;
155 		case USB_CDC_LINE_CODING_EVEN_PARITY: data |= FTDI_SIO_SET_DATA_PARITY_EVEN; break;
156 		case USB_CDC_LINE_CODING_ODD_PARITY:	data |= FTDI_SIO_SET_DATA_PARITY_ODD; break;
157 		default:
158 			TRACE_ALWAYS("= FTDIDevice::SetLineCoding(): Wrong parity param: %d\n",
159 				lineCoding->parity);
160 			break;
161 	}
162 
163 	switch (lineCoding->databits) {
164 		case 8: data |= FTDI_SIO_SET_DATA_BITS(8); break;
165 		case 7: data |= FTDI_SIO_SET_DATA_BITS(7); break;
166 		default:
167 			TRACE_ALWAYS("= FTDIDevice::SetLineCoding(): Wrong databits param: %d\n",
168 				lineCoding->databits);
169 			break;
170 	}
171 
172 	length = 0;
173 	status = gUSBModule->send_request(Device(),
174 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
175 		FTDI_SIO_SET_DATA, data,
176 		FTDI_PIT_DEFAULT, 0, NULL, &length);
177 	if (status != B_OK)
178 		TRACE_ALWAYS("= FTDIDevice::SetLineCoding(): data set request failed: %08x\n", status);
179 
180 	TRACE_FUNCRET("< FTDIDevice::SetLineCoding() returns: 0x%08x\n", status);
181 	return status;
182 }
183 
184 
185 status_t
186 FTDIDevice::SetControlLineState(uint16 state)
187 {
188 	TRACE_FUNCALLS("> FTDIDevice::SetControlLineState(0x%08x, 0x%04x)\n",
189 		this, state);
190 
191 	int32 control;
192 	control = (state & USB_CDC_CONTROL_SIGNAL_STATE_RTS) ? FTDI_SIO_SET_RTS_HIGH
193 		: FTDI_SIO_SET_RTS_LOW;
194 
195 	size_t length = 0;
196 	status_t status = gUSBModule->send_request(Device(),
197 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
198 		FTDI_SIO_MODEM_CTRL, control,
199 		FTDI_PIT_DEFAULT, 0, NULL, &length);
200 
201 	if (status != B_OK) {
202 		TRACE_ALWAYS("= FTDIDevice::SetControlLineState(): "
203 			"control set request failed: 0x%08x\n", status);
204 	}
205 
206 	control = (state & USB_CDC_CONTROL_SIGNAL_STATE_DTR) ? FTDI_SIO_SET_DTR_HIGH
207 		: FTDI_SIO_SET_DTR_LOW;
208 
209 	status = gUSBModule->send_request(Device(),
210 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
211 		FTDI_SIO_MODEM_CTRL, control,
212 		FTDI_PIT_DEFAULT, 0, NULL, &length);
213 
214 	if (status != B_OK) {
215 		TRACE_ALWAYS("= FTDIDevice::SetControlLineState(): "
216 			"control set request failed: 0x%08x\n", status);
217 	}
218 
219 	TRACE_FUNCRET("< FTDIDevice::SetControlLineState() returns: 0x%08x\n",
220 		status);
221 	return status;
222 }
223 
224 
225 status_t
226 FTDIDevice::SetHardwareFlowControl(bool enable)
227 {
228 	TRACE_FUNCALLS("> FTDIDevice::SetHardwareFlowControl(0x%08x, %d)\n",
229 		this, enable);
230 
231 	uint32 control = enable ? FTDI_SIO_RTS_CTS_HS : FTDI_SIO_DISABLE_FLOW_CTRL;
232 
233 	size_t length = 0;
234 	status_t status = gUSBModule->send_request(Device(),
235 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
236 		FTDI_SIO_SET_FLOW_CTRL, 0,
237 		FTDI_PIT_DEFAULT | (control << 8), 0, NULL, &length);
238 
239 	if (status != B_OK)
240 		TRACE_ALWAYS("= FTDIDevice::SetHardwareFlowControl(): "
241 			"request failed: 0x%08x\n", status);
242 
243 	TRACE_FUNCRET("< FTDIDevice::SetHardwareFlowControl() returns: 0x%08x\n",
244 		status);
245 	return status;
246 }
247 
248 
249 void
250 FTDIDevice::OnRead(char **buffer, size_t *numBytes)
251 {
252 	/* The input consists of 64-byte packets, in which the first two bytes
253 	 * give the status (16C550 like) of the UART. A single buffer may have
254 	 * several of these packets in it.
255 	 */
256 
257 	size_t i = 0;
258 	size_t j = 0;
259 
260 	while (i < *numBytes) {
261 		if ((i % 64) == 0) {
262 			fStatusMSR = FTDI_GET_MSR(*buffer + i);
263 			fStatusLSR = FTDI_GET_LSR(*buffer + i);
264 			TRACE("FTDIDevice::OnRead(): MSR: 0x%02x LSR: 0x%02x\n",
265 				fStatusMSR, fStatusLSR);
266 
267 			// Skip over the buffer header
268 			i += 2;
269 		} else {
270 			// Group normal bytes towards the start of the buffer
271 			(*buffer)[j++] = (*buffer)[i++];
272 		}
273 	}
274 
275 	// Tell the caller about the number of "real" bytes remaining in the buffer
276 	*numBytes = j;
277 }
278 
279 
280 void
281 FTDIDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
282 {
283 	if (*numBytes > FTDI_BUFFER_SIZE)
284 		*numBytes = *packetBytes = FTDI_BUFFER_SIZE;
285 
286 	char *writeBuffer = WriteBuffer();
287 	if (fHeaderLength > 0) {
288 		if (*numBytes > WriteBufferSize() - fHeaderLength)
289 			*numBytes = *packetBytes = WriteBufferSize() - fHeaderLength;
290 
291 		*writeBuffer = FTDI_OUT_TAG(*numBytes, FTDI_PIT_DEFAULT);
292 	}
293 
294 	memcpy(writeBuffer + fHeaderLength, buffer, *packetBytes);
295 	*packetBytes += fHeaderLength;
296 }
297