1 | /* $NetBSD: umodem_common.c,v 1.24 2016/07/07 06:55:42 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1998 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Lennart Augustsson (lennart@augustsson.net) at |
9 | * Carlstedt Research & Technology. |
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf |
35 | * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf |
36 | */ |
37 | |
38 | /* |
39 | * TODO: |
40 | * - Add error recovery in various places; the big problem is what |
41 | * to do in a callback if there is an error. |
42 | * - Implement a Call Device for modems without multiplexed commands. |
43 | * |
44 | */ |
45 | |
46 | #include <sys/cdefs.h> |
47 | __KERNEL_RCSID(0, "$NetBSD: umodem_common.c,v 1.24 2016/07/07 06:55:42 msaitoh Exp $" ); |
48 | |
49 | #include <sys/param.h> |
50 | #include <sys/systm.h> |
51 | #include <sys/kernel.h> |
52 | #include <sys/ioctl.h> |
53 | #include <sys/conf.h> |
54 | #include <sys/tty.h> |
55 | #include <sys/file.h> |
56 | #include <sys/select.h> |
57 | #include <sys/proc.h> |
58 | #include <sys/vnode.h> |
59 | #include <sys/device.h> |
60 | #include <sys/poll.h> |
61 | |
62 | #include <dev/usb/usb.h> |
63 | #include <dev/usb/usbcdc.h> |
64 | |
65 | #include <dev/usb/usbdi.h> |
66 | #include <dev/usb/usbdi_util.h> |
67 | #include <dev/usb/usbdevs.h> |
68 | #include <dev/usb/usb_quirks.h> |
69 | |
70 | #include <dev/usb/ucomvar.h> |
71 | #include <dev/usb/umodemvar.h> |
72 | |
73 | #ifdef UMODEM_DEBUG |
74 | #define DPRINTFN(n, x) if (umodemdebug > (n)) printf x |
75 | int umodemdebug = 0; |
76 | #else |
77 | #define DPRINTFN(n, x) |
78 | #endif |
79 | #define DPRINTF(x) DPRINTFN(0, x) |
80 | |
81 | /* |
82 | * These are the maximum number of bytes transferred per frame. |
83 | * If some really high speed devices should use this driver they |
84 | * may need to be increased, but this is good enough for normal modems. |
85 | * |
86 | * Note: increased from 64/256, to better support EVDO wireless PPP. |
87 | * The sizes should not be increased further, or there |
88 | * will be problems with contiguous storage allocation. |
89 | */ |
90 | #define UMODEMIBUFSIZE 4096 |
91 | #define UMODEMOBUFSIZE 4096 |
92 | |
93 | Static usbd_status umodem_set_comm_feature(struct umodem_softc *, |
94 | int, int); |
95 | Static usbd_status umodem_set_line_coding(struct umodem_softc *, |
96 | usb_cdc_line_state_t *); |
97 | |
98 | Static void umodem_dtr(struct umodem_softc *, int); |
99 | Static void umodem_rts(struct umodem_softc *, int); |
100 | Static void umodem_break(struct umodem_softc *, int); |
101 | Static void umodem_set_line_state(struct umodem_softc *); |
102 | Static void umodem_intr(struct usbd_xfer *, void *, usbd_status); |
103 | |
104 | int |
105 | umodem_common_attach(device_t self, struct umodem_softc *sc, |
106 | struct usbif_attach_arg *uiaa, struct ucom_attach_args *ucaa) |
107 | { |
108 | struct usbd_device *dev = uiaa->uiaa_device; |
109 | usb_interface_descriptor_t *id; |
110 | usb_endpoint_descriptor_t *ed; |
111 | char *devinfop; |
112 | usbd_status err; |
113 | int data_ifcno; |
114 | int i; |
115 | |
116 | sc->sc_dev = self; |
117 | sc->sc_udev = dev; |
118 | sc->sc_ctl_iface = uiaa->uiaa_iface; |
119 | |
120 | aprint_naive("\n" ); |
121 | aprint_normal("\n" ); |
122 | |
123 | id = usbd_get_interface_descriptor(sc->sc_ctl_iface); |
124 | devinfop = usbd_devinfo_alloc(uiaa->uiaa_device, 0); |
125 | aprint_normal_dev(self, "%s, iclass %d/%d\n" , |
126 | devinfop, id->bInterfaceClass, id->bInterfaceSubClass); |
127 | usbd_devinfo_free(devinfop); |
128 | |
129 | sc->sc_ctl_iface_no = id->bInterfaceNumber; |
130 | |
131 | /* Get the data interface no. */ |
132 | sc->sc_data_iface_no = data_ifcno = |
133 | umodem_get_caps(dev, &sc->sc_cm_cap, &sc->sc_acm_cap, id); |
134 | |
135 | if (data_ifcno == -1) { |
136 | aprint_error_dev(self, "no pointer to data interface\n" ); |
137 | goto bad; |
138 | } |
139 | |
140 | aprint_normal_dev(self, |
141 | "data interface %d, has %sCM over data, has %sbreak\n" , |
142 | data_ifcno, sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no " , |
143 | sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no " ); |
144 | |
145 | /* Get the data interface too. */ |
146 | for (i = 0; i < uiaa->uiaa_nifaces; i++) { |
147 | if (uiaa->uiaa_ifaces[i] != NULL) { |
148 | id = usbd_get_interface_descriptor(uiaa->uiaa_ifaces[i]); |
149 | if (id != NULL && id->bInterfaceNumber == data_ifcno) { |
150 | sc->sc_data_iface = uiaa->uiaa_ifaces[i]; |
151 | uiaa->uiaa_ifaces[i] = NULL; |
152 | } |
153 | } |
154 | } |
155 | if (sc->sc_data_iface == NULL) { |
156 | aprint_error_dev(self, "no data interface\n" ); |
157 | goto bad; |
158 | } |
159 | |
160 | /* |
161 | * Find the bulk endpoints. |
162 | * Iterate over all endpoints in the data interface and take note. |
163 | */ |
164 | ucaa->ucaa_bulkin = ucaa->ucaa_bulkout = -1; |
165 | |
166 | id = usbd_get_interface_descriptor(sc->sc_data_iface); |
167 | for (i = 0; i < id->bNumEndpoints; i++) { |
168 | ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i); |
169 | if (ed == NULL) { |
170 | aprint_error_dev(self, |
171 | "no endpoint descriptor for %d\n)" , i); |
172 | goto bad; |
173 | } |
174 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
175 | (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { |
176 | ucaa->ucaa_bulkin = ed->bEndpointAddress; |
177 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && |
178 | (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { |
179 | ucaa->ucaa_bulkout = ed->bEndpointAddress; |
180 | } |
181 | } |
182 | |
183 | if (ucaa->ucaa_bulkin == -1) { |
184 | aprint_error_dev(self, "Could not find data bulk in\n" ); |
185 | goto bad; |
186 | } |
187 | if (ucaa->ucaa_bulkout == -1) { |
188 | aprint_error_dev(self, "Could not find data bulk out\n" ); |
189 | goto bad; |
190 | } |
191 | |
192 | if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_ASSUME_CM_OVER_DATA) { |
193 | sc->sc_cm_over_data = 1; |
194 | } else { |
195 | if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) { |
196 | if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) |
197 | err = umodem_set_comm_feature(sc, |
198 | UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED); |
199 | else |
200 | err = 0; |
201 | if (err) { |
202 | aprint_error_dev(self, |
203 | "could not set data multiplex mode\n" ); |
204 | goto bad; |
205 | } |
206 | sc->sc_cm_over_data = 1; |
207 | } |
208 | } |
209 | |
210 | /* |
211 | * The standard allows for notification messages (to indicate things |
212 | * like a modem hangup) to come in via an interrupt endpoint |
213 | * off of the control interface. Iterate over the endpoints on |
214 | * the control interface and see if there are any interrupt |
215 | * endpoints; if there are, then register it. |
216 | */ |
217 | |
218 | sc->sc_ctl_notify = -1; |
219 | sc->sc_notify_pipe = NULL; |
220 | |
221 | id = usbd_get_interface_descriptor(sc->sc_ctl_iface); |
222 | for (i = 0; i < id->bNumEndpoints; i++) { |
223 | ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i); |
224 | if (ed == NULL) |
225 | continue; |
226 | |
227 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
228 | (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) { |
229 | aprint_error_dev(self, |
230 | "status change notification available\n" ); |
231 | sc->sc_ctl_notify = ed->bEndpointAddress; |
232 | } |
233 | } |
234 | |
235 | sc->sc_dtr = -1; |
236 | |
237 | /* ucaa_bulkin, ucaa_bulkout set above */ |
238 | ucaa->ucaa_ibufsize = UMODEMIBUFSIZE; |
239 | ucaa->ucaa_obufsize = UMODEMOBUFSIZE; |
240 | ucaa->ucaa_ibufsizepad = UMODEMIBUFSIZE; |
241 | ucaa->ucaa_opkthdrlen = 0; |
242 | ucaa->ucaa_device = sc->sc_udev; |
243 | ucaa->ucaa_iface = sc->sc_data_iface; |
244 | ucaa->ucaa_arg = sc; |
245 | |
246 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); |
247 | |
248 | DPRINTF(("umodem_common_attach: sc=%p\n" , sc)); |
249 | sc->sc_subdev = config_found_sm_loc(self, "ucombus" , NULL, ucaa, |
250 | ucomprint, ucomsubmatch); |
251 | |
252 | return 0; |
253 | |
254 | bad: |
255 | sc->sc_dying = 1; |
256 | return 1; |
257 | } |
258 | |
259 | int |
260 | umodem_open(void *addr, int portno) |
261 | { |
262 | struct umodem_softc *sc = addr; |
263 | int err; |
264 | |
265 | DPRINTF(("umodem_open: sc=%p\n" , sc)); |
266 | |
267 | if (sc->sc_ctl_notify != -1 && sc->sc_notify_pipe == NULL) { |
268 | err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_ctl_notify, |
269 | USBD_SHORT_XFER_OK, &sc->sc_notify_pipe, sc, |
270 | &sc->sc_notify_buf, sizeof(sc->sc_notify_buf), |
271 | umodem_intr, USBD_DEFAULT_INTERVAL); |
272 | |
273 | if (err) { |
274 | DPRINTF(("Failed to establish notify pipe: %s\n" , |
275 | usbd_errstr(err))); |
276 | return EIO; |
277 | } |
278 | } |
279 | |
280 | return 0; |
281 | } |
282 | |
283 | void |
284 | umodem_close(void *addr, int portno) |
285 | { |
286 | struct umodem_softc *sc = addr; |
287 | int err; |
288 | |
289 | DPRINTF(("umodem_close: sc=%p\n" , sc)); |
290 | |
291 | if (sc->sc_notify_pipe != NULL) { |
292 | err = usbd_abort_pipe(sc->sc_notify_pipe); |
293 | if (err) |
294 | printf("%s: abort notify pipe failed: %s\n" , |
295 | device_xname(sc->sc_dev), usbd_errstr(err)); |
296 | err = usbd_close_pipe(sc->sc_notify_pipe); |
297 | if (err) |
298 | printf("%s: close notify pipe failed: %s\n" , |
299 | device_xname(sc->sc_dev), usbd_errstr(err)); |
300 | sc->sc_notify_pipe = NULL; |
301 | } |
302 | } |
303 | |
304 | Static void |
305 | umodem_intr(struct usbd_xfer *xfer, void *priv, |
306 | usbd_status status) |
307 | { |
308 | struct umodem_softc *sc = priv; |
309 | u_char mstatus; |
310 | |
311 | if (sc->sc_dying) |
312 | return; |
313 | |
314 | if (status != USBD_NORMAL_COMPLETION) { |
315 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) |
316 | return; |
317 | printf("%s: abnormal status: %s\n" , device_xname(sc->sc_dev), |
318 | usbd_errstr(status)); |
319 | if (status == USBD_STALLED) |
320 | usbd_clear_endpoint_stall_async(sc->sc_notify_pipe); |
321 | return; |
322 | } |
323 | |
324 | if (sc->sc_notify_buf.bmRequestType != UCDC_NOTIFICATION) { |
325 | DPRINTF(("%s: unknown message type (%02x) on notify pipe\n" , |
326 | device_xname(sc->sc_dev), |
327 | sc->sc_notify_buf.bmRequestType)); |
328 | return; |
329 | } |
330 | |
331 | switch (sc->sc_notify_buf.bNotification) { |
332 | case UCDC_N_SERIAL_STATE: |
333 | /* |
334 | * Set the serial state in ucom driver based on |
335 | * the bits from the notify message |
336 | */ |
337 | if (UGETW(sc->sc_notify_buf.wLength) != 2) { |
338 | printf("%s: Invalid notification length! (%d)\n" , |
339 | device_xname(sc->sc_dev), |
340 | UGETW(sc->sc_notify_buf.wLength)); |
341 | break; |
342 | } |
343 | DPRINTF(("%s: notify bytes = %02x%02x\n" , |
344 | device_xname(sc->sc_dev), |
345 | sc->sc_notify_buf.data[0], |
346 | sc->sc_notify_buf.data[1])); |
347 | /* Currently, lsr is always zero. */ |
348 | sc->sc_lsr = sc->sc_msr = 0; |
349 | mstatus = sc->sc_notify_buf.data[0]; |
350 | |
351 | if (ISSET(mstatus, UCDC_N_SERIAL_RI)) |
352 | sc->sc_msr |= UMSR_RI; |
353 | if (ISSET(mstatus, UCDC_N_SERIAL_DSR)) |
354 | sc->sc_msr |= UMSR_DSR; |
355 | if (ISSET(mstatus, UCDC_N_SERIAL_DCD)) |
356 | sc->sc_msr |= UMSR_DCD; |
357 | ucom_status_change(device_private(sc->sc_subdev)); |
358 | break; |
359 | default: |
360 | DPRINTF(("%s: unknown notify message: %02x\n" , |
361 | device_xname(sc->sc_dev), |
362 | sc->sc_notify_buf.bNotification)); |
363 | break; |
364 | } |
365 | } |
366 | |
367 | int |
368 | umodem_get_caps(struct usbd_device *dev, int *cm, int *acm, |
369 | usb_interface_descriptor_t *id) |
370 | { |
371 | const usb_cdc_cm_descriptor_t *cmd; |
372 | const usb_cdc_acm_descriptor_t *cad; |
373 | const usb_cdc_union_descriptor_t *cud; |
374 | uint32_t uq_flags; |
375 | |
376 | *cm = *acm = 0; |
377 | uq_flags = usbd_get_quirks(dev)->uq_flags; |
378 | |
379 | if (uq_flags & UQ_NO_UNION_NRM) { |
380 | DPRINTF(("umodem_get_caps: NO_UNION_NRM quirk - returning 0\n" )); |
381 | return 0; |
382 | } |
383 | |
384 | if (uq_flags & UQ_LOST_CS_DESC) |
385 | id = NULL; |
386 | |
387 | cmd = (const usb_cdc_cm_descriptor_t *)usb_find_desc_if(dev, |
388 | UDESC_CS_INTERFACE, |
389 | UDESCSUB_CDC_CM, id); |
390 | if (cmd == NULL) { |
391 | DPRINTF(("umodem_get_caps: no CM desc\n" )); |
392 | } else { |
393 | *cm = cmd->bmCapabilities; |
394 | } |
395 | |
396 | cad = (const usb_cdc_acm_descriptor_t *)usb_find_desc_if(dev, |
397 | UDESC_CS_INTERFACE, |
398 | UDESCSUB_CDC_ACM, |
399 | id); |
400 | if (cad == NULL) { |
401 | DPRINTF(("umodem_get_caps: no ACM desc\n" )); |
402 | } else { |
403 | *acm = cad->bmCapabilities; |
404 | } |
405 | |
406 | cud = (const usb_cdc_union_descriptor_t *)usb_find_desc_if(dev, |
407 | UDESC_CS_INTERFACE, |
408 | UDESCSUB_CDC_UNION, |
409 | id); |
410 | if (cud == NULL) { |
411 | DPRINTF(("umodem_get_caps: no UNION desc\n" )); |
412 | } |
413 | |
414 | return cmd ? cmd->bDataInterface : cud ? cud->bSlaveInterface[0] : -1; |
415 | } |
416 | |
417 | void |
418 | umodem_get_status(void *addr, int portno, u_char *lsr, u_char *msr) |
419 | { |
420 | struct umodem_softc *sc = addr; |
421 | |
422 | DPRINTF(("umodem_get_status:\n" )); |
423 | |
424 | if (lsr != NULL) |
425 | *lsr = sc->sc_lsr; |
426 | if (msr != NULL) |
427 | *msr = sc->sc_msr; |
428 | } |
429 | |
430 | int |
431 | umodem_param(void *addr, int portno, struct termios *t) |
432 | { |
433 | struct umodem_softc *sc = addr; |
434 | usbd_status err; |
435 | usb_cdc_line_state_t ls; |
436 | |
437 | DPRINTF(("umodem_param: sc=%p\n" , sc)); |
438 | |
439 | USETDW(ls.dwDTERate, t->c_ospeed); |
440 | if (ISSET(t->c_cflag, CSTOPB)) |
441 | ls.bCharFormat = UCDC_STOP_BIT_2; |
442 | else |
443 | ls.bCharFormat = UCDC_STOP_BIT_1; |
444 | if (ISSET(t->c_cflag, PARENB)) { |
445 | if (ISSET(t->c_cflag, PARODD)) |
446 | ls.bParityType = UCDC_PARITY_ODD; |
447 | else |
448 | ls.bParityType = UCDC_PARITY_EVEN; |
449 | } else |
450 | ls.bParityType = UCDC_PARITY_NONE; |
451 | switch (ISSET(t->c_cflag, CSIZE)) { |
452 | case CS5: |
453 | ls.bDataBits = 5; |
454 | break; |
455 | case CS6: |
456 | ls.bDataBits = 6; |
457 | break; |
458 | case CS7: |
459 | ls.bDataBits = 7; |
460 | break; |
461 | case CS8: |
462 | ls.bDataBits = 8; |
463 | break; |
464 | } |
465 | |
466 | err = umodem_set_line_coding(sc, &ls); |
467 | if (err) { |
468 | DPRINTF(("umodem_param: err=%s\n" , usbd_errstr(err))); |
469 | return EPASSTHROUGH; |
470 | } |
471 | return 0; |
472 | } |
473 | |
474 | int |
475 | umodem_ioctl(void *addr, int portno, u_long cmd, void *data, |
476 | int flag, proc_t *p) |
477 | { |
478 | struct umodem_softc *sc = addr; |
479 | int error = 0; |
480 | |
481 | if (sc->sc_dying) |
482 | return EIO; |
483 | |
484 | DPRINTF(("umodem_ioctl: cmd=0x%08lx\n" , cmd)); |
485 | |
486 | switch (cmd) { |
487 | case USB_GET_CM_OVER_DATA: |
488 | *(int *)data = sc->sc_cm_over_data; |
489 | break; |
490 | |
491 | case USB_SET_CM_OVER_DATA: |
492 | if (*(int *)data != sc->sc_cm_over_data) { |
493 | /* XXX change it */ |
494 | } |
495 | break; |
496 | |
497 | default: |
498 | DPRINTF(("umodem_ioctl: unknown\n" )); |
499 | error = EPASSTHROUGH; |
500 | break; |
501 | } |
502 | |
503 | return error; |
504 | } |
505 | |
506 | void |
507 | umodem_dtr(struct umodem_softc *sc, int onoff) |
508 | { |
509 | DPRINTF(("umodem_dtr: onoff=%d\n" , onoff)); |
510 | |
511 | if (sc->sc_dtr == onoff) |
512 | return; |
513 | sc->sc_dtr = onoff; |
514 | |
515 | umodem_set_line_state(sc); |
516 | } |
517 | |
518 | void |
519 | umodem_rts(struct umodem_softc *sc, int onoff) |
520 | { |
521 | DPRINTF(("umodem_rts: onoff=%d\n" , onoff)); |
522 | |
523 | if (sc->sc_rts == onoff) |
524 | return; |
525 | sc->sc_rts = onoff; |
526 | |
527 | umodem_set_line_state(sc); |
528 | } |
529 | |
530 | void |
531 | umodem_set_line_state(struct umodem_softc *sc) |
532 | { |
533 | usb_device_request_t req; |
534 | int ls; |
535 | |
536 | ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) | |
537 | (sc->sc_rts ? UCDC_LINE_RTS : 0); |
538 | req.bmRequestType = UT_WRITE_CLASS_INTERFACE; |
539 | req.bRequest = UCDC_SET_CONTROL_LINE_STATE; |
540 | USETW(req.wValue, ls); |
541 | USETW(req.wIndex, sc->sc_ctl_iface_no); |
542 | USETW(req.wLength, 0); |
543 | |
544 | (void)usbd_do_request(sc->sc_udev, &req, 0); |
545 | |
546 | } |
547 | |
548 | void |
549 | umodem_break(struct umodem_softc *sc, int onoff) |
550 | { |
551 | usb_device_request_t req; |
552 | |
553 | DPRINTF(("umodem_break: onoff=%d\n" , onoff)); |
554 | |
555 | if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) |
556 | return; |
557 | |
558 | req.bmRequestType = UT_WRITE_CLASS_INTERFACE; |
559 | req.bRequest = UCDC_SEND_BREAK; |
560 | USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF); |
561 | USETW(req.wIndex, sc->sc_ctl_iface_no); |
562 | USETW(req.wLength, 0); |
563 | |
564 | (void)usbd_do_request(sc->sc_udev, &req, 0); |
565 | } |
566 | |
567 | void |
568 | umodem_set(void *addr, int portno, int reg, int onoff) |
569 | { |
570 | struct umodem_softc *sc = addr; |
571 | |
572 | switch (reg) { |
573 | case UCOM_SET_DTR: |
574 | umodem_dtr(sc, onoff); |
575 | break; |
576 | case UCOM_SET_RTS: |
577 | umodem_rts(sc, onoff); |
578 | break; |
579 | case UCOM_SET_BREAK: |
580 | umodem_break(sc, onoff); |
581 | break; |
582 | default: |
583 | break; |
584 | } |
585 | } |
586 | |
587 | usbd_status |
588 | umodem_set_line_coding(struct umodem_softc *sc, usb_cdc_line_state_t *state) |
589 | { |
590 | usb_device_request_t req; |
591 | usbd_status err; |
592 | |
593 | DPRINTF(("umodem_set_line_coding: rate=%d fmt=%d parity=%d bits=%d\n" , |
594 | UGETDW(state->dwDTERate), state->bCharFormat, |
595 | state->bParityType, state->bDataBits)); |
596 | |
597 | if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) { |
598 | DPRINTF(("umodem_set_line_coding: already set\n" )); |
599 | return USBD_NORMAL_COMPLETION; |
600 | } |
601 | |
602 | req.bmRequestType = UT_WRITE_CLASS_INTERFACE; |
603 | req.bRequest = UCDC_SET_LINE_CODING; |
604 | USETW(req.wValue, 0); |
605 | USETW(req.wIndex, sc->sc_ctl_iface_no); |
606 | USETW(req.wLength, UCDC_LINE_STATE_LENGTH); |
607 | |
608 | err = usbd_do_request(sc->sc_udev, &req, state); |
609 | if (err) { |
610 | DPRINTF(("umodem_set_line_coding: failed, err=%s\n" , |
611 | usbd_errstr(err))); |
612 | return err; |
613 | } |
614 | |
615 | sc->sc_line_state = *state; |
616 | |
617 | return USBD_NORMAL_COMPLETION; |
618 | } |
619 | |
620 | usbd_status |
621 | umodem_set_comm_feature(struct umodem_softc *sc, int feature, int state) |
622 | { |
623 | usb_device_request_t req; |
624 | usbd_status err; |
625 | usb_cdc_abstract_state_t ast; |
626 | |
627 | DPRINTF(("umodem_set_comm_feature: feature=%d state=%d\n" , feature, |
628 | state)); |
629 | |
630 | req.bmRequestType = UT_WRITE_CLASS_INTERFACE; |
631 | req.bRequest = UCDC_SET_COMM_FEATURE; |
632 | USETW(req.wValue, feature); |
633 | USETW(req.wIndex, sc->sc_ctl_iface_no); |
634 | USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH); |
635 | USETW(ast.wState, state); |
636 | |
637 | err = usbd_do_request(sc->sc_udev, &req, &ast); |
638 | if (err) { |
639 | DPRINTF(("umodem_set_comm_feature: feature=%d, err=%s\n" , |
640 | feature, usbd_errstr(err))); |
641 | return err; |
642 | } |
643 | |
644 | return USBD_NORMAL_COMPLETION; |
645 | } |
646 | |
647 | int |
648 | umodem_common_activate(struct umodem_softc *sc, enum devact act) |
649 | { |
650 | switch (act) { |
651 | case DVACT_DEACTIVATE: |
652 | sc->sc_dying = 1; |
653 | return 0; |
654 | default: |
655 | return EOPNOTSUPP; |
656 | } |
657 | } |
658 | |
659 | void |
660 | umodem_common_childdet(struct umodem_softc *sc, device_t child) |
661 | { |
662 | KASSERT(sc->sc_subdev == child); |
663 | sc->sc_subdev = NULL; |
664 | } |
665 | |
666 | int |
667 | umodem_common_detach(struct umodem_softc *sc, int flags) |
668 | { |
669 | int rv = 0; |
670 | |
671 | DPRINTF(("umodem_common_detach: sc=%p flags=%d\n" , sc, flags)); |
672 | |
673 | sc->sc_dying = 1; |
674 | |
675 | if (sc->sc_subdev != NULL) |
676 | rv = config_detach(sc->sc_subdev, flags); |
677 | |
678 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); |
679 | |
680 | return rv; |
681 | } |
682 | |