Connect to the interrupt endpoint on the control interface to receive

notification messages.  This lets us properly pass line status changes
down to the ucom driver (specifically, DSR, DCD, and RI).
This commit is contained in:
kenh 2001-02-16 20:15:57 +00:00
parent 3b3698bd48
commit d6cdd9ad5b
2 changed files with 161 additions and 7 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: umodem.c,v 1.38 2001/01/23 21:56:17 augustss Exp $ */
/* $NetBSD: umodem.c,v 1.39 2001/02/16 20:15:57 kenh Exp $ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@ -113,6 +113,12 @@ struct umodem_softc {
u_char sc_opening; /* lock during open */
u_char sc_dying; /* disconnecting */
int sc_ctl_notify; /* Notification endpoint */
usbd_pipe_handle sc_notify_pipe; /* Notification pipe */
usb_cdc_notification_t sc_notify_buf; /* Notification structure */
u_char sc_lsr; /* Local status register */
u_char sc_msr; /* Modem status register */
};
Static void *umodem_get_desc(usbd_device_handle dev, int type, int subtype);
@ -131,14 +137,17 @@ Static void umodem_break(struct umodem_softc *, int);
Static void umodem_set_line_state(struct umodem_softc *);
Static int umodem_param(void *, int, struct termios *);
Static int umodem_ioctl(void *, int, u_long, caddr_t, int, struct proc *);
Static int umodem_open(void *, int portno);
Static void umodem_close(void *, int portno);
Static void umodem_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
Static struct ucom_methods umodem_methods = {
umodem_get_status,
umodem_set,
umodem_param,
umodem_ioctl,
NULL,
NULL,
umodem_open,
umodem_close,
NULL,
NULL,
};
@ -209,7 +218,6 @@ USB_ATTACH(umodem)
sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
/* Get the data interface too. */
for (i = 0; i < uaa->nifaces; i++) {
if (uaa->ifaces[i] != NULL) {
@ -276,6 +284,31 @@ USB_ATTACH(umodem)
sc->sc_cm_over_data = 1;
}
}
/*
* The standard allows for notification messages (to indicate things
* like a modem hangup) to come in via an interrupt endpoint
* off of the control interface. Interate over the endpoints on
* the control interface and see if there are any interrupt
* endpoints; if there are, then register it.
*/
sc->sc_ctl_notify = -1;
sc->sc_notify_pipe = NULL;
for (i = 0; i < id->bNumEndpoints; i++) {
ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i);
if (ed == NULL)
continue;
if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
(ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
printf("%s: status change notification available\n",
USBDEVNAME(sc->sc_dev));
sc->sc_ctl_notify = ed->bEndpointAddress;
}
}
sc->sc_dtr = -1;
uca.portno = UCOM_UNK_PORTNO;
@ -303,6 +336,113 @@ USB_ATTACH(umodem)
USB_ATTACH_ERROR_RETURN;
}
Static int
umodem_open(void *addr, int portno)
{
struct umodem_softc *sc = addr;
int err;
DPRINTF(("umodem_open: sc=%p\n", sc));
if (sc->sc_ctl_notify != -1 && sc->sc_notify_pipe == NULL) {
err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_ctl_notify,
USBD_SHORT_XFER_OK, &sc->sc_notify_pipe, sc,
&sc->sc_notify_buf, sizeof(sc->sc_notify_buf),
umodem_intr, USBD_DEFAULT_INTERVAL);
if (err) {
DPRINTF(("Failed to establish notify pipe: %s\n",
usbd_errstr(err)));
return EIO;
}
}
return 0;
}
Static void
umodem_close(void *addr, int portno)
{
struct umodem_softc *sc = addr;
int err;
DPRINTF(("umodem_close: sc=%p\n", sc));
if (sc->sc_notify_pipe != NULL) {
err = usbd_abort_pipe(sc->sc_notify_pipe);
if (err)
printf("%s: abort notify pipe failed: %s\n",
USBDEVNAME(sc->sc_dev), usbd_errstr(err));
err = usbd_close_pipe(sc->sc_notify_pipe);
if (err)
printf("%s: close notify pipe failed: %s\n",
USBDEVNAME(sc->sc_dev), usbd_errstr(err));
sc->sc_notify_pipe = NULL;
}
}
Static void
umodem_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct umodem_softc *sc = priv;
u_char mstatus;
if (sc->sc_dying)
return;
if (status != USBD_NORMAL_COMPLETION) {
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
return;
printf("%s: abnormal status: %s\n", USBDEVNAME(sc->sc_dev),
usbd_errstr(status));
return;
}
if (sc->sc_notify_buf.bmRequestType == UCDC_NOTIFICATION)
switch (sc->sc_notify_buf.bNotification) {
case UCDC_N_SERIAL_STATE:
/*
* Set the serial state in ucom driver based on
* the bits from the notify message
*/
if (UGETW(sc->sc_notify_buf.wLength) != 2) {
printf("%s: Invalid notification length! "
"(%d)\n", USBDEVNAME(sc->sc_dev),
UGETW(sc->sc_notify_buf.wLength));
break;
}
DPRINTF(("%s: notify bytes = %02x%02x\n",
USBDEVNAME(sc->sc_dev),
sc->sc_notify_buf.data[0],
sc->sc_notify_buf.data[1]));
/*
* Currently, lsr is always zero
*/
sc->sc_lsr = sc->sc_msr = 0;
mstatus = sc->sc_notify_buf.data[0];
if (ISSET(mstatus, UCDC_N_SERIAL_RI))
sc->sc_msr |= UMSR_RI;
if (ISSET(mstatus, UCDC_N_SERIAL_DSR))
sc->sc_msr |= UMSR_DSR;
if (ISSET(mstatus, UCDC_N_SERIAL_DCD))
sc->sc_msr |= UMSR_DCD;
ucom_status_change((struct ucom_softc *) sc->sc_subdev);
break;
default:
DPRINTF(("%s: unknown notify message: %02x\n",
USBDEVNAME(sc->sc_dev),
sc->sc_notify_buf.bNotification));
break;
}
else
DPRINTF(("%s: unknown message type (%02x) on notify pipe\n",
USBDEVNAME(sc->sc_dev),
sc->sc_notify_buf.bmRequestType));
return;
}
void
umodem_get_caps(usbd_device_handle dev, int *cm, int *acm)
{
@ -329,12 +469,14 @@ umodem_get_caps(usbd_device_handle dev, int *cm, int *acm)
void
umodem_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
{
struct umodem_softc *sc = addr;
DPRINTF(("umodem_get_status:\n"));
if (lsr != NULL)
*lsr = 0; /* XXX */
*lsr = sc->sc_lsr;
if (msr != NULL)
*msr = 0; /* XXX */
*msr = sc->sc_msr;
}
int

View File

@ -1,4 +1,4 @@
/* $NetBSD: usbcdc.h,v 1.7 2000/05/30 10:10:18 augustss Exp $ */
/* $NetBSD: usbcdc.h,v 1.8 2001/02/16 20:15:57 kenh Exp $ */
/* $FreeBSD: src/sys/dev/usb/usbcdc.h,v 1.7 1999/11/17 22:33:48 n_hibma Exp $ */
/*
@ -146,4 +146,16 @@ typedef struct {
} UPACKED usb_cdc_notification_t;
#define UCDC_NOTIFICATION_LENGTH 8
/*
* Bits set in the SERIAL STATE notifcation (first byte of data)
*/
#define UCDC_N_SERIAL_OVERRUN 0x40
#define UCDC_N_SERIAL_PARITY 0x20
#define UCDC_N_SERIAL_FRAMING 0x10
#define UCDC_N_SERIAL_RI 0x08
#define UCDC_N_SERIAL_BREAK 0x04
#define UCDC_N_SERIAL_DSR 0x02
#define UCDC_N_SERIAL_DCD 0x01
#endif /* _USBCDC_H_ */