1/*
2 * Copyright 2011, Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Alexander von Gluck IV, kallisti5@unixzen.com
7 */
8
9
10#include "Silicon.h"
11
12
13static const int kBaudrateGeneratorFrequency = 0x384000;
14
15
16SiliconDevice::SiliconDevice(usb_device device, uint16 vendorID, uint16 productID,
17	const char *description)
18	:	SerialDevice(device, vendorID, productID, description)
19{
20}
21
22
23// Called for each configuration of the device. Return B_OK if the given
24// configuration sounds like it is the usb serial one.
25status_t
26SiliconDevice::AddDevice(const usb_configuration_info *config)
27{
28	status_t status = ENODEV;
29	if (config->interface_count > 0) {
30		int32 pipesSet = 0;
31		usb_interface_info *interface = config->interface[0].active;
32		for (size_t i = 0; i < interface->endpoint_count; i++) {
33			usb_endpoint_info *endpoint = &interface->endpoint[i];
34			if (endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) {
35				if (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) {
36					SetReadPipe(endpoint->handle);
37					if (++pipesSet >= 3)
38						break;
39				} else {
40					if (endpoint->descr->endpoint_address) {
41						SetControlPipe(endpoint->handle);
42						SetWritePipe(endpoint->handle);
43						pipesSet += 2;
44						if (pipesSet >= 3)
45							break;
46					}
47				}
48			}
49		}
50
51		if (pipesSet >= 3) {
52			status = B_OK;
53		}
54	}
55	return status;
56}
57
58
59// Called on opening the device - Good time to enable the UART ?
60status_t
61SiliconDevice::ResetDevice()
62{
63	uint16_t enableUart = 1;
64	return WriteConfig(ENABLE_UART, &enableUart, 2);
65}
66
67
68status_t
69SiliconDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
70{
71	uint16_t divider = kBaudrateGeneratorFrequency / lineCoding->speed ;
72	status_t result = WriteConfig(SET_BAUDRATE_DIVIDER, &divider, 2);
73
74	if (result != B_OK) return result;
75
76	uint16_t data = 0;
77
78	switch (lineCoding->stopbits) {
79		case USB_CDC_LINE_CODING_1_STOPBIT: data = 0; break;
80		case USB_CDC_LINE_CODING_2_STOPBITS: data = 2; break;
81		default:
82			TRACE_ALWAYS("= SiliconDevice::SetLineCoding(): Wrong stopbits param: %d\n",
83				lineCoding->stopbits);
84			break;
85	}
86
87	switch (lineCoding->parity) {
88		case USB_CDC_LINE_CODING_NO_PARITY: data |= 0 << 4; break;
89		case USB_CDC_LINE_CODING_EVEN_PARITY: data |= 2 << 4; break;
90		case USB_CDC_LINE_CODING_ODD_PARITY:	data |= 1 << 4; break;
91		default:
92			TRACE_ALWAYS("= SiliconDevice::SetLineCoding(): Wrong parity param: %d\n",
93				lineCoding->parity);
94			break;
95	}
96
97	data |= lineCoding->databits << 8;
98
99	return WriteConfig(SET_LINE_FORMAT, &data, 2);
100}
101
102
103status_t
104SiliconDevice::SetControlLineState(uint16 state)
105{
106	uint16_t control = 0;
107	control |= 0x0300; // We are updating DTR and RTS
108	control |= (state & USB_CDC_CONTROL_SIGNAL_STATE_RTS) ? 2 : 0;
109	control |= (state & USB_CDC_CONTROL_SIGNAL_STATE_DTR) ? 1 : 0;
110
111	return WriteConfig(SET_STATUS, &control, 2);
112}
113
114
115status_t SiliconDevice::WriteConfig(CP210XRequest request, uint16_t* data,
116	size_t size)
117{
118	size_t replyLength = 0;
119	status_t result;
120	// Small requests (16 bits and less) use the "value" field for their data.
121	// Bigger ones use the actual buffer.
122	if (size <= 2) {
123		result = gUSBModule->send_request(Device(),
124			USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, request, data[0], 0, 0,
125			NULL, &replyLength);
126	} else {
127		result = gUSBModule->send_request(Device(),
128			USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, request, 0x0000, 0,
129			size, data, &replyLength);
130	}
131
132	if (result != B_OK) {
133		TRACE_ALWAYS("= SiliconDevice request failed: 0x%08x (%s)\n",
134			result, strerror(result));
135	}
136
137	return result;
138}
139