]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/usb/ufoma.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / dev / usb / ufoma.c
1 /*      $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $       */
2
3
4 #include <sys/cdefs.h>
5 __FBSDID("$FreeBSD$");
6 /*-
7  * Copyright (c) 2005, Takanori Watanabe
8  * Copyright (c) 2003, M. Warner Losh <imp@freebsd.org>.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
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.
19  *
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
30  * SUCH DAMAGE.
31  */
32
33 /*-
34  * Copyright (c) 1998 The NetBSD Foundation, Inc.
35  * All rights reserved.
36  *
37  * This code is derived from software contributed to The NetBSD Foundation
38  * by Lennart Augustsson (lennart@augustsson.net) at
39  * Carlstedt Research & Technology.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
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.
56  *
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.
68  */
69
70 /*
71  * Comm Class spec:  http://www.usb.org/developers/devclass_docs/usbccs10.pdf
72  *                   http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
73  */
74
75 /*
76  * TODO:
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.
80  *
81  */
82
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>
88 #include <sys/conf.h>
89 #include <sys/serial.h>
90 #include <sys/tty.h>
91 #include <sys/clist.h>
92 #include <sys/file.h>
93 #include <sys/select.h>
94 #include <sys/sysctl.h>
95 #include <sys/proc.h>
96 #include <sys/bus.h>
97 #include <sys/sbuf.h>
98 #include <sys/poll.h>
99 #include <sys/uio.h>
100 #include <sys/taskqueue.h>
101
102 #include <dev/usb/usb.h>
103 #include <dev/usb/usbcdc.h>
104
105 #include <dev/usb/usbdi.h>
106 #include <dev/usb/usbdi_util.h>
107 #include <dev/usb/usb_quirks.h>
108
109 #include <dev/usb/ucomvar.h>
110
111 #include "usbdevs.h"
112
113 typedef struct ufoma_mobile_acm_descriptor{
114         uByte bFunctionLength;
115         uByte bDescriptorType;
116         uByte bDescriptorSubtype;
117         uByte bType;
118         uByte bMode[1];
119 }usb_mcpc_acm_descriptor;
120
121 #define UISUBCLASS_MCPC 0x88
122
123 #define UDESC_VS_INTERFACE 0x44
124 #define UDESCSUB_MCPC_ACM  0x11
125
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
130
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
138
139 #define UMCPC_CM_MOBILE_ACM 0x0
140
141 #define UMCPC_ACTIVATE_MODE 0x60
142 #define UMCPC_GET_MODETABLE 0x61
143 #define UMCPC_SET_LINK 0x62
144 #define UMCPC_CLEAR_LINK 0x63
145
146 #define UMCPC_REQUEST_ACKNOLEDGE 0x31
147
148 #define UFOMA_MAX_TIMEOUT 15 /*Standard says 10(sec)*/
149 #define UFOMA_CMD_BUF_SIZE 64
150
151 #define UMODEMIBUFSIZE 64
152 #define UMODEMOBUFSIZE 256
153 #define DPRINTF(a)
154
155 struct ufoma_softc{
156         struct ucom_softc sc_ucom;
157         int sc_is_ucom;
158         int sc_isopen;
159
160         struct mtx sc_mtx;
161         int sc_ctl_iface_no;
162         usbd_interface_handle sc_ctl_iface;
163         usbd_interface_handle   sc_data_iface;
164         int sc_data_iface_no;
165         int sc_cm_cap;
166         int sc_acm_cap;
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 */
170
171         usbd_pipe_handle sc_notify_pipe;
172         usb_cdc_notification_t sc_notify_buf;
173         u_char sc_lsr;
174         u_char sc_msr;
175         
176         struct task sc_task;
177         uByte *sc_modetable;
178         uByte sc_modetoactivate;
179         uByte sc_currentmode;
180         char sc_resbuffer[UFOMA_CMD_BUF_SIZE+1];
181         int sc_cmdbp;
182         int sc_nummsg;
183         usbd_xfer_handle sc_msgxf;
184 };
185 static usbd_status
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 *);
195
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;
201
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);
212
213 /*sysctl stuff*/
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);
217
218
219 static struct ucom_callback ufoma_callback = {
220         ufoma_get_status,
221         ufoma_set,
222         ufoma_param,
223         NULL,
224         ufoma_ucom_open,
225         ufoma_ucom_close,
226         NULL,
227         NULL,
228 };
229
230
231 static device_method_t ufoma_methods[] = { 
232         /**/
233         DEVMETHOD(device_probe, ufoma_match),
234         DEVMETHOD(device_attach, ufoma_attach),
235         DEVMETHOD(device_detach, ufoma_detach),
236         {0, 0}
237 };
238 struct umcpc_modetostr_tab{
239         int mode;
240         char *str;
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"},
249         {0, NULL}
250 };
251
252 static driver_t ufoma_driver = {
253         "ucom",
254         ufoma_methods,
255         sizeof(struct ufoma_softc)
256 };
257
258
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);
262
263 static int
264 ufoma_match(device_t self)
265 {
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;
270         int ret;
271
272         ret = UMATCH_NONE;
273
274         if(uaa->iface == NULL)
275                 return(UMATCH_NONE);
276         id = usbd_get_interface_descriptor(uaa->iface);
277         cd = usbd_get_config_descriptor(uaa->device);
278
279         if(id == NULL || cd == NULL)
280                 return (UMATCH_NONE);
281         
282         if( id->bInterfaceClass == UICLASS_CDC &&
283             id->bInterfaceSubClass == UISUBCLASS_MCPC){
284                 ret = (UMATCH_IFACECLASS_IFACESUBCLASS);
285         }else{
286                 return UMATCH_NONE;
287         }
288
289         mad = ufoma_get_intconf(cd, id , UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
290         if(mad == NULL){
291                 return (UMATCH_NONE);
292         }
293
294 #if 0
295         if(mad->bType != UMCPC_ACM_TYPE_AB5){
296                 return UMATCH_NONE;
297         }
298 #endif
299         return ret;
300 }
301                         
302 static int
303 ufoma_attach(device_t self)
304 {
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;
314         int ctl_notify;
315         int i,err;
316         int elements;
317         uByte *mode;
318         struct sysctl_ctx_list *sctx;
319         struct sysctl_oid *soid;
320         
321         ucom->sc_dev = self;
322         ucom->sc_udev = dev;
323         sc->sc_ctl_iface = uaa->iface;
324         mtx_init(&sc->sc_mtx, "ufoma", NULL, MTX_DEF);  
325
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;
329         
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);
333
334         ctl_notify = -1;
335         for (i = 0; i < id->bNumEndpoints; i++) {
336                 ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i);
337                 if (ed == NULL)
338                         continue;
339
340                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
341                     (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
342                         ctl_notify = ed->bEndpointAddress;
343                 }
344         }
345
346         if(ctl_notify== -1){
347                 /*NOTIFY is mandatory.*/
348                 printf("NOTIFY interface not found\n");
349                 goto error;
350         }
351
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);
355         if(err){
356                 printf("PIPE open error %d\n", err);
357                 goto error;
358         }
359         mad = ufoma_get_intconf(cd, id , UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
360         if(mad ==NULL){
361                 goto error;
362         }
363
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);
368                 if(modename){
369                         printf("%s", ufoma_mode_to_str(*mode));
370                 }else{
371                         printf("(%x)", *mode);
372                 }
373                 if(mode != ((uByte*)mad + mad->bFunctionLength-1)){
374                         printf(",");
375                 }
376         }
377         printf("\n");
378
379         if((mad->bType == UMCPC_ACM_TYPE_AB5)
380            ||(mad->bType == UMCPC_ACM_TYPE_AB6)){
381                 /*These does not have data interface*/
382                 sc->sc_is_ucom = 0;
383                 ufoma_init_pseudo_ucom(sc);
384         }else{
385                 if(ufoma_init_modem(sc, uaa)){
386                         goto error;
387                 }
388         }
389         elements = mad->bFunctionLength - sizeof(*mad)+1;
390
391         sc->sc_msgxf = usbd_alloc_xfer(ucom->sc_udev);
392         sc->sc_nummsg = 0;
393
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];
400         /*Sysctls*/
401         sctx = device_get_sysctl_ctx(self);
402         soid = device_get_sysctl_tree(self);
403
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");
407
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");
411
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");
415         return 0;
416  error:
417         if(sc->sc_modetable)
418                 free(sc->sc_modetable, M_USBDEV);
419         return EIO;
420 }
421
422 static int
423 ufoma_detach(device_t self)
424 {
425         struct ufoma_softc *sc = device_get_softc(self);
426         int rv = 0;
427
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);
432         if(sc->sc_is_ucom)
433                 ucom_detach(&sc->sc_ucom);
434         else
435                 ttyfree(sc->sc_ucom.sc_tty);
436         free(sc->sc_modetable, M_USBDEV);
437         return rv;
438 }
439
440
441 static char *ufoma_mode_to_str(int mode)
442 {
443         int i;
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;
447                 }
448         }
449         return NULL;
450 }
451
452 static int ufoma_str_to_mode(char *str)
453 {
454         int i;
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;
458                 }
459         }
460         return -1;
461 }
462
463 static void *ufoma_get_intconf( usb_config_descriptor_t *cd,
464         usb_interface_descriptor_t *id, int type, int subtype)
465 {
466         uByte *p, *end;
467         usb_descriptor_t *ud=NULL;
468         int flag=0;
469
470                                              
471         for(p = (uByte *)cd,end = p + UGETW(cd->wTotalLength); p < end;
472             p += ud->bLength){
473                 ud = (usb_descriptor_t *)p;
474                 if(flag && ud->bDescriptorType==UDESC_INTERFACE){
475                         return NULL;
476                 }
477                 /*Read through this interface desc.*/
478                 if(bcmp(p, id, sizeof(*id))==0){
479                         flag=1;
480                         continue;
481                 }
482                 if(flag==0)
483                         continue;
484                 if(ud->bDescriptorType == type 
485                     && ud->bDescriptorSubtype == subtype){
486                         break;
487                 }
488         }
489         return ud;
490 }
491
492
493
494 static int ufoma_link_state(struct ufoma_softc *sc)
495 {
496         usb_device_request_t req;
497         struct ucom_softc *ucom = &sc->sc_ucom;
498         int err;
499
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);
506         if(err){
507                 printf("SET_LINK:%d\n", err);
508                 return EIO;
509         }
510         err = tsleep(&sc->sc_currentmode, PZERO|PCATCH, "fmalnk", hz);
511         if(err){
512                 printf("NO response");
513                 return EIO;
514         }
515         if(sc->sc_currentmode != UMCPC_ACM_MODE_DEACTIVATED){
516                 return EIO;
517         }
518         return 0;
519 }
520
521 static int ufoma_activate_state(struct ufoma_softc *sc, int state)
522 {
523         usb_device_request_t req;
524         int err;
525         struct ucom_softc *ucom = &sc->sc_ucom;
526
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);  
532         
533         err = usbd_do_request(ucom->sc_udev, &req, NULL);
534         if(err){
535                 printf("%s:ACTIVATE:%s\n", device_get_nameunit(ucom->sc_dev), usbd_errstr(err));
536                 return EIO;
537         }
538
539         err = tsleep(&sc->sc_currentmode, PZERO|PCATCH, "fmaact", UFOMA_MAX_TIMEOUT*hz);
540         if(err){
541                 printf("%s:NO response", device_get_nameunit(ucom->sc_dev));
542                 return EIO;
543         }
544         if(sc->sc_currentmode != state){
545                 return EIO;
546         }
547         return 0;
548 }
549
550
551 static inline void ufoma_setup_msg_req(struct ufoma_softc *sc, usb_device_request_t *req)
552 {
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);
558 }
559
560
561 static void ufoma_msg(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
562 {
563         usb_device_request_t req;
564         struct ufoma_softc *sc = priv;
565         int actlen,i;
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]);
573         }
574         sc->sc_nummsg--;
575         if(sc->sc_nummsg){
576                 usbd_setup_default_xfer(sc->sc_msgxf, ucom->sc_udev, 
577                     priv, USBD_DEFAULT_TIMEOUT, &req,
578                     sc->sc_resbuffer,
579                     UFOMA_CMD_BUF_SIZE,
580                     0, ufoma_msg);
581                 usbd_transfer(sc->sc_msgxf);
582         }
583         mtx_unlock(&sc->sc_mtx);
584
585 }
586
587 static void ufoma_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
588 {
589         struct ufoma_softc *sc = priv;
590         unsigned int a;
591         struct ucom_softc *ucom =&sc->sc_ucom;
592         usb_device_request_t req;
593         ufoma_setup_msg_req(sc, &req);
594         u_char mstatus;
595
596         if (sc->sc_ucom.sc_dying)
597                 return;
598
599         if (status != USBD_NORMAL_COMPLETION) {
600                 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
601                         return;
602                 printf("%s: abnormal status: %s\n", device_get_nameunit(ucom->sc_dev),
603                        usbd_errstr(status));
604                 return;
605         }
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;
610                 if(!(a&0xff)){
611                         printf("%s:Mode change Failed\n", device_get_nameunit(ucom->sc_dev));
612                 }
613                 wakeup(&sc->sc_currentmode);
614         }
615         if(sc->sc_notify_buf.bmRequestType != UCDC_NOTIFICATION){
616                 return;
617         }
618         switch(sc->sc_notify_buf.bNotification){
619         case UCDC_N_RESPONSE_AVAILABLE:
620                 if(sc->sc_is_ucom){
621                         printf("%s:wrong response request?\n", device_get_nameunit(ucom->sc_dev));
622                         break;
623                 }
624                 mtx_lock(&sc->sc_mtx);
625                 if(!sc->sc_nummsg){
626                         usbd_setup_default_xfer(sc->sc_msgxf, ucom->sc_udev, 
627                             priv, USBD_DEFAULT_TIMEOUT, &req, sc->sc_resbuffer,
628                             UFOMA_CMD_BUF_SIZE,
629                             0, ufoma_msg);
630                         usbd_transfer(sc->sc_msgxf);
631                 }
632                 sc->sc_nummsg++;
633                 mtx_unlock(&sc->sc_mtx);
634                 break;
635         case UCDC_N_SERIAL_STATE:
636                 if(!sc->sc_is_ucom){
637                         printf("%s:wrong sereal request?\n",device_get_nameunit(ucom->sc_dev));
638                         break;
639                 }
640
641                 /*
642                  * Set the serial state in ucom driver based on
643                  * the bits from the notify message
644                  */
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));
649                         break;
650                 }
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];
658
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);
667                 break;
668         default:
669                 break;
670         }
671 }
672
673 static int ufoma_init_pseudo_ucom(struct ufoma_softc *sc)
674 {
675         struct tty *tp;
676         struct ucom_softc *ucom = &sc->sc_ucom;
677         tp = ucom->sc_tty = ttyalloc();
678         tp->t_sc = sc;
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));
684
685         return 0;
686 }
687
688
689 static int ufomaopen(struct tty * tty, struct cdev *cdev)
690 {
691
692         struct ufoma_softc *sc = tty->t_sc;
693         
694         if(sc->sc_ucom.sc_dying)
695                 return (ENXIO);
696         
697         mtx_lock(&sc->sc_mtx);
698         if(sc->sc_isopen){
699                 mtx_unlock(&sc->sc_mtx);
700                 return EBUSY;
701         }
702         mtx_unlock(&sc->sc_mtx);
703
704         return  ufoma_ucom_open(sc, 0);
705 }
706
707 static void ufomaclose(struct tty *tty)
708 {
709         struct ufoma_softc *sc = tty->t_sc;
710         ufoma_ucom_close(sc, 0);
711 }
712
713 static void ufomastart(struct tty *tp)
714 {
715         struct ufoma_softc *sc = tp->t_sc;
716         struct ucom_softc *ucom = &sc->sc_ucom;
717         usb_device_request_t req;
718         int x;
719         uByte c;
720         x = spltty();
721         if(tp->t_state  & (TS_TIMEOUT | TS_TTSTOP)){
722                 ttwwakeup(tp);
723                 return;
724         }
725
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);
735         }
736         tp->t_state &= ~TS_BUSY;
737         ttwwakeup(tp);
738         splx(x);
739 }
740
741 static int ufoma_ucom_open(void *p, int portno)
742 {
743         struct ufoma_softc *sc = p;
744         if(sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED){
745                 ufoma_link_state(sc);
746         }
747         sc->sc_cmdbp = 0;
748         if(sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED){
749                 ufoma_activate_state(sc, sc->sc_modetoactivate);
750         }
751
752         return 0;
753 }
754
755 static void ufoma_ucom_close(void *p, int portno)
756 {
757         struct ufoma_softc *sc = p;
758         ufoma_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED);
759         return ;
760 }
761
762 void
763 ufoma_break(struct ufoma_softc *sc, int onoff)
764 {
765         usb_device_request_t req;
766         struct ucom_softc *ucom = &sc->sc_ucom;
767         DPRINTF(("ufoma_break: onoff=%d\n", onoff));
768
769         if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK))
770                 return;
771
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);
777
778         (void)usbd_do_request(ucom->sc_udev, &req, 0);
779 }
780
781 void
782 ufoma_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
783 {
784         struct ufoma_softc *sc = addr;
785
786         if (lsr != NULL)
787                 *lsr = sc->sc_lsr;
788         if (msr != NULL)
789                 *msr = sc->sc_msr;
790 }
791
792 static void ufoma_set(void * addr, int portno, int reg, int onoff)
793 {
794         struct ufoma_softc *sc = addr;
795
796         switch (reg) {
797         case UCOM_SET_DTR:
798                 ufoma_dtr(sc, onoff);
799                 break;
800         case UCOM_SET_RTS:
801                 ufoma_rts(sc, onoff);
802                 break;
803         case UCOM_SET_BREAK:
804                 ufoma_break(sc, onoff);
805                 break;
806         default:
807                 break;
808         }
809
810 }
811
812 static void
813 ufoma_set_line_state(struct ufoma_softc *sc)
814 {
815         usb_device_request_t req;
816         struct ucom_softc *ucom = &sc->sc_ucom;
817         int ls;
818
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);
826
827         (void)usbd_do_request(ucom->sc_udev, &req, 0);
828
829 }
830
831 void
832 ufoma_dtr(struct ufoma_softc *sc, int onoff)
833 {
834         DPRINTF(("ufoma_foma: onoff=%d\n", onoff));
835
836         if (sc->sc_dtr == onoff)
837                 return;
838         sc->sc_dtr = onoff;
839
840         ufoma_set_line_state(sc);
841 }
842
843 void
844 ufoma_rts(struct ufoma_softc *sc, int onoff)
845 {
846         DPRINTF(("ufoma_foma: onoff=%d\n", onoff));
847
848         if (sc->sc_rts == onoff)
849                 return;
850         sc->sc_rts = onoff;
851
852         ufoma_set_line_state(sc);
853 }
854
855 usbd_status
856 ufoma_set_line_coding(struct ufoma_softc *sc, usb_cdc_line_state_t *state)
857 {
858
859         usbd_status err;
860         usb_device_request_t req;
861         struct ucom_softc *ucom = &sc->sc_ucom;
862
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));
866
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);
870         }
871
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);
877
878         err = usbd_do_request(ucom->sc_udev, &req, state);
879         if (err) {
880                 DPRINTF(("ufoma_set_line_coding: failed, err=%s\n",
881                          usbd_errstr(err)));
882                 return (err);
883         }
884
885         sc->sc_line_state = *state;
886
887         return (USBD_NORMAL_COMPLETION);
888 }
889
890 static int
891 ufoma_param(void *addr, int portno, struct termios *t)
892 {
893
894         struct ufoma_softc *sc = addr;
895         usbd_status err;
896         usb_cdc_line_state_t ls;
897
898         DPRINTF(("ufoma_param: sc=%p\n", sc));
899
900         USETDW(ls.dwDTERate, t->c_ospeed);
901         if (ISSET(t->c_cflag, CSTOPB))
902                 ls.bCharFormat = UCDC_STOP_BIT_2;
903         else
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;
908                 else
909                         ls.bParityType = UCDC_PARITY_EVEN;
910         } else
911                 ls.bParityType = UCDC_PARITY_NONE;
912         switch (ISSET(t->c_cflag, CSIZE)) {
913         case CS5:
914                 ls.bDataBits = 5;
915                 break;
916         case CS6:
917                 ls.bDataBits = 6;
918                 break;
919         case CS7:
920                 ls.bDataBits = 7;
921                 break;
922         case CS8:
923                 ls.bDataBits = 8;
924                 break;
925         }
926
927         err = ufoma_set_line_coding(sc, &ls);
928         if (err) {
929                 DPRINTF(("ufoma_param: err=%s\n", usbd_errstr(err)));
930                 return (ENOTTY);
931         }
932
933         return (0);
934 }
935
936 static int ufoma_init_modem(struct ufoma_softc *sc,struct usb_attach_arg *uaa)
937 {
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);
945         int i;
946         cd = usbd_get_config_descriptor(ucom->sc_udev);
947         id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
948
949         sc->sc_is_ucom = 1;
950
951         cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
952         if(cmd == NULL)
953                 return -1;
954         sc->sc_cm_cap = cmd->bmCapabilities;
955
956         acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
957         if(acm == NULL)
958                 return -1;
959         sc->sc_acm_cap = acm->bmCapabilities;
960         
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 ");
966         
967         for(i = 0; i < uaa->nifaces; i++){
968                 if(!uaa->ifaces[i])
969                         continue;
970                 id = usbd_get_interface_descriptor(uaa->ifaces[i]);
971                 if(id != NULL &&
972                     id->bInterfaceNumber == sc->sc_data_iface_no){
973                         sc->sc_data_iface = uaa->ifaces[i];
974                         //uaa->ifaces[i] = NULL;
975                 }
976         }
977
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);
983                 if(ed == NULL){
984                         printf("%s: endpoint descriptor for %d\n",
985                             devname,i);
986                         return -1;
987                 }
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;
994                 }
995         }
996         
997         if (ucom->sc_bulkin_no == -1) {
998                 printf("%s: Could not find data bulk in\n", devname);
999                 return -1;
1000         }
1001         if (ucom->sc_bulkout_no == -1) {
1002                 printf("%s: Could not find data bulk out\n", devname);
1003                 return -1;
1004         }
1005         
1006         sc->sc_dtr = -1;
1007         
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);
1018         
1019         return 0;
1020 }
1021
1022 static void
1023 ufoma_notify(void *arg, int count)
1024 {
1025         struct ufoma_softc *sc;
1026
1027         sc = (struct ufoma_softc *)arg;
1028         if (sc->sc_ucom.sc_dying)
1029                 return;
1030         ucom_status_change(&sc->sc_ucom);
1031 }
1032 static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS)
1033 {
1034         struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
1035         struct sbuf sb;
1036         int i;
1037         char *mode;
1038
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]);
1042                 if(mode !=NULL){
1043                         sbuf_cat(&sb, mode);
1044                 }else{
1045                         sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]);
1046                 }
1047                 if(i < (sc->sc_modetable[0]-1))
1048                         sbuf_cat(&sb, ",");
1049         }
1050         sbuf_trim(&sb);
1051         sbuf_finish(&sb);
1052         sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
1053         sbuf_delete(&sb);
1054         
1055         return 0;
1056 }
1057 static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS)
1058 {
1059         struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
1060         char *mode;
1061         char subbuf[]="(XXX)";
1062         mode = ufoma_mode_to_str(sc->sc_currentmode);
1063         if(!mode){
1064                 mode = subbuf;
1065                 snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode);
1066         }
1067         sysctl_handle_string(oidp, mode, strlen(mode), req);
1068         
1069         return 0;
1070         
1071 }
1072 static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS)
1073 {
1074         struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
1075         char *mode;
1076         char subbuf[40];
1077         int newmode;
1078         int error;
1079         int i;
1080
1081         mode = ufoma_mode_to_str(sc->sc_modetoactivate);
1082         if(mode){
1083                 strncpy(subbuf, mode, sizeof(subbuf));
1084         }else{
1085                 snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate);
1086         }
1087         error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req);
1088         if(error != 0 || req->newptr == NULL){
1089                 return error;
1090         }
1091         
1092         if((newmode = ufoma_str_to_mode(subbuf)) == -1){
1093                 return EINVAL;
1094         }
1095         
1096         for(i = 1 ; i < sc->sc_modetable[0] ; i++){
1097                 if(sc->sc_modetable[i] == newmode){
1098                         sc->sc_modetoactivate = newmode;
1099                         return 0;
1100                 }
1101         }
1102         
1103         return EINVAL;
1104 }