xref: /haiku/src/add-ons/kernel/drivers/ports/usb_serial/ACM.cpp (revision f11b30463296494633476a81bdb9a64e8cb2e85d)
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 
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 		if (interface == NULL)
34 			continue;
35 		usb_interface_descriptor *descriptor = interface->descr;
36 		if (descriptor == NULL)
37 			continue;
38 		if (descriptor->interface_class != USB_CDC_COMMUNICATION_INTERFACE_CLASS
39 			|| descriptor->interface_subclass != USB_CDC_COMMUNICATION_INTERFACE_ACM_SUBCLASS)
40 			continue;
41 
42 		// Found ACM Communication Interface!
43 		// Get functional descriptors of some interest, if any
44 		for (size_t j = 0; j < interface->generic_count; j++) {
45 			usb_generic_descriptor *generic = &interface->generic[j]->generic;
46 			switch (generic->data[0]) {
47 				case USB_CDC_CM_FUNCTIONAL_DESCRIPTOR:
48 					cmDesc = (usb_cdc_cm_functional_descriptor*)generic;
49 					break;
50 
51 				case USB_CDC_ACM_FUNCTIONAL_DESCRIPTOR:
52 					break;
53 
54 				case USB_CDC_UNION_FUNCTIONAL_DESCRIPTOR:
55 					unionDesc = (usb_cdc_union_functional_descriptor*)generic;
56 					break;
57 			}
58 		}
59 
60 		masterIndex = unionDesc ? unionDesc->master_interface : i;
61 		slaveIndex = cmDesc ? cmDesc->data_interface
62 			: unionDesc ? unionDesc->slave_interfaces[0] : 0;
63 
64 		TRACE("ACM device found on configuration #%d: master itf: %d, "
65 				"slave/data itf: %d\n", config->descr->configuration,
66 				masterIndex, slaveIndex);
67 
68 		// Some ACM USB devices report the wrong unions which rightfully
69 		// breaks probing. Some drivers keep a list of these devices,
70 		// for now we just assume identical indexes are wrong.
71 		if (masterIndex == slaveIndex) {
72 			TRACE_ALWAYS("Command interface matches data interface, "
73 				"assuming broken union quirk!\n");
74 			masterIndex = 0;
75 			slaveIndex = 1;
76 		}
77 
78 		status = B_OK;
79 		break;
80 	}
81 
82 	if (status == B_OK && masterIndex < config->interface_count) {
83 		// check that the indicated master interface fits our need
84 		usb_interface_info *interface = config->interface[masterIndex].active;
85 		usb_interface_descriptor *descriptor = interface->descr;
86 		if ((descriptor->interface_class == USB_CDC_COMMUNICATION_INTERFACE_CLASS
87 			|| descriptor->interface_class == USB_CDC_DATA_INTERFACE_CLASS)
88 			&& interface->endpoint_count >= 1) {
89 			SetControlPipe(interface->endpoint[0].handle);
90 			SetInterruptBufferSize(interface->endpoint[0].descr->max_packet_size);
91 		} else {
92 			TRACE("Indicated command interface doesn't fit our needs!\n");
93 			status = ENODEV;
94 		}
95 	}
96 
97 	if (status == B_OK && slaveIndex < config->interface_count) {
98 		// check that the indicated slave interface fits our need
99 		usb_interface_info *interface = config->interface[slaveIndex].active;
100 		usb_interface_descriptor *descriptor = interface->descr;
101 		if (descriptor->interface_class == USB_CDC_DATA_INTERFACE_CLASS
102 			&& interface->endpoint_count >= 2) {
103 			if (!(interface->endpoint[0].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)) {
104 				SetWriteBufferSize(ROUNDUP(interface->endpoint[0].descr->max_packet_size, 16));
105 				SetWritePipe(interface->endpoint[0].handle);
106 			} else {
107 				SetReadBufferSize(ROUNDUP(interface->endpoint[0].descr->max_packet_size, 16));
108 				SetReadPipe(interface->endpoint[0].handle);
109 			}
110 
111 			if (interface->endpoint[1].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) {
112 				SetReadBufferSize(ROUNDUP(interface->endpoint[1].descr->max_packet_size, 16));
113 				SetReadPipe(interface->endpoint[1].handle);
114 			} else {
115 				SetWriteBufferSize(ROUNDUP(interface->endpoint[1].descr->max_packet_size, 16));
116 				SetWritePipe(interface->endpoint[1].handle);
117 			}
118 		} else {
119 			TRACE("Indicated data interface doesn't fit our needs!\n");
120 			status = ENODEV;
121 		}
122 	}
123 
124 	TRACE_FUNCRET("< ACMDevice::AddDevice() returns: 0x%08x\n", status);
125 	return status;
126 }
127 
128 
129 status_t
130 ACMDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
131 {
132 	TRACE_FUNCALLS("> ACMDevice::SetLineCoding(0x%08x, {%d, 0x%02x, 0x%02x, 0x%02x})\n",
133 		this, lineCoding->speed, lineCoding->stopbits, lineCoding->parity,
134 		lineCoding->databits);
135 
136 	size_t length = 0;
137 	status_t status = gUSBModule->send_request(Device(),
138 		USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT,
139 		USB_CDC_SET_LINE_CODING, 0, 0,
140 		sizeof(usb_cdc_line_coding),
141 		lineCoding, &length);
142 
143 	TRACE_FUNCRET("< ACMDevice::SetLineCoding() returns: 0x%08x\n", status);
144 	return status;
145 }
146 
147 
148 status_t
149 ACMDevice::SetControlLineState(uint16 state)
150 {
151 	TRACE_FUNCALLS("> ACMDevice::SetControlLineState(0x%08x, 0x%04x)\n", this, state);
152 
153 	size_t length = 0;
154 	status_t status = gUSBModule->send_request(Device(),
155 		USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT,
156 		USB_CDC_SET_CONTROL_LINE_STATE, state, 0, 0, NULL, &length);
157 
158 	TRACE_FUNCRET("< ACMDevice::SetControlLineState() returns: 0x%08x\n", status);
159 	return status;
160 }
161