1 /* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */
7 * Copyright (c) 2005, Takanori Watanabe
8 * Copyright (c) 2003, M. Warner Losh <imp@freebsd.org>.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * Copyright (c) 1998 The NetBSD Foundation, Inc.
35 * All rights reserved.
37 * This code is derived from software contributed to The NetBSD Foundation
38 * by Lennart Augustsson (lennart@augustsson.net) at
39 * Carlstedt Research & Technology.
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
44 * 1. Redistributions of source code must retain the above copyright
45 * notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 * notice, this list of conditions and the following disclaimer in the
48 * documentation and/or other materials provided with the distribution.
49 * 3. All advertising materials mentioning features or use of this software
50 * must display the following acknowledgement:
51 * This product includes software developed by the NetBSD
52 * Foundation, Inc. and its contributors.
53 * 4. Neither the name of The NetBSD Foundation nor the names of its
54 * contributors may be used to endorse or promote products derived
55 * from this software without specific prior written permission.
57 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67 * POSSIBILITY OF SUCH DAMAGE.
71 * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
72 * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
77 * - Add error recovery in various places; the big problem is what
78 * to do in a callback if there is an error.
79 * - Implement a Call Device for modems without multiplexed commands.
83 #include <sys/param.h>
84 #include <sys/systm.h>
85 #include <sys/kernel.h>
86 #include <sys/module.h>
87 #include <sys/ioccom.h>
89 #include <sys/serial.h>
91 #include <sys/clist.h>
93 #include <sys/select.h>
94 #include <sys/sysctl.h>
100 #include <sys/taskqueue.h>
102 #include <dev/usb/usb.h>
103 #include <dev/usb/usbcdc.h>
105 #include <dev/usb/usbdi.h>
106 #include <dev/usb/usbdi_util.h>
107 #include <dev/usb/usb_quirks.h>
109 #include <dev/usb/ucomvar.h>
113 typedef struct ufoma_mobile_acm_descriptor{
114 uByte bFunctionLength;
115 uByte bDescriptorType;
116 uByte bDescriptorSubtype;
119 }usb_mcpc_acm_descriptor;
121 #define UISUBCLASS_MCPC 0x88
123 #define UDESC_VS_INTERFACE 0x44
124 #define UDESCSUB_MCPC_ACM 0x11
126 #define UMCPC_ACM_TYPE_AB1 0x1
127 #define UMCPC_ACM_TYPE_AB2 0x2
128 #define UMCPC_ACM_TYPE_AB5 0x5
129 #define UMCPC_ACM_TYPE_AB6 0x6
131 #define UMCPC_ACM_MODE_DEACTIVATED 0x0
132 #define UMCPC_ACM_MODE_MODEM 0x1
133 #define UMCPC_ACM_MODE_ATCOMMAND 0x2
134 #define UMCPC_ACM_MODE_OBEX 0x60
135 #define UMCPC_ACM_MODE_VENDOR1 0xc0
136 #define UMCPC_ACM_MODE_VENDOR2 0xfe
137 #define UMCPC_ACM_MODE_UNLINKED 0xff
139 #define UMCPC_CM_MOBILE_ACM 0x0
141 #define UMCPC_ACTIVATE_MODE 0x60
142 #define UMCPC_GET_MODETABLE 0x61
143 #define UMCPC_SET_LINK 0x62
144 #define UMCPC_CLEAR_LINK 0x63
146 #define UMCPC_REQUEST_ACKNOLEDGE 0x31
148 #define UFOMA_MAX_TIMEOUT 15 /*Standard says 10(sec)*/
149 #define UFOMA_CMD_BUF_SIZE 64
151 #define UMODEMIBUFSIZE 64
152 #define UMODEMOBUFSIZE 256
156 struct ucom_softc sc_ucom;
162 usbd_interface_handle sc_ctl_iface;
163 usbd_interface_handle sc_data_iface;
164 int sc_data_iface_no;
167 usb_cdc_line_state_t sc_line_state; /* current line state */
168 u_char sc_dtr; /* current DTR state */
169 u_char sc_rts; /* current RTS state */
171 usbd_pipe_handle sc_notify_pipe;
172 usb_cdc_notification_t sc_notify_buf;
178 uByte sc_modetoactivate;
179 uByte sc_currentmode;
180 char sc_resbuffer[UFOMA_CMD_BUF_SIZE+1];
183 usbd_xfer_handle sc_msgxf;
186 ufoma_set_line_coding(struct ufoma_softc *sc, usb_cdc_line_state_t *state);
187 static device_probe_t ufoma_match;
188 static device_attach_t ufoma_attach;
189 static device_detach_t ufoma_detach;
190 static void *ufoma_get_intconf(usb_config_descriptor_t *cd, usb_interface_descriptor_t *id,int type, int subtype);
191 static void ufoma_notify(void * ,int count);
192 static void ufoma_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
193 static char *ufoma_mode_to_str(int);
194 static int ufoma_str_to_mode(char *);
196 /*Pseudo ucom stuff*/
197 static int ufoma_init_pseudo_ucom(struct ufoma_softc *);
198 static t_open_t ufomaopen;
199 static t_close_t ufomaclose;
200 static t_oproc_t ufomastart;
202 /*umodem like stuff*/
203 static int ufoma_init_modem(struct ufoma_softc *, struct usb_attach_arg *);
204 static void ufoma_get_status(void *, int portno, u_char *lst, u_char *msr);
205 static void ufoma_set(void *, int portno, int reg, int onoff);
206 static int ufoma_param(void *, int portno, struct termios *);
207 static int ufoma_ucom_open(void *, int portno);
208 static void ufoma_ucom_close(void *, int portno);
209 static void ufoma_break(struct ufoma_softc *sc, int onoff);
210 static void ufoma_dtr(struct ufoma_softc *sc, int onoff);
211 static void ufoma_rts(struct ufoma_softc *sc, int onoff);
214 static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS);
215 static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS);
216 static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS);
219 static struct ucom_callback ufoma_callback = {
231 static device_method_t ufoma_methods[] = {
233 DEVMETHOD(device_probe, ufoma_match),
234 DEVMETHOD(device_attach, ufoma_attach),
235 DEVMETHOD(device_detach, ufoma_detach),
238 struct umcpc_modetostr_tab{
241 }umcpc_modetostr_tab[]={
242 {UMCPC_ACM_MODE_DEACTIVATED, "deactivated"},
243 {UMCPC_ACM_MODE_MODEM, "modem"},
244 {UMCPC_ACM_MODE_ATCOMMAND, "handsfree"},
245 {UMCPC_ACM_MODE_OBEX, "obex"},
246 {UMCPC_ACM_MODE_VENDOR1, "vendor1"},
247 {UMCPC_ACM_MODE_VENDOR2, "vendor2"},
248 {UMCPC_ACM_MODE_UNLINKED, "unlinked"},
252 static driver_t ufoma_driver = {
255 sizeof(struct ufoma_softc)
259 DRIVER_MODULE(ufoma, uhub, ufoma_driver, ucom_devclass, usbd_driver_load, 0);
260 MODULE_DEPEND(ufoma, usb, 1, 1, 1);
261 MODULE_DEPEND(ufoma, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
264 ufoma_match(device_t self)
266 struct usb_attach_arg *uaa = device_get_ivars(self);
267 usb_interface_descriptor_t *id;
268 usb_config_descriptor_t *cd;
269 usb_mcpc_acm_descriptor *mad;
274 if(uaa->iface == NULL)
276 id = usbd_get_interface_descriptor(uaa->iface);
277 cd = usbd_get_config_descriptor(uaa->device);
279 if(id == NULL || cd == NULL)
280 return (UMATCH_NONE);
282 if( id->bInterfaceClass == UICLASS_CDC &&
283 id->bInterfaceSubClass == UISUBCLASS_MCPC){
284 ret = (UMATCH_IFACECLASS_IFACESUBCLASS);
289 mad = ufoma_get_intconf(cd, id , UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
291 return (UMATCH_NONE);
295 if(mad->bType != UMCPC_ACM_TYPE_AB5){
303 ufoma_attach(device_t self)
305 struct ufoma_softc *sc = device_get_softc(self);
306 struct usb_attach_arg *uaa = device_get_ivars(self);
307 usbd_device_handle dev = uaa->device;
308 usb_config_descriptor_t *cd;
309 usb_interface_descriptor_t *id;
310 usb_endpoint_descriptor_t *ed;
311 usb_mcpc_acm_descriptor *mad;
312 struct ucom_softc *ucom = &sc->sc_ucom;
313 const char *devname,*modename;
318 struct sysctl_ctx_list *sctx;
319 struct sysctl_oid *soid;
323 sc->sc_ctl_iface = uaa->iface;
324 mtx_init(&sc->sc_mtx, "ufoma", NULL, MTX_DEF);
326 cd = usbd_get_config_descriptor(ucom->sc_udev);
327 id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
328 sc->sc_ctl_iface_no = id->bInterfaceNumber;
330 devname = device_get_nameunit(self);
331 device_printf(self, "iclass %d/%d ifno:%d\n",
332 id->bInterfaceClass, id->bInterfaceSubClass, sc->sc_ctl_iface_no);
335 for (i = 0; i < id->bNumEndpoints; i++) {
336 ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i);
340 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
341 (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
342 ctl_notify = ed->bEndpointAddress;
347 /*NOTIFY is mandatory.*/
348 printf("NOTIFY interface not found\n");
352 err = usbd_open_pipe_intr(sc->sc_ctl_iface, ctl_notify,
353 USBD_SHORT_XFER_OK, &sc->sc_notify_pipe, sc, &sc->sc_notify_buf,
354 sizeof(sc->sc_notify_buf), ufoma_intr, USBD_DEFAULT_INTERVAL);
356 printf("PIPE open error %d\n", err);
359 mad = ufoma_get_intconf(cd, id , UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
364 printf("%s:Supported Mode:", devname);
365 for(mode = mad->bMode;
366 mode < ((uByte *)mad + mad->bFunctionLength); mode++){
367 modename = ufoma_mode_to_str(*mode);
369 printf("%s", ufoma_mode_to_str(*mode));
371 printf("(%x)", *mode);
373 if(mode != ((uByte*)mad + mad->bFunctionLength-1)){
379 if((mad->bType == UMCPC_ACM_TYPE_AB5)
380 ||(mad->bType == UMCPC_ACM_TYPE_AB6)){
381 /*These does not have data interface*/
383 ufoma_init_pseudo_ucom(sc);
385 if(ufoma_init_modem(sc, uaa)){
389 elements = mad->bFunctionLength - sizeof(*mad)+1;
391 sc->sc_msgxf = usbd_alloc_xfer(ucom->sc_udev);
394 /*Initialize Mode vars.*/
395 sc->sc_modetable = malloc(elements + 1, M_USBDEV, M_WAITOK);
396 sc->sc_modetable[0] = elements + 1;
397 bcopy(mad->bMode, &sc->sc_modetable[1], elements);
398 sc->sc_currentmode = UMCPC_ACM_MODE_UNLINKED;
399 sc->sc_modetoactivate = mad->bMode[0];
401 sctx = device_get_sysctl_ctx(self);
402 soid = device_get_sysctl_tree(self);
404 SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode",
405 CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_support,
406 "A", "Supporting port role");
408 SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode",
409 CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_current,
410 "A", "Current port role");
412 SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode",
413 CTLFLAG_RW|CTLTYPE_STRING, sc, 0, ufoma_sysctl_open,
414 "A", "Mode to transit when port is opened");
418 free(sc->sc_modetable, M_USBDEV);
423 ufoma_detach(device_t self)
425 struct ufoma_softc *sc = device_get_softc(self);
428 usbd_free_xfer(sc->sc_msgxf);
429 sc->sc_ucom.sc_dying = 1;
430 usbd_abort_pipe(sc->sc_notify_pipe);
431 usbd_close_pipe(sc->sc_notify_pipe);
433 ucom_detach(&sc->sc_ucom);
435 ttyfree(sc->sc_ucom.sc_tty);
436 free(sc->sc_modetable, M_USBDEV);
441 static char *ufoma_mode_to_str(int mode)
444 for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){
445 if(umcpc_modetostr_tab[i].mode == mode){
446 return umcpc_modetostr_tab[i].str;
452 static int ufoma_str_to_mode(char *str)
455 for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){
456 if(strcmp(str, umcpc_modetostr_tab[i].str)==0){
457 return umcpc_modetostr_tab[i].mode;
463 static void *ufoma_get_intconf( usb_config_descriptor_t *cd,
464 usb_interface_descriptor_t *id, int type, int subtype)
467 usb_descriptor_t *ud=NULL;
471 for(p = (uByte *)cd,end = p + UGETW(cd->wTotalLength); p < end;
473 ud = (usb_descriptor_t *)p;
474 if(flag && ud->bDescriptorType==UDESC_INTERFACE){
477 /*Read through this interface desc.*/
478 if(bcmp(p, id, sizeof(*id))==0){
484 if(ud->bDescriptorType == type
485 && ud->bDescriptorSubtype == subtype){
494 static int ufoma_link_state(struct ufoma_softc *sc)
496 usb_device_request_t req;
497 struct ucom_softc *ucom = &sc->sc_ucom;
500 req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
501 req.bRequest = UMCPC_SET_LINK;
502 USETW(req.wValue, UMCPC_CM_MOBILE_ACM);
503 USETW(req.wIndex, sc->sc_ctl_iface_no);
504 USETW(req.wLength, sc->sc_modetable[0]);
505 err = usbd_do_request(ucom->sc_udev, &req, sc->sc_modetable);
507 printf("SET_LINK:%d\n", err);
510 err = tsleep(&sc->sc_currentmode, PZERO|PCATCH, "fmalnk", hz);
512 printf("NO response");
515 if(sc->sc_currentmode != UMCPC_ACM_MODE_DEACTIVATED){
521 static int ufoma_activate_state(struct ufoma_softc *sc, int state)
523 usb_device_request_t req;
525 struct ucom_softc *ucom = &sc->sc_ucom;
527 req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
528 req.bRequest = UMCPC_ACTIVATE_MODE;
529 USETW(req.wValue, state);
530 USETW(req.wIndex, sc->sc_ctl_iface_no);
531 USETW(req.wLength, 0);
533 err = usbd_do_request(ucom->sc_udev, &req, NULL);
535 printf("%s:ACTIVATE:%s\n", device_get_nameunit(ucom->sc_dev), usbd_errstr(err));
539 err = tsleep(&sc->sc_currentmode, PZERO|PCATCH, "fmaact", UFOMA_MAX_TIMEOUT*hz);
541 printf("%s:NO response", device_get_nameunit(ucom->sc_dev));
544 if(sc->sc_currentmode != state){
551 static inline void ufoma_setup_msg_req(struct ufoma_softc *sc, usb_device_request_t *req)
553 req->bmRequestType = UT_READ_CLASS_INTERFACE;
554 req->bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
555 USETW(req->wIndex, sc->sc_ctl_iface_no);
556 USETW(req->wValue, 0);
557 USETW(req->wLength, UFOMA_CMD_BUF_SIZE);
561 static void ufoma_msg(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
563 usb_device_request_t req;
564 struct ufoma_softc *sc = priv;
566 struct ucom_softc *ucom= &sc->sc_ucom;
567 usbd_get_xfer_status(xfer, NULL, NULL, &actlen ,NULL);
568 ufoma_setup_msg_req(sc, &req);
569 mtx_lock(&sc->sc_mtx);
570 for(i = 0;i < actlen; i++){
571 if(sc->sc_ucom.sc_tty->t_state & TS_ISOPEN)
572 ttyld_rint(sc->sc_ucom.sc_tty, sc->sc_resbuffer[i]);
576 usbd_setup_default_xfer(sc->sc_msgxf, ucom->sc_udev,
577 priv, USBD_DEFAULT_TIMEOUT, &req,
581 usbd_transfer(sc->sc_msgxf);
583 mtx_unlock(&sc->sc_mtx);
587 static void ufoma_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
589 struct ufoma_softc *sc = priv;
591 struct ucom_softc *ucom =&sc->sc_ucom;
592 usb_device_request_t req;
593 ufoma_setup_msg_req(sc, &req);
596 if (sc->sc_ucom.sc_dying)
599 if (status != USBD_NORMAL_COMPLETION) {
600 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
602 printf("%s: abnormal status: %s\n", device_get_nameunit(ucom->sc_dev),
603 usbd_errstr(status));
606 if((sc->sc_notify_buf.bmRequestType == UT_READ_VENDOR_INTERFACE)&&
607 (sc->sc_notify_buf.bNotification == UMCPC_REQUEST_ACKNOLEDGE)){
608 a = UGETW(sc->sc_notify_buf.wValue);
609 sc->sc_currentmode = a>>8;
611 printf("%s:Mode change Failed\n", device_get_nameunit(ucom->sc_dev));
613 wakeup(&sc->sc_currentmode);
615 if(sc->sc_notify_buf.bmRequestType != UCDC_NOTIFICATION){
618 switch(sc->sc_notify_buf.bNotification){
619 case UCDC_N_RESPONSE_AVAILABLE:
621 printf("%s:wrong response request?\n", device_get_nameunit(ucom->sc_dev));
624 mtx_lock(&sc->sc_mtx);
626 usbd_setup_default_xfer(sc->sc_msgxf, ucom->sc_udev,
627 priv, USBD_DEFAULT_TIMEOUT, &req, sc->sc_resbuffer,
630 usbd_transfer(sc->sc_msgxf);
633 mtx_unlock(&sc->sc_mtx);
635 case UCDC_N_SERIAL_STATE:
637 printf("%s:wrong sereal request?\n",device_get_nameunit(ucom->sc_dev));
642 * Set the serial state in ucom driver based on
643 * the bits from the notify message
645 if (UGETW(sc->sc_notify_buf.wLength) != 2) {
646 printf("%s: Invalid notification length! (%d)\n",
647 device_get_nameunit(ucom->sc_dev),
648 UGETW(sc->sc_notify_buf.wLength));
651 DPRINTF(("%s: notify bytes = %02x%02x\n",
652 device_get_nameunit(ucom->sc_dev),
653 sc->sc_notify_buf.data[0],
654 sc->sc_notify_buf.data[1]));
655 /* Currently, lsr is always zero. */
656 sc->sc_lsr = sc->sc_msr = 0;
657 mstatus = sc->sc_notify_buf.data[0];
659 if (ISSET(mstatus, UCDC_N_SERIAL_RI))
660 sc->sc_msr |= SER_RI;
661 if (ISSET(mstatus, UCDC_N_SERIAL_DSR))
662 sc->sc_msr |= SER_DSR;
663 if (ISSET(mstatus, UCDC_N_SERIAL_DCD))
664 sc->sc_msr |= SER_DCD;
665 /* Deferred notifying to the ucom layer */
666 taskqueue_enqueue(taskqueue_swi_giant, &sc->sc_task);
673 static int ufoma_init_pseudo_ucom(struct ufoma_softc *sc)
676 struct ucom_softc *ucom = &sc->sc_ucom;
677 tp = ucom->sc_tty = ttyalloc();
679 tp->t_oproc = ufomastart;
680 tp->t_stop = nottystop;
681 tp->t_open = ufomaopen;
682 tp->t_close = ufomaclose;
683 ttycreate(tp, TS_CALLOUT, "U%d", device_get_unit(ucom->sc_dev));
689 static int ufomaopen(struct tty * tty, struct cdev *cdev)
692 struct ufoma_softc *sc = tty->t_sc;
694 if(sc->sc_ucom.sc_dying)
697 mtx_lock(&sc->sc_mtx);
699 mtx_unlock(&sc->sc_mtx);
702 mtx_unlock(&sc->sc_mtx);
704 return ufoma_ucom_open(sc, 0);
707 static void ufomaclose(struct tty *tty)
709 struct ufoma_softc *sc = tty->t_sc;
710 ufoma_ucom_close(sc, 0);
713 static void ufomastart(struct tty *tp)
715 struct ufoma_softc *sc = tp->t_sc;
716 struct ucom_softc *ucom = &sc->sc_ucom;
717 usb_device_request_t req;
721 if(tp->t_state & (TS_TIMEOUT | TS_TTSTOP)){
726 tp->t_state |= TS_BUSY;
727 while(tp->t_outq.c_cc != 0){
728 c = getc(&tp->t_outq);
729 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
730 req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND;
731 USETW(req.wIndex, sc->sc_ctl_iface_no);
732 USETW(req.wValue, 0);
733 USETW(req.wLength, 1);
734 usbd_do_request(ucom->sc_udev, &req, &c);
736 tp->t_state &= ~TS_BUSY;
741 static int ufoma_ucom_open(void *p, int portno)
743 struct ufoma_softc *sc = p;
744 if(sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED){
745 ufoma_link_state(sc);
748 if(sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED){
749 ufoma_activate_state(sc, sc->sc_modetoactivate);
755 static void ufoma_ucom_close(void *p, int portno)
757 struct ufoma_softc *sc = p;
758 ufoma_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED);
763 ufoma_break(struct ufoma_softc *sc, int onoff)
765 usb_device_request_t req;
766 struct ucom_softc *ucom = &sc->sc_ucom;
767 DPRINTF(("ufoma_break: onoff=%d\n", onoff));
769 if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK))
772 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
773 req.bRequest = UCDC_SEND_BREAK;
774 USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
775 USETW(req.wIndex, sc->sc_ctl_iface_no);
776 USETW(req.wLength, 0);
778 (void)usbd_do_request(ucom->sc_udev, &req, 0);
782 ufoma_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
784 struct ufoma_softc *sc = addr;
792 static void ufoma_set(void * addr, int portno, int reg, int onoff)
794 struct ufoma_softc *sc = addr;
798 ufoma_dtr(sc, onoff);
801 ufoma_rts(sc, onoff);
804 ufoma_break(sc, onoff);
813 ufoma_set_line_state(struct ufoma_softc *sc)
815 usb_device_request_t req;
816 struct ucom_softc *ucom = &sc->sc_ucom;
819 ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
820 (sc->sc_rts ? UCDC_LINE_RTS : 0);
821 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
822 req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
823 USETW(req.wValue, ls);
824 USETW(req.wIndex, sc->sc_ctl_iface_no);
825 USETW(req.wLength, 0);
827 (void)usbd_do_request(ucom->sc_udev, &req, 0);
832 ufoma_dtr(struct ufoma_softc *sc, int onoff)
834 DPRINTF(("ufoma_foma: onoff=%d\n", onoff));
836 if (sc->sc_dtr == onoff)
840 ufoma_set_line_state(sc);
844 ufoma_rts(struct ufoma_softc *sc, int onoff)
846 DPRINTF(("ufoma_foma: onoff=%d\n", onoff));
848 if (sc->sc_rts == onoff)
852 ufoma_set_line_state(sc);
856 ufoma_set_line_coding(struct ufoma_softc *sc, usb_cdc_line_state_t *state)
860 usb_device_request_t req;
861 struct ucom_softc *ucom = &sc->sc_ucom;
863 DPRINTF(("ufoma_set_line_coding: rate=%d fmt=%d parity=%d bits=%d\n",
864 UGETDW(state->dwDTERate), state->bCharFormat,
865 state->bParityType, state->bDataBits));
867 if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) {
868 DPRINTF(("ufoma_set_line_coding: already set\n"));
869 return (USBD_NORMAL_COMPLETION);
872 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
873 req.bRequest = UCDC_SET_LINE_CODING;
874 USETW(req.wValue, 0);
875 USETW(req.wIndex, sc->sc_ctl_iface_no);
876 USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
878 err = usbd_do_request(ucom->sc_udev, &req, state);
880 DPRINTF(("ufoma_set_line_coding: failed, err=%s\n",
885 sc->sc_line_state = *state;
887 return (USBD_NORMAL_COMPLETION);
891 ufoma_param(void *addr, int portno, struct termios *t)
894 struct ufoma_softc *sc = addr;
896 usb_cdc_line_state_t ls;
898 DPRINTF(("ufoma_param: sc=%p\n", sc));
900 USETDW(ls.dwDTERate, t->c_ospeed);
901 if (ISSET(t->c_cflag, CSTOPB))
902 ls.bCharFormat = UCDC_STOP_BIT_2;
904 ls.bCharFormat = UCDC_STOP_BIT_1;
905 if (ISSET(t->c_cflag, PARENB)) {
906 if (ISSET(t->c_cflag, PARODD))
907 ls.bParityType = UCDC_PARITY_ODD;
909 ls.bParityType = UCDC_PARITY_EVEN;
911 ls.bParityType = UCDC_PARITY_NONE;
912 switch (ISSET(t->c_cflag, CSIZE)) {
927 err = ufoma_set_line_coding(sc, &ls);
929 DPRINTF(("ufoma_param: err=%s\n", usbd_errstr(err)));
936 static int ufoma_init_modem(struct ufoma_softc *sc,struct usb_attach_arg *uaa)
938 struct ucom_softc *ucom = &sc->sc_ucom;
939 usb_config_descriptor_t *cd;
940 usb_cdc_acm_descriptor_t *acm;
941 usb_cdc_cm_descriptor_t *cmd;
942 usb_endpoint_descriptor_t *ed;
943 usb_interface_descriptor_t *id;
944 const char *devname = device_get_nameunit(ucom->sc_dev);
946 cd = usbd_get_config_descriptor(ucom->sc_udev);
947 id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
951 cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
954 sc->sc_cm_cap = cmd->bmCapabilities;
956 acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
959 sc->sc_acm_cap = acm->bmCapabilities;
961 sc->sc_data_iface_no = cmd->bDataInterface;
962 printf("%s: data interface %d, has %sCM over data, has %sbreak\n",
963 devname, sc->sc_data_iface_no,
964 sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
965 sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
967 for(i = 0; i < uaa->nifaces; i++){
970 id = usbd_get_interface_descriptor(uaa->ifaces[i]);
972 id->bInterfaceNumber == sc->sc_data_iface_no){
973 sc->sc_data_iface = uaa->ifaces[i];
974 //uaa->ifaces[i] = NULL;
978 ucom->sc_iface = sc->sc_data_iface;
979 ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
980 id = usbd_get_interface_descriptor(sc->sc_data_iface);
981 for(i = 0 ; i < id->bNumEndpoints; i++){
982 ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i);
984 printf("%s: endpoint descriptor for %d\n",
988 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
989 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
990 ucom->sc_bulkin_no = ed->bEndpointAddress;
991 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
992 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
993 ucom->sc_bulkout_no = ed->bEndpointAddress;
997 if (ucom->sc_bulkin_no == -1) {
998 printf("%s: Could not find data bulk in\n", devname);
1001 if (ucom->sc_bulkout_no == -1) {
1002 printf("%s: Could not find data bulk out\n", devname);
1008 ucom->sc_parent = sc;
1009 ucom->sc_portno = UCOM_UNK_PORTNO;
1010 /* bulkin, bulkout set above */
1011 ucom->sc_ibufsize = UMODEMIBUFSIZE;
1012 ucom->sc_obufsize = UMODEMOBUFSIZE;
1013 ucom->sc_ibufsizepad = UMODEMIBUFSIZE;
1014 ucom->sc_opkthdrlen = 0;
1015 ucom->sc_callback = &ufoma_callback;
1016 TASK_INIT(&sc->sc_task, 0, ufoma_notify, sc);
1017 ucom_attach(&sc->sc_ucom);
1023 ufoma_notify(void *arg, int count)
1025 struct ufoma_softc *sc;
1027 sc = (struct ufoma_softc *)arg;
1028 if (sc->sc_ucom.sc_dying)
1030 ucom_status_change(&sc->sc_ucom);
1032 static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS)
1034 struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
1039 sbuf_new(&sb, NULL, 1, SBUF_AUTOEXTEND);
1040 for(i = 1; i < sc->sc_modetable[0]; i++){
1041 mode = ufoma_mode_to_str(sc->sc_modetable[i]);
1043 sbuf_cat(&sb, mode);
1045 sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]);
1047 if(i < (sc->sc_modetable[0]-1))
1052 sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
1057 static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS)
1059 struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
1061 char subbuf[]="(XXX)";
1062 mode = ufoma_mode_to_str(sc->sc_currentmode);
1065 snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode);
1067 sysctl_handle_string(oidp, mode, strlen(mode), req);
1072 static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS)
1074 struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
1081 mode = ufoma_mode_to_str(sc->sc_modetoactivate);
1083 strncpy(subbuf, mode, sizeof(subbuf));
1085 snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate);
1087 error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req);
1088 if(error != 0 || req->newptr == NULL){
1092 if((newmode = ufoma_str_to_mode(subbuf)) == -1){
1096 for(i = 1 ; i < sc->sc_modetable[0] ; i++){
1097 if(sc->sc_modetable[i] == newmode){
1098 sc->sc_modetoactivate = newmode;