xref: /haiku/src/add-ons/kernel/drivers/ports/usb_serial/FTDI.cpp (revision fce4895d1884da5ae6fb299d23c735c598e690b1)
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", this, state);
189 
190 	int32 control;
191 	control = (state & USB_CDC_CONTROL_SIGNAL_STATE_RTS) ? FTDI_SIO_SET_RTS_HIGH
192 		: FTDI_SIO_SET_RTS_LOW;
193 
194 	size_t length = 0;
195 	status_t status = gUSBModule->send_request(Device(),
196 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
197 		FTDI_SIO_MODEM_CTRL, control,
198 		FTDI_PIT_DEFAULT, 0, NULL, &length);
199 
200 	if (status != B_OK)
201 		TRACE_ALWAYS("= FTDIDevice::SetControlLineState(): control set request failed: 0x%08x\n", status);
202 
203 	control = (state & USB_CDC_CONTROL_SIGNAL_STATE_DTR) ? FTDI_SIO_SET_DTR_HIGH
204 		: FTDI_SIO_SET_DTR_LOW;
205 
206 	status = gUSBModule->send_request(Device(),
207 		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
208 		FTDI_SIO_MODEM_CTRL, control,
209 		FTDI_PIT_DEFAULT, 0, NULL, &length);
210 
211 	if (status != B_OK)
212 		TRACE_ALWAYS("= FTDIDevice::SetControlLineState(): control set request failed: 0x%08x\n", status);
213 
214 	TRACE_FUNCRET("< FTDIDevice::SetControlLineState() returns: 0x%08x\n", status);
215 	return status;
216 }
217 
218 
219 void
220 FTDIDevice::OnRead(char **buffer, size_t *numBytes)
221 {
222 	fStatusMSR = FTDI_GET_MSR(*buffer);
223 	fStatusLSR = FTDI_GET_LSR(*buffer);
224 	TRACE("FTDIDevice::OnRead(): MSR: 0x%02x LSR: 0x%02x\n", fStatusMSR, fStatusLSR);
225 	*buffer += 2;
226 	*numBytes -= 2;
227 }
228 
229 
230 void
231 FTDIDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
232 {
233 	if (*numBytes > FTDI_BUFFER_SIZE)
234 		*numBytes = *packetBytes = FTDI_BUFFER_SIZE;
235 
236 	char *writeBuffer = WriteBuffer();
237 	if (fHeaderLength > 0) {
238 		if (*numBytes > WriteBufferSize() - fHeaderLength)
239 			*numBytes = *packetBytes = WriteBufferSize() - fHeaderLength;
240 
241 		*writeBuffer = FTDI_OUT_TAG(*numBytes, FTDI_PIT_DEFAULT);
242 	}
243 
244 	memcpy(writeBuffer + fHeaderLength, buffer, *packetBytes);
245 	*packetBytes += fHeaderLength;
246 }
247