diff --git a/sys/dev/usb/umodem.c b/sys/dev/usb/umodem.c index 676aed4e32bb..8d164d008891 100644 --- a/sys/dev/usb/umodem.c +++ b/sys/dev/usb/umodem.c @@ -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 diff --git a/sys/dev/usb/usbcdc.h b/sys/dev/usb/usbcdc.h index e676c1b6215f..03b58a7e03ea 100644 --- a/sys/dev/usb/usbcdc.h +++ b/sys/dev/usb/usbcdc.h @@ -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_ */