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 "ACM.h"
9#include "Driver.h"
10
11
12ACMDevice::ACMDevice(usb_device device, uint16 vendorID, uint16 productID,
13	const char *description)
14	:	SerialDevice(device, vendorID, productID, description)
15{
16}
17
18
19status_t
20ACMDevice::AddDevice(const usb_configuration_info *config)
21{
22	TRACE_FUNCALLS("> ACMDevice::AddDevice(0x%08x, 0x%08x)\n", this, config);
23
24	status_t status = ENODEV;
25	uint8 masterIndex = 0;
26	uint8 slaveIndex = 0;
27	usb_cdc_cm_functional_descriptor* cmDesc = NULL;
28	usb_cdc_union_functional_descriptor* unionDesc = NULL;
29
30	// Search ACM Communication Interface
31	for (size_t i = 0; i < config->interface_count && status < B_OK; i++) {
32		usb_interface_info *interface = config->interface[i].active;
33		usb_interface_descriptor *descriptor = interface->descr;
34		if (descriptor->interface_class != USB_CDC_COMMUNICATION_INTERFACE_CLASS
35			|| descriptor->interface_subclass != USB_CDC_COMMUNICATION_INTERFACE_ACM_SUBCLASS)
36			continue;
37
38		// Found ACM Communication Interface!
39		// Get functional descriptors of some interest, if any
40		for (size_t j = 0; j < interface->generic_count; j++) {
41			usb_generic_descriptor *generic = &interface->generic[j]->generic;
42			switch (generic->data[0]) {
43				case USB_CDC_CM_FUNCTIONAL_DESCRIPTOR:
44					cmDesc = (usb_cdc_cm_functional_descriptor*)generic;
45					break;
46
47				case USB_CDC_ACM_FUNCTIONAL_DESCRIPTOR:
48					break;
49
50				case USB_CDC_UNION_FUNCTIONAL_DESCRIPTOR:
51					unionDesc = (usb_cdc_union_functional_descriptor*)generic;
52					break;
53			}
54		}
55
56		masterIndex = unionDesc ? unionDesc->master_interface : i;
57		slaveIndex = cmDesc ? cmDesc->data_interface
58			: unionDesc ? unionDesc->slave_interfaces[0] : 0;
59
60		TRACE("ACM device found on configuration #%d: master itf: %d, slave/data itf: %d\n",
61				config->descr->configuration, masterIndex, slaveIndex);
62		status = B_OK;
63		break;
64	}
65
66
67	if (status == B_OK && masterIndex < config->interface_count) {
68		// check that the indicated master interface fits our need
69		usb_interface_info *interface = config->interface[masterIndex].active;
70		usb_interface_descriptor *descriptor = interface->descr;
71		if ((descriptor->interface_class == USB_CDC_COMMUNICATION_INTERFACE_CLASS
72			|| descriptor->interface_class == USB_CDC_DATA_INTERFACE_CLASS)
73			&& interface->endpoint_count >= 1) {
74			SetControlPipe(interface->endpoint[0].handle);
75		} else
76			status = ENODEV;
77	}
78
79	if (status == B_OK && slaveIndex < config->interface_count) {
80		// check that the indicated slave interface fits our need
81		usb_interface_info *interface = config->interface[slaveIndex].active;
82		usb_interface_descriptor *descriptor = interface->descr;
83		if (descriptor->interface_class == USB_CDC_DATA_INTERFACE_CLASS
84			&& interface->endpoint_count >= 2) {
85			if (!(interface->endpoint[0].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN))
86				SetWritePipe(interface->endpoint[0].handle);
87			else
88				SetReadPipe(interface->endpoint[0].handle);
89
90			if (interface->endpoint[1].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
91				SetReadPipe(interface->endpoint[1].handle);
92			else
93				SetWritePipe(interface->endpoint[1].handle);
94		} else
95			status = ENODEV;
96	}
97
98	TRACE_FUNCRET("< ACMDevice::AddDevice() returns: 0x%08x\n", status);
99	return status;
100}
101
102
103status_t
104ACMDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
105{
106	TRACE_FUNCALLS("> ACMDevice::SetLineCoding(0x%08x, {%d, 0x%02x, 0x%02x, 0x%02x})\n",
107		this, lineCoding->speed, lineCoding->stopbits, lineCoding->parity,
108		lineCoding->databits);
109
110	size_t length = 0;
111	status_t status = gUSBModule->send_request(Device(),
112		USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT,
113		USB_CDC_SET_LINE_CODING, 0, 0,
114		sizeof(usb_cdc_line_coding),
115		lineCoding, &length);
116
117	TRACE_FUNCRET("< ACMDevice::SetLineCoding() returns: 0x%08x\n", status);
118	return status;
119}
120
121
122status_t
123ACMDevice::SetControlLineState(uint16 state)
124{
125	TRACE_FUNCALLS("> ACMDevice::SetControlLineState(0x%08x, 0x%04x)\n", this, state);
126
127	size_t length = 0;
128	status_t status = gUSBModule->send_request(Device(),
129		USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT,
130		USB_CDC_SET_CONTROL_LINE_STATE, state, 0, 0, NULL, &length);
131
132	TRACE_FUNCRET("< ACMDevice::SetControlLineState() returns: 0x%08x\n", status);
133	return status;
134}
135