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
17FTDIDevice::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
27status_t
28FTDIDevice::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
69status_t
70FTDIDevice::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
85status_t
86FTDIDevice::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
185status_t
186FTDIDevice::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
225status_t
226FTDIDevice::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
249void
250FTDIDevice::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
280void
281FTDIDevice::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