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