1 /* $NetBSD: ucycom.c,v 1.29 2009/08/06 07:07:30 skrll Exp $ */
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 * This code is based on the ucom driver.
36 * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: ucycom.c,v 1.29 2009/08/06 07:07:30 skrll Exp $");
43 #include <sys/param.h>
44 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
48 #include <sys/device.h>
49 #include <sys/sysctl.h>
52 #include <sys/vnode.h>
53 #include <sys/kauth.h>
55 #include <dev/usb/usb.h>
56 #include <dev/usb/usbhid.h>
58 #include <dev/usb/usbdi.h>
59 #include <dev/usb/usbdi_util.h>
60 #include <dev/usb/usbdevs.h>
61 #include <dev/usb/uhidev.h>
62 #include <dev/usb/hid.h>
67 #define DPRINTF(x) if (ucycomdebug) logprintf x
68 #define DPRINTFN(n, x) if (ucycomdebug > (n)) logprintf x
76 #define UCYCOMUNIT_MASK 0x3ffff
77 #define UCYCOMDIALOUT_MASK 0x80000
78 #define UCYCOMCALLUNIT_MASK 0x40000
80 #define UCYCOMUNIT(x) (minor(x) & UCYCOMUNIT_MASK)
81 #define UCYCOMDIALOUT(x) (minor(x) & UCYCOMDIALOUT_MASK)
82 #define UCYCOMCALLUNIT(x) (minor(x) & UCYCOMCALLUNIT_MASK)
84 /* Configuration Byte */
85 #define UCYCOM_RESET 0x80
86 #define UCYCOM_PARITY_TYPE_MASK 0x20
87 #define UCYCOM_PARITY_ODD 0x20
88 #define UCYCOM_PARITY_EVEN 0x00
89 #define UCYCOM_PARITY_MASK 0x10
90 #define UCYCOM_PARITY_ON 0x10
91 #define UCYCOM_PARITY_OFF 0x00
92 #define UCYCOM_STOP_MASK 0x08
93 #define UCYCOM_STOP_BITS_2 0x08
94 #define UCYCOM_STOP_BITS_1 0x00
95 #define UCYCOM_DATA_MASK 0x03
96 #define UCYCOM_DATA_BITS_8 0x03
97 #define UCYCOM_DATA_BITS_7 0x02
98 #define UCYCOM_DATA_BITS_6 0x01
99 #define UCYCOM_DATA_BITS_5 0x00
101 /* Modem (Input) status byte */
102 #define UCYCOM_RI 0x80
103 #define UCYCOM_DCD 0x40
104 #define UCYCOM_DSR 0x20
105 #define UCYCOM_CTS 0x10
106 #define UCYCOM_ERROR 0x08
107 #define UCYCOM_LMASK 0x07
109 /* Modem (Output) control byte */
110 #define UCYCOM_DTR 0x20
111 #define UCYCOM_RTS 0x10
112 #define UCYCOM_ORESET 0x08
114 struct ucycom_softc
{
115 struct uhidev sc_hdev
;
119 /* uhidev parameters */
120 size_t sc_flen
; /* feature report length */
121 size_t sc_ilen
; /* input report length */
122 size_t sc_olen
; /* output report length */
129 uint8_t sc_cfg
; /* Data format */
130 uint8_t sc_mcr
; /* Modem control */
131 uint8_t sc_msr
; /* Modem status */
138 dev_type_open(ucycomopen
);
139 dev_type_close(ucycomclose
);
140 dev_type_read(ucycomread
);
141 dev_type_write(ucycomwrite
);
142 dev_type_ioctl(ucycomioctl
);
143 dev_type_stop(ucycomstop
);
144 dev_type_tty(ucycomtty
);
145 dev_type_poll(ucycompoll
);
147 const struct cdevsw ucycom_cdevsw
= {
148 ucycomopen
, ucycomclose
, ucycomread
, ucycomwrite
, ucycomioctl
,
149 ucycomstop
, ucycomtty
, ucycompoll
, nommap
, ttykqfilter
, D_TTY
152 Static
int ucycomparam(struct tty
*, struct termios
*);
153 Static
void ucycomstart(struct tty
*);
154 Static
void ucycomwritecb(usbd_xfer_handle
, usbd_private_handle
, usbd_status
);
155 Static
void ucycom_intr(struct uhidev
*, void *, u_int
);
156 Static
int ucycom_configure(struct ucycom_softc
*, uint32_t, uint8_t);
157 Static
void tiocm_to_ucycom(struct ucycom_softc
*, u_long
, int);
158 Static
int ucycom_to_tiocm(struct ucycom_softc
*);
159 Static
void ucycom_set_status(struct ucycom_softc
*);
160 Static
void ucycom_dtr(struct ucycom_softc
*, int);
162 Static
void ucycom_rts(struct ucycom_softc
*, int);
164 Static
void ucycom_cleanup(struct ucycom_softc
*sc
);
167 Static
void ucycom_get_cfg(struct ucycom_softc
*);
170 Static
const struct usb_devno ucycom_devs
[] = {
171 { USB_VENDOR_CYPRESS
, USB_PRODUCT_CYPRESS_USBRS232
},
172 { USB_VENDOR_DELORME
, USB_PRODUCT_DELORME_EARTHMATE
},
174 #define ucycom_lookup(v, p) usb_lookup(ucycom_devs, v, p)
176 USB_DECLARE_DRIVER(ucycom
);
179 ucycom_match(device_t parent
, cfdata_t match
, void *aux
)
181 struct uhidev_attach_arg
*uha
= aux
;
183 return (ucycom_lookup(uha
->uaa
->vendor
, uha
->uaa
->product
) != NULL
?
184 UMATCH_VENDOR_PRODUCT
: UMATCH_NONE
);
188 ucycom_attach(device_t parent
, device_t self
, void *aux
)
190 struct ucycom_softc
*sc
= device_private(self
);
191 struct uhidev_attach_arg
*uha
= aux
;
195 sc
->sc_hdev
.sc_dev
= self
;
196 sc
->sc_hdev
.sc_intr
= ucycom_intr
;
197 sc
->sc_hdev
.sc_parent
= uha
->parent
;
198 sc
->sc_hdev
.sc_report_id
= uha
->reportid
;
200 uhidev_get_report_desc(uha
->parent
, &desc
, &size
);
201 repid
= uha
->reportid
;
202 sc
->sc_ilen
= hid_report_size(desc
, size
, hid_input
, repid
);
203 sc
->sc_olen
= hid_report_size(desc
, size
, hid_output
, repid
);
204 sc
->sc_flen
= hid_report_size(desc
, size
, hid_feature
, repid
);
206 sc
->sc_msr
= sc
->sc_mcr
= 0;
209 sc
->sc_tty
= ttymalloc();
210 sc
->sc_tty
->t_sc
= sc
;
211 sc
->sc_tty
->t_oproc
= ucycomstart
;
212 sc
->sc_tty
->t_param
= ucycomparam
;
214 tty_attach(sc
->sc_tty
);
216 /* Nothing interesting to report */
222 ucycom_detach(device_t self
, int flags
)
224 struct ucycom_softc
*sc
= device_private(self
);
225 struct tty
*tp
= sc
->sc_tty
;
229 DPRINTF(("ucycom_detach: sc=%p flags=%d tp=%p\n", sc
, flags
, tp
));
235 mutex_spin_enter(&tty_lock
);
236 CLR(tp
->t_state
, TS_CARR_ON
);
237 CLR(tp
->t_cflag
, CLOCAL
| MDMBUF
);
238 ttyflush(tp
, FREAD
|FWRITE
);
239 mutex_spin_exit(&tty_lock
);
241 /* Wait for processes to go away. */
242 usb_detach_wait(USBDEV(sc
->sc_hdev
.sc_dev
));
245 /* locate the major number */
246 maj
= cdevsw_lookup_major(&ucycom_cdevsw
);
248 /* Nuke the vnodes for any open instances. */
249 mn
= device_unit(self
);
251 DPRINTFN(2, ("ucycom_detach: maj=%d mn=%d\n", maj
, mn
));
252 vdevgone(maj
, mn
, mn
, VCHR
);
253 vdevgone(maj
, mn
| UCYCOMDIALOUT_MASK
, mn
| UCYCOMDIALOUT_MASK
, VCHR
);
254 vdevgone(maj
, mn
| UCYCOMCALLUNIT_MASK
, mn
| UCYCOMCALLUNIT_MASK
, VCHR
);
256 /* Detach and free the tty. */
258 DPRINTF(("ucycom_detach: tty_detach %p\n", tp
));
268 ucycom_activate(device_ptr_t self
, enum devact act
)
270 struct ucycom_softc
*sc
= device_private(self
);
272 DPRINTFN(5,("ucycom_activate: %d\n", act
));
275 case DVACT_DEACTIVATE
:
285 ucycom_shutdown(struct ucycom_softc
*sc
)
287 struct tty
*tp
= sc
->sc_tty
;
289 DPRINTF(("ucycom_shutdown\n"));
291 * Hang up if necessary. Wait a bit, so the other side has time to
292 * notice even if we immediately open the port again.
294 if (ISSET(tp
->t_cflag
, HUPCL
)) {
296 (void)tsleep(sc
, TTIPRI
, ttclos
, hz
);
302 ucycomopen(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
304 struct ucycom_softc
*sc
=
305 device_lookup_private(&ucycom_cd
, UCYCOMUNIT(dev
));
309 DPRINTF(("ucycomopen: unit=%d\n", UCYCOMUNIT(dev
)));
310 DPRINTF(("ucycomopen: sc=%p\n", sc
));
318 if (!device_is_active(sc
->sc_hdev
.sc_dev
))
323 DPRINTF(("ucycomopen: tp=%p\n", tp
));
325 if (kauth_authorize_device_tty(l
->l_cred
, KAUTH_DEVICE_TTY_OPEN
, tp
))
330 if (!ISSET(tp
->t_state
, TS_ISOPEN
) && tp
->t_wopen
== 0) {
335 err
= uhidev_open(&sc
->sc_hdev
);
343 * Initialize the termios status to the defaults. Add in the
344 * sticky bits from TIOCSFLAGS.
347 t
.c_ospeed
= TTYDEF_SPEED
;
348 t
.c_cflag
= TTYDEF_CFLAG
;
349 if (ISSET(sc
->sc_swflags
, TIOCFLAG_CLOCAL
))
350 SET(t
.c_cflag
, CLOCAL
);
351 if (ISSET(sc
->sc_swflags
, TIOCFLAG_CRTSCTS
))
352 SET(t
.c_cflag
, CRTSCTS
);
353 if (ISSET(sc
->sc_swflags
, TIOCFLAG_MDMBUF
))
354 SET(t
.c_cflag
, MDMBUF
);
357 (void) ucycomparam(tp
, &t
);
358 tp
->t_iflag
= TTYDEF_IFLAG
;
359 tp
->t_oflag
= TTYDEF_OFLAG
;
360 tp
->t_lflag
= TTYDEF_LFLAG
;
364 /* Allocate an output report buffer */
365 sc
->sc_obuf
= malloc(sc
->sc_olen
, M_USBDEV
, M_WAITOK
);
367 DPRINTF(("ucycomopen: sc->sc_obuf=%p\n", sc
->sc_obuf
));
370 /* XXX Don't do this as for some reason trying to do an
371 * XXX interrupt out transfer at this point means everything
375 * Turn on DTR. We must always do this, even if carrier is not
376 * present, because otherwise we'd have to use TIOCSDTR
377 * immediately after setting CLOCAL, which applications do not
378 * expect. We always assert DTR while the device is open
379 * unless explicitly requested to deassert it.
385 /* XXX CLR(sc->sc_rx_flags, RX_ANY_BLOCK);*/
392 err
= ttyopen(tp
, UCYCOMDIALOUT(dev
), ISSET(flag
, O_NONBLOCK
));
396 err
= (*tp
->t_linesw
->l_open
)(dev
, tp
);
403 if (!ISSET(tp
->t_state
, TS_ISOPEN
) && tp
->t_wopen
== 0) {
405 * We failed to open the device, and nobody else had it opened.
406 * Clean up the state as appropriate.
417 ucycomclose(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
419 struct ucycom_softc
*sc
=
420 device_lookup_private(&ucycom_cd
, UCYCOMUNIT(dev
));
421 struct tty
*tp
= sc
->sc_tty
;
423 DPRINTF(("ucycomclose: unit=%d\n", UCYCOMUNIT(dev
)));
424 if (!ISSET(tp
->t_state
, TS_ISOPEN
))
427 (*tp
->t_linesw
->l_close
)(tp
, flag
);
430 if (!ISSET(tp
->t_state
, TS_ISOPEN
) && tp
->t_wopen
== 0) {
432 * Although we got a last close, the device may still be in
433 * use; e.g. if this was the dialout node, and there are still
434 * processes waiting for carrier on the non-dialout node.
443 ucycomstart(struct tty
*tp
)
445 struct ucycom_softc
*sc
=
446 device_lookup_private(&ucycom_cd
, UCYCOMUNIT(tp
->t_dev
));
455 if (ISSET(tp
->t_state
, TS_BUSY
| TS_TIMEOUT
| TS_TTSTOP
)) {
456 DPRINTFN(4,("ucycomstart: no go, state=0x%x\n", tp
->t_state
));
462 if (sc
->sc_tx_stopped
)
466 if (ttypull(tp
) == 0)
469 /* Grab the first contiguous region of buffer space. */
470 data
= tp
->t_outq
.c_cf
;
471 cnt
= ndqb(&tp
->t_outq
, 0);
474 DPRINTF(("ucycomstart: cnt == 0\n"));
478 SET(tp
->t_state
, TS_BUSY
);
481 * The 8 byte output report uses byte 0 for control and byte
484 * The 32 byte output report uses byte 0 for control. Byte 1
485 * is used for byte count.
487 memset(sc
->sc_obuf
, 0, sc
->sc_olen
);
489 switch (sc
->sc_olen
) {
491 if (cnt
> sc
->sc_olen
- 1) {
492 DPRINTF(("ucycomstart(8): big buffer %d chars\n", len
));
493 len
= sc
->sc_olen
- 1;
496 memcpy(sc
->sc_obuf
+ 1, data
, len
);
497 sc
->sc_obuf
[0] = len
| sc
->sc_mcr
;
499 DPRINTF(("ucycomstart(8): sc->sc_obuf[0] = %d | %d = %d\n", len
,
500 sc
->sc_mcr
, sc
->sc_obuf
[0]));
502 if (ucycomdebug
> 10) {
506 DPRINTF(("ucycomstart(8): data ="));
507 for (i
= 0; i
< len
; i
++)
508 DPRINTF((" %02x", d
[i
]));
515 if (cnt
> sc
->sc_olen
- 2) {
516 DPRINTF(("ucycomstart(32): big buffer %d chars\n",
518 len
= sc
->sc_olen
- 2;
521 memcpy(sc
->sc_obuf
+ 2, data
, len
);
522 sc
->sc_obuf
[0] = sc
->sc_mcr
;
523 sc
->sc_obuf
[1] = len
;
524 DPRINTF(("ucycomstart(32): sc->sc_obuf[0] = %d\n"
525 "sc->sc_obuf[1] = %d\n", sc
->sc_obuf
[0], sc
->sc_obuf
[1]));
527 if (ucycomdebug
> 10) {
531 DPRINTF(("ucycomstart(32): data ="));
532 for (i
= 0; i
< len
; i
++)
533 DPRINTF((" %02x", d
[i
]));
540 DPRINTFN(2,("ucycomstart: unknown output report size (%zd)\n",
548 if (ucycomdebug
> 5) {
552 DPRINTF(("ucycomstart: sc->sc_obuf[0..%zd) =",
554 for (i
= 0; i
< sc
->sc_olen
; i
++)
555 DPRINTF((" %02x", sc
->sc_obuf
[i
]));
560 DPRINTFN(4,("ucycomstart: %d chars\n", len
));
561 usbd_setup_xfer(sc
->sc_hdev
.sc_parent
->sc_oxfer
,
562 sc
->sc_hdev
.sc_parent
->sc_opipe
, (usbd_private_handle
)sc
,
563 sc
->sc_obuf
, sc
->sc_olen
, 0 /* USBD_NO_COPY */, USBD_NO_TIMEOUT
,
566 /* What can we do on error? */
567 err
= usbd_transfer(sc
->sc_hdev
.sc_parent
->sc_oxfer
);
570 if (err
!= USBD_IN_PROGRESS
)
571 DPRINTF(("ucycomstart: err=%s\n", usbd_errstr(err
)));
580 ucycomwritecb(usbd_xfer_handle xfer
, usbd_private_handle p
, usbd_status status
)
582 struct ucycom_softc
*sc
= (struct ucycom_softc
*)p
;
583 struct tty
*tp
= sc
->sc_tty
;
587 if (status
== USBD_CANCELLED
|| sc
->sc_dying
)
591 DPRINTF(("ucycomwritecb: status=%d\n", status
));
592 usbd_clear_endpoint_stall(sc
->sc_hdev
.sc_parent
->sc_opipe
);
593 /* XXX we should restart after some delay. */
597 usbd_get_xfer_status(xfer
, NULL
, NULL
, &len
, &stat
);
599 if (status
!= USBD_NORMAL_COMPLETION
) {
600 DPRINTFN(4,("ucycomwritecb: status = %d\n", status
));
604 DPRINTFN(4,("ucycomwritecb: did %d/%d chars\n", sc
->sc_wlen
, len
));
607 CLR(tp
->t_state
, TS_BUSY
);
608 if (ISSET(tp
->t_state
, TS_FLUSH
))
609 CLR(tp
->t_state
, TS_FLUSH
);
611 ndflush(&tp
->t_outq
, sc
->sc_wlen
);
612 (*tp
->t_linesw
->l_start
)(tp
);
618 CLR(tp
->t_state
, TS_BUSY
);
623 ucycomparam(struct tty
*tp
, struct termios
*t
)
625 struct ucycom_softc
*sc
= tp
->t_sc
;
630 if (t
->c_ospeed
< 0) {
631 DPRINTF(("ucycomparam: c_ospeed < 0\n"));
635 /* Check requested parameters. */
636 if (t
->c_ispeed
&& t
->c_ispeed
!= t
->c_ospeed
)
640 * For the console, always force CLOCAL and !HUPCL, so that the port
643 if (ISSET(sc
->sc_swflags
, TIOCFLAG_SOFTCAR
)) {
644 SET(t
->c_cflag
, CLOCAL
);
645 CLR(t
->c_cflag
, HUPCL
);
649 * If there were no changes, don't do anything. This avoids dropping
650 * input and improves performance when all we did was frob things like
653 if (tp
->t_ospeed
== t
->c_ospeed
&&
654 tp
->t_cflag
== t
->c_cflag
)
657 /* XXX lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag); */
659 /* And copy to tty. */
661 tp
->t_ospeed
= t
->c_ospeed
;
662 tp
->t_cflag
= t
->c_cflag
;
665 DPRINTF(("ucycomparam: baud=%d\n", baud
));
667 if (t
->c_cflag
& CIGNORE
) {
671 switch (t
->c_cflag
& CSIZE
) {
673 cfg
|= UCYCOM_DATA_BITS_8
;
676 cfg
|= UCYCOM_DATA_BITS_7
;
679 cfg
|= UCYCOM_DATA_BITS_6
;
682 cfg
|= UCYCOM_DATA_BITS_5
;
687 cfg
|= ISSET(t
->c_cflag
, CSTOPB
) ?
688 UCYCOM_STOP_BITS_2
: UCYCOM_STOP_BITS_1
;
689 cfg
|= ISSET(t
->c_cflag
, PARENB
) ?
690 UCYCOM_PARITY_ON
: UCYCOM_PARITY_OFF
;
691 cfg
|= ISSET(t
->c_cflag
, PARODD
) ?
692 UCYCOM_PARITY_ODD
: UCYCOM_PARITY_EVEN
;
696 * Update the tty layer's idea of the carrier bit, in case we changed
697 * CLOCAL or MDMBUF. We don't hang up here; we only do that by
700 DPRINTF(("ucycomparam: l_modem\n"));
701 (void) (*tp
->t_linesw
->l_modem
)(tp
, 1 /* XXX carrier */ );
703 err
= ucycom_configure(sc
, baud
, cfg
);
708 ucycomstop(struct tty
*tp
, int flag
)
710 DPRINTF(("ucycomstop: flag=%d\n", flag
));
714 ucycomread(dev_t dev
, struct uio
*uio
, int flag
)
716 struct ucycom_softc
*sc
=
717 device_lookup_private(&ucycom_cd
, UCYCOMUNIT(dev
));
718 struct tty
*tp
= sc
->sc_tty
;
721 DPRINTF(("ucycomread: sc=%p, tp=%p, uio=%p, flag=%d\n", sc
, tp
, uio
,
726 err
= ((*tp
->t_linesw
->l_read
)(tp
, uio
, flag
));
732 ucycomwrite(dev_t dev
, struct uio
*uio
, int flag
)
734 struct ucycom_softc
*sc
=
735 device_lookup_private(&ucycom_cd
, UCYCOMUNIT(dev
));
736 struct tty
*tp
= sc
->sc_tty
;
739 DPRINTF(("ucycomwrite: sc=%p, tp=%p, uio=%p, flag=%d\n", sc
, tp
, uio
,
744 err
= ((*tp
->t_linesw
->l_write
)(tp
, uio
, flag
));
751 struct ucycom_softc
*sc
=
752 device_lookup_private(&ucycom_cd
, UCYCOMUNIT(dev
));
753 struct tty
*tp
= sc
->sc_tty
;
755 DPRINTF(("ucycomtty: sc=%p, tp=%p\n", sc
, tp
));
761 ucycomioctl(dev_t dev
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
763 struct ucycom_softc
*sc
=
764 device_lookup_private(&ucycom_cd
, UCYCOMUNIT(dev
));
765 struct tty
*tp
= sc
->sc_tty
;
772 DPRINTF(("ucycomioctl: sc=%p, tp=%p, data=%p\n", sc
, tp
, data
));
774 err
= (*tp
->t_linesw
->l_ioctl
)(tp
, cmd
, data
, flag
, l
);
775 if (err
!= EPASSTHROUGH
)
778 err
= ttioctl(tp
, cmd
, data
, flag
, l
);
779 if (err
!= EPASSTHROUGH
)
784 DPRINTF(("ucycomioctl: our cmd=0x%08lx\n", cmd
));
805 *(int *)data
= sc
->sc_swflags
;
809 err
= kauth_authorize_device_tty(l
->l_cred
,
810 KAUTH_DEVICE_TTY_PRIVSET
, tp
);
813 sc
->sc_swflags
= *(int *)data
;
819 tiocm_to_ucycom(sc
, cmd
, *(int *)data
);
823 *(int *)data
= ucycom_to_tiocm(sc
);
837 ucycompoll(dev_t dev
, int events
, struct lwp
*l
)
839 struct ucycom_softc
*sc
=
840 device_lookup_private(&ucycom_cd
, UCYCOMUNIT(dev
));
841 struct tty
*tp
= sc
->sc_tty
;
844 DPRINTF(("ucycompoll: sc=%p, tp=%p, events=%d, lwp=%p\n", sc
, tp
,
850 err
= ((*tp
->t_linesw
->l_poll
)(tp
, events
, l
));
855 ucycom_configure(struct ucycom_softc
*sc
, uint32_t baud
, uint8_t cfg
)
871 * Stock chips only support standard baud rates in the 600 - 57600
872 * range, but higher rates can be achieved using custom firmware.
883 DPRINTF(("ucycom_configure: setting %d baud, %d-%c-%d (%d)\n", baud
,
884 5 + (cfg
& UCYCOM_DATA_MASK
),
885 (cfg
& UCYCOM_PARITY_MASK
) ?
886 ((cfg
& UCYCOM_PARITY_TYPE_MASK
) ? 'O' : 'E') : 'N',
887 (cfg
& UCYCOM_STOP_MASK
) ? 2 : 1, cfg
));
889 report
[0] = baud
& 0xff;
890 report
[1] = (baud
>> 8) & 0xff;
891 report
[2] = (baud
>> 16) & 0xff;
892 report
[3] = (baud
>> 24) & 0xff;
894 err
= uhidev_set_report(&sc
->sc_hdev
, UHID_FEATURE_REPORT
,
895 report
, sc
->sc_flen
);
897 DPRINTF(("%s\n", usbd_errstr(err
)));
911 ucycom_intr(struct uhidev
*addr
, void *ibuf
, u_int len
)
913 struct ucycom_softc
*sc
= (struct ucycom_softc
*)addr
;
914 struct tty
*tp
= sc
->sc_tty
;
915 int (*rint
)(int , struct tty
*) = tp
->t_linesw
->l_rint
;
919 /* We understand 8 byte and 32 byte input records */
922 n
= cp
[0] & UCYCOM_LMASK
;
923 st
= cp
[0] & ~UCYCOM_LMASK
;
934 DPRINTFN(3,("ucycom_intr: Unknown input report length\n"));
939 if (ucycomdebug
> 5) {
943 DPRINTF(("ucycom_intr: ibuf[0..%d) =", n
));
944 for (i
= 0; i
< n
; i
++)
945 DPRINTF((" %02x", cp
[i
]));
951 /* Give characters to tty layer. */
954 DPRINTFN(7,("ucycom_intr: char=0x%02x\n", *cp
));
955 if ((*rint
)(*cp
++, tp
) == -1) {
956 /* XXX what should we do? */
957 aprint_error_dev(sc
->sc_hdev
.sc_dev
,
958 "lost a character\n");
963 chg
= st
^ sc
->sc_msr
;
965 if (ISSET(chg
, UCYCOM_DCD
))
966 (*tp
->t_linesw
->l_modem
)(tp
,
967 ISSET(sc
->sc_msr
, UCYCOM_DCD
));
972 tiocm_to_ucycom(struct ucycom_softc
*sc
, u_long how
, int ttybits
)
975 u_char before
= sc
->sc_mcr
;
978 if (ISSET(ttybits
, TIOCM_DTR
))
979 SET(combits
, UCYCOM_DTR
);
980 if (ISSET(ttybits
, TIOCM_RTS
))
981 SET(combits
, UCYCOM_RTS
);
985 CLR(sc
->sc_mcr
, combits
);
989 SET(sc
->sc_mcr
, combits
);
993 CLR(sc
->sc_mcr
, UCYCOM_DTR
| UCYCOM_RTS
);
994 SET(sc
->sc_mcr
, combits
);
997 if (before
^ sc
->sc_mcr
) {
998 DPRINTF(("tiocm_to_ucycom: something has changed\n"));
999 ucycom_set_status(sc
);
1004 ucycom_to_tiocm(struct ucycom_softc
*sc
)
1009 combits
= sc
->sc_mcr
;
1010 if (ISSET(combits
, UCYCOM_DTR
))
1011 SET(ttybits
, TIOCM_DTR
);
1012 if (ISSET(combits
, UCYCOM_RTS
))
1013 SET(ttybits
, TIOCM_RTS
);
1015 combits
= sc
->sc_msr
;
1016 if (ISSET(combits
, UCYCOM_DCD
))
1017 SET(ttybits
, TIOCM_CD
);
1018 if (ISSET(combits
, UCYCOM_CTS
))
1019 SET(ttybits
, TIOCM_CTS
);
1020 if (ISSET(combits
, UCYCOM_DSR
))
1021 SET(ttybits
, TIOCM_DSR
);
1022 if (ISSET(combits
, UCYCOM_RI
))
1023 SET(ttybits
, TIOCM_RI
);
1029 ucycom_dtr(struct ucycom_softc
*sc
, int set
)
1035 SET(sc
->sc_mcr
, UCYCOM_DTR
);
1037 CLR(sc
->sc_mcr
, UCYCOM_DTR
);
1039 if (old
^ sc
->sc_mcr
)
1040 ucycom_set_status(sc
);
1045 ucycom_rts(struct ucycom_softc
*sc
, int set
)
1051 SET(sc
->sc_mcr
, UCYCOM_RTS
);
1053 CLR(sc
->sc_mcr
, UCYCOM_RTS
);
1055 if (old
^ sc
->sc_mcr
)
1056 ucycom_set_status(sc
);
1061 ucycom_set_status(struct ucycom_softc
*sc
)
1065 if (sc
->sc_olen
!= 8 && sc
->sc_olen
!= 32) {
1066 DPRINTFN(2,("ucycom_set_status: unknown output report "
1067 "size (%zd)\n", sc
->sc_olen
));
1071 DPRINTF(("ucycom_set_status: %d\n", sc
->sc_mcr
));
1073 memset(sc
->sc_obuf
, 0, sc
->sc_olen
);
1074 sc
->sc_obuf
[0] = sc
->sc_mcr
;
1076 err
= uhidev_write(sc
->sc_hdev
.sc_parent
, sc
->sc_obuf
, sc
->sc_olen
);
1078 DPRINTF(("ucycom_set_status: err=%d\n", err
));
1084 ucycom_get_cfg(struct ucycom_softc
*sc
)
1089 err
= uhidev_get_report(&sc
->sc_hdev
, UHID_FEATURE_REPORT
,
1090 report
, sc
->sc_flen
);
1092 baud
= (report
[3] << 24) + (report
[2] << 16) + (report
[1] << 8) +
1094 DPRINTF(("ucycom_get_cfg: device reports %d baud, %d-%c-%d (%d)\n",
1095 baud
, 5 + (cfg
& UCYCOM_DATA_MASK
),
1096 (cfg
& UCYCOM_PARITY_MASK
) ?
1097 ((cfg
& UCYCOM_PARITY_TYPE_MASK
) ? 'O' : 'E') : 'N',
1098 (cfg
& UCYCOM_STOP_MASK
) ? 2 : 1, cfg
));
1103 ucycom_cleanup(struct ucycom_softc
*sc
)
1105 DPRINTF(("ucycom_cleanup: closing uhidev\n"));
1107 if (sc
->sc_obuf
!=NULL
)
1108 free (sc
->sc_obuf
, M_USBDEV
);
1109 uhidev_close(&sc
->sc_hdev
);