xref: /openbsd-src/sys/dev/usb/ukspan.c (revision c520a48cc23c2654af7c9d96f1ddb9073e7223fc)
1*c520a48cSnaddy /*	$OpenBSD: ukspan.c,v 1.4 2022/04/09 20:07:44 naddy Exp $ */
2a51ea3e3Sjasper 
3867dd35eSjcs /*
4867dd35eSjcs  * Copyright (c) 2019 Cody Cutler <ccutler@csail.mit.edu>
5867dd35eSjcs  *
6867dd35eSjcs  * Permission to use, copy, modify, and distribute this software for any
7867dd35eSjcs  * purpose with or without fee is hereby granted, provided that the above
8867dd35eSjcs  * copyright notice and this permission notice appear in all copies.
9867dd35eSjcs  *
10867dd35eSjcs  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11867dd35eSjcs  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12867dd35eSjcs  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13867dd35eSjcs  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14867dd35eSjcs  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15867dd35eSjcs  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16867dd35eSjcs  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17867dd35eSjcs  */
18867dd35eSjcs 
19867dd35eSjcs /*
20867dd35eSjcs  * I don't know of any technical documentation for the Keyspan USA-19HS. I
21867dd35eSjcs  * inspected the Linux driver (drivers/usb/serial/keyspan_usa90msg.h) to learn
22867dd35eSjcs  * the device message format and the procedure for setting the baud rate.
23867dd35eSjcs  */
24867dd35eSjcs 
25867dd35eSjcs #include <sys/param.h>
26867dd35eSjcs #include <sys/systm.h>
27867dd35eSjcs #include <sys/tty.h>
28867dd35eSjcs 
29867dd35eSjcs #include <dev/usb/usb.h>
30867dd35eSjcs #include <dev/usb/usbdevs.h>
31867dd35eSjcs #include <dev/usb/usbdi.h>
32867dd35eSjcs #include <dev/usb/usbdi_util.h>
33867dd35eSjcs 
34867dd35eSjcs #include <dev/usb/ucomvar.h>
35867dd35eSjcs 
36867dd35eSjcs /*#define UKSPAN_DEBUG */
37867dd35eSjcs 
38867dd35eSjcs #ifdef UKSPAN_DEBUG
39867dd35eSjcs 	#define DPRINTF(x...)	do { printf(x); } while (0)
40867dd35eSjcs #else
41867dd35eSjcs 	#define DPRINTF(x...)	do { ; } while (0)
42867dd35eSjcs #endif
43867dd35eSjcs 
44867dd35eSjcs #define UKSPAN_PARITY_NONE	0x0
45867dd35eSjcs #define UKSPAN_PARITY_ODD	0x08
46867dd35eSjcs #define UKSPAN_PARITY_EVEN	0x18
47867dd35eSjcs 
48867dd35eSjcs #define UKSPAN_DATA_5		0x0
49867dd35eSjcs #define UKSPAN_DATA_6		0x1
50867dd35eSjcs #define UKSPAN_DATA_7		0x2
51867dd35eSjcs #define UKSPAN_DATA_8		0x3
52867dd35eSjcs 
53867dd35eSjcs #define UKSPAN_STOP_1		0x0
54867dd35eSjcs #define UKSPAN_STOP_2		0x4
55867dd35eSjcs 
56867dd35eSjcs #define UKSPAN_MAGIC		0x2
57867dd35eSjcs 
58867dd35eSjcs #define UKSPAN_CLOCK		14769231
59867dd35eSjcs 
60867dd35eSjcs /*
61867dd35eSjcs  * The following USB indexes and endpoint addresses may be specific to the
62867dd35eSjcs  * Keyspan USA19HS device
63867dd35eSjcs  */
64867dd35eSjcs #define UKSPAN_CONFIG_IDX	1
65867dd35eSjcs #define UKSPAN_IFACE_IDX	0
66867dd35eSjcs 
67867dd35eSjcs #define UKSPAN_EA_BULKIN	(UE_DIR_IN  | 1)
68867dd35eSjcs #define UKSPAN_EA_BULKOUT	(UE_DIR_OUT | 1)
69867dd35eSjcs #define UKSPAN_EA_CONFIGIN	(UE_DIR_IN  | 2)
70867dd35eSjcs #define UKSPAN_EA_CONFIGOUT	(UE_DIR_OUT | 2)
71867dd35eSjcs 
72867dd35eSjcs /* Sent to device on control out endpoint */
73867dd35eSjcs struct ukspan_cmsg {
74867dd35eSjcs 	uint8_t	setclock;
75867dd35eSjcs 	uint8_t	baudlo;
76867dd35eSjcs 	uint8_t	baudhi;
77867dd35eSjcs 	uint8_t	setlcr;
78867dd35eSjcs 	uint8_t	lcr;
79867dd35eSjcs 	uint8_t	setrxmode;
80867dd35eSjcs 	uint8_t	rxmode;
81867dd35eSjcs 	uint8_t	settxmode;
82867dd35eSjcs 	uint8_t	txmode;
83867dd35eSjcs 	uint8_t	settxflowcontrol;
84867dd35eSjcs 	uint8_t	txflowcontrol;
85867dd35eSjcs 	uint8_t	setrxflowcontrol;
86867dd35eSjcs 	uint8_t	rxflowcontrol;
87867dd35eSjcs 	uint8_t	sendxoff;
88867dd35eSjcs 	uint8_t	sendxon;
89867dd35eSjcs 	uint8_t	xonchar;
90867dd35eSjcs 	uint8_t	xoffchar;
91867dd35eSjcs 	uint8_t	sendchar;
92867dd35eSjcs 	uint8_t	txchar;
93867dd35eSjcs 	uint8_t	setrts;
94867dd35eSjcs 	uint8_t	rts;
95867dd35eSjcs 	uint8_t	setdtr;
96867dd35eSjcs 	uint8_t	dtr;
97867dd35eSjcs 
98867dd35eSjcs 	uint8_t	rxforwardingchars;
99867dd35eSjcs 	uint8_t	rxforwardingtimeoutms;
100867dd35eSjcs 	uint8_t	txacksetting;
101867dd35eSjcs 
102867dd35eSjcs 	uint8_t	portenabled;
103867dd35eSjcs 	uint8_t	txflush;
104867dd35eSjcs 	uint8_t	txbreak;
105867dd35eSjcs 	uint8_t	loopbackmode;
106867dd35eSjcs 
107867dd35eSjcs 	uint8_t	rxflush;
108867dd35eSjcs 	uint8_t	rxforward;
109867dd35eSjcs 	uint8_t	cancelrxoff;
110867dd35eSjcs 	uint8_t	returnstatus;
111867dd35eSjcs } __packed;
112867dd35eSjcs 
113867dd35eSjcs /* Received from device on control in endpoint */
114867dd35eSjcs struct ukspan_smsg {
115867dd35eSjcs 	uint8_t msr;
116867dd35eSjcs 	uint8_t	cts;
117867dd35eSjcs 	uint8_t	dcd;
118867dd35eSjcs 	uint8_t	dsr;
119867dd35eSjcs 	uint8_t	ri;
120867dd35eSjcs 	uint8_t	txxoff;
121867dd35eSjcs 	uint8_t	rxbreak;
122867dd35eSjcs 	uint8_t	rxoverrun;
123867dd35eSjcs 	uint8_t	rxparity;
124867dd35eSjcs 	uint8_t	rxframe;
125867dd35eSjcs 	uint8_t	portstate;
126867dd35eSjcs 	uint8_t	messageack;
127867dd35eSjcs 	uint8_t	charack;
128867dd35eSjcs 	uint8_t	controlresp;
129867dd35eSjcs } __packed;
130867dd35eSjcs 
131867dd35eSjcs struct ukspan_softc {
132867dd35eSjcs 	struct device sc_dev;
133867dd35eSjcs 	struct usbd_device *udev;
134867dd35eSjcs 	struct usbd_interface *iface;
135867dd35eSjcs 	struct usbd_pipe *cout_pipe;
136867dd35eSjcs 	struct usbd_pipe *cin_pipe;
137867dd35eSjcs 	struct usbd_xfer *ixfer;
138867dd35eSjcs 	struct usbd_xfer *oxfer;
139867dd35eSjcs 	struct device *ucom_dev;
140867dd35eSjcs 	struct ukspan_smsg smsg;
141867dd35eSjcs 	struct ukspan_cmsg cmsg;
142867dd35eSjcs 	u_char lsr;
143867dd35eSjcs 	u_char msr;
144867dd35eSjcs };
145867dd35eSjcs 
146867dd35eSjcs int  ukspan_match(struct device *, void *, void *);
147867dd35eSjcs void ukspan_attach(struct device *, struct device *, void *);
148867dd35eSjcs int  ukspan_detach(struct device *, int);
149867dd35eSjcs 
150867dd35eSjcs void ukspan_close(void *, int);
151867dd35eSjcs int  ukspan_open(void *, int);
152867dd35eSjcs int  ukspan_param(void *, int, struct termios *);
153867dd35eSjcs void ukspan_set(void *, int, int, int);
154867dd35eSjcs void ukspan_get_status(void *, int, u_char *, u_char *);
155867dd35eSjcs 
156867dd35eSjcs void ukspan_cmsg_init(bool, struct ukspan_cmsg *);
157867dd35eSjcs int  ukspan_cmsg_send(struct ukspan_softc *);
158867dd35eSjcs void ukspan_incb(struct usbd_xfer *, void *, usbd_status);
159867dd35eSjcs void ukspan_outcb(struct usbd_xfer *, void *, usbd_status);
160867dd35eSjcs void ukspan_destroy(struct ukspan_softc *);
161867dd35eSjcs 
162867dd35eSjcs struct cfdriver ukspan_cd = {
163867dd35eSjcs 	NULL, "ukspan", DV_DULL
164867dd35eSjcs };
165867dd35eSjcs 
166867dd35eSjcs const struct cfattach ukspan_ca = {
167867dd35eSjcs 	sizeof(struct ukspan_softc), ukspan_match, ukspan_attach,
168867dd35eSjcs 	ukspan_detach
169867dd35eSjcs };
170867dd35eSjcs 
171867dd35eSjcs static const struct usb_devno ukspan_devs[] = {
172867dd35eSjcs 	{ USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19HS },
173867dd35eSjcs };
174867dd35eSjcs 
175*c520a48cSnaddy static const struct ucom_methods ukspan_methods = {
176867dd35eSjcs 	.ucom_get_status = ukspan_get_status,
177867dd35eSjcs 	.ucom_set = ukspan_set,
178867dd35eSjcs 	.ucom_param = ukspan_param,
179867dd35eSjcs 	.ucom_ioctl = NULL,
180867dd35eSjcs 	.ucom_open = ukspan_open,
181867dd35eSjcs 	.ucom_close = ukspan_close,
182867dd35eSjcs 	.ucom_read = NULL,
183867dd35eSjcs 	.ucom_write = NULL,
184867dd35eSjcs };
185867dd35eSjcs 
186867dd35eSjcs int
ukspan_match(struct device * parent,void * match,void * aux)187867dd35eSjcs ukspan_match(struct device *parent, void *match, void *aux)
188867dd35eSjcs {
189867dd35eSjcs 	struct usb_attach_arg *uaa = aux;
190867dd35eSjcs 
191867dd35eSjcs 	if (uaa->iface != NULL)
192867dd35eSjcs 		return UMATCH_NONE;
193867dd35eSjcs 
194867dd35eSjcs 	int found = usb_lookup(ukspan_devs, uaa->vendor, uaa->product) != NULL;
195867dd35eSjcs 	return found ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
196867dd35eSjcs }
197867dd35eSjcs 
198867dd35eSjcs void
ukspan_attach(struct device * parent,struct device * self,void * aux)199867dd35eSjcs ukspan_attach(struct device *parent, struct device *self, void *aux)
200867dd35eSjcs {
201867dd35eSjcs 	struct ukspan_softc *sc = (struct ukspan_softc *)self;
202867dd35eSjcs 	struct usb_attach_arg *uaa = aux;
203867dd35eSjcs 	struct usbd_device *dev = uaa->device;
204867dd35eSjcs 	struct ucom_attach_args uca = {0};
205867dd35eSjcs 	usb_endpoint_descriptor_t *ed;
206867dd35eSjcs 	const char *devname = sc->sc_dev.dv_xname;
207867dd35eSjcs 	usbd_status err;
208867dd35eSjcs 	int t1, t2, t3, t4;
209867dd35eSjcs 
210867dd35eSjcs 	DPRINTF("attach\n");
211867dd35eSjcs 
212867dd35eSjcs 	sc->udev = dev;
213867dd35eSjcs 	sc->cin_pipe = sc->cout_pipe = NULL;
214867dd35eSjcs 	sc->ixfer = sc->oxfer = NULL;
215867dd35eSjcs 	sc->ucom_dev = NULL;
216867dd35eSjcs 
217867dd35eSjcs 	/*
218867dd35eSjcs 	 * Switch to configuration 1 where the transfer mode of the input
219867dd35eSjcs 	 * endpoints is bulk instead of interrupt, as ucom expects
220867dd35eSjcs 	 */
221867dd35eSjcs 	err = usbd_set_config_index(sc->udev, UKSPAN_CONFIG_IDX, 1);
222867dd35eSjcs 	if (err) {
223867dd35eSjcs 		printf("%s: set config failed\n", devname);
224867dd35eSjcs 		goto fail;
225867dd35eSjcs 	}
226867dd35eSjcs 
227867dd35eSjcs 	err = usbd_device2interface_handle(sc->udev, UKSPAN_IFACE_IDX,
228867dd35eSjcs 	    &sc->iface);
229867dd35eSjcs 	if (err) {
230867dd35eSjcs 		printf("%s: get interface failed\n", devname);
231867dd35eSjcs 		goto fail;
232867dd35eSjcs 	}
233867dd35eSjcs 
234867dd35eSjcs 	ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_BULKIN);
235867dd35eSjcs 	t1 = UE_GET_XFERTYPE(ed->bmAttributes);
236867dd35eSjcs 	uca.ibufsize = UGETW(ed->wMaxPacketSize);
237867dd35eSjcs 	uca.bulkin = UKSPAN_EA_BULKIN;
238867dd35eSjcs 
239867dd35eSjcs 	ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_BULKOUT);
240867dd35eSjcs 	t2 = UE_GET_XFERTYPE(ed->bmAttributes);
241867dd35eSjcs 	uca.obufsize = UGETW(ed->wMaxPacketSize);
242867dd35eSjcs 	uca.bulkout = UKSPAN_EA_BULKOUT;
243867dd35eSjcs 
244867dd35eSjcs 	ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_CONFIGIN);
245867dd35eSjcs 	t3 = UE_GET_XFERTYPE(ed->bmAttributes);
246867dd35eSjcs 	if (UGETW(ed->wMaxPacketSize) < sizeof(struct ukspan_smsg)) {
247867dd35eSjcs 		printf("%s: in config packet size too small\n", devname);
248867dd35eSjcs 		goto fail;
249867dd35eSjcs 	}
250867dd35eSjcs 
251867dd35eSjcs 	ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_CONFIGOUT);
252867dd35eSjcs 	t4 = UE_GET_XFERTYPE(ed->bmAttributes);
253867dd35eSjcs 	if (UGETW(ed->wMaxPacketSize) < sizeof(struct ukspan_cmsg)) {
254867dd35eSjcs 		printf("%s: out config packet size too small\n", devname);
255867dd35eSjcs 		goto fail;
256867dd35eSjcs 	}
257867dd35eSjcs 
258867dd35eSjcs 	if (t1 != UE_BULK || t2 != UE_BULK || t3 != UE_BULK || t4 != UE_BULK) {
259867dd35eSjcs 		printf("%s: unexpected xfertypes %x %x %x %x != %x\n", devname,
260867dd35eSjcs 		    t1, t2, t3, t4, UE_BULK);
261867dd35eSjcs 		goto fail;
262867dd35eSjcs 	}
263867dd35eSjcs 
264867dd35eSjcs 	/* Resource acquisition starts here */
265867dd35eSjcs 	err = usbd_open_pipe(sc->iface, UKSPAN_EA_CONFIGOUT, USBD_EXCLUSIVE_USE,
266867dd35eSjcs 	    &sc->cout_pipe);
267867dd35eSjcs 	if (err) {
268867dd35eSjcs 		printf("%s: failed to create control out pipe\n", devname);
269867dd35eSjcs 		goto fail;
270867dd35eSjcs 	}
271867dd35eSjcs 
272867dd35eSjcs 	err = usbd_open_pipe(sc->iface, UKSPAN_EA_CONFIGIN, USBD_EXCLUSIVE_USE,
273867dd35eSjcs 	    &sc->cin_pipe);
274867dd35eSjcs 	if (err) {
275867dd35eSjcs 		printf("%s: failed to create control out pipe\n", devname);
276867dd35eSjcs 		goto fail;
277867dd35eSjcs 	}
278867dd35eSjcs 
279867dd35eSjcs 	sc->ixfer = usbd_alloc_xfer(sc->udev);
280867dd35eSjcs 	sc->oxfer = usbd_alloc_xfer(sc->udev);
281867dd35eSjcs 	if (!sc->ixfer || !sc->oxfer) {
282867dd35eSjcs 		printf("%s: failed to allocate xfers\n", devname);
283867dd35eSjcs 		goto fail;
284867dd35eSjcs 	}
285867dd35eSjcs 
286867dd35eSjcs 	usbd_setup_xfer(sc->ixfer, sc->cin_pipe, sc, &sc->smsg,
287867dd35eSjcs 	    sizeof(sc->smsg), 0, USBD_NO_TIMEOUT, ukspan_incb);
288867dd35eSjcs 	err = usbd_transfer(sc->ixfer);
289867dd35eSjcs 	if (err && err != USBD_IN_PROGRESS) {
290867dd35eSjcs 		printf("%s: failed to start ixfer\n", devname);
291867dd35eSjcs 		goto fail;
292867dd35eSjcs 	}
293867dd35eSjcs 
294867dd35eSjcs 	uca.portno = UCOM_UNK_PORTNO;
295867dd35eSjcs 	uca.ibufsizepad = uca.ibufsize;
296867dd35eSjcs 	uca.opkthdrlen = 0;
297867dd35eSjcs 	uca.device = dev;
298867dd35eSjcs 	uca.iface = sc->iface;
299867dd35eSjcs 	uca.methods = &ukspan_methods;
300867dd35eSjcs 	uca.arg = sc;
301867dd35eSjcs 	uca.info = NULL;
302867dd35eSjcs 
303867dd35eSjcs 	sc->ucom_dev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
304867dd35eSjcs 
305867dd35eSjcs 	DPRINTF("attach done\n");
306867dd35eSjcs 
307867dd35eSjcs 	return;
308867dd35eSjcs fail:
309867dd35eSjcs 	ukspan_destroy(sc);
310867dd35eSjcs 	usbd_deactivate(sc->udev);
311867dd35eSjcs }
312867dd35eSjcs 
313867dd35eSjcs int
ukspan_detach(struct device * self,int flags)314867dd35eSjcs ukspan_detach(struct device *self, int flags)
315867dd35eSjcs {
316867dd35eSjcs 	struct ukspan_softc *sc = (struct ukspan_softc *)self;
317867dd35eSjcs 	DPRINTF("detach\n");
318867dd35eSjcs 
319867dd35eSjcs 	ukspan_destroy(sc);
320867dd35eSjcs 
321867dd35eSjcs 	if (sc->ucom_dev) {
322867dd35eSjcs 		config_detach(sc->ucom_dev, flags);
323867dd35eSjcs 		sc->ucom_dev = NULL;
324867dd35eSjcs 	}
325867dd35eSjcs 	return 0;
326867dd35eSjcs }
327867dd35eSjcs 
328867dd35eSjcs void
ukspan_outcb(struct usbd_xfer * xfer,void * priv,usbd_status status)329867dd35eSjcs ukspan_outcb(struct usbd_xfer *xfer, void *priv, usbd_status status)
330867dd35eSjcs {
331867dd35eSjcs 	struct ukspan_softc *sc = priv;
332867dd35eSjcs 	const char *devname = sc->sc_dev.dv_xname;
333867dd35eSjcs 
334867dd35eSjcs 	DPRINTF("outcb\n");
335867dd35eSjcs 
336867dd35eSjcs 	if (usbd_is_dying(sc->udev)) {
337867dd35eSjcs 		DPRINTF("usb dying\n");
338867dd35eSjcs 		return;
339867dd35eSjcs 	}
340867dd35eSjcs 	if (status != USBD_NORMAL_COMPLETION) {
341867dd35eSjcs 		printf("%s: oxfer failed\n", devname);
342867dd35eSjcs 		return;
343867dd35eSjcs 	}
344867dd35eSjcs }
345867dd35eSjcs 
346867dd35eSjcs void
ukspan_incb(struct usbd_xfer * xfer,void * priv,usbd_status status)347867dd35eSjcs ukspan_incb(struct usbd_xfer *xfer, void *priv, usbd_status status)
348867dd35eSjcs {
349867dd35eSjcs 	struct ukspan_softc *sc = priv;
350867dd35eSjcs 	const char *devname = sc->sc_dev.dv_xname;
351867dd35eSjcs 	const struct ukspan_smsg *smsg = &sc->smsg;
352867dd35eSjcs 	usbd_status err;
353867dd35eSjcs 	u_int32_t len;
354867dd35eSjcs 
355867dd35eSjcs 	DPRINTF("incb\n");
356867dd35eSjcs 
357867dd35eSjcs 	if (usbd_is_dying(sc->udev)) {
358867dd35eSjcs 		printf("%s: usb dying\n", devname);
359867dd35eSjcs 		return;
360867dd35eSjcs 	}
361867dd35eSjcs 	if (!sc->cin_pipe || !sc->ixfer) {
362867dd35eSjcs 		printf("%s: no cin_pipe, but not dying?\n", devname);
363867dd35eSjcs 		return;
364867dd35eSjcs 	}
365867dd35eSjcs 	if (status != USBD_NORMAL_COMPLETION) {
366867dd35eSjcs 		if (status != USBD_NOT_STARTED && status != USBD_CANCELLED)
367867dd35eSjcs 			printf("%s: ixfer failed\n", devname);
368867dd35eSjcs 		return;
369867dd35eSjcs 	}
370867dd35eSjcs 
371867dd35eSjcs 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
372867dd35eSjcs 	if (len < sizeof(struct ukspan_smsg)) {
373867dd35eSjcs 		printf("%s: short read\n", devname);
374867dd35eSjcs 		return;
375867dd35eSjcs 	}
376867dd35eSjcs 
377867dd35eSjcs 	/* The device provides the actual MSR register */
378867dd35eSjcs 	sc->msr = smsg->msr;
379867dd35eSjcs 	/* But not LSR... */
380867dd35eSjcs 	sc->lsr = (smsg->rxoverrun ? ULSR_OE : 0) |
381867dd35eSjcs 		  (smsg->rxparity  ? ULSR_PE : 0) |
382867dd35eSjcs 		  (smsg->rxframe   ? ULSR_FE : 0) |
383867dd35eSjcs 		  (smsg->rxbreak   ? ULSR_BI : 0);
384867dd35eSjcs 	ucom_status_change((struct ucom_softc *)sc->ucom_dev);
385867dd35eSjcs 
386867dd35eSjcs 	usbd_setup_xfer(sc->ixfer, sc->cin_pipe, sc, &sc->smsg,
387867dd35eSjcs 	    sizeof(sc->smsg), USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
388867dd35eSjcs 	    ukspan_incb);
389867dd35eSjcs 	err = usbd_transfer(sc->ixfer);
390867dd35eSjcs 	if (err && err != USBD_IN_PROGRESS)
391867dd35eSjcs 		printf("%s: usbd transfer failed\n", devname);
392867dd35eSjcs }
393867dd35eSjcs 
394867dd35eSjcs void
ukspan_get_status(void * addr,int portno,u_char * lsr,u_char * msr)395867dd35eSjcs ukspan_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
396867dd35eSjcs {
397867dd35eSjcs 	struct ukspan_softc *sc = addr;
398867dd35eSjcs 	DPRINTF("get status\n");
399867dd35eSjcs 	if (lsr)
400867dd35eSjcs 		*lsr = sc->lsr;
401867dd35eSjcs 	if (msr)
402867dd35eSjcs 		*msr = sc->msr;
403867dd35eSjcs }
404867dd35eSjcs 
405867dd35eSjcs void
ukspan_cmsg_init(bool opening,struct ukspan_cmsg * omsg)406867dd35eSjcs ukspan_cmsg_init(bool opening, struct ukspan_cmsg *omsg)
407867dd35eSjcs {
408867dd35eSjcs 	bzero(omsg, sizeof(*omsg));
409867dd35eSjcs 
410867dd35eSjcs 	omsg->xonchar = 17;
411867dd35eSjcs 	omsg->xoffchar = 19;
412867dd35eSjcs 
413867dd35eSjcs 	omsg->rxforwardingchars = 16;
414867dd35eSjcs 	omsg->rxforwardingtimeoutms = 16;
415867dd35eSjcs 	omsg->txacksetting = 0;
416867dd35eSjcs 	omsg->txbreak = 0;
417867dd35eSjcs 	if (opening) {
418867dd35eSjcs 		omsg->portenabled = 1;
419867dd35eSjcs 		omsg->rxflush = 1;
420867dd35eSjcs 	}
421867dd35eSjcs }
422867dd35eSjcs 
423867dd35eSjcs int
ukspan_cmsg_send(struct ukspan_softc * sc)424867dd35eSjcs ukspan_cmsg_send(struct ukspan_softc *sc)
425867dd35eSjcs {
426867dd35eSjcs 	const char *devname = sc->sc_dev.dv_xname;
427867dd35eSjcs 	usbd_status err;
428867dd35eSjcs 
429867dd35eSjcs 	usbd_setup_xfer(sc->oxfer, sc->cout_pipe, sc, &sc->cmsg,
430867dd35eSjcs 	    sizeof(sc->cmsg), USBD_SYNCHRONOUS, USBD_NO_TIMEOUT, ukspan_outcb);
431867dd35eSjcs 	err = usbd_transfer(sc->oxfer);
432867dd35eSjcs 	if (err != USBD_NORMAL_COMPLETION) {
433867dd35eSjcs 		printf("%s: control xfer failed\n", devname);
434867dd35eSjcs 		return EIO;
435867dd35eSjcs 	}
436867dd35eSjcs 	return 0;
437867dd35eSjcs }
438867dd35eSjcs 
439867dd35eSjcs void
ukspan_set(void * addr,int portno,int reg,int onoff)440867dd35eSjcs ukspan_set(void *addr, int portno, int reg, int onoff)
441867dd35eSjcs {
442867dd35eSjcs 	struct ukspan_softc *sc = addr;
443867dd35eSjcs 	const char *devname = sc->sc_dev.dv_xname;
444867dd35eSjcs 	DPRINTF("set %#x = %#x\n", reg, onoff);
445867dd35eSjcs 	int flag = !!onoff;
446867dd35eSjcs 	switch (reg) {
447867dd35eSjcs 	case UCOM_SET_DTR:
448867dd35eSjcs 		sc->cmsg.setdtr = 1;
449867dd35eSjcs 		sc->cmsg.dtr = flag;
450867dd35eSjcs 		break;
451867dd35eSjcs 	case UCOM_SET_RTS:
452867dd35eSjcs 		sc->cmsg.setrts = 1;
453867dd35eSjcs 		sc->cmsg.rts = flag;
454867dd35eSjcs 		break;
455867dd35eSjcs 	case UCOM_SET_BREAK:
456867dd35eSjcs 		sc->cmsg.txbreak = flag;
457867dd35eSjcs 		break;
458867dd35eSjcs 	default:
459867dd35eSjcs 		printf("%s: unhandled reg %#x\n", devname, reg);
460867dd35eSjcs 		return;
461867dd35eSjcs 	}
462867dd35eSjcs 	ukspan_cmsg_send(sc);
463867dd35eSjcs }
464867dd35eSjcs 
465867dd35eSjcs int
ukspan_param(void * addr,int portno,struct termios * ti)466867dd35eSjcs ukspan_param(void *addr, int portno, struct termios *ti)
467867dd35eSjcs {
468867dd35eSjcs 	struct ukspan_softc *sc = addr;
469867dd35eSjcs 	const char *devname = sc->sc_dev.dv_xname;
470867dd35eSjcs 	struct ukspan_cmsg *cmsg = &sc->cmsg;
471867dd35eSjcs 	speed_t baud;
472867dd35eSjcs 	tcflag_t cflag;
473867dd35eSjcs 	u_int32_t div;
474867dd35eSjcs 	u_int8_t lcr;
475867dd35eSjcs 
476867dd35eSjcs 	DPRINTF("param: %#x %#x %#x\n", ti->c_ospeed, ti->c_cflag, ti->c_iflag);
477867dd35eSjcs 
478867dd35eSjcs 	/* Set baud */
479867dd35eSjcs 	div = 1;
480867dd35eSjcs 	baud = ti->c_ospeed;
481867dd35eSjcs 	switch (baud) {
482867dd35eSjcs 	case B300:
483867dd35eSjcs 	case B600:
484867dd35eSjcs 	case B1200:
485867dd35eSjcs 	case B2400:
486867dd35eSjcs 	case B4800:
487867dd35eSjcs 	case B9600:
488867dd35eSjcs 	case B19200:
489867dd35eSjcs 	case B38400:
490867dd35eSjcs 	case B57600:
491867dd35eSjcs 	case B115200:
492867dd35eSjcs 	case B230400:
493867dd35eSjcs 		div = UKSPAN_CLOCK / (baud * 16);
494867dd35eSjcs 		break;
495867dd35eSjcs 	default:
496867dd35eSjcs 		printf("%s: unexpected baud: %d\n", devname, baud);
497867dd35eSjcs 		return EINVAL;
498867dd35eSjcs 	}
499867dd35eSjcs 
500867dd35eSjcs 	cmsg->setclock = 1;
501867dd35eSjcs 	cmsg->baudlo = div & 0xff;
502867dd35eSjcs 	cmsg->baudhi = div >> 8;
503867dd35eSjcs 
504867dd35eSjcs 	cmsg->setrxmode = 1;
505867dd35eSjcs 	cmsg->settxmode = 1;
506867dd35eSjcs 	if (baud > 57600)
507867dd35eSjcs 		cmsg->rxmode = cmsg->txmode = UKSPAN_MAGIC;
508867dd35eSjcs 	else
509867dd35eSjcs 		cmsg->rxmode = cmsg->txmode = 0;
510867dd35eSjcs 
511867dd35eSjcs 	/* Set parity, data, and stop bits */
512867dd35eSjcs 	cflag = ti->c_cflag;
513f8f595b4Sjsg 	if ((cflag & CIGNORE) == 0) {
514867dd35eSjcs 		if (cflag & PARENB)
515867dd35eSjcs 			lcr = (cflag & PARODD) ? UKSPAN_PARITY_ODD :
516867dd35eSjcs 			    UKSPAN_PARITY_EVEN;
517867dd35eSjcs 		else
518867dd35eSjcs 			lcr = UKSPAN_PARITY_NONE;
519867dd35eSjcs 		switch (cflag & CSIZE) {
520867dd35eSjcs 		case CS5:
521867dd35eSjcs 			lcr |= UKSPAN_DATA_5;
522867dd35eSjcs 			break;
523867dd35eSjcs 		case CS6:
524867dd35eSjcs 			lcr |= UKSPAN_DATA_6;
525867dd35eSjcs 			break;
526867dd35eSjcs 		case CS7:
527867dd35eSjcs 			lcr |= UKSPAN_DATA_7;
528867dd35eSjcs 			break;
529867dd35eSjcs 		case CS8:
530867dd35eSjcs 			lcr |= UKSPAN_DATA_8;
531867dd35eSjcs 			break;
532867dd35eSjcs 		}
533867dd35eSjcs 
534867dd35eSjcs 		lcr |= (cflag & CSTOPB) ? UKSPAN_STOP_2 : UKSPAN_STOP_1;
535867dd35eSjcs 
536867dd35eSjcs 		cmsg->setlcr = 1;
537867dd35eSjcs 		cmsg->lcr = lcr;
538867dd35eSjcs 	}
539867dd35eSjcs 
540867dd35eSjcs 	/* XXX flow control? */
541867dd35eSjcs 
542867dd35eSjcs 	ukspan_cmsg_send(sc);
543867dd35eSjcs 	return 0;
544867dd35eSjcs }
545867dd35eSjcs 
546867dd35eSjcs int
ukspan_open(void * addr,int portno)547867dd35eSjcs ukspan_open(void *addr, int portno)
548867dd35eSjcs {
549867dd35eSjcs 	struct ukspan_softc *sc = addr;
550867dd35eSjcs 	int ret;
551867dd35eSjcs 
552867dd35eSjcs 	DPRINTF("open\n");
553867dd35eSjcs 	if (usbd_is_dying(sc->udev)) {
554867dd35eSjcs 		DPRINTF("usb dying\n");
555867dd35eSjcs 		return ENXIO;
556867dd35eSjcs 	}
557867dd35eSjcs 
558867dd35eSjcs 	ukspan_cmsg_init(true, &sc->cmsg);
559867dd35eSjcs 	ret = ukspan_cmsg_send(sc);
560867dd35eSjcs 	return ret;
561867dd35eSjcs }
562867dd35eSjcs 
563867dd35eSjcs void
ukspan_close(void * addr,int portno)564867dd35eSjcs ukspan_close(void *addr, int portno)
565867dd35eSjcs {
566867dd35eSjcs 	struct ukspan_softc *sc = addr;
567867dd35eSjcs 	DPRINTF("close\n");
568867dd35eSjcs 	if (usbd_is_dying(sc->udev)) {
569867dd35eSjcs 		DPRINTF("usb dying\n");
570867dd35eSjcs 		return;
571867dd35eSjcs 	}
572867dd35eSjcs 	ukspan_cmsg_init(false, &sc->cmsg);
573867dd35eSjcs 	ukspan_cmsg_send(sc);
574867dd35eSjcs }
575867dd35eSjcs 
576867dd35eSjcs void
ukspan_destroy(struct ukspan_softc * sc)577867dd35eSjcs ukspan_destroy(struct ukspan_softc *sc)
578867dd35eSjcs {
579867dd35eSjcs 	DPRINTF("destroy\n");
580867dd35eSjcs 	if (sc->cin_pipe) {
581867dd35eSjcs 		usbd_close_pipe(sc->cin_pipe);
582867dd35eSjcs 		sc->cin_pipe = NULL;
583867dd35eSjcs 	}
584867dd35eSjcs 	if (sc->cout_pipe) {
585867dd35eSjcs 		usbd_close_pipe(sc->cout_pipe);
586867dd35eSjcs 		sc->cout_pipe = NULL;
587867dd35eSjcs 	}
588867dd35eSjcs 	if (sc->oxfer) {
589867dd35eSjcs 		usbd_free_xfer(sc->oxfer);
590867dd35eSjcs 		sc->oxfer = NULL;
591867dd35eSjcs 	}
592867dd35eSjcs 	if (sc->ixfer) {
593867dd35eSjcs 		usbd_free_xfer(sc->ixfer);
594867dd35eSjcs 		sc->ixfer = NULL;
595867dd35eSjcs 	}
596867dd35eSjcs }
597