1 /* $NetBSD: uslsa.c,v 1.33 2025/02/16 18:30:28 jakllsch Exp $ */
2 
3 /* from ugensa.c */
4 
5 /*
6  * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by Roland C. Dowdeswell <elric@netbsd.org>.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * Copyright (c) 2007, 2009 Jonathan A. Kollasch.
36  * All rights reserved.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
49  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
50  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
51  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
52  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
56  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57  *
58  */
59 
60 #include <sys/cdefs.h>
61 __KERNEL_RCSID(0, "$NetBSD: uslsa.c,v 1.33 2025/02/16 18:30:28 jakllsch Exp $");
62 
63 #ifdef _KERNEL_OPT
64 #include "opt_usb.h"
65 #endif
66 
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/kernel.h>
70 #include <sys/device.h>
71 #include <sys/conf.h>
72 #include <sys/tty.h>
73 
74 #include <dev/usb/usb.h>
75 
76 #include <dev/usb/usbdi.h>
77 #include <dev/usb/usbdi_util.h>
78 #include <dev/usb/usbdevs.h>
79 
80 #include <dev/usb/ucomvar.h>
81 
82 #include <dev/usb/uslsareg.h>
83 
84 #include <fs/unicode.h>
85 
86 #ifdef USLSA_DEBUG
87 #define DPRINTF(x)  if (uslsadebug) device_printf x
88 int uslsadebug = 0;
89 #else
90 #define DPRINTF(x)
91 #endif
92 
93 struct uslsa_softc {
94           device_t            sc_dev;             /* base device */
95           device_t            sc_subdev;          /* ucom device */
96           struct usbd_device *          sc_udev;  /* usb device */
97           struct usbd_interface *       sc_iface; /* interface */
98           uint8_t                       sc_ifnum; /* interface number */
99           bool                          sc_dying; /* disconnecting */
100 };
101 
102 static void uslsa_get_status(void *sc, int, u_char *, u_char *);
103 static void uslsa_set(void *, int, int, int);
104 static int uslsa_param(void *, int, struct termios *);
105 static int uslsa_ioctl(void *, int, u_long, void *, int, proc_t *);
106 static int uslsa_open(void *, int);
107 static void uslsa_close(void *, int);
108 
109 static int uslsa_usbd_errno(usbd_status);
110 static int uslsa_request_set(struct uslsa_softc *, uint8_t, uint16_t);
111 static int uslsa_set_flow(struct uslsa_softc *, tcflag_t, tcflag_t);
112 
113 static const struct ucom_methods uslsa_methods = {
114           .ucom_get_status = uslsa_get_status,
115           .ucom_set = uslsa_set,
116           .ucom_param = uslsa_param,
117           .ucom_ioctl = uslsa_ioctl,
118           .ucom_open = uslsa_open,
119           .ucom_close = uslsa_close,
120 };
121 
122 #define USLSA_CONFIG_INDEX    0
123 #define USLSA_IFACE_INDEX     0
124 #define USLSA_BUFSIZE                   256
125 
126 static const struct usb_devno uslsa_devs[] = {
127         { USB_VENDOR_BALTECH,           USB_PRODUCT_BALTECH_CARDREADER },
128         { USB_VENDOR_DYNASTREAM,        USB_PRODUCT_DYNASTREAM_ANTDEVBOARD },
129         { USB_VENDOR_JABLOTRON,         USB_PRODUCT_JABLOTRON_PC60B },
130         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_ARGUSISP },
131         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_CRUMB128 },
132         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_DEGREECONT },
133         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_DESKTOPMOBILE },
134         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_IPLINK1220 },
135         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_LIPOWSKY_HARP },
136         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_LIPOWSKY_JTAG },
137         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_LIPOWSKY_LIN },
138         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_POLOLU },
139         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_CP210X_1 },
140         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_CP210X_2 },
141         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_CP210X_3 },
142         { USB_VENDOR_SILABS,            USB_PRODUCT_SILABS_SUNNTO },
143         { USB_VENDOR_SILABS2,           USB_PRODUCT_SILABS2_DCU11CLONE },
144         { USB_VENDOR_USI,               USB_PRODUCT_USI_MC60 },
145           { USB_VENDOR_WMR,             USB_PRODUCT_WMR_RIGBLASTER },
146 };
147 
148 static int uslsa_match(device_t, cfdata_t, void *);
149 static void uslsa_attach(device_t, device_t, void *);
150 static void uslsa_childdet(device_t, device_t);
151 static int uslsa_detach(device_t, int);
152 
153 CFATTACH_DECL2_NEW(uslsa, sizeof(struct uslsa_softc), uslsa_match,
154     uslsa_attach, uslsa_detach, NULL, NULL, uslsa_childdet);
155 
156 static int
uslsa_match(device_t parent,cfdata_t match,void * aux)157 uslsa_match(device_t parent, cfdata_t match, void *aux)
158 {
159           const struct usbif_attach_arg *uiaa = aux;
160 
161           if (usb_lookup(uslsa_devs, uiaa->uiaa_vendor, uiaa->uiaa_product)
162               != NULL)
163                     return UMATCH_VENDOR_PRODUCT;
164           else
165                     return UMATCH_NONE;
166 }
167 
168 static void
uslsa_attach(device_t parent,device_t self,void * aux)169 uslsa_attach(device_t parent, device_t self, void *aux)
170 {
171           struct uslsa_softc *sc;
172           const struct usbif_attach_arg *uiaa = aux;
173           const usb_interface_descriptor_t *id;
174           const usb_endpoint_descriptor_t *ed;
175           char *devinfop;
176           struct ucom_attach_args ucaa;
177           int i;
178 
179           sc = device_private(self);
180 
181           sc->sc_dev = self;
182           sc->sc_udev = uiaa->uiaa_device;
183           sc->sc_iface = uiaa->uiaa_iface;
184           sc->sc_dying = false;
185 
186           aprint_naive("\n");
187           aprint_normal("\n");
188 
189           devinfop = usbd_devinfo_alloc(sc->sc_udev, 0);
190           aprint_normal_dev(self, "%s\n", devinfop);
191           usbd_devinfo_free(devinfop);
192 
193           id = usbd_get_interface_descriptor(sc->sc_iface);
194 
195           sc->sc_ifnum = id->bInterfaceNumber;
196 
197           ucaa.ucaa_info = "Silicon Labs CP210x";
198           ucaa.ucaa_portno = UCOM_UNK_PORTNO;
199           ucaa.ucaa_ibufsize = USLSA_BUFSIZE;
200           ucaa.ucaa_obufsize = USLSA_BUFSIZE;
201           ucaa.ucaa_ibufsizepad = USLSA_BUFSIZE;
202           ucaa.ucaa_opkthdrlen = 0;
203           ucaa.ucaa_device = sc->sc_udev;
204           ucaa.ucaa_iface = sc->sc_iface;
205           ucaa.ucaa_methods = &uslsa_methods;
206           ucaa.ucaa_arg = sc;
207 
208           usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
209 
210           ucaa.ucaa_bulkin = ucaa.ucaa_bulkout = -1;
211           for (i = 0; i < id->bNumEndpoints; i++) {
212                     int addr, dir, attr;
213 
214                     ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
215                     if (ed == NULL) {
216                               aprint_error_dev(self,
217                                   "could not read endpoint descriptor\n");
218                               sc->sc_dying = true;
219                               return;
220                     }
221                     addr = ed->bEndpointAddress;
222                     dir = UE_GET_DIR(ed->bEndpointAddress);
223                     attr = ed->bmAttributes & UE_XFERTYPE;
224                     if (dir == UE_DIR_IN && attr == UE_BULK) {
225                               ucaa.ucaa_bulkin = addr;
226                     } else if (dir == UE_DIR_OUT && attr == UE_BULK) {
227                               ucaa.ucaa_bulkout = addr;
228                     } else {
229                               aprint_error_dev(self, "unexpected endpoint\n");
230                     }
231           }
232           aprint_debug_dev(sc->sc_dev, "EPs: in=%#x out=%#x\n",
233                     ucaa.ucaa_bulkin, ucaa.ucaa_bulkout);
234           if ((ucaa.ucaa_bulkin == -1) || (ucaa.ucaa_bulkout == -1)) {
235                     aprint_error_dev(self, "could not find endpoints\n");
236                     sc->sc_dying = true;
237                     return;
238           }
239 
240           sc->sc_subdev = config_found(self, &ucaa, ucomprint,
241               CFARGS(.submatch = ucomsubmatch));
242 
243           if (!pmf_device_register(self, NULL, NULL))
244                     aprint_error_dev(self, "couldn't establish power handler\n");
245 
246           return;
247 }
248 
249 static void
uslsa_childdet(device_t self,device_t child)250 uslsa_childdet(device_t self, device_t child)
251 {
252           struct uslsa_softc *sc = device_private(self);
253 
254           KASSERT(sc->sc_subdev == child);
255           sc->sc_subdev = NULL;
256 }
257 
258 static int
uslsa_detach(device_t self,int flags)259 uslsa_detach(device_t self, int flags)
260 {
261           struct uslsa_softc *sc = device_private(self);
262           int rv = 0;
263 
264           DPRINTF((self, "%s(%p, %#x)\n", __func__, self, flags));
265 
266           sc->sc_dying = true;
267 
268           if (sc->sc_subdev != NULL) {
269                     rv = config_detach(sc->sc_subdev, flags);
270                     sc->sc_subdev = NULL;
271           }
272 
273           usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
274 
275           return rv;
276 }
277 
278 static int
uslsa_usbd_errno(usbd_status status)279 uslsa_usbd_errno(usbd_status status)
280 {
281           switch (status) {
282           case USBD_NORMAL_COMPLETION:
283                     return 0;
284           case USBD_STALLED:
285                     return EINVAL;
286           default:
287                     return EIO;
288           }
289 }
290 
291 static void
uslsa_get_status(void * vsc,int portno,u_char * lsr,u_char * msr)292 uslsa_get_status(void *vsc, int portno, u_char *lsr, u_char *msr)
293 {
294           struct uslsa_softc *sc;
295           usb_device_request_t req;
296           usbd_status status;
297           uint8_t mdmsts;
298 
299           sc = vsc;
300 
301           DPRINTF((sc->sc_dev, "%s(%p, %d, ....)\n", __func__, vsc, portno));
302 
303           if (sc->sc_dying)
304                     return;
305 
306           req.bmRequestType = UT_READ_VENDOR_INTERFACE;
307           req.bRequest = SLSA_R_GET_MDMSTS;
308           USETW(req.wValue, 0);
309           USETW(req.wIndex, sc->sc_ifnum);
310           USETW(req.wLength, SLSA_RL_GET_MDMSTS);
311 
312           status = usbd_do_request(sc->sc_udev, &req, &mdmsts);
313           if (status != USBD_NORMAL_COMPLETION) {
314                     device_printf(sc->sc_dev, "%s: GET_MDMSTS %s\n",
315                         __func__, usbd_errstr(status));
316                     return;
317           }
318 
319           DPRINTF((sc->sc_dev, "%s: GET_MDMSTS %#x\n", __func__, mdmsts));
320 
321           *lsr = 0;
322 
323           *msr  = ISSET(mdmsts, SLSA_MDMSTS_CTS) ? UMSR_CTS : 0;
324           *msr |= ISSET(mdmsts, SLSA_MDMSTS_DSR) ? UMSR_DSR : 0;
325           *msr |= ISSET(mdmsts, SLSA_MDMSTS_RI) ? UMSR_RI : 0;
326           *msr |= ISSET(mdmsts, SLSA_MDMSTS_DCD) ? UMSR_DCD : 0;
327 }
328 
329 static void
uslsa_set(void * vsc,int portno,int reg,int onoff)330 uslsa_set(void *vsc, int portno, int reg, int onoff)
331 {
332           struct uslsa_softc *sc;
333 
334           sc = vsc;
335 
336           DPRINTF((sc->sc_dev, "%s(%p, %d, %d, %d)\n", __func__, vsc, portno,
337               reg, onoff));
338 
339           if (sc->sc_dying)
340                     return;
341 
342           switch (reg) {
343           case UCOM_SET_DTR:
344                     if (uslsa_request_set(sc, SLSA_R_SET_MHS,
345                         SLSA_RV_SET_MHS_DTR_MASK |
346                         (onoff ? SLSA_RV_SET_MHS_DTR : 0))) {
347                               device_printf(sc->sc_dev, "SET_MHS/DTR failed\n");
348                     }
349                     break;
350           case UCOM_SET_RTS:
351                     if (uslsa_request_set(sc, SLSA_R_SET_MHS,
352                         SLSA_RV_SET_MHS_RTS_MASK |
353                         (onoff ? SLSA_RV_SET_MHS_RTS : 0))) {
354                               device_printf(sc->sc_dev, "SET_MHS/RTS failed\n");
355                     }
356                     break;
357           case UCOM_SET_BREAK:
358                     if (uslsa_request_set(sc, SLSA_R_SET_BREAK,
359                         (onoff ? SLSA_RV_SET_BREAK_ENABLE :
360                          SLSA_RV_SET_BREAK_DISABLE))) {
361                               device_printf(sc->sc_dev, "SET_BREAK failed\n");
362                     }
363                     break;
364           default:
365                     break;
366           }
367 }
368 
369 static int
uslsa_param(void * vsc,int portno,struct termios * t)370 uslsa_param(void *vsc, int portno, struct termios *t)
371 {
372           struct uslsa_softc *sc;
373           usb_device_request_t req;
374           usbd_status status;
375           uint16_t value;
376           uint32_t baud;
377           int ret;
378 
379           sc = vsc;
380 
381           DPRINTF((sc->sc_dev, "%s(%p, %d, %p)\n", __func__, vsc, portno, t));
382 
383           if (sc->sc_dying)
384                     return EIO;
385 
386           req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
387           req.bRequest = SLSA_R_SET_BAUDRATE;
388           USETW(req.wValue, 0);
389           USETW(req.wIndex, sc->sc_ifnum);
390           USETW(req.wLength, 4);
391 
392           baud = htole32(t->c_ospeed);
393           status = usbd_do_request(sc->sc_udev, &req, &baud);
394           if (status != USBD_NORMAL_COMPLETION) {
395                     /* fallback method for devices that don't know SET_BAUDRATE */
396                     /* hope we calculate it right */
397                     device_printf(sc->sc_dev, "%s: set baudrate %d, failed %s,"
398                                         " using set bauddiv\n",
399                         __func__, baud, usbd_errstr(status));
400 
401                     value = SLSA_RV_BAUDDIV(t->c_ospeed);
402                     if ((ret = uslsa_request_set(sc, SLSA_R_SET_BAUDDIV, value))
403                         != 0) {
404                               device_printf(sc->sc_dev, "%s: SET_BAUDDIV failed\n",
405                                      __func__);
406                               return ret;
407                     }
408           }
409 
410           value = 0;
411 
412           if (ISSET(t->c_cflag, CSTOPB)) {
413                     value |= SLSA_RV_LINE_CTL_STOP_2;
414           } else {
415                     value |= SLSA_RV_LINE_CTL_STOP_1;
416           }
417 
418           if (ISSET(t->c_cflag, PARENB)) {
419                     if (ISSET(t->c_cflag, PARODD)) {
420                               value |= SLSA_RV_LINE_CTL_PARITY_ODD;
421                     } else {
422                               value |= SLSA_RV_LINE_CTL_PARITY_EVEN;
423                     }
424           } else {
425                     value |= SLSA_RV_LINE_CTL_PARITY_NONE;
426           }
427 
428           switch (ISSET(t->c_cflag, CSIZE)) {
429           case CS5:
430                     value |= SLSA_RV_LINE_CTL_LEN_5;
431                     break;
432           case CS6:
433                     value |= SLSA_RV_LINE_CTL_LEN_6;
434                     break;
435           case CS7:
436                     value |= SLSA_RV_LINE_CTL_LEN_7;
437                     break;
438           case CS8:
439                     value |= SLSA_RV_LINE_CTL_LEN_8;
440                     break;
441           }
442 
443           DPRINTF((sc->sc_dev, "%s: setting LINE_CTL to %#x\n",
444               __func__, value));
445           if ((ret = uslsa_request_set(sc, SLSA_R_SET_LINE_CTL, value)) != 0) {
446                     device_printf(sc->sc_dev, "SET_LINE_CTL failed\n");
447                     return ret;
448           }
449 
450           if ((ret = uslsa_set_flow(sc, t->c_cflag, t->c_iflag)) != 0) {
451                     device_printf(sc->sc_dev, "SET_LINE_CTL failed\n");
452           }
453 
454           return ret;
455 }
456 
457 static int
uslsa_ioctl(void * vsc,int portno,u_long cmd,void * data,int flag,proc_t * p)458 uslsa_ioctl(void *vsc, int portno, u_long cmd, void *data, int flag, proc_t *p)
459 {
460           struct uslsa_softc *sc;
461 
462           sc = vsc;
463 
464           if (sc->sc_dying)
465                     return EIO;
466 
467           switch (cmd) {
468           case TIOCMGET:
469                     ucom_status_change(device_private(sc->sc_subdev));
470                     return EPASSTHROUGH;
471           default:
472                     return EPASSTHROUGH;
473           }
474 
475           return 0;
476 }
477 
478 static int
uslsa_open(void * vsc,int portno)479 uslsa_open(void *vsc, int portno)
480 {
481           struct uslsa_softc *sc;
482 
483           sc = vsc;
484 
485           DPRINTF((sc->sc_dev, "%s(%p, %d)\n", __func__, vsc, portno));
486 
487           if (sc->sc_dying)
488                     return EIO;
489 
490           return uslsa_request_set(sc, SLSA_R_IFC_ENABLE,
491               SLSA_RV_IFC_ENABLE_ENABLE);
492 }
493 
494 static void
uslsa_close(void * vsc,int portno)495 uslsa_close(void *vsc, int portno)
496 {
497           struct uslsa_softc *sc;
498 
499           sc = vsc;
500 
501           DPRINTF((sc->sc_dev, "%s(%p, %d)\n", __func__, vsc, portno));
502 
503           if (sc->sc_dying)
504                     return;
505 
506           uslsa_request_set(sc, SLSA_R_IFC_ENABLE, SLSA_RV_IFC_ENABLE_DISABLE);
507 }
508 
509 static int
uslsa_request_set(struct uslsa_softc * sc,uint8_t request,uint16_t value)510 uslsa_request_set(struct uslsa_softc * sc, uint8_t request, uint16_t value)
511 {
512           usb_device_request_t req;
513           usbd_status status;
514 
515           req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
516           req.bRequest = request;
517           USETW(req.wValue, value);
518           USETW(req.wIndex, sc->sc_ifnum);
519           USETW(req.wLength, 0);
520 
521           status = usbd_do_request(sc->sc_udev, &req, NULL);
522 
523           return uslsa_usbd_errno(status);
524 }
525 
526 static int
uslsa_set_flow(struct uslsa_softc * sc,tcflag_t cflag,tcflag_t iflag)527 uslsa_set_flow(struct uslsa_softc *sc, tcflag_t cflag, tcflag_t iflag)
528 {
529           struct slsa_fcs fcs;
530           usb_device_request_t req;
531           uint32_t ulControlHandshake;
532           uint32_t ulFlowReplace;
533           usbd_status status;
534 
535           DPRINTF((sc->sc_dev, "%s(%p, %#x, %#x)\n", __func__, sc, cflag, iflag));
536 
537           req.bmRequestType = UT_READ_VENDOR_INTERFACE;
538           req.bRequest = SLSA_R_GET_FLOW;
539           USETW(req.wValue, 0);
540           USETW(req.wIndex, sc->sc_ifnum);
541           USETW(req.wLength, SLSA_RL_GET_FLOW);
542 
543           status = usbd_do_request(sc->sc_udev, &req, &fcs);
544           if (status != USBD_NORMAL_COMPLETION) {
545                     device_printf(sc->sc_dev, "%s: GET_FLOW %s\n",
546                               __func__, usbd_errstr(status));
547                     return uslsa_usbd_errno(status);
548           }
549 
550           ulControlHandshake = le32toh(fcs.ulControlHandshake);
551           ulFlowReplace = le32toh(fcs.ulFlowReplace);
552 
553           if (ISSET(cflag, CRTSCTS)) {
554                     ulControlHandshake =
555                         SERIAL_CTS_HANDSHAKE | __SHIFTIN(1, SERIAL_DTR_MASK);
556                     ulFlowReplace = __SHIFTIN(2, SERIAL_RTS_MASK);
557           } else {
558                     ulControlHandshake = __SHIFTIN(1, SERIAL_DTR_MASK);
559                     ulFlowReplace = __SHIFTIN(1, SERIAL_RTS_MASK);
560           }
561 
562           fcs.ulControlHandshake = htole32(ulControlHandshake);
563           fcs.ulFlowReplace = htole32(ulFlowReplace);
564 
565           req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
566           req.bRequest = SLSA_R_SET_FLOW;
567           USETW(req.wValue, 0);
568           USETW(req.wIndex, sc->sc_ifnum);
569           USETW(req.wLength, SLSA_RL_SET_FLOW);
570 
571           status = usbd_do_request(sc->sc_udev, &req, &fcs);
572 
573           return uslsa_usbd_errno(status);
574 }
575