FreeBSD/Linux Kernel Cross Reference
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: releng/7.3/sys/dev/usb/ufoma.c 171614 2007-07-27 12:00:29Z takawata $");
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 }
Cache object: 93f58a5bc60ff8870833616c3ea55095
|