xref: /haiku/src/add-ons/kernel/drivers/ports/usb_serial/ACM.cpp (revision 243ac0b9a66c0fd9e00b39b21f87a6309585c766)
17aa661d4SMichael Lotz /*
27aa661d4SMichael Lotz  * Copyright (c) 2007-2008 by Michael Lotz
37aa661d4SMichael Lotz  * Heavily based on the original usb_serial driver which is:
47aa661d4SMichael Lotz  *
57aa661d4SMichael Lotz  * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li>
67aa661d4SMichael Lotz  * Distributed under the terms of the MIT License.
77aa661d4SMichael Lotz  */
87aa661d4SMichael Lotz #include "ACM.h"
97aa661d4SMichael Lotz #include "Driver.h"
107aa661d4SMichael Lotz 
11c5f2df28SPhilippe Houdoin 
ACMDevice(usb_device device,uint16 vendorID,uint16 productID,const char * description)127aa661d4SMichael Lotz ACMDevice::ACMDevice(usb_device device, uint16 vendorID, uint16 productID,
137aa661d4SMichael Lotz 	const char *description)
147aa661d4SMichael Lotz 	:	SerialDevice(device, vendorID, productID, description)
157aa661d4SMichael Lotz {
167aa661d4SMichael Lotz }
177aa661d4SMichael Lotz 
187aa661d4SMichael Lotz 
197aa661d4SMichael Lotz status_t
AddDevice(const usb_configuration_info * config)207aa661d4SMichael Lotz ACMDevice::AddDevice(const usb_configuration_info *config)
217aa661d4SMichael Lotz {
227aa661d4SMichael Lotz 	TRACE_FUNCALLS("> ACMDevice::AddDevice(0x%08x, 0x%08x)\n", this, config);
237aa661d4SMichael Lotz 
247aa661d4SMichael Lotz 	status_t status = ENODEV;
257aa661d4SMichael Lotz 	uint8 masterIndex = 0;
267aa661d4SMichael Lotz 	uint8 slaveIndex = 0;
27c5f2df28SPhilippe Houdoin 	usb_cdc_cm_functional_descriptor* cmDesc = NULL;
28c5f2df28SPhilippe Houdoin 	usb_cdc_union_functional_descriptor* unionDesc = NULL;
29c5f2df28SPhilippe Houdoin 
30c5f2df28SPhilippe Houdoin 	// Search ACM Communication Interface
317aa661d4SMichael Lotz 	for (size_t i = 0; i < config->interface_count && status < B_OK; i++) {
327aa661d4SMichael Lotz 		usb_interface_info *interface = config->interface[i].active;
338d1a3621SPulkoMandy 		if (interface == NULL)
348d1a3621SPulkoMandy 			continue;
357aa661d4SMichael Lotz 		usb_interface_descriptor *descriptor = interface->descr;
368d1a3621SPulkoMandy 		if (descriptor == NULL)
378d1a3621SPulkoMandy 			continue;
38c5f2df28SPhilippe Houdoin 		if (descriptor->interface_class != USB_CDC_COMMUNICATION_INTERFACE_CLASS
39c5f2df28SPhilippe Houdoin 			|| descriptor->interface_subclass != USB_CDC_COMMUNICATION_INTERFACE_ACM_SUBCLASS)
40c5f2df28SPhilippe Houdoin 			continue;
41c5f2df28SPhilippe Houdoin 
42c5f2df28SPhilippe Houdoin 		// Found ACM Communication Interface!
43c5f2df28SPhilippe Houdoin 		// Get functional descriptors of some interest, if any
447aa661d4SMichael Lotz 		for (size_t j = 0; j < interface->generic_count; j++) {
457aa661d4SMichael Lotz 			usb_generic_descriptor *generic = &interface->generic[j]->generic;
46c5f2df28SPhilippe Houdoin 			switch (generic->data[0]) {
47c5f2df28SPhilippe Houdoin 				case USB_CDC_CM_FUNCTIONAL_DESCRIPTOR:
48c5f2df28SPhilippe Houdoin 					cmDesc = (usb_cdc_cm_functional_descriptor*)generic;
49c5f2df28SPhilippe Houdoin 					break;
50c5f2df28SPhilippe Houdoin 
51c5f2df28SPhilippe Houdoin 				case USB_CDC_ACM_FUNCTIONAL_DESCRIPTOR:
52c5f2df28SPhilippe Houdoin 					break;
53c5f2df28SPhilippe Houdoin 
54c5f2df28SPhilippe Houdoin 				case USB_CDC_UNION_FUNCTIONAL_DESCRIPTOR:
55c5f2df28SPhilippe Houdoin 					unionDesc = (usb_cdc_union_functional_descriptor*)generic;
567aa661d4SMichael Lotz 					break;
577aa661d4SMichael Lotz 			}
587aa661d4SMichael Lotz 		}
59c5f2df28SPhilippe Houdoin 
60c5f2df28SPhilippe Houdoin 		masterIndex = unionDesc ? unionDesc->master_interface : i;
61c5f2df28SPhilippe Houdoin 		slaveIndex = cmDesc ? cmDesc->data_interface
62c5f2df28SPhilippe Houdoin 			: unionDesc ? unionDesc->slave_interfaces[0] : 0;
63c5f2df28SPhilippe Houdoin 
6461c3b8c1SAlexander von Gluck IV 		TRACE("ACM device found on configuration #%d: master itf: %d, "
6561c3b8c1SAlexander von Gluck IV 				"slave/data itf: %d\n", config->descr->configuration,
6661c3b8c1SAlexander von Gluck IV 				masterIndex, slaveIndex);
6761c3b8c1SAlexander von Gluck IV 
6861c3b8c1SAlexander von Gluck IV 		// Some ACM USB devices report the wrong unions which rightfully
6961c3b8c1SAlexander von Gluck IV 		// breaks probing. Some drivers keep a list of these devices,
70df2961aeSAugustin Cavalier 		// for now we just assume identical indexes are wrong.
7161c3b8c1SAlexander von Gluck IV 		if (masterIndex == slaveIndex) {
7261c3b8c1SAlexander von Gluck IV 			TRACE_ALWAYS("Command interface matches data interface, "
7361c3b8c1SAlexander von Gluck IV 				"assuming broken union quirk!\n");
7461c3b8c1SAlexander von Gluck IV 			masterIndex = 0;
7561c3b8c1SAlexander von Gluck IV 			slaveIndex = 1;
7661c3b8c1SAlexander von Gluck IV 		}
7761c3b8c1SAlexander von Gluck IV 
78c5f2df28SPhilippe Houdoin 		status = B_OK;
79c5f2df28SPhilippe Houdoin 		break;
807aa661d4SMichael Lotz 	}
81c5f2df28SPhilippe Houdoin 
827aa661d4SMichael Lotz 	if (status == B_OK && masterIndex < config->interface_count) {
837aa661d4SMichael Lotz 		// check that the indicated master interface fits our need
847aa661d4SMichael Lotz 		usb_interface_info *interface = config->interface[masterIndex].active;
857aa661d4SMichael Lotz 		usb_interface_descriptor *descriptor = interface->descr;
86c5f2df28SPhilippe Houdoin 		if ((descriptor->interface_class == USB_CDC_COMMUNICATION_INTERFACE_CLASS
87c5f2df28SPhilippe Houdoin 			|| descriptor->interface_class == USB_CDC_DATA_INTERFACE_CLASS)
887aa661d4SMichael Lotz 			&& interface->endpoint_count >= 1) {
897aa661d4SMichael Lotz 			SetControlPipe(interface->endpoint[0].handle);
90*243ac0b9SStian Skjelstad 			SetInterruptBufferSize(interface->endpoint[0].descr->max_packet_size);
9161c3b8c1SAlexander von Gluck IV 		} else {
9261c3b8c1SAlexander von Gluck IV 			TRACE("Indicated command interface doesn't fit our needs!\n");
937aa661d4SMichael Lotz 			status = ENODEV;
947aa661d4SMichael Lotz 		}
9561c3b8c1SAlexander von Gluck IV 	}
967aa661d4SMichael Lotz 
977aa661d4SMichael Lotz 	if (status == B_OK && slaveIndex < config->interface_count) {
987aa661d4SMichael Lotz 		// check that the indicated slave interface fits our need
997aa661d4SMichael Lotz 		usb_interface_info *interface = config->interface[slaveIndex].active;
1007aa661d4SMichael Lotz 		usb_interface_descriptor *descriptor = interface->descr;
101c5f2df28SPhilippe Houdoin 		if (descriptor->interface_class == USB_CDC_DATA_INTERFACE_CLASS
1027aa661d4SMichael Lotz 			&& interface->endpoint_count >= 2) {
103*243ac0b9SStian Skjelstad 			if (!(interface->endpoint[0].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)) {
104*243ac0b9SStian Skjelstad 				SetWriteBufferSize(ROUNDUP(interface->endpoint[0].descr->max_packet_size, 16));
1057aa661d4SMichael Lotz 				SetWritePipe(interface->endpoint[0].handle);
106*243ac0b9SStian Skjelstad 			} else {
107*243ac0b9SStian Skjelstad 				SetReadBufferSize(ROUNDUP(interface->endpoint[0].descr->max_packet_size, 16));
1087aa661d4SMichael Lotz 				SetReadPipe(interface->endpoint[0].handle);
109*243ac0b9SStian Skjelstad 			}
1107aa661d4SMichael Lotz 
111*243ac0b9SStian Skjelstad 			if (interface->endpoint[1].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) {
112*243ac0b9SStian Skjelstad 				SetReadBufferSize(ROUNDUP(interface->endpoint[1].descr->max_packet_size, 16));
1137aa661d4SMichael Lotz 				SetReadPipe(interface->endpoint[1].handle);
114*243ac0b9SStian Skjelstad 			} else {
115*243ac0b9SStian Skjelstad 				SetWriteBufferSize(ROUNDUP(interface->endpoint[1].descr->max_packet_size, 16));
1167aa661d4SMichael Lotz 				SetWritePipe(interface->endpoint[1].handle);
117*243ac0b9SStian Skjelstad 			}
11861c3b8c1SAlexander von Gluck IV 		} else {
11961c3b8c1SAlexander von Gluck IV 			TRACE("Indicated data interface doesn't fit our needs!\n");
1207aa661d4SMichael Lotz 			status = ENODEV;
1217aa661d4SMichael Lotz 		}
12261c3b8c1SAlexander von Gluck IV 	}
1237aa661d4SMichael Lotz 
1247aa661d4SMichael Lotz 	TRACE_FUNCRET("< ACMDevice::AddDevice() returns: 0x%08x\n", status);
1257aa661d4SMichael Lotz 	return status;
1267aa661d4SMichael Lotz }
1277aa661d4SMichael Lotz 
1287aa661d4SMichael Lotz 
1297aa661d4SMichael Lotz status_t
SetLineCoding(usb_cdc_line_coding * lineCoding)130c5f2df28SPhilippe Houdoin ACMDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
1317aa661d4SMichael Lotz {
1327aa661d4SMichael Lotz 	TRACE_FUNCALLS("> ACMDevice::SetLineCoding(0x%08x, {%d, 0x%02x, 0x%02x, 0x%02x})\n",
1337aa661d4SMichael Lotz 		this, lineCoding->speed, lineCoding->stopbits, lineCoding->parity,
1347aa661d4SMichael Lotz 		lineCoding->databits);
1357aa661d4SMichael Lotz 
1367aa661d4SMichael Lotz 	size_t length = 0;
1377aa661d4SMichael Lotz 	status_t status = gUSBModule->send_request(Device(),
1387aa661d4SMichael Lotz 		USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT,
139c5f2df28SPhilippe Houdoin 		USB_CDC_SET_LINE_CODING, 0, 0,
140c5f2df28SPhilippe Houdoin 		sizeof(usb_cdc_line_coding),
1417aa661d4SMichael Lotz 		lineCoding, &length);
1427aa661d4SMichael Lotz 
1437aa661d4SMichael Lotz 	TRACE_FUNCRET("< ACMDevice::SetLineCoding() returns: 0x%08x\n", status);
1447aa661d4SMichael Lotz 	return status;
1457aa661d4SMichael Lotz }
1467aa661d4SMichael Lotz 
1477aa661d4SMichael Lotz 
1487aa661d4SMichael Lotz status_t
SetControlLineState(uint16 state)1497aa661d4SMichael Lotz ACMDevice::SetControlLineState(uint16 state)
1507aa661d4SMichael Lotz {
1517aa661d4SMichael Lotz 	TRACE_FUNCALLS("> ACMDevice::SetControlLineState(0x%08x, 0x%04x)\n", this, state);
1527aa661d4SMichael Lotz 
1537aa661d4SMichael Lotz 	size_t length = 0;
1547aa661d4SMichael Lotz 	status_t status = gUSBModule->send_request(Device(),
1557aa661d4SMichael Lotz 		USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT,
156c5f2df28SPhilippe Houdoin 		USB_CDC_SET_CONTROL_LINE_STATE, state, 0, 0, NULL, &length);
1577aa661d4SMichael Lotz 
1587aa661d4SMichael Lotz 	TRACE_FUNCRET("< ACMDevice::SetControlLineState() returns: 0x%08x\n", status);
1597aa661d4SMichael Lotz 	return status;
1607aa661d4SMichael Lotz }
161