xref: /freebsd/sys/dev/usb/serial/ufoma.c (revision b3aaa0cc21c63d388230c7ef2a80abd631ff20d5)
1 /*	$NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $	*/
2 
3 #include <sys/cdefs.h>
4 __FBSDID("$FreeBSD$");
5 #define UFOMA_HANDSFREE
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  * - Implement a Call Device for modems without multiplexed commands.
78  */
79 
80 /*
81  * NOTE: all function names beginning like "ufoma_cfg_" can only
82  * be called from within the config thread function !
83  */
84 
85 #include "usbdevs.h"
86 #include <dev/usb/usb.h>
87 #include <dev/usb/usb_mfunc.h>
88 #include <dev/usb/usb_error.h>
89 #include <dev/usb/usb_cdc.h>
90 
91 #define	USB_DEBUG_VAR usb2_debug
92 
93 #include <dev/usb/usb_core.h>
94 #include <dev/usb/usb_debug.h>
95 #include <dev/usb/usb_process.h>
96 #include <dev/usb/usb_request.h>
97 #include <dev/usb/usb_lookup.h>
98 #include <dev/usb/usb_util.h>
99 #include <dev/usb/usb_parse.h>
100 #include <dev/usb/usb_busdma.h>
101 
102 #include <dev/usb/serial/usb_serial.h>
103 #include <sys/sysctl.h>
104 #include <sys/sbuf.h>
105 
106 typedef struct ufoma_mobile_acm_descriptor {
107 	uint8_t	bFunctionLength;
108 	uint8_t	bDescriptorType;
109 	uint8_t	bDescriptorSubtype;
110 	uint8_t	bType;
111 	uint8_t	bMode[1];
112 } __packed usb2_mcpc_acm_descriptor;
113 
114 #define	UISUBCLASS_MCPC 0x88
115 
116 #define	UDESC_VS_INTERFACE 0x44
117 #define	UDESCSUB_MCPC_ACM  0x11
118 
119 #define	UMCPC_ACM_TYPE_AB1 0x1
120 #define	UMCPC_ACM_TYPE_AB2 0x2
121 #define	UMCPC_ACM_TYPE_AB5 0x5
122 #define	UMCPC_ACM_TYPE_AB6 0x6
123 
124 #define	UMCPC_ACM_MODE_DEACTIVATED 0x0
125 #define	UMCPC_ACM_MODE_MODEM 0x1
126 #define	UMCPC_ACM_MODE_ATCOMMAND 0x2
127 #define	UMCPC_ACM_MODE_OBEX 0x60
128 #define	UMCPC_ACM_MODE_VENDOR1 0xc0
129 #define	UMCPC_ACM_MODE_VENDOR2 0xfe
130 #define	UMCPC_ACM_MODE_UNLINKED 0xff
131 
132 #define	UMCPC_CM_MOBILE_ACM 0x0
133 
134 #define	UMCPC_ACTIVATE_MODE 0x60
135 #define	UMCPC_GET_MODETABLE 0x61
136 #define	UMCPC_SET_LINK 0x62
137 #define	UMCPC_CLEAR_LINK 0x63
138 
139 #define	UMCPC_REQUEST_ACKNOWLEDGE 0x31
140 
141 #define	UFOMA_MAX_TIMEOUT 15		/* standard says 10 seconds */
142 #define	UFOMA_CMD_BUF_SIZE 64		/* bytes */
143 
144 #define	UFOMA_BULK_BUF_SIZE 1024	/* bytes */
145 
146 enum {
147 	UFOMA_CTRL_ENDPT_INTR,
148 	UFOMA_CTRL_ENDPT_READ,
149 	UFOMA_CTRL_ENDPT_WRITE,
150 	UFOMA_CTRL_ENDPT_MAX,
151 };
152 
153 enum {
154 	UFOMA_BULK_ENDPT_WRITE,
155 	UFOMA_BULK_ENDPT_READ,
156 	UFOMA_BULK_ENDPT_MAX,
157 };
158 
159 struct ufoma_softc {
160 	struct usb2_com_super_softc sc_super_ucom;
161 	struct usb2_com_softc sc_ucom;
162 	struct cv sc_cv;
163 
164 	struct usb2_xfer *sc_ctrl_xfer[UFOMA_CTRL_ENDPT_MAX];
165 	struct usb2_xfer *sc_bulk_xfer[UFOMA_BULK_ENDPT_MAX];
166 	uint8_t *sc_modetable;
167 	device_t sc_dev;
168 	struct usb2_device *sc_udev;
169 
170 	uint32_t sc_unit;
171 
172 	uint16_t sc_line;
173 
174 	uint8_t	sc_num_msg;
175 	uint8_t	sc_nobulk;
176 	uint8_t	sc_ctrl_iface_no;
177 	uint8_t	sc_ctrl_iface_index;
178 	uint8_t	sc_data_iface_no;
179 	uint8_t	sc_data_iface_index;
180 	uint8_t	sc_cm_cap;
181 	uint8_t	sc_acm_cap;
182 	uint8_t	sc_lsr;
183 	uint8_t	sc_msr;
184 	uint8_t	sc_modetoactivate;
185 	uint8_t	sc_currentmode;
186 	uint8_t	sc_name[16];
187 };
188 
189 /* prototypes */
190 
191 static device_probe_t ufoma_probe;
192 static device_attach_t ufoma_attach;
193 static device_detach_t ufoma_detach;
194 
195 static usb2_callback_t ufoma_ctrl_read_callback;
196 static usb2_callback_t ufoma_ctrl_write_callback;
197 static usb2_callback_t ufoma_intr_callback;
198 static usb2_callback_t ufoma_bulk_write_callback;
199 static usb2_callback_t ufoma_bulk_read_callback;
200 
201 static void	*ufoma_get_intconf(struct usb2_config_descriptor *,
202 		    struct usb2_interface_descriptor *, uint8_t, uint8_t);
203 static void	ufoma_cfg_link_state(struct ufoma_softc *);
204 static void	ufoma_cfg_activate_state(struct ufoma_softc *, uint16_t);
205 static void	ufoma_cfg_open(struct usb2_com_softc *);
206 static void	ufoma_cfg_close(struct usb2_com_softc *);
207 static void	ufoma_cfg_set_break(struct usb2_com_softc *, uint8_t);
208 static void	ufoma_cfg_get_status(struct usb2_com_softc *, uint8_t *,
209 		    uint8_t *);
210 static void	ufoma_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
211 static void	ufoma_cfg_set_rts(struct usb2_com_softc *, uint8_t);
212 static int	ufoma_pre_param(struct usb2_com_softc *, struct termios *);
213 static void	ufoma_cfg_param(struct usb2_com_softc *, struct termios *);
214 static int	ufoma_modem_setup(device_t, struct ufoma_softc *,
215 		    struct usb2_attach_arg *);
216 static void	ufoma_start_read(struct usb2_com_softc *);
217 static void	ufoma_stop_read(struct usb2_com_softc *);
218 static void	ufoma_start_write(struct usb2_com_softc *);
219 static void	ufoma_stop_write(struct usb2_com_softc *);
220 
221 /*sysctl stuff*/
222 static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS);
223 static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS);
224 static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS);
225 
226 
227 static const struct usb2_config
228 	ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = {
229 
230 	[UFOMA_CTRL_ENDPT_INTR] = {
231 		.type = UE_INTERRUPT,
232 		.endpoint = UE_ADDR_ANY,
233 		.direction = UE_DIR_IN,
234 		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
235 		.mh.bufsize = sizeof(struct usb2_cdc_notification),
236 		.mh.callback = &ufoma_intr_callback,
237 	},
238 
239 	[UFOMA_CTRL_ENDPT_READ] = {
240 		.type = UE_CONTROL,
241 		.endpoint = 0x00,	/* Control pipe */
242 		.direction = UE_DIR_ANY,
243 		.mh.bufsize = (sizeof(struct usb2_device_request) + UFOMA_CMD_BUF_SIZE),
244 		.mh.flags = {.short_xfer_ok = 1,},
245 		.mh.callback = &ufoma_ctrl_read_callback,
246 		.mh.timeout = 1000,	/* 1 second */
247 	},
248 
249 	[UFOMA_CTRL_ENDPT_WRITE] = {
250 		.type = UE_CONTROL,
251 		.endpoint = 0x00,	/* Control pipe */
252 		.direction = UE_DIR_ANY,
253 		.mh.bufsize = (sizeof(struct usb2_device_request) + 1),
254 		.mh.flags = {},
255 		.mh.callback = &ufoma_ctrl_write_callback,
256 		.mh.timeout = 1000,	/* 1 second */
257 	},
258 };
259 
260 static const struct usb2_config
261 	ufoma_bulk_config[UFOMA_BULK_ENDPT_MAX] = {
262 
263 	[UFOMA_BULK_ENDPT_WRITE] = {
264 		.type = UE_BULK,
265 		.endpoint = UE_ADDR_ANY,
266 		.direction = UE_DIR_OUT,
267 		.mh.bufsize = UFOMA_BULK_BUF_SIZE,
268 		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
269 		.mh.callback = &ufoma_bulk_write_callback,
270 	},
271 
272 	[UFOMA_BULK_ENDPT_READ] = {
273 		.type = UE_BULK,
274 		.endpoint = UE_ADDR_ANY,
275 		.direction = UE_DIR_IN,
276 		.mh.bufsize = UFOMA_BULK_BUF_SIZE,
277 		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
278 		.mh.callback = &ufoma_bulk_read_callback,
279 	},
280 };
281 
282 static const struct usb2_com_callback ufoma_callback = {
283 	.usb2_com_cfg_get_status = &ufoma_cfg_get_status,
284 	.usb2_com_cfg_set_dtr = &ufoma_cfg_set_dtr,
285 	.usb2_com_cfg_set_rts = &ufoma_cfg_set_rts,
286 	.usb2_com_cfg_set_break = &ufoma_cfg_set_break,
287 	.usb2_com_cfg_param = &ufoma_cfg_param,
288 	.usb2_com_cfg_open = &ufoma_cfg_open,
289 	.usb2_com_cfg_close = &ufoma_cfg_close,
290 	.usb2_com_pre_param = &ufoma_pre_param,
291 	.usb2_com_start_read = &ufoma_start_read,
292 	.usb2_com_stop_read = &ufoma_stop_read,
293 	.usb2_com_start_write = &ufoma_start_write,
294 	.usb2_com_stop_write = &ufoma_stop_write,
295 };
296 
297 static device_method_t ufoma_methods[] = {
298 	/* Device methods */
299 	DEVMETHOD(device_probe, ufoma_probe),
300 	DEVMETHOD(device_attach, ufoma_attach),
301 	DEVMETHOD(device_detach, ufoma_detach),
302 	{0, 0}
303 };
304 
305 static devclass_t ufoma_devclass;
306 
307 static driver_t ufoma_driver = {
308 	.name = "ufoma",
309 	.methods = ufoma_methods,
310 	.size = sizeof(struct ufoma_softc),
311 };
312 
313 DRIVER_MODULE(ufoma, ushub, ufoma_driver, ufoma_devclass, NULL, 0);
314 MODULE_DEPEND(ufoma, ucom, 1, 1, 1);
315 MODULE_DEPEND(ufoma, usb, 1, 1, 1);
316 
317 static int
318 ufoma_probe(device_t dev)
319 {
320 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
321 	struct usb2_interface_descriptor *id;
322 	struct usb2_config_descriptor *cd;
323 	usb2_mcpc_acm_descriptor *mad;
324 
325 	if (uaa->usb2_mode != USB_MODE_HOST) {
326 		return (ENXIO);
327 	}
328 	id = usb2_get_interface_descriptor(uaa->iface);
329 	cd = usb2_get_config_descriptor(uaa->device);
330 
331 	if ((id == NULL) ||
332 	    (cd == NULL) ||
333 	    (id->bInterfaceClass != UICLASS_CDC) ||
334 	    (id->bInterfaceSubClass != UISUBCLASS_MCPC)) {
335 		return (ENXIO);
336 	}
337 	mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
338 	if (mad == NULL) {
339 		return (ENXIO);
340 	}
341 #ifndef UFOMA_HANDSFREE
342 	if ((mad->bType == UMCPC_ACM_TYPE_AB5) ||
343 	    (mad->bType == UMCPC_ACM_TYPE_AB6)) {
344 		return (ENXIO);
345 	}
346 #endif
347 	return (0);
348 }
349 
350 static int
351 ufoma_attach(device_t dev)
352 {
353 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
354 	struct ufoma_softc *sc = device_get_softc(dev);
355 	struct usb2_config_descriptor *cd;
356 	struct usb2_interface_descriptor *id;
357 	struct sysctl_ctx_list *sctx;
358 	struct sysctl_oid *soid;
359 
360 	usb2_mcpc_acm_descriptor *mad;
361 	uint8_t elements;
362 	int32_t error;
363 
364 	sc->sc_udev = uaa->device;
365 	sc->sc_dev = dev;
366 	sc->sc_unit = device_get_unit(dev);
367 
368 	usb2_cv_init(&sc->sc_cv, "CWAIT");
369 
370 	device_set_usb2_desc(dev);
371 
372 	snprintf(sc->sc_name, sizeof(sc->sc_name),
373 	    "%s", device_get_nameunit(dev));
374 
375 	DPRINTF("\n");
376 
377 	/* setup control transfers */
378 
379 	cd = usb2_get_config_descriptor(uaa->device);
380 	id = usb2_get_interface_descriptor(uaa->iface);
381 	sc->sc_ctrl_iface_no = id->bInterfaceNumber;
382 	sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex;
383 
384 	error = usb2_transfer_setup(uaa->device,
385 	    &sc->sc_ctrl_iface_index, sc->sc_ctrl_xfer,
386 	    ufoma_ctrl_config, UFOMA_CTRL_ENDPT_MAX, sc, &Giant);
387 
388 	if (error) {
389 		device_printf(dev, "allocating control USB "
390 		    "transfers failed!\n");
391 		goto detach;
392 	}
393 	mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
394 	if (mad == NULL) {
395 		goto detach;
396 	}
397 	if (mad->bFunctionLength < sizeof(*mad)) {
398 		device_printf(dev, "invalid MAD descriptor\n");
399 		goto detach;
400 	}
401 	if ((mad->bType == UMCPC_ACM_TYPE_AB5) ||
402 	    (mad->bType == UMCPC_ACM_TYPE_AB6)) {
403 		sc->sc_nobulk = 1;
404 	} else {
405 		sc->sc_nobulk = 0;
406 		if (ufoma_modem_setup(dev, sc, uaa)) {
407 			goto detach;
408 		}
409 	}
410 
411 	elements = (mad->bFunctionLength - sizeof(*mad) + 1);
412 
413 	/* initialize mode variables */
414 
415 	sc->sc_modetable = malloc(elements + 1, M_USBDEV, M_WAITOK);
416 
417 	if (sc->sc_modetable == NULL) {
418 		goto detach;
419 	}
420 	sc->sc_modetable[0] = (elements + 1);
421 	bcopy(mad->bMode, &sc->sc_modetable[1], elements);
422 
423 	sc->sc_currentmode = UMCPC_ACM_MODE_UNLINKED;
424 	sc->sc_modetoactivate = mad->bMode[0];
425 
426 	/* clear stall at first run, if any */
427 	usb2_transfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
428 	usb2_transfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
429 
430 	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
431 	    &ufoma_callback, &Giant);
432 	if (error) {
433 		DPRINTF("usb2_com_attach failed\n");
434 		goto detach;
435 	}
436 	/*Sysctls*/
437 	sctx = device_get_sysctl_ctx(dev);
438 	soid = device_get_sysctl_tree(dev);
439 
440 	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode",
441 			CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_support,
442 			"A", "Supporting port role");
443 
444 	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode",
445 			CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_current,
446 			"A", "Current port role");
447 
448 	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode",
449 			CTLFLAG_RW|CTLTYPE_STRING, sc, 0, ufoma_sysctl_open,
450 			"A", "Mode to transit when port is opened");
451 	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "comunit",
452 			CTLFLAG_RD, &(sc->sc_ucom.sc_unit), 0,
453 			"Unit number as USB serial");
454 
455 	return (0);			/* success */
456 
457 detach:
458 	ufoma_detach(dev);
459 	return (ENXIO);			/* failure */
460 }
461 
462 static int
463 ufoma_detach(device_t dev)
464 {
465 	struct ufoma_softc *sc = device_get_softc(dev);
466 
467 	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
468 
469 	usb2_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX);
470 
471 	usb2_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX);
472 
473 	if (sc->sc_modetable) {
474 		free(sc->sc_modetable, M_USBDEV);
475 	}
476 	usb2_cv_destroy(&sc->sc_cv);
477 
478 	return (0);
479 }
480 
481 static void *
482 ufoma_get_intconf(struct usb2_config_descriptor *cd, struct usb2_interface_descriptor *id,
483     uint8_t type, uint8_t subtype)
484 {
485 	struct usb2_descriptor *desc = (void *)id;
486 
487 	while ((desc = usb2_desc_foreach(cd, desc))) {
488 
489 		if (desc->bDescriptorType == UDESC_INTERFACE) {
490 			return (NULL);
491 		}
492 		if ((desc->bDescriptorType == type) &&
493 		    (desc->bDescriptorSubtype == subtype)) {
494 			break;
495 		}
496 	}
497 	return (desc);
498 }
499 
500 static void
501 ufoma_cfg_link_state(struct ufoma_softc *sc)
502 {
503 	struct usb2_device_request req;
504 	int32_t error;
505 
506 	req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
507 	req.bRequest = UMCPC_SET_LINK;
508 	USETW(req.wValue, UMCPC_CM_MOBILE_ACM);
509 	USETW(req.wIndex, sc->sc_ctrl_iface_no);
510 	USETW(req.wLength, sc->sc_modetable[0]);
511 
512 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
513 	    &req, sc->sc_modetable, 0, 1000);
514 
515 	error = usb2_cv_timedwait(&sc->sc_cv, &Giant, hz);
516 
517 	if (error) {
518 		DPRINTF("NO response\n");
519 	}
520 }
521 
522 static void
523 ufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state)
524 {
525 	struct usb2_device_request req;
526 	int32_t error;
527 
528 	req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
529 	req.bRequest = UMCPC_ACTIVATE_MODE;
530 	USETW(req.wValue, state);
531 	USETW(req.wIndex, sc->sc_ctrl_iface_no);
532 	USETW(req.wLength, 0);
533 
534 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
535 	    &req, NULL, 0, 1000);
536 
537 	error = usb2_cv_timedwait(&sc->sc_cv, &Giant,
538 	    (UFOMA_MAX_TIMEOUT * hz));
539 	if (error) {
540 		DPRINTF("No response\n");
541 	}
542 }
543 
544 static void
545 ufoma_ctrl_read_callback(struct usb2_xfer *xfer)
546 {
547 	struct ufoma_softc *sc = xfer->priv_sc;
548 	struct usb2_device_request req;
549 
550 	switch (USB_GET_STATE(xfer)) {
551 	case USB_ST_TRANSFERRED:
552 tr_transferred:
553 		if (xfer->aframes != xfer->nframes) {
554 			goto tr_setup;
555 		}
556 		if (xfer->frlengths[1] > 0) {
557 			usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers + 1,
558 			    0, xfer->frlengths[1]);
559 		}
560 	case USB_ST_SETUP:
561 tr_setup:
562 		if (sc->sc_num_msg) {
563 			sc->sc_num_msg--;
564 
565 			req.bmRequestType = UT_READ_CLASS_INTERFACE;
566 			req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
567 			USETW(req.wIndex, sc->sc_ctrl_iface_no);
568 			USETW(req.wValue, 0);
569 			USETW(req.wLength, UFOMA_CMD_BUF_SIZE);
570 
571 			usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
572 
573 			xfer->frlengths[0] = sizeof(req);
574 			xfer->frlengths[1] = UFOMA_CMD_BUF_SIZE;
575 			xfer->nframes = 2;
576 			usb2_start_hardware(xfer);
577 		}
578 		return;
579 
580 	default:			/* Error */
581 		DPRINTF("error = %s\n",
582 		    usb2_errstr(xfer->error));
583 
584 		if (xfer->error == USB_ERR_CANCELLED) {
585 			return;
586 		} else {
587 			goto tr_setup;
588 		}
589 
590 		goto tr_transferred;
591 	}
592 }
593 
594 static void
595 ufoma_ctrl_write_callback(struct usb2_xfer *xfer)
596 {
597 	struct ufoma_softc *sc = xfer->priv_sc;
598 	struct usb2_device_request req;
599 	uint32_t actlen;
600 
601 	switch (USB_GET_STATE(xfer)) {
602 	case USB_ST_TRANSFERRED:
603 tr_transferred:
604 	case USB_ST_SETUP:
605 tr_setup:
606 		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1,
607 		    0, 1, &actlen)) {
608 
609 			req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
610 			req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND;
611 			USETW(req.wIndex, sc->sc_ctrl_iface_no);
612 			USETW(req.wValue, 0);
613 			USETW(req.wLength, 1);
614 
615 			usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
616 
617 			xfer->frlengths[0] = sizeof(req);
618 			xfer->frlengths[1] = 1;
619 			xfer->nframes = 2;
620 
621 			usb2_start_hardware(xfer);
622 		}
623 		return;
624 
625 	default:			/* Error */
626 		DPRINTF("error = %s\n",
627 		    usb2_errstr(xfer->error));
628 
629 		if (xfer->error == USB_ERR_CANCELLED) {
630 			return;
631 		} else {
632 			goto tr_setup;
633 		}
634 
635 		goto tr_transferred;
636 	}
637 }
638 
639 static void
640 ufoma_intr_callback(struct usb2_xfer *xfer)
641 {
642 	struct ufoma_softc *sc = xfer->priv_sc;
643 	struct usb2_cdc_notification pkt;
644 	uint16_t wLen;
645 	uint16_t temp;
646 	uint8_t mstatus;
647 
648 	switch (USB_GET_STATE(xfer)) {
649 	case USB_ST_TRANSFERRED:
650 		if (xfer->actlen < 8) {
651 			DPRINTF("too short message\n");
652 			goto tr_setup;
653 		}
654 		if (xfer->actlen > sizeof(pkt)) {
655 			DPRINTF("truncating message\n");
656 			xfer->actlen = sizeof(pkt);
657 		}
658 		usb2_copy_out(xfer->frbuffers, 0, &pkt, xfer->actlen);
659 
660 		xfer->actlen -= 8;
661 
662 		wLen = UGETW(pkt.wLength);
663 		if (xfer->actlen > wLen) {
664 			xfer->actlen = wLen;
665 		}
666 		if ((pkt.bmRequestType == UT_READ_VENDOR_INTERFACE) &&
667 		    (pkt.bNotification == UMCPC_REQUEST_ACKNOWLEDGE)) {
668 			temp = UGETW(pkt.wValue);
669 			sc->sc_currentmode = (temp >> 8);
670 			if (!(temp & 0xff)) {
671 				DPRINTF("Mode change failed!\n");
672 			}
673 			usb2_cv_signal(&sc->sc_cv);
674 		}
675 		if (pkt.bmRequestType != UCDC_NOTIFICATION) {
676 			goto tr_setup;
677 		}
678 		switch (pkt.bNotification) {
679 		case UCDC_N_RESPONSE_AVAILABLE:
680 			if (!(sc->sc_nobulk)) {
681 				DPRINTF("Wrong serial state!\n");
682 				break;
683 			}
684 			if (sc->sc_num_msg != 0xFF) {
685 				sc->sc_num_msg++;
686 			}
687 			usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
688 			break;
689 
690 		case UCDC_N_SERIAL_STATE:
691 			if (sc->sc_nobulk) {
692 				DPRINTF("Wrong serial state!\n");
693 				break;
694 			}
695 			/*
696 		         * Set the serial state in ucom driver based on
697 		         * the bits from the notify message
698 		         */
699 			if (xfer->actlen < 2) {
700 				DPRINTF("invalid notification "
701 				    "length, %d bytes!\n", xfer->actlen);
702 				break;
703 			}
704 			DPRINTF("notify bytes = 0x%02x, 0x%02x\n",
705 			    pkt.data[0], pkt.data[1]);
706 
707 			/* currently, lsr is always zero. */
708 			sc->sc_lsr = 0;
709 			sc->sc_msr = 0;
710 
711 			mstatus = pkt.data[0];
712 
713 			if (mstatus & UCDC_N_SERIAL_RI) {
714 				sc->sc_msr |= SER_RI;
715 			}
716 			if (mstatus & UCDC_N_SERIAL_DSR) {
717 				sc->sc_msr |= SER_DSR;
718 			}
719 			if (mstatus & UCDC_N_SERIAL_DCD) {
720 				sc->sc_msr |= SER_DCD;
721 			}
722 			usb2_com_status_change(&sc->sc_ucom);
723 			break;
724 
725 		default:
726 			break;
727 		}
728 
729 	case USB_ST_SETUP:
730 tr_setup:
731 		xfer->frlengths[0] = xfer->max_data_length;
732 		usb2_start_hardware(xfer);
733 		return;
734 
735 	default:			/* Error */
736 		if (xfer->error != USB_ERR_CANCELLED) {
737 			/* try to clear stall first */
738 			xfer->flags.stall_pipe = 1;
739 			goto tr_setup;
740 		}
741 		return;
742 	}
743 }
744 
745 static void
746 ufoma_bulk_write_callback(struct usb2_xfer *xfer)
747 {
748 	struct ufoma_softc *sc = xfer->priv_sc;
749 	uint32_t actlen;
750 
751 	switch (USB_GET_STATE(xfer)) {
752 	case USB_ST_SETUP:
753 	case USB_ST_TRANSFERRED:
754 tr_setup:
755 		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
756 		    UFOMA_BULK_BUF_SIZE, &actlen)) {
757 			xfer->frlengths[0] = actlen;
758 			usb2_start_hardware(xfer);
759 		}
760 		return;
761 
762 	default:			/* Error */
763 		if (xfer->error != USB_ERR_CANCELLED) {
764 			/* try to clear stall first */
765 			xfer->flags.stall_pipe = 1;
766 			goto tr_setup;
767 		}
768 		return;
769 	}
770 }
771 
772 static void
773 ufoma_bulk_read_callback(struct usb2_xfer *xfer)
774 {
775 	struct ufoma_softc *sc = xfer->priv_sc;
776 
777 	switch (USB_GET_STATE(xfer)) {
778 	case USB_ST_TRANSFERRED:
779 		usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
780 		    xfer->actlen);
781 
782 	case USB_ST_SETUP:
783 tr_setup:
784 		xfer->frlengths[0] = xfer->max_data_length;
785 		usb2_start_hardware(xfer);
786 		return;
787 
788 	default:			/* Error */
789 		if (xfer->error != USB_ERR_CANCELLED) {
790 			/* try to clear stall first */
791 			xfer->flags.stall_pipe = 1;
792 			goto tr_setup;
793 		}
794 		return;
795 	}
796 }
797 
798 static void
799 ufoma_cfg_open(struct usb2_com_softc *ucom)
800 {
801 	struct ufoma_softc *sc = ucom->sc_parent;
802 
803 	/* empty input queue */
804 
805 	if (sc->sc_num_msg != 0xFF) {
806 		sc->sc_num_msg++;
807 	}
808 	if (sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED) {
809 		ufoma_cfg_link_state(sc);
810 	}
811 	if (sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED) {
812 		ufoma_cfg_activate_state(sc, sc->sc_modetoactivate);
813 	}
814 }
815 
816 static void
817 ufoma_cfg_close(struct usb2_com_softc *ucom)
818 {
819 	struct ufoma_softc *sc = ucom->sc_parent;
820 
821 	ufoma_cfg_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED);
822 }
823 
824 static void
825 ufoma_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
826 {
827 	struct ufoma_softc *sc = ucom->sc_parent;
828 	struct usb2_device_request req;
829 	uint16_t wValue;
830 
831 	if (sc->sc_nobulk ||
832 	    (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) {
833 		return;
834 	}
835 	if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) {
836 		return;
837 	}
838 	wValue = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
839 
840 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
841 	req.bRequest = UCDC_SEND_BREAK;
842 	USETW(req.wValue, wValue);
843 	req.wIndex[0] = sc->sc_ctrl_iface_no;
844 	req.wIndex[1] = 0;
845 	USETW(req.wLength, 0);
846 
847 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
848 	    &req, NULL, 0, 1000);
849 }
850 
851 static void
852 ufoma_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
853 {
854 	struct ufoma_softc *sc = ucom->sc_parent;
855 
856 	*lsr = sc->sc_lsr;
857 	*msr = sc->sc_msr;
858 }
859 
860 static void
861 ufoma_cfg_set_line_state(struct ufoma_softc *sc)
862 {
863 	struct usb2_device_request req;
864 
865 	/* Don't send line state emulation request for OBEX port */
866 	if (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX) {
867 		return;
868 	}
869 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
870 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
871 	USETW(req.wValue, sc->sc_line);
872 	req.wIndex[0] = sc->sc_ctrl_iface_no;
873 	req.wIndex[1] = 0;
874 	USETW(req.wLength, 0);
875 
876 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
877 	    &req, NULL, 0, 1000);
878 }
879 
880 static void
881 ufoma_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
882 {
883 	struct ufoma_softc *sc = ucom->sc_parent;
884 
885 	if (sc->sc_nobulk) {
886 		return;
887 	}
888 	if (onoff)
889 		sc->sc_line |= UCDC_LINE_DTR;
890 	else
891 		sc->sc_line &= ~UCDC_LINE_DTR;
892 
893 	ufoma_cfg_set_line_state(sc);
894 }
895 
896 static void
897 ufoma_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
898 {
899 	struct ufoma_softc *sc = ucom->sc_parent;
900 
901 	if (sc->sc_nobulk) {
902 		return;
903 	}
904 	if (onoff)
905 		sc->sc_line |= UCDC_LINE_RTS;
906 	else
907 		sc->sc_line &= ~UCDC_LINE_RTS;
908 
909 	ufoma_cfg_set_line_state(sc);
910 }
911 
912 static int
913 ufoma_pre_param(struct usb2_com_softc *ucom, struct termios *t)
914 {
915 	return (0);			/* we accept anything */
916 }
917 
918 static void
919 ufoma_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
920 {
921 	struct ufoma_softc *sc = ucom->sc_parent;
922 	struct usb2_device_request req;
923 	struct usb2_cdc_line_state ls;
924 
925 	if (sc->sc_nobulk ||
926 	    (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) {
927 		return;
928 	}
929 	DPRINTF("\n");
930 
931 	bzero(&ls, sizeof(ls));
932 
933 	USETDW(ls.dwDTERate, t->c_ospeed);
934 
935 	if (t->c_cflag & CSTOPB) {
936 		ls.bCharFormat = UCDC_STOP_BIT_2;
937 	} else {
938 		ls.bCharFormat = UCDC_STOP_BIT_1;
939 	}
940 
941 	if (t->c_cflag & PARENB) {
942 		if (t->c_cflag & PARODD) {
943 			ls.bParityType = UCDC_PARITY_ODD;
944 		} else {
945 			ls.bParityType = UCDC_PARITY_EVEN;
946 		}
947 	} else {
948 		ls.bParityType = UCDC_PARITY_NONE;
949 	}
950 
951 	switch (t->c_cflag & CSIZE) {
952 	case CS5:
953 		ls.bDataBits = 5;
954 		break;
955 	case CS6:
956 		ls.bDataBits = 6;
957 		break;
958 	case CS7:
959 		ls.bDataBits = 7;
960 		break;
961 	case CS8:
962 		ls.bDataBits = 8;
963 		break;
964 	}
965 
966 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
967 	req.bRequest = UCDC_SET_LINE_CODING;
968 	USETW(req.wValue, 0);
969 	req.wIndex[0] = sc->sc_ctrl_iface_no;
970 	req.wIndex[1] = 0;
971 	USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
972 
973 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
974 	    &req, &ls, 0, 1000);
975 }
976 
977 static int
978 ufoma_modem_setup(device_t dev, struct ufoma_softc *sc,
979     struct usb2_attach_arg *uaa)
980 {
981 	struct usb2_config_descriptor *cd;
982 	struct usb2_cdc_acm_descriptor *acm;
983 	struct usb2_cdc_cm_descriptor *cmd;
984 	struct usb2_interface_descriptor *id;
985 	struct usb2_interface *iface;
986 	uint8_t i;
987 	int32_t error;
988 
989 	cd = usb2_get_config_descriptor(uaa->device);
990 	id = usb2_get_interface_descriptor(uaa->iface);
991 
992 	cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
993 
994 	if ((cmd == NULL) ||
995 	    (cmd->bLength < sizeof(*cmd))) {
996 		return (EINVAL);
997 	}
998 	sc->sc_cm_cap = cmd->bmCapabilities;
999 	sc->sc_data_iface_no = cmd->bDataInterface;
1000 
1001 	acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
1002 
1003 	if ((acm == NULL) ||
1004 	    (acm->bLength < sizeof(*acm))) {
1005 		return (EINVAL);
1006 	}
1007 	sc->sc_acm_cap = acm->bmCapabilities;
1008 
1009 	device_printf(dev, "data interface %d, has %sCM over data, "
1010 	    "has %sbreak\n",
1011 	    sc->sc_data_iface_no,
1012 	    sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
1013 	    sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
1014 
1015 	/* get the data interface too */
1016 
1017 	for (i = 0;; i++) {
1018 
1019 		iface = usb2_get_iface(uaa->device, i);
1020 
1021 		if (iface) {
1022 
1023 			id = usb2_get_interface_descriptor(iface);
1024 
1025 			if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
1026 				sc->sc_data_iface_index = i;
1027 				usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
1028 				break;
1029 			}
1030 		} else {
1031 			device_printf(dev, "no data interface!\n");
1032 			return (EINVAL);
1033 		}
1034 	}
1035 
1036 	error = usb2_transfer_setup(uaa->device,
1037 	    &sc->sc_data_iface_index, sc->sc_bulk_xfer,
1038 	    ufoma_bulk_config, UFOMA_BULK_ENDPT_MAX, sc, &Giant);
1039 
1040 	if (error) {
1041 		device_printf(dev, "allocating BULK USB "
1042 		    "transfers failed!\n");
1043 		return (EINVAL);
1044 	}
1045 	return (0);
1046 }
1047 
1048 static void
1049 ufoma_start_read(struct usb2_com_softc *ucom)
1050 {
1051 	struct ufoma_softc *sc = ucom->sc_parent;
1052 
1053 	/* start interrupt transfer */
1054 	usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]);
1055 
1056 	/* start data transfer */
1057 	if (sc->sc_nobulk) {
1058 		usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
1059 	} else {
1060 		usb2_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
1061 	}
1062 }
1063 
1064 static void
1065 ufoma_stop_read(struct usb2_com_softc *ucom)
1066 {
1067 	struct ufoma_softc *sc = ucom->sc_parent;
1068 
1069 	/* stop interrupt transfer */
1070 	usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]);
1071 
1072 	/* stop data transfer */
1073 	if (sc->sc_nobulk) {
1074 		usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
1075 	} else {
1076 		usb2_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
1077 	}
1078 }
1079 
1080 static void
1081 ufoma_start_write(struct usb2_com_softc *ucom)
1082 {
1083 	struct ufoma_softc *sc = ucom->sc_parent;
1084 
1085 	if (sc->sc_nobulk) {
1086 		usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]);
1087 	} else {
1088 		usb2_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
1089 	}
1090 }
1091 
1092 static void
1093 ufoma_stop_write(struct usb2_com_softc *ucom)
1094 {
1095 	struct ufoma_softc *sc = ucom->sc_parent;
1096 
1097 	if (sc->sc_nobulk) {
1098 		usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]);
1099 	} else {
1100 		usb2_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
1101 	}
1102 }
1103 
1104 struct umcpc_modetostr_tab{
1105 	int mode;
1106 	char *str;
1107 }umcpc_modetostr_tab[]={
1108 	{UMCPC_ACM_MODE_DEACTIVATED, "deactivated"},
1109 	{UMCPC_ACM_MODE_MODEM, "modem"},
1110 	{UMCPC_ACM_MODE_ATCOMMAND, "handsfree"},
1111 	{UMCPC_ACM_MODE_OBEX, "obex"},
1112 	{UMCPC_ACM_MODE_VENDOR1, "vendor1"},
1113 	{UMCPC_ACM_MODE_VENDOR2, "vendor2"},
1114 	{UMCPC_ACM_MODE_UNLINKED, "unlinked"},
1115 	{0, NULL}
1116 };
1117 
1118 static char *ufoma_mode_to_str(int mode)
1119 {
1120 	int i;
1121 	for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){
1122 		if(umcpc_modetostr_tab[i].mode == mode){
1123 			return umcpc_modetostr_tab[i].str;
1124 		}
1125 	}
1126 	return NULL;
1127 }
1128 
1129 static int ufoma_str_to_mode(char *str)
1130 {
1131 	int i;
1132 	for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){
1133 		if(strcmp(str, umcpc_modetostr_tab[i].str)==0){
1134 			return umcpc_modetostr_tab[i].mode;
1135 		}
1136 	}
1137 	return -1;
1138 }
1139 
1140 static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS)
1141 {
1142 	struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
1143 	struct sbuf sb;
1144 	int i;
1145 	char *mode;
1146 
1147 	sbuf_new(&sb, NULL, 1, SBUF_AUTOEXTEND);
1148 	for(i = 1; i < sc->sc_modetable[0]; i++){
1149 		mode = ufoma_mode_to_str(sc->sc_modetable[i]);
1150 		if(mode !=NULL){
1151 			sbuf_cat(&sb, mode);
1152 		}else{
1153 			sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]);
1154 		}
1155 		if(i < (sc->sc_modetable[0]-1))
1156 			sbuf_cat(&sb, ",");
1157 	}
1158 	sbuf_trim(&sb);
1159 	sbuf_finish(&sb);
1160 	sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
1161 	sbuf_delete(&sb);
1162 
1163 	return 0;
1164 }
1165 static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS)
1166 {
1167 	struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
1168 	char *mode;
1169 	char subbuf[]="(XXX)";
1170 	mode = ufoma_mode_to_str(sc->sc_currentmode);
1171 	if(!mode){
1172 		mode = subbuf;
1173 		snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode);
1174 	}
1175 	sysctl_handle_string(oidp, mode, strlen(mode), req);
1176 
1177 	return 0;
1178 
1179 }
1180 static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS)
1181 {
1182 	struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
1183 	char *mode;
1184 	char subbuf[40];
1185 	int newmode;
1186 	int error;
1187 	int i;
1188 
1189 	mode = ufoma_mode_to_str(sc->sc_modetoactivate);
1190 	if(mode){
1191 		strncpy(subbuf, mode, sizeof(subbuf));
1192 	}else{
1193 		snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate);
1194 	}
1195 	error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req);
1196 	if(error != 0 || req->newptr == NULL){
1197 		return error;
1198 	}
1199 
1200 	if((newmode = ufoma_str_to_mode(subbuf)) == -1){
1201 		return EINVAL;
1202 	}
1203 
1204 	for(i = 1 ; i < sc->sc_modetable[0] ; i++){
1205 		if(sc->sc_modetable[i] == newmode){
1206 			sc->sc_modetoactivate = newmode;
1207 			return 0;
1208 		}
1209 	}
1210 
1211 	return EINVAL;
1212 }
1213