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