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#include "KLSI.h"
9#include <ByteOrder.h>
10
11KLSIDevice::KLSIDevice(usb_device device, uint16 vendorID, uint16 productID,
12	const char *description)
13	:	SerialDevice(device, vendorID, productID, description)
14{
15}
16
17
18status_t
19KLSIDevice::AddDevice(const usb_configuration_info *config)
20{
21	TRACE_FUNCALLS("> KLSIDevice::AddDevice(%08x, %08x)\n", this, config);
22
23	int32 pipesSet = 0;
24	status_t status = ENODEV;
25	if (config->interface_count > 0) {
26		usb_interface_info *interface = config->interface[0].active;
27		for (size_t i = 0; i < interface->endpoint_count; i++) {
28			usb_endpoint_info *endpoint = &interface->endpoint[i];
29			if (endpoint->descr->attributes == USB_ENDPOINT_ATTR_INTERRUPT) {
30				if (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) {
31					SetControlPipe(endpoint->handle);
32					SetInterruptBufferSize(endpoint->descr->max_packet_size);
33				}
34			} else if (endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) {
35				if (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) {
36					SetReadBufferSize(ROUNDUP(endpoint->descr->max_packet_size, 16));
37					SetReadPipe(endpoint->handle);
38				} else {
39					SetWriteBufferSize(ROUNDUP(endpoint->descr->max_packet_size, 16));
40					SetWritePipe(endpoint->handle);
41				}
42			}
43			if (++pipesSet >= 3)
44				break;
45		}
46	}
47
48	if (pipesSet >= 3)
49		status = B_OK;
50
51	TRACE_FUNCRET("< KLSIDevice::AddDevice() returns: 0x%08x\n", status);
52	return status;
53}
54
55
56status_t
57KLSIDevice::ResetDevice()
58{
59	TRACE_FUNCALLS("> KLSIDevice::ResetDevice(%08x)\n", this);
60
61	size_t length = 0;
62	status_t status;
63	usb_cdc_line_coding lineCoding = { 9600, 1, 0, 8 };
64	uint8 linestate[2];
65
66	status = SetLineCoding(&lineCoding);
67	status = gUSBModule->send_request(Device(),
68		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
69		KLSI_CONF_REQUEST,
70		KLSI_CONF_REQUEST_READ_ON, 0, 0, NULL, &length);
71	TRACE("= KLSIDevice::ResetDevice(): set conf read_on returns: 0x%08x\n",
72		status);
73
74	linestate[0] = 0xff;
75	linestate[1] = 0xff;
76	length = 0;
77	status = gUSBModule->send_request(Device(),
78		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
79		KLSI_POLL_REQUEST,
80		0, 0, 2, linestate, &length);
81
82	TRACE_FUNCRET("< KLSIDevice::ResetDevice() returns: 0x%08x\n", status);
83	return status;
84}
85
86
87status_t
88KLSIDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
89{
90	TRACE_FUNCALLS("> KLSIDevice::SetLineCoding(0x%08x, {%d, 0x%02x, 0x%02x, 0x%02x})\n",
91		this, lineCoding->speed, lineCoding->stopbits, lineCoding->parity,
92		lineCoding->databits);
93
94	uint8 rate;
95	switch (lineCoding->speed) {
96		case 300: rate = klsi_sio_b300; break;
97		case 600: rate = klsi_sio_b600; break;
98		case 1200: rate = klsi_sio_b1200; break;
99		case 2400: rate = klsi_sio_b2400; break;
100		case 4800: rate = klsi_sio_b4800; break;
101		case 9600: rate = klsi_sio_b9600; break;
102		case 19200: rate = klsi_sio_b19200; break;
103		case 38400: rate = klsi_sio_b38400; break;
104		case 57600: rate = klsi_sio_b57600; break;
105		case 115200: rate = klsi_sio_b115200; break;
106		default:
107			rate = klsi_sio_b19200;
108			TRACE_ALWAYS("= KLSIDevice::SetLineCoding(): Datarate: %d is not "
109				"supported by this hardware. Defaulted to %d\n",
110				lineCoding->speed, rate);
111		break;
112	}
113
114	uint8 codingPacket[5];
115	codingPacket[0] = 5; /* size */
116	codingPacket[1] = rate;
117	codingPacket[2] = lineCoding->databits; /* only 7,8 */
118	codingPacket[3] = 0; /* unknown */
119	codingPacket[4] = 1; /* unknown */
120
121	size_t length = 0;
122	status_t status = gUSBModule->send_request(Device(),
123		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
124		KLSI_SET_REQUEST, 0, 0,
125		sizeof(codingPacket), codingPacket, &length);
126
127	if (status != B_OK)
128		TRACE_ALWAYS("= KLSIDevice::SetLineCoding(): datarate set request failed: 0x%08x\n", status);
129
130	TRACE_FUNCRET("< KLSIDevice::SetLineCoding() returns: 0x%08x\n", status);
131	return status;
132}
133
134
135void
136KLSIDevice::OnRead(char **buffer, size_t *numBytes)
137{
138	if (*numBytes <= 2) {
139		*numBytes = 0;
140		return;
141	}
142
143	size_t bytes = B_LENDIAN_TO_HOST_INT16(*(uint16 *)(*buffer));
144	*numBytes = MIN(bytes, *numBytes - 2);
145	*buffer += 2;
146}
147
148
149void
150KLSIDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
151{
152	if (*numBytes > KLSI_BUFFER_SIZE)
153		*numBytes = *packetBytes = KLSI_BUFFER_SIZE;
154
155	if (*numBytes > WriteBufferSize() - 2)
156		*numBytes = *packetBytes = WriteBufferSize() - 2;
157
158	char *writeBuffer = WriteBuffer();
159	*((uint16 *)writeBuffer) = B_HOST_TO_LENDIAN_INT16(*numBytes);
160	memcpy(writeBuffer + 2, buffer, *packetBytes);
161	*packetBytes += 2;
162}
163
164
165void
166KLSIDevice::OnClose()
167{
168	TRACE_FUNCALLS("> KLSIDevice::OnClose(%08x)\n", this);
169
170	size_t length = 0;
171	status_t status = gUSBModule->send_request(Device(),
172		USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
173		KLSI_CONF_REQUEST,
174		KLSI_CONF_REQUEST_READ_OFF, 0, 0,
175		NULL, &length);
176
177	TRACE_FUNCRET("< KLSIDevice::OnClose() returns: 0x%08x\n", status);
178}
179