ucycom.c revision 188746
1184610Salfred#include <sys/cdefs.h>
2184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/ucycom2.c 188746 2009-02-18 06:33:10Z thompsa $");
3184610Salfred
4184610Salfred/*-
5184610Salfred * Copyright (c) 2004 Dag-Erling Co�dan Sm�rgrav
6184610Salfred * All rights reserved.
7184610Salfred *
8184610Salfred * Redistribution and use in source and binary forms, with or without
9184610Salfred * modification, are permitted provided that the following conditions
10184610Salfred * are met:
11184610Salfred * 1. Redistributions of source code must retain the above copyright
12184610Salfred *    notice, this list of conditions and the following disclaimer
13184610Salfred *    in this position and unchanged.
14184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
15184610Salfred *    notice, this list of conditions and the following disclaimer in the
16184610Salfred *    documentation and/or other materials provided with the distribution.
17184610Salfred * 3. The name of the author may not be used to endorse or promote products
18184610Salfred *    derived from this software without specific prior written permission.
19184610Salfred *
20184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21184610Salfred * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22184610Salfred * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23184610Salfred * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24184610Salfred * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25184610Salfred * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26184610Salfred * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27184610Salfred * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28184610Salfred * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29184610Salfred * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30184610Salfred */
31184610Salfred
32184610Salfred/*
33184610Salfred * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to
34184610Salfred * RS232 bridges.
35184610Salfred */
36184610Salfred
37188746Sthompsa#include "usbdevs.h"
38184610Salfred#include <dev/usb2/include/usb2_standard.h>
39184610Salfred#include <dev/usb2/include/usb2_mfunc.h>
40184610Salfred#include <dev/usb2/include/usb2_error.h>
41184610Salfred#include <dev/usb2/include/usb2_cdc.h>
42184610Salfred#include <dev/usb2/include/usb2_ioctl.h>
43184610Salfred#include <dev/usb2/include/usb2_hid.h>
44184610Salfred
45184610Salfred#define	USB_DEBUG_VAR usb2_debug
46184610Salfred
47184610Salfred#include <dev/usb2/core/usb2_core.h>
48184610Salfred#include <dev/usb2/core/usb2_debug.h>
49184610Salfred#include <dev/usb2/core/usb2_process.h>
50184610Salfred#include <dev/usb2/core/usb2_request.h>
51184610Salfred#include <dev/usb2/core/usb2_lookup.h>
52184610Salfred#include <dev/usb2/core/usb2_util.h>
53184610Salfred#include <dev/usb2/core/usb2_busdma.h>
54184610Salfred#include <dev/usb2/core/usb2_hid.h>
55184610Salfred
56184610Salfred#include <dev/usb2/serial/usb2_serial.h>
57184610Salfred
58184610Salfred#define	UCYCOM_MAX_IOLEN	(1024 + 2)	/* bytes */
59184610Salfred
60184610Salfred#define	UCYCOM_IFACE_INDEX	0
61184610Salfred
62187259Sthompsaenum {
63187259Sthompsa	UCYCOM_CTRL_RD,
64187259Sthompsa	UCYCOM_INTR_RD,
65188413Sthompsa	UCYCOM_N_TRANSFER,
66187259Sthompsa};
67187259Sthompsa
68184610Salfredstruct ucycom_softc {
69184610Salfred	struct usb2_com_super_softc sc_super_ucom;
70184610Salfred	struct usb2_com_softc sc_ucom;
71184610Salfred
72184610Salfred	struct usb2_device *sc_udev;
73187259Sthompsa	struct usb2_xfer *sc_xfer[UCYCOM_N_TRANSFER];
74184610Salfred
75184610Salfred	uint32_t sc_model;
76184610Salfred#define	MODEL_CY7C63743		0x63743
77184610Salfred#define	MODEL_CY7C64013		0x64013
78184610Salfred
79184610Salfred	uint16_t sc_flen;		/* feature report length */
80184610Salfred	uint16_t sc_ilen;		/* input report length */
81184610Salfred	uint16_t sc_olen;		/* output report length */
82184610Salfred
83184610Salfred	uint8_t	sc_fid;			/* feature report id */
84184610Salfred	uint8_t	sc_iid;			/* input report id */
85184610Salfred	uint8_t	sc_oid;			/* output report id */
86184610Salfred	uint8_t	sc_cfg;
87184610Salfred#define	UCYCOM_CFG_RESET	0x80
88184610Salfred#define	UCYCOM_CFG_PARODD	0x20
89184610Salfred#define	UCYCOM_CFG_PAREN	0x10
90184610Salfred#define	UCYCOM_CFG_STOPB	0x08
91184610Salfred#define	UCYCOM_CFG_DATAB	0x03
92184610Salfred	uint8_t	sc_ist;			/* status flags from last input */
93184610Salfred	uint8_t	sc_name[16];
94184610Salfred	uint8_t	sc_iface_no;
95184610Salfred	uint8_t	sc_temp_cfg[32];
96184610Salfred};
97184610Salfred
98184610Salfred/* prototypes */
99184610Salfred
100184610Salfredstatic device_probe_t ucycom_probe;
101184610Salfredstatic device_attach_t ucycom_attach;
102184610Salfredstatic device_detach_t ucycom_detach;
103184610Salfred
104184610Salfredstatic usb2_callback_t ucycom_ctrl_write_callback;
105184610Salfredstatic usb2_callback_t ucycom_intr_read_callback;
106184610Salfred
107185948Sthompsastatic void	ucycom_cfg_open(struct usb2_com_softc *);
108185948Sthompsastatic void	ucycom_start_read(struct usb2_com_softc *);
109185948Sthompsastatic void	ucycom_stop_read(struct usb2_com_softc *);
110185948Sthompsastatic void	ucycom_start_write(struct usb2_com_softc *);
111185948Sthompsastatic void	ucycom_stop_write(struct usb2_com_softc *);
112185948Sthompsastatic void	ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t);
113185948Sthompsastatic int	ucycom_pre_param(struct usb2_com_softc *, struct termios *);
114185948Sthompsastatic void	ucycom_cfg_param(struct usb2_com_softc *, struct termios *);
115184610Salfred
116187259Sthompsastatic const struct usb2_config ucycom_config[UCYCOM_N_TRANSFER] = {
117184610Salfred
118187259Sthompsa	[UCYCOM_CTRL_RD] = {
119184610Salfred		.type = UE_CONTROL,
120184610Salfred		.endpoint = 0x00,	/* Control pipe */
121184610Salfred		.direction = UE_DIR_ANY,
122184610Salfred		.mh.bufsize = (sizeof(struct usb2_device_request) + UCYCOM_MAX_IOLEN),
123184610Salfred		.mh.flags = {},
124184610Salfred		.mh.callback = &ucycom_ctrl_write_callback,
125184610Salfred		.mh.timeout = 1000,	/* 1 second */
126184610Salfred	},
127184610Salfred
128187259Sthompsa	[UCYCOM_INTR_RD] = {
129184610Salfred		.type = UE_INTERRUPT,
130184610Salfred		.endpoint = UE_ADDR_ANY,
131184610Salfred		.direction = UE_DIR_IN,
132184610Salfred		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
133184610Salfred		.mh.bufsize = UCYCOM_MAX_IOLEN,
134184610Salfred		.mh.callback = &ucycom_intr_read_callback,
135184610Salfred	},
136184610Salfred};
137184610Salfred
138184610Salfredstatic const struct usb2_com_callback ucycom_callback = {
139184610Salfred	.usb2_com_cfg_param = &ucycom_cfg_param,
140184610Salfred	.usb2_com_cfg_open = &ucycom_cfg_open,
141184610Salfred	.usb2_com_pre_param = &ucycom_pre_param,
142184610Salfred	.usb2_com_start_read = &ucycom_start_read,
143184610Salfred	.usb2_com_stop_read = &ucycom_stop_read,
144184610Salfred	.usb2_com_start_write = &ucycom_start_write,
145184610Salfred	.usb2_com_stop_write = &ucycom_stop_write,
146184610Salfred};
147184610Salfred
148184610Salfredstatic device_method_t ucycom_methods[] = {
149184610Salfred	DEVMETHOD(device_probe, ucycom_probe),
150184610Salfred	DEVMETHOD(device_attach, ucycom_attach),
151184610Salfred	DEVMETHOD(device_detach, ucycom_detach),
152184610Salfred	{0, 0}
153184610Salfred};
154184610Salfred
155184610Salfredstatic devclass_t ucycom_devclass;
156184610Salfred
157184610Salfredstatic driver_t ucycom_driver = {
158184610Salfred	.name = "ucycom",
159184610Salfred	.methods = ucycom_methods,
160184610Salfred	.size = sizeof(struct ucycom_softc),
161184610Salfred};
162184610Salfred
163184610SalfredDRIVER_MODULE(ucycom, ushub, ucycom_driver, ucycom_devclass, NULL, 0);
164184610SalfredMODULE_DEPEND(ucycom, usb2_serial, 1, 1, 1);
165184610SalfredMODULE_DEPEND(ucycom, usb2_core, 1, 1, 1);
166184610Salfred
167184610Salfred/*
168184610Salfred * Supported devices
169184610Salfred */
170184610Salfredstatic const struct usb2_device_id ucycom_devs[] = {
171184610Salfred	{USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)},
172184610Salfred};
173184610Salfred
174184610Salfred#define	UCYCOM_DEFAULT_RATE	 4800
175184610Salfred#define	UCYCOM_DEFAULT_CFG	 0x03	/* N-8-1 */
176184610Salfred
177184610Salfredstatic int
178184610Salfreducycom_probe(device_t dev)
179184610Salfred{
180184610Salfred	struct usb2_attach_arg *uaa = device_get_ivars(dev);
181184610Salfred
182184610Salfred	if (uaa->usb2_mode != USB_MODE_HOST) {
183184610Salfred		return (ENXIO);
184184610Salfred	}
185184610Salfred	if (uaa->info.bConfigIndex != 0) {
186184610Salfred		return (ENXIO);
187184610Salfred	}
188184610Salfred	if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) {
189184610Salfred		return (ENXIO);
190184610Salfred	}
191184610Salfred	return (usb2_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa));
192184610Salfred}
193184610Salfred
194184610Salfredstatic int
195184610Salfreducycom_attach(device_t dev)
196184610Salfred{
197184610Salfred	struct usb2_attach_arg *uaa = device_get_ivars(dev);
198184610Salfred	struct ucycom_softc *sc = device_get_softc(dev);
199184610Salfred	void *urd_ptr = NULL;
200184610Salfred	int32_t error;
201184610Salfred	uint16_t urd_len;
202184610Salfred	uint8_t iface_index;
203184610Salfred
204184610Salfred	sc->sc_udev = uaa->device;
205184610Salfred
206184610Salfred	device_set_usb2_desc(dev);
207184610Salfred
208184610Salfred	snprintf(sc->sc_name, sizeof(sc->sc_name),
209184610Salfred	    "%s", device_get_nameunit(dev));
210184610Salfred
211184610Salfred	DPRINTF("\n");
212184610Salfred
213184610Salfred	/* get chip model */
214184610Salfred	sc->sc_model = USB_GET_DRIVER_INFO(uaa);
215184610Salfred	if (sc->sc_model == 0) {
216184610Salfred		device_printf(dev, "unsupported device\n");
217184610Salfred		goto detach;
218184610Salfred	}
219184610Salfred	device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model);
220184610Salfred
221184610Salfred	/* get report descriptor */
222184610Salfred
223184610Salfred	error = usb2_req_get_hid_desc
224184610Salfred	    (uaa->device, &Giant,
225184610Salfred	    &urd_ptr, &urd_len, M_USBDEV,
226184610Salfred	    UCYCOM_IFACE_INDEX);
227184610Salfred
228184610Salfred	if (error) {
229184610Salfred		device_printf(dev, "failed to get report "
230184610Salfred		    "descriptor: %s\n",
231184610Salfred		    usb2_errstr(error));
232184610Salfred		goto detach;
233184610Salfred	}
234184610Salfred	/* get report sizes */
235184610Salfred
236184610Salfred	sc->sc_flen = hid_report_size(urd_ptr, urd_len, hid_feature, &sc->sc_fid);
237184610Salfred	sc->sc_ilen = hid_report_size(urd_ptr, urd_len, hid_input, &sc->sc_iid);
238184610Salfred	sc->sc_olen = hid_report_size(urd_ptr, urd_len, hid_output, &sc->sc_oid);
239184610Salfred
240184610Salfred	if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) ||
241184610Salfred	    (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) ||
242184610Salfred	    (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) {
243184610Salfred		device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n",
244184610Salfred		    sc->sc_ilen, sc->sc_olen, sc->sc_flen,
245184610Salfred		    UCYCOM_MAX_IOLEN);
246184610Salfred		goto detach;
247184610Salfred	}
248184610Salfred	sc->sc_iface_no = uaa->info.bIfaceNum;
249184610Salfred
250184610Salfred	iface_index = UCYCOM_IFACE_INDEX;
251184610Salfred	error = usb2_transfer_setup(uaa->device, &iface_index,
252187259Sthompsa	    sc->sc_xfer, ucycom_config, UCYCOM_N_TRANSFER,
253184610Salfred	    sc, &Giant);
254184610Salfred	if (error) {
255184610Salfred		device_printf(dev, "allocating USB "
256184610Salfred		    "transfers failed!\n");
257184610Salfred		goto detach;
258184610Salfred	}
259184610Salfred	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
260184610Salfred	    &ucycom_callback, &Giant);
261184610Salfred
262184610Salfred	if (error) {
263184610Salfred		goto detach;
264184610Salfred	}
265184610Salfred	if (urd_ptr) {
266184610Salfred		free(urd_ptr, M_USBDEV);
267184610Salfred	}
268184610Salfred	return (0);			/* success */
269184610Salfred
270184610Salfreddetach:
271184610Salfred	if (urd_ptr) {
272184610Salfred		free(urd_ptr, M_USBDEV);
273184610Salfred	}
274184610Salfred	ucycom_detach(dev);
275184610Salfred	return (ENXIO);
276184610Salfred}
277184610Salfred
278184610Salfredstatic int
279184610Salfreducycom_detach(device_t dev)
280184610Salfred{
281184610Salfred	struct ucycom_softc *sc = device_get_softc(dev);
282184610Salfred
283184610Salfred	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
284184610Salfred
285187259Sthompsa	usb2_transfer_unsetup(sc->sc_xfer, UCYCOM_N_TRANSFER);
286184610Salfred
287184610Salfred	return (0);
288184610Salfred}
289184610Salfred
290184610Salfredstatic void
291184610Salfreducycom_cfg_open(struct usb2_com_softc *ucom)
292184610Salfred{
293184610Salfred	struct ucycom_softc *sc = ucom->sc_parent;
294184610Salfred
295184610Salfred	/* set default configuration */
296184610Salfred	ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG);
297184610Salfred}
298184610Salfred
299184610Salfredstatic void
300184610Salfreducycom_start_read(struct usb2_com_softc *ucom)
301184610Salfred{
302184610Salfred	struct ucycom_softc *sc = ucom->sc_parent;
303184610Salfred
304187259Sthompsa	usb2_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]);
305184610Salfred}
306184610Salfred
307184610Salfredstatic void
308184610Salfreducycom_stop_read(struct usb2_com_softc *ucom)
309184610Salfred{
310184610Salfred	struct ucycom_softc *sc = ucom->sc_parent;
311184610Salfred
312187259Sthompsa	usb2_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]);
313184610Salfred}
314184610Salfred
315184610Salfredstatic void
316184610Salfreducycom_start_write(struct usb2_com_softc *ucom)
317184610Salfred{
318184610Salfred	struct ucycom_softc *sc = ucom->sc_parent;
319184610Salfred
320187259Sthompsa	usb2_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]);
321184610Salfred}
322184610Salfred
323184610Salfredstatic void
324184610Salfreducycom_stop_write(struct usb2_com_softc *ucom)
325184610Salfred{
326184610Salfred	struct ucycom_softc *sc = ucom->sc_parent;
327184610Salfred
328187259Sthompsa	usb2_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]);
329184610Salfred}
330184610Salfred
331184610Salfredstatic void
332184610Salfreducycom_ctrl_write_callback(struct usb2_xfer *xfer)
333184610Salfred{
334184610Salfred	struct ucycom_softc *sc = xfer->priv_sc;
335184610Salfred	struct usb2_device_request req;
336184610Salfred	uint8_t data[2];
337184610Salfred	uint8_t offset;
338184610Salfred	uint32_t actlen;
339184610Salfred
340184610Salfred	switch (USB_GET_STATE(xfer)) {
341184610Salfred	case USB_ST_TRANSFERRED:
342184610Salfredtr_transferred:
343184610Salfred	case USB_ST_SETUP:
344184610Salfred
345184610Salfred		switch (sc->sc_model) {
346184610Salfred		case MODEL_CY7C63743:
347184610Salfred			offset = 1;
348184610Salfred			break;
349184610Salfred		case MODEL_CY7C64013:
350184610Salfred			offset = 2;
351184610Salfred			break;
352184610Salfred		default:
353184610Salfred			offset = 0;
354184610Salfred			break;
355184610Salfred		}
356184610Salfred
357184610Salfred		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1, offset,
358184610Salfred		    sc->sc_olen - offset, &actlen)) {
359184610Salfred
360184610Salfred			req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
361184610Salfred			req.bRequest = UR_SET_REPORT;
362184610Salfred			USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid);
363184610Salfred			req.wIndex[0] = sc->sc_iface_no;
364184610Salfred			req.wIndex[1] = 0;
365184610Salfred			USETW(req.wLength, sc->sc_olen);
366184610Salfred
367184610Salfred			switch (sc->sc_model) {
368184610Salfred			case MODEL_CY7C63743:
369184610Salfred				data[0] = actlen;
370184610Salfred				break;
371184610Salfred			case MODEL_CY7C64013:
372184610Salfred				data[0] = 0;
373184610Salfred				data[1] = actlen;
374184610Salfred				break;
375184610Salfred			default:
376184610Salfred				break;
377184610Salfred			}
378184610Salfred
379184610Salfred			usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
380184610Salfred			usb2_copy_in(xfer->frbuffers + 1, 0, data, offset);
381184610Salfred
382184610Salfred			xfer->frlengths[0] = sizeof(req);
383184610Salfred			xfer->frlengths[1] = sc->sc_olen;
384184610Salfred			xfer->nframes = xfer->frlengths[1] ? 2 : 1;
385184610Salfred			usb2_start_hardware(xfer);
386184610Salfred		}
387184610Salfred		return;
388184610Salfred
389184610Salfred	default:			/* Error */
390184610Salfred		if (xfer->error == USB_ERR_CANCELLED) {
391184610Salfred			return;
392184610Salfred		}
393184610Salfred		DPRINTF("error=%s\n",
394184610Salfred		    usb2_errstr(xfer->error));
395184610Salfred		goto tr_transferred;
396184610Salfred	}
397184610Salfred}
398184610Salfred
399184610Salfredstatic void
400184610Salfreducycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg)
401184610Salfred{
402184610Salfred	struct usb2_device_request req;
403184610Salfred	uint16_t len;
404184610Salfred	usb2_error_t err;
405184610Salfred
406184610Salfred	len = sc->sc_flen;
407184610Salfred	if (len > sizeof(sc->sc_temp_cfg)) {
408184610Salfred		len = sizeof(sc->sc_temp_cfg);
409184610Salfred	}
410184610Salfred	sc->sc_cfg = cfg;
411184610Salfred
412184610Salfred	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
413184610Salfred	req.bRequest = UR_SET_REPORT;
414184610Salfred	USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid);
415184610Salfred	req.wIndex[0] = sc->sc_iface_no;
416184610Salfred	req.wIndex[1] = 0;
417184610Salfred	USETW(req.wLength, len);
418184610Salfred
419184610Salfred	sc->sc_temp_cfg[0] = (baud & 0xff);
420184610Salfred	sc->sc_temp_cfg[1] = (baud >> 8) & 0xff;
421184610Salfred	sc->sc_temp_cfg[2] = (baud >> 16) & 0xff;
422184610Salfred	sc->sc_temp_cfg[3] = (baud >> 24) & 0xff;
423184610Salfred	sc->sc_temp_cfg[4] = cfg;
424184610Salfred
425188413Sthompsa	err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
426188413Sthompsa	    &req, sc->sc_temp_cfg, 0, 1000);
427184610Salfred	if (err) {
428184610Salfred		DPRINTFN(0, "device request failed, err=%s "
429184610Salfred		    "(ignored)\n", usb2_errstr(err));
430184610Salfred	}
431184610Salfred}
432184610Salfred
433184610Salfredstatic int
434184610Salfreducycom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
435184610Salfred{
436184610Salfred	switch (t->c_ospeed) {
437184610Salfred		case 600:
438184610Salfred		case 1200:
439184610Salfred		case 2400:
440184610Salfred		case 4800:
441184610Salfred		case 9600:
442184610Salfred		case 19200:
443184610Salfred		case 38400:
444184610Salfred		case 57600:
445184610Salfred#if 0
446184610Salfred		/*
447184610Salfred		 * Stock chips only support standard baud rates in the 600 - 57600
448184610Salfred		 * range, but higher rates can be achieved using custom firmware.
449184610Salfred		 */
450184610Salfred		case 115200:
451184610Salfred		case 153600:
452184610Salfred		case 192000:
453184610Salfred#endif
454184610Salfred		break;
455184610Salfred	default:
456184610Salfred		return (EINVAL);
457184610Salfred	}
458184610Salfred	return (0);
459184610Salfred}
460184610Salfred
461184610Salfredstatic void
462184610Salfreducycom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
463184610Salfred{
464184610Salfred	struct ucycom_softc *sc = ucom->sc_parent;
465184610Salfred	uint8_t cfg;
466184610Salfred
467184610Salfred	DPRINTF("\n");
468184610Salfred
469184610Salfred	if (t->c_cflag & CIGNORE) {
470184610Salfred		cfg = sc->sc_cfg;
471184610Salfred	} else {
472184610Salfred		cfg = 0;
473184610Salfred		switch (t->c_cflag & CSIZE) {
474184610Salfred		default:
475184610Salfred		case CS8:
476184610Salfred			++cfg;
477184610Salfred		case CS7:
478184610Salfred			++cfg;
479184610Salfred		case CS6:
480184610Salfred			++cfg;
481184610Salfred		case CS5:
482184610Salfred			break;
483184610Salfred		}
484184610Salfred
485184610Salfred		if (t->c_cflag & CSTOPB)
486184610Salfred			cfg |= UCYCOM_CFG_STOPB;
487184610Salfred		if (t->c_cflag & PARENB)
488184610Salfred			cfg |= UCYCOM_CFG_PAREN;
489184610Salfred		if (t->c_cflag & PARODD)
490184610Salfred			cfg |= UCYCOM_CFG_PARODD;
491184610Salfred	}
492184610Salfred
493184610Salfred	ucycom_cfg_write(sc, t->c_ospeed, cfg);
494184610Salfred}
495184610Salfred
496184610Salfredstatic void
497184610Salfreducycom_intr_read_callback(struct usb2_xfer *xfer)
498184610Salfred{
499184610Salfred	struct ucycom_softc *sc = xfer->priv_sc;
500184610Salfred	uint8_t buf[2];
501184610Salfred	uint32_t offset;
502184610Salfred	uint32_t len;
503184610Salfred
504184610Salfred	switch (USB_GET_STATE(xfer)) {
505184610Salfred	case USB_ST_TRANSFERRED:
506184610Salfred		switch (sc->sc_model) {
507184610Salfred		case MODEL_CY7C63743:
508184610Salfred			if (xfer->actlen < 1) {
509184610Salfred				goto tr_setup;
510184610Salfred			}
511184610Salfred			usb2_copy_out(xfer->frbuffers, 0, buf, 1);
512184610Salfred
513184610Salfred			sc->sc_ist = buf[0] & ~0x07;
514184610Salfred			len = buf[0] & 0x07;
515184610Salfred
516184610Salfred			(xfer->actlen)--;
517184610Salfred
518184610Salfred			offset = 1;
519184610Salfred
520184610Salfred			break;
521184610Salfred
522184610Salfred		case MODEL_CY7C64013:
523184610Salfred			if (xfer->actlen < 2) {
524184610Salfred				goto tr_setup;
525184610Salfred			}
526184610Salfred			usb2_copy_out(xfer->frbuffers, 0, buf, 2);
527184610Salfred
528184610Salfred			sc->sc_ist = buf[0] & ~0x07;
529184610Salfred			len = buf[1];
530184610Salfred
531184610Salfred			(xfer->actlen) -= 2;
532184610Salfred
533184610Salfred			offset = 2;
534184610Salfred
535184610Salfred			break;
536184610Salfred
537184610Salfred		default:
538184610Salfred			DPRINTFN(0, "unsupported model number!\n");
539184610Salfred			goto tr_setup;
540184610Salfred		}
541184610Salfred
542184610Salfred		if (len > xfer->actlen) {
543184610Salfred			len = xfer->actlen;
544184610Salfred		}
545184610Salfred		if (len) {
546184610Salfred			usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers,
547184610Salfred			    offset, len);
548184610Salfred		}
549184610Salfred	case USB_ST_SETUP:
550184610Salfredtr_setup:
551188413Sthompsa		xfer->frlengths[0] = sc->sc_ilen;
552188413Sthompsa		usb2_start_hardware(xfer);
553184610Salfred		return;
554184610Salfred
555184610Salfred	default:			/* Error */
556184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
557188413Sthompsa			/* try to clear stall first */
558188413Sthompsa			xfer->flags.stall_pipe = 1;
559188413Sthompsa			goto tr_setup;
560184610Salfred		}
561184610Salfred		return;
562184610Salfred
563184610Salfred	}
564184610Salfred}
565