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