140804Ssemenu/*
240804Ssemenu * Copyright (c) 2007-2008 by Michael Lotz
340804Ssemenu * Heavily based on the original usb_serial driver which is:
440804Ssemenu *
540804Ssemenu * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li>
640804Ssemenu * Distributed under the terms of the MIT License.
740804Ssemenu */
840804Ssemenu#include "Prolific.h"
940804Ssemenu
1040804SsemenuProlificDevice::ProlificDevice(usb_device device, uint16 vendorID,
1140804Ssemenu	uint16 productID, const char *description)
1240804Ssemenu	:	ACMDevice(device, vendorID, productID, description),
1340804Ssemenu		fIsHX(false)
1440804Ssemenu{
1540804Ssemenu}
1640804Ssemenu
1740804Ssemenu
1840804Ssemenustatus_t
1940804SsemenuProlificDevice::AddDevice(const usb_configuration_info *config)
2040804Ssemenu{
2140804Ssemenu	TRACE_FUNCALLS("> ProlificDevice::AddDevice(%08x, %08x)\n", this, config);
2240804Ssemenu
2340804Ssemenu	// check for device type.
2440804Ssemenu	// Linux checks for type 0, 1 and HX, but handles 0 and 1 the same.
25105666Ssemenu	// We'll just check for HX then.
26105666Ssemenu	const usb_device_descriptor *deviceDescriptor = NULL;
2740804Ssemenu	deviceDescriptor = gUSBModule->get_device_descriptor(Device());
2840804Ssemenu	if (deviceDescriptor) {
2940804Ssemenu		fIsHX = (deviceDescriptor->device_class != 0x02
3040804Ssemenu			&& deviceDescriptor->max_packet_size_0 == 0x40);
3140804Ssemenu	}
32105666Ssemenu
3359164Ssemenu	int32 pipesSet = 0;
34105666Ssemenu	status_t status = ENODEV;
3559164Ssemenu	if (config->interface_count > 0) {
3640804Ssemenu		usb_interface_info *interface = config->interface[0].active;
3740804Ssemenu		for (size_t i = 0; i < interface->endpoint_count; i++) {
3840804Ssemenu			usb_endpoint_info *endpoint = &interface->endpoint[i];
3940804Ssemenu			if (endpoint->descr->attributes == USB_ENDPOINT_ATTR_INTERRUPT) {
4040804Ssemenu				if (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) {
4140804Ssemenu					SetControlPipe(endpoint->handle);
4240804Ssemenu					pipesSet++;
4340804Ssemenu				}
4440804Ssemenu			}
4540804Ssemenu		}
4640804Ssemenu
4740804Ssemenu		/* They say that USB-RSAQ1 has 2 interfaces */
4840804Ssemenu		if (config->interface_count >= 2)
4940804Ssemenu			interface = config->interface[1].active;
5040804Ssemenu
51105966Ssam		for (size_t i = 0; i < interface->endpoint_count; i++) {
5240804Ssemenu			usb_endpoint_info *endpoint = &interface->endpoint[i];
5340804Ssemenu			if (endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) {
5440804Ssemenu				if (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
55113754Smux					SetReadPipe(endpoint->handle);
56113754Smux				else
5740804Ssemenu					SetWritePipe(endpoint->handle);
5840804Ssemenu
5940804Ssemenu				if (++pipesSet >= 3)
60113754Smux					break;
61113754Smux			}
6240804Ssemenu		}
6340804Ssemenu
6472134Ssemenu		if (pipesSet >= 3)
6572134Ssemenu			status = B_OK;
6672134Ssemenu	}
6772134Ssemenu
6872134Ssemenu	TRACE_FUNCRET("< ProlificDevice::AddDevice() returns: 0x%08x\n", status);
6972134Ssemenu	return status;
7072134Ssemenu}
7140804Ssemenu
7240804Ssemenu
73147256Sbrooksstruct request_item {
7459164Ssemenu	bool out;
7559164Ssemenu	uint16 value;
7659164Ssemenu	uint16 index;
7759164Ssemenu};
7861906Ssemenu
79179706Sjhb/* Linux sends all those, and it seems to work */
80179706Sjhb/* see drivers/usb/serial/pl2303.c */
81179706Sjhbstatic struct request_item prolific_reset_common[] = {
8259164Ssemenu	{ false, 0x8484, 0 },
8340804Ssemenu	{ true, 0x0404, 0 },
84113754Smux	{ false, 0x8484, 0 },
85113754Smux	{ false, 0x8383, 0 },
86113754Smux	{ false, 0x8484, 0 },
87113754Smux	{ true, 0x0404, 1 },
88113754Smux	{ false, 0x8484, 0 },
89113754Smux	{ false, 0x8383, 0 },
90113754Smux	{ true, 0x0000, 1 },
91113754Smux	{ true, 0x0001, 0 }
9259164Ssemenu};
9340804Ssemenu
9440804Ssemenustatic struct request_item prolific_reset_common_hx[] = {
9540804Ssemenu	{ true, 2, 0x44 },
9640804Ssemenu	{ true, 8, 0 },
9740804Ssemenu	{ true, 0, 0 }
9840804Ssemenu};
9940804Ssemenu
10040804Ssemenustatic struct request_item prolific_reset_common_nhx[] = {
101113754Smux	{ true, 2, 0x24 }
102113754Smux};
103113754Smux
10440804Ssemenu
10540804Ssemenustatus_t
10640804SsemenuProlificDevice::SendRequestList(request_item *list, size_t length)
10772134Ssemenu{
10840804Ssemenu	for (size_t i = 0; i < length; i++) {
10940804Ssemenu		char buffer[10];
11040804Ssemenu		size_t bufferLength = 1;
11140804Ssemenu		status_t status = gUSBModule->send_request(Device(),
11272134Ssemenu			USB_REQTYPE_VENDOR | (list[i].out ? USB_REQTYPE_DEVICE_OUT : USB_REQTYPE_DEVICE_IN),
11372134Ssemenu			PROLIFIC_SET_REQUEST,
11472134Ssemenu			list[i].value,
11572134Ssemenu			list[i].index,
11672134Ssemenu			list[i].out ? 0 : bufferLength,
117105666Ssemenu			list[i].out ? NULL : buffer,
11840804Ssemenu			&bufferLength);
11940804Ssemenu		TRACE(" ProlificDevice::SendRequestList(): request[%d]: 0x%08lx\n", i, status);
120179706Sjhb		if (status != B_OK) {
121179706Sjhb			TRACE_ALWAYS("sending request list failed:0x%08lx\n", status);
122179706Sjhb		}
123179706Sjhb	}
12459164Ssemenu
12559164Ssemenu	return B_OK;
12659164Ssemenu}
12759164Ssemenu
12859164Ssemenu
12959164Ssemenustatus_t
130105666SsemenuProlificDevice::ResetDevice()
131179706Sjhb{
132105666Ssemenu	TRACE_FUNCALLS("> ProlificDevice::ResetDevice(%08x)\n", this);
133179706Sjhb
134105666Ssemenu	SendRequestList(prolific_reset_common, B_COUNT_OF(prolific_reset_common));
135179706Sjhb	if (fIsHX)
136105666Ssemenu		SendRequestList(prolific_reset_common_hx, B_COUNT_OF(prolific_reset_common_hx));
137179706Sjhb	else
138105666Ssemenu		SendRequestList(prolific_reset_common_nhx, B_COUNT_OF(prolific_reset_common_nhx));
139179706Sjhb
140105666Ssemenu	status_t status = B_OK; /* discard */
141179706Sjhb	TRACE_FUNCRET("< ProlificDevice::ResetDevice() returns: 0x%08x\n", status);
14240804Ssemenu	return status;
143105666Ssemenu}
144105666Ssemenu