File: | dev/usb/uticom.c |
Warning: | line 318, column 2 Value stored to 'dd' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: uticom.c,v 1.34 2020/07/31 10:49:33 mglocker Exp $ */ |
2 | /* |
3 | * Copyright (c) 2005 Dmitry Komissaroff <dxi@mail.ru>. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. |
13 | * |
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
24 | * SUCH DAMAGE. |
25 | */ |
26 | |
27 | #include <sys/param.h> |
28 | #include <sys/systm.h> |
29 | #include <sys/kernel.h> |
30 | #include <sys/device.h> |
31 | #include <sys/malloc.h> |
32 | #include <sys/conf.h> |
33 | #include <sys/tty.h> |
34 | |
35 | #include <machine/bus.h> |
36 | |
37 | #include <dev/usb/usb.h> |
38 | #include <dev/usb/usbdi.h> |
39 | #include <dev/usb/usbdivar.h> |
40 | #include <dev/usb/usbdi_util.h> |
41 | #include <dev/usb/usbdevs.h> |
42 | #include <dev/usb/usbcdc.h> |
43 | |
44 | #include <dev/usb/ucomvar.h> |
45 | |
46 | #ifdef UTICOM_DEBUG |
47 | static int uticomdebug = 0; |
48 | #define DPRINTFN(n, x) do { if (uticomdebug > (n)) printf x; } while (0) |
49 | #else |
50 | #define DPRINTFN(n, x) |
51 | #endif |
52 | |
53 | #define DPRINTF(x) DPRINTFN(0, x) |
54 | |
55 | #define UTICOM_CONFIG_INDEX1 1 |
56 | #define UTICOM_ACTIVE_INDEX2 2 |
57 | |
58 | #define UTICOM_IFACE_INDEX0 0 |
59 | |
60 | /* |
61 | * These are the maximum number of bytes transferred per frame. |
62 | * The output buffer size cannot be increased due to the size encoding. |
63 | */ |
64 | #define UTICOM_IBUFSZ64 64 |
65 | #define UTICOM_OBUFSZ64 64 |
66 | |
67 | #define UTICOM_FW_BUFSZ16284 16284 |
68 | |
69 | #define UTICOM_INTR_INTERVAL100 100 /* ms */ |
70 | |
71 | #define UTICOM_RQ_LINE0 0 |
72 | /* Used to sync data0/1-toggle on reopen bulk pipe. */ |
73 | #define UTICOM_RQ_SOF1 1 |
74 | #define UTICOM_RQ_SON2 2 |
75 | |
76 | #define UTICOM_RQ_BAUD3 3 |
77 | #define UTICOM_RQ_LCR4 4 |
78 | #define UTICOM_RQ_FCR5 5 |
79 | #define UTICOM_RQ_RTS6 6 |
80 | #define UTICOM_RQ_DTR7 7 |
81 | #define UTICOM_RQ_BREAK8 8 |
82 | #define UTICOM_RQ_CRTSCTS9 9 |
83 | |
84 | #define UTICOM_BRATE_REF923077 923077 |
85 | |
86 | #define UTICOM_SET_DATA_BITS(x)(x - 5) (x - 5) |
87 | |
88 | #define UTICOM_STOP_BITS_10x00 0x00 |
89 | #define UTICOM_STOP_BITS_20x40 0x40 |
90 | |
91 | #define UTICOM_PARITY_NONE0x00 0x00 |
92 | #define UTICOM_PARITY_ODD0x08 0x08 |
93 | #define UTICOM_PARITY_EVEN0x18 0x18 |
94 | |
95 | #define UTICOM_LCR_OVR0x1 0x1 |
96 | #define UTICOM_LCR_PTE0x2 0x2 |
97 | #define UTICOM_LCR_FRE0x4 0x4 |
98 | #define UTICOM_LCR_BRK0x8 0x8 |
99 | |
100 | #define UTICOM_MCR_CTS0x1 0x1 |
101 | #define UTICOM_MCR_DSR0x2 0x2 |
102 | #define UTICOM_MCR_CD0x4 0x4 |
103 | #define UTICOM_MCR_RI0x8 0x8 |
104 | |
105 | /* Structures */ |
106 | struct uticom_fw_header { |
107 | uint16_t length; |
108 | uint8_t checkSum; |
109 | } __packed__attribute__((__packed__)); |
110 | |
111 | struct uticom_buf { |
112 | unsigned int buf_size; |
113 | char *buf_buf; |
114 | char *buf_get; |
115 | char *buf_put; |
116 | }; |
117 | |
118 | struct uticom_softc { |
119 | struct device sc_dev; /* base device */ |
120 | struct usbd_device *sc_udev; /* device */ |
121 | struct usbd_interface *sc_iface; /* interface */ |
122 | |
123 | struct usbd_interface *sc_intr_iface; /* interrupt interface */ |
124 | int sc_intr_number; /* interrupt number */ |
125 | struct usbd_pipe *sc_intr_pipe; /* interrupt pipe */ |
126 | u_char *sc_intr_buf; /* interrupt buffer */ |
127 | int sc_isize; |
128 | |
129 | u_char sc_dtr; /* current DTR state */ |
130 | u_char sc_rts; /* current RTS state */ |
131 | u_char sc_status; |
132 | |
133 | u_char sc_lsr; /* Local status register */ |
134 | u_char sc_msr; /* uticom status register */ |
135 | |
136 | struct device *sc_subdev; |
137 | }; |
138 | |
139 | static usbd_status uticom_reset(struct uticom_softc *); |
140 | static usbd_status uticom_set_crtscts(struct uticom_softc *); |
141 | static void uticom_intr(struct usbd_xfer *, void *, usbd_status); |
142 | |
143 | static void uticom_set(void *, int, int, int); |
144 | static void uticom_dtr(struct uticom_softc *, int); |
145 | static void uticom_rts(struct uticom_softc *, int); |
146 | static void uticom_break(struct uticom_softc *, int); |
147 | static void uticom_get_status(void *, int, u_char *, u_char *); |
148 | static int uticom_param(void *, int, struct termios *); |
149 | static int uticom_open(void *, int); |
150 | static void uticom_close(void *, int); |
151 | |
152 | void uticom_attach_hook(struct device *); |
153 | |
154 | static int uticom_download_fw(struct uticom_softc *sc, int pipeno, |
155 | struct usbd_device *dev); |
156 | |
157 | struct ucom_methods uticom_methods = { |
158 | uticom_get_status, |
159 | uticom_set, |
160 | uticom_param, |
161 | NULL((void *)0), |
162 | uticom_open, |
163 | uticom_close, |
164 | NULL((void *)0), |
165 | NULL((void *)0) |
166 | }; |
167 | |
168 | int uticom_match(struct device *, void *, void *); |
169 | void uticom_attach(struct device *, struct device *, void *); |
170 | int uticom_detach(struct device *, int); |
171 | |
172 | struct cfdriver uticom_cd = { |
173 | NULL((void *)0), "uticom", DV_DULL |
174 | }; |
175 | |
176 | const struct cfattach uticom_ca = { |
177 | sizeof(struct uticom_softc), uticom_match, uticom_attach, uticom_detach |
178 | }; |
179 | |
180 | static const struct usb_devno uticom_devs[] = { |
181 | { USB_VENDOR_TI0x0451, USB_PRODUCT_TI_TUSB34100x3410 }, |
182 | { USB_VENDOR_TI0x0451, USB_PRODUCT_TI_MSP430_JTAG0xf430 }, |
183 | { USB_VENDOR_STARTECH0x14b0, USB_PRODUCT_STARTECH_ICUSB232X0x3410 }, |
184 | { USB_VENDOR_MOXA0x110a, USB_PRODUCT_MOXA_UPORT11100x1110 }, |
185 | { USB_VENDOR_ABBOTT0x1a61, USB_PRODUCT_ABBOTT_STEREO_PLUG0x3410 } |
186 | }; |
187 | |
188 | int |
189 | uticom_match(struct device *parent, void *match, void *aux) |
190 | { |
191 | struct usb_attach_arg *uaa = aux; |
192 | |
193 | if (uaa->iface != NULL((void *)0)) |
194 | return (UMATCH_NONE0); |
195 | |
196 | return (usb_lookup(uticom_devs, uaa->vendor, uaa->product)usbd_match_device((const struct usb_devno *)(uticom_devs), sizeof (uticom_devs) / sizeof ((uticom_devs)[0]), sizeof ((uticom_devs )[0]), (uaa->vendor), (uaa->product)) != NULL((void *)0) ? |
197 | UMATCH_VENDOR_PRODUCT13 : UMATCH_NONE0); |
198 | } |
199 | |
200 | void |
201 | uticom_attach(struct device *parent, struct device *self, void *aux) |
202 | { |
203 | struct uticom_softc *sc = (struct uticom_softc *)self; |
204 | struct usb_attach_arg *uaa = aux; |
205 | struct usbd_device *dev = uaa->device; |
206 | |
207 | sc->sc_udev = dev; |
208 | sc->sc_iface = uaa->iface; |
209 | |
210 | config_mountroot(self, uticom_attach_hook); |
211 | } |
212 | |
213 | void |
214 | uticom_attach_hook(struct device *self) |
215 | { |
216 | struct uticom_softc *sc = (struct uticom_softc *)self; |
217 | usb_config_descriptor_t *cdesc; |
218 | usb_interface_descriptor_t *id; |
219 | usb_endpoint_descriptor_t *ed; |
220 | usbd_status err; |
221 | int status, i; |
222 | usb_device_descriptor_t *dd; |
223 | struct ucom_attach_args uca; |
224 | |
225 | /* Initialize endpoints. */ |
226 | uca.bulkin = uca.bulkout = -1; |
227 | sc->sc_intr_number = -1; |
228 | sc->sc_intr_pipe = NULL((void *)0); |
229 | |
230 | dd = usbd_get_device_descriptor(sc->sc_udev); |
231 | DPRINTF(("%s: uticom_attach: num of configurations %d\n", |
232 | sc->sc_dev.dv_xname, dd->bNumConfigurations)); |
233 | |
234 | /* The device without firmware has single configuration with single |
235 | * bulk out interface. */ |
236 | if (dd->bNumConfigurations > 1) |
237 | goto fwload_done; |
238 | |
239 | /* Loading firmware. */ |
240 | DPRINTF(("%s: uticom_attach: starting loading firmware\n", |
241 | sc->sc_dev.dv_xname)); |
242 | |
243 | err = usbd_set_config_index(sc->sc_udev, UTICOM_CONFIG_INDEX1, 1); |
244 | if (err) { |
245 | printf("%s: failed to set configuration: %s\n", |
246 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
247 | usbd_deactivate(sc->sc_udev); |
248 | return; |
249 | } |
250 | |
251 | /* Get the config descriptor. */ |
252 | cdesc = usbd_get_config_descriptor(sc->sc_udev); |
253 | |
254 | if (cdesc == NULL((void *)0)) { |
255 | printf("%s: failed to get configuration descriptor\n", |
256 | sc->sc_dev.dv_xname); |
257 | usbd_deactivate(sc->sc_udev); |
258 | return; |
259 | } |
260 | |
261 | err = usbd_device2interface_handle(sc->sc_udev, UTICOM_IFACE_INDEX0, |
262 | &sc->sc_iface); |
263 | if (err) { |
264 | printf("%s: failed to get interface: %s\n", |
265 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
266 | usbd_deactivate(sc->sc_udev); |
267 | return; |
268 | } |
269 | |
270 | /* Find the bulk out interface used to upload firmware. */ |
271 | id = usbd_get_interface_descriptor(sc->sc_iface); |
272 | |
273 | for (i = 0; i < id->bNumEndpoints; i++) { |
274 | ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); |
275 | if (ed == NULL((void *)0)) { |
276 | printf("%s: no endpoint descriptor for %d\n", |
277 | sc->sc_dev.dv_xname, i); |
278 | usbd_deactivate(sc->sc_udev); |
279 | return; |
280 | } |
281 | |
282 | if (UE_GET_DIR(ed->bEndpointAddress)((ed->bEndpointAddress) & 0x80) == UE_DIR_OUT0x00 && |
283 | UE_GET_XFERTYPE(ed->bmAttributes)((ed->bmAttributes) & 0x03) == UE_BULK0x02) { |
284 | uca.bulkout = ed->bEndpointAddress; |
285 | DPRINTF(("%s: uticom_attach: data bulk out num: %d\n", |
286 | sc->sc_dev.dv_xname, ed->bEndpointAddress)); |
287 | } |
288 | |
289 | if (uca.bulkout == -1) { |
290 | printf("%s: could not find data bulk out\n", |
291 | sc->sc_dev.dv_xname); |
292 | usbd_deactivate(sc->sc_udev); |
293 | return; |
294 | } |
295 | } |
296 | |
297 | status = uticom_download_fw(sc, uca.bulkout, sc->sc_udev); |
298 | |
299 | if (status) { |
300 | printf("%s: firmware download failed\n", |
301 | sc->sc_dev.dv_xname); |
302 | usbd_deactivate(sc->sc_udev); |
303 | return; |
304 | } else { |
305 | DPRINTF(("%s: firmware download succeeded\n", |
306 | sc->sc_dev.dv_xname)); |
307 | } |
308 | |
309 | status = usbd_reload_device_desc(sc->sc_udev); |
310 | if (status) { |
311 | printf("%s: error reloading device descriptor\n", |
312 | sc->sc_dev.dv_xname); |
313 | usbd_deactivate(sc->sc_udev); |
314 | return; |
315 | } |
316 | |
317 | fwload_done: |
318 | dd = usbd_get_device_descriptor(sc->sc_udev); |
Value stored to 'dd' is never read | |
319 | DPRINTF(("%s: uticom_attach: num of configurations %d\n", |
320 | sc->sc_dev.dv_xname, dd->bNumConfigurations)); |
321 | |
322 | err = usbd_set_config_index(sc->sc_udev, UTICOM_ACTIVE_INDEX2, 1); |
323 | if (err) { |
324 | printf("%s: failed to set configuration: %s\n", |
325 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
326 | usbd_deactivate(sc->sc_udev); |
327 | return; |
328 | } |
329 | |
330 | /* Get the config descriptor. */ |
331 | cdesc = usbd_get_config_descriptor(sc->sc_udev); |
332 | if (cdesc == NULL((void *)0)) { |
333 | printf("%s: failed to get configuration descriptor\n", |
334 | sc->sc_dev.dv_xname); |
335 | usbd_deactivate(sc->sc_udev); |
336 | return; |
337 | } |
338 | |
339 | /* Get the interface (XXX: multiport chips are not supported yet). */ |
340 | err = usbd_device2interface_handle(sc->sc_udev, UTICOM_IFACE_INDEX0, |
341 | &sc->sc_iface); |
342 | if (err) { |
343 | printf("%s: failed to get interface: %s\n", |
344 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
345 | usbd_deactivate(sc->sc_udev); |
346 | return; |
347 | } |
348 | |
349 | /* Find the interrupt endpoints. */ |
350 | id = usbd_get_interface_descriptor(sc->sc_iface); |
351 | |
352 | for (i = 0; i < id->bNumEndpoints; i++) { |
353 | ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); |
354 | if (ed == NULL((void *)0)) { |
355 | printf("%s: no endpoint descriptor for %d\n", |
356 | sc->sc_dev.dv_xname, i); |
357 | usbd_deactivate(sc->sc_udev); |
358 | return; |
359 | } |
360 | |
361 | if (UE_GET_DIR(ed->bEndpointAddress)((ed->bEndpointAddress) & 0x80) == UE_DIR_IN0x80 && |
362 | UE_GET_XFERTYPE(ed->bmAttributes)((ed->bmAttributes) & 0x03) == UE_INTERRUPT0x03) { |
363 | sc->sc_intr_number = ed->bEndpointAddress; |
364 | sc->sc_isize = UGETW(ed->wMaxPacketSize)(*(u_int16_t *)(ed->wMaxPacketSize)); |
365 | |
366 | } |
367 | } |
368 | |
369 | if (sc->sc_intr_number == -1) { |
370 | printf("%s: could not find interrupt in\n", |
371 | sc->sc_dev.dv_xname); |
372 | usbd_deactivate(sc->sc_udev); |
373 | return; |
374 | } |
375 | |
376 | /* Keep interface for interrupt. */ |
377 | sc->sc_intr_iface = sc->sc_iface; |
378 | |
379 | /* Find the bulk{in,out} endpoints. */ |
380 | id = usbd_get_interface_descriptor(sc->sc_iface); |
381 | |
382 | for (i = 0; i < id->bNumEndpoints; i++) { |
383 | ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); |
384 | if (ed == NULL((void *)0)) { |
385 | printf("%s: no endpoint descriptor for %d\n", |
386 | sc->sc_dev.dv_xname, i); |
387 | usbd_deactivate(sc->sc_udev); |
388 | return; |
389 | } |
390 | |
391 | if (UE_GET_DIR(ed->bEndpointAddress)((ed->bEndpointAddress) & 0x80) == UE_DIR_IN0x80 && |
392 | UE_GET_XFERTYPE(ed->bmAttributes)((ed->bmAttributes) & 0x03) == UE_BULK0x02) { |
393 | uca.bulkin = ed->bEndpointAddress; |
394 | } else if (UE_GET_DIR(ed->bEndpointAddress)((ed->bEndpointAddress) & 0x80) == UE_DIR_OUT0x00 && |
395 | UE_GET_XFERTYPE(ed->bmAttributes)((ed->bmAttributes) & 0x03) == UE_BULK0x02) { |
396 | uca.bulkout = ed->bEndpointAddress; |
397 | } |
398 | } |
399 | |
400 | if (uca.bulkin == -1) { |
401 | printf("%s: could not find data bulk in\n", |
402 | sc->sc_dev.dv_xname); |
403 | usbd_deactivate(sc->sc_udev); |
404 | return; |
405 | } |
406 | |
407 | if (uca.bulkout == -1) { |
408 | printf("%s: could not find data bulk out\n", |
409 | sc->sc_dev.dv_xname); |
410 | usbd_deactivate(sc->sc_udev); |
411 | return; |
412 | } |
413 | |
414 | sc->sc_dtr = sc->sc_rts = -1; |
415 | |
416 | uca.portno = UCOM_UNK_PORTNO-1; |
417 | uca.ibufsize = UTICOM_IBUFSZ64; |
418 | uca.obufsize = UTICOM_OBUFSZ64; |
419 | uca.ibufsizepad = UTICOM_IBUFSZ64; |
420 | uca.device = sc->sc_udev; |
421 | uca.iface = sc->sc_iface; |
422 | uca.opkthdrlen = 0; |
423 | uca.methods = &uticom_methods; |
424 | uca.arg = sc; |
425 | uca.info = NULL((void *)0); |
426 | |
427 | err = uticom_reset(sc); |
428 | if (err) { |
429 | printf("%s: reset failed: %s\n", |
430 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
431 | usbd_deactivate(sc->sc_udev); |
432 | return; |
433 | } |
434 | |
435 | DPRINTF(("%s: uticom_attach: in = 0x%x, out = 0x%x, intr = 0x%x\n", |
436 | sc->sc_dev.dv_xname, uca.bulkin, |
437 | uca.bulkout, sc->sc_intr_number)); |
438 | |
439 | sc->sc_subdev = config_found_sm((struct device *)sc, &uca, ucomprint, ucomsubmatch); |
440 | } |
441 | |
442 | int |
443 | uticom_detach(struct device *self, int flags) |
444 | { |
445 | struct uticom_softc *sc = (struct uticom_softc *)self; |
446 | |
447 | DPRINTF(("%s: uticom_detach: sc = %p\n", |
448 | sc->sc_dev.dv_xname, sc)); |
449 | |
450 | if (sc->sc_subdev != NULL((void *)0)) { |
451 | config_detach(sc->sc_subdev, flags); |
452 | sc->sc_subdev = NULL((void *)0); |
453 | } |
454 | |
455 | if (sc->sc_intr_pipe != NULL((void *)0)) { |
456 | usbd_close_pipe(sc->sc_intr_pipe); |
457 | free(sc->sc_intr_buf, M_USBDEV102, sc->sc_isize); |
458 | sc->sc_intr_pipe = NULL((void *)0); |
459 | } |
460 | |
461 | return (0); |
462 | } |
463 | |
464 | static usbd_status |
465 | uticom_reset(struct uticom_softc *sc) |
466 | { |
467 | usb_device_request_t req; |
468 | usbd_status err; |
469 | |
470 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
471 | req.bRequest = UTICOM_RQ_SON2; |
472 | USETW(req.wValue, 0)(*(u_int16_t *)(req.wValue) = (0)); |
473 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
474 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
475 | |
476 | err = usbd_do_request(sc->sc_udev, &req, NULL((void *)0)); |
477 | if (err){ |
478 | printf("%s: uticom_reset: %s\n", |
479 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
480 | return (EIO5); |
481 | } |
482 | |
483 | DPRINTF(("%s: uticom_reset: done\n", sc->sc_dev.dv_xname)); |
484 | return (0); |
485 | } |
486 | |
487 | static void |
488 | uticom_set(void *addr, int portno, int reg, int onoff) |
489 | { |
490 | struct uticom_softc *sc = addr; |
491 | |
492 | switch (reg) { |
493 | case UCOM_SET_DTR1: |
494 | uticom_dtr(sc, onoff); |
495 | break; |
496 | case UCOM_SET_RTS2: |
497 | uticom_rts(sc, onoff); |
498 | break; |
499 | case UCOM_SET_BREAK3: |
500 | uticom_break(sc, onoff); |
501 | break; |
502 | default: |
503 | break; |
504 | } |
505 | } |
506 | |
507 | static void |
508 | uticom_dtr(struct uticom_softc *sc, int onoff) |
509 | { |
510 | usb_device_request_t req; |
511 | usbd_status err; |
512 | |
513 | DPRINTF(("%s: uticom_dtr: onoff = %d\n", sc->sc_dev.dv_xname, |
514 | onoff)); |
515 | |
516 | if (sc->sc_dtr == onoff) |
517 | return; |
518 | sc->sc_dtr = onoff; |
519 | |
520 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
521 | req.bRequest = UTICOM_RQ_DTR7; |
522 | USETW(req.wValue, sc->sc_dtr ? UCDC_LINE_DTR : 0)(*(u_int16_t *)(req.wValue) = (sc->sc_dtr ? 0x0001 : 0)); |
523 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
524 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
525 | |
526 | err = usbd_do_request(sc->sc_udev, &req, NULL((void *)0)); |
527 | if (err) |
528 | printf("%s: uticom_dtr: %s\n", |
529 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
530 | } |
531 | |
532 | static void |
533 | uticom_rts(struct uticom_softc *sc, int onoff) |
534 | { |
535 | usb_device_request_t req; |
536 | usbd_status err; |
537 | |
538 | DPRINTF(("%s: uticom_rts: onoff = %d\n", sc->sc_dev.dv_xname, |
539 | onoff)); |
540 | |
541 | if (sc->sc_rts == onoff) |
542 | return; |
543 | sc->sc_rts = onoff; |
544 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
545 | req.bRequest = UTICOM_RQ_RTS6; |
546 | USETW(req.wValue, sc->sc_rts ? UCDC_LINE_RTS : 0)(*(u_int16_t *)(req.wValue) = (sc->sc_rts ? 0x0002 : 0)); |
547 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
548 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
549 | |
550 | err = usbd_do_request(sc->sc_udev, &req, NULL((void *)0)); |
551 | if (err) |
552 | printf("%s: uticom_rts: %s\n", |
553 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
554 | } |
555 | |
556 | static void |
557 | uticom_break(struct uticom_softc *sc, int onoff) |
558 | { |
559 | usb_device_request_t req; |
560 | usbd_status err; |
561 | |
562 | DPRINTF(("%s: uticom_break: onoff = %d\n", sc->sc_dev.dv_xname, |
563 | onoff)); |
564 | |
565 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
566 | req.bRequest = UTICOM_RQ_BREAK8; |
567 | USETW(req.wValue, onoff ? 1 : 0)(*(u_int16_t *)(req.wValue) = (onoff ? 1 : 0)); |
568 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
569 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
570 | |
571 | err = usbd_do_request(sc->sc_udev, &req, NULL((void *)0)); |
572 | if (err) |
573 | printf("%s: uticom_break: %s\n", |
574 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
575 | } |
576 | |
577 | static usbd_status |
578 | uticom_set_crtscts(struct uticom_softc *sc) |
579 | { |
580 | usb_device_request_t req; |
581 | usbd_status err; |
582 | |
583 | DPRINTF(("%s: uticom_set_crtscts: on\n", sc->sc_dev.dv_xname)); |
584 | |
585 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
586 | req.bRequest = UTICOM_RQ_CRTSCTS9; |
587 | USETW(req.wValue, 1)(*(u_int16_t *)(req.wValue) = (1)); |
588 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
589 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
590 | |
591 | err = usbd_do_request(sc->sc_udev, &req, NULL((void *)0)); |
592 | if (err) { |
593 | printf("%s: uticom_set_crtscts: %s\n", |
594 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
595 | return (err); |
596 | } |
597 | |
598 | return (USBD_NORMAL_COMPLETION); |
599 | } |
600 | |
601 | static int |
602 | uticom_param(void *vsc, int portno, struct termios *t) |
603 | { |
604 | struct uticom_softc *sc = (struct uticom_softc *)vsc; |
605 | usb_device_request_t req; |
606 | usbd_status err; |
607 | uint8_t data; |
608 | |
609 | DPRINTF(("%s: uticom_param\n", sc->sc_dev.dv_xname)); |
610 | |
611 | switch (t->c_ospeed) { |
612 | case 1200: |
613 | case 2400: |
614 | case 4800: |
615 | case 7200: |
616 | case 9600: |
617 | case 14400: |
618 | case 19200: |
619 | case 38400: |
620 | case 57600: |
621 | case 115200: |
622 | case 230400: |
623 | case 460800: |
624 | case 921600: |
625 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
626 | req.bRequest = UTICOM_RQ_BAUD3; |
627 | USETW(req.wValue, (UTICOM_BRATE_REF / t->c_ospeed))(*(u_int16_t *)(req.wValue) = ((923077 / t->c_ospeed))); |
628 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
629 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
630 | |
631 | err = usbd_do_request(sc->sc_udev, &req, 0); |
632 | if (err) { |
633 | printf("%s: uticom_param: %s\n", |
634 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
635 | return (EIO5); |
636 | } |
637 | break; |
638 | default: |
639 | printf("%s: uticom_param: unsupported baud rate %d\n", |
640 | sc->sc_dev.dv_xname, t->c_ospeed); |
641 | return (EINVAL22); |
642 | } |
643 | |
644 | switch (ISSET(t->c_cflag, CSIZE)((t->c_cflag) & (0x00000300))) { |
645 | case CS50x00000000: |
646 | data = UTICOM_SET_DATA_BITS(5)(5 - 5); |
647 | break; |
648 | case CS60x00000100: |
649 | data = UTICOM_SET_DATA_BITS(6)(6 - 5); |
650 | break; |
651 | case CS70x00000200: |
652 | data = UTICOM_SET_DATA_BITS(7)(7 - 5); |
653 | break; |
654 | case CS80x00000300: |
655 | data = UTICOM_SET_DATA_BITS(8)(8 - 5); |
656 | break; |
657 | default: |
658 | return (EIO5); |
659 | } |
660 | |
661 | if (ISSET(t->c_cflag, CSTOPB)((t->c_cflag) & (0x00000400))) |
662 | data |= UTICOM_STOP_BITS_20x40; |
663 | else |
664 | data |= UTICOM_STOP_BITS_10x00; |
665 | |
666 | if (ISSET(t->c_cflag, PARENB)((t->c_cflag) & (0x00001000))) { |
667 | if (ISSET(t->c_cflag, PARODD)((t->c_cflag) & (0x00002000))) |
668 | data |= UTICOM_PARITY_ODD0x08; |
669 | else |
670 | data |= UTICOM_PARITY_EVEN0x18; |
671 | } else |
672 | data |= UTICOM_PARITY_NONE0x00; |
673 | |
674 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
675 | req.bRequest = UTICOM_RQ_LCR4; |
676 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
677 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
678 | USETW(req.wValue, data)(*(u_int16_t *)(req.wValue) = (data)); |
679 | |
680 | err = usbd_do_request(sc->sc_udev, &req, NULL((void *)0)); |
681 | if (err) { |
682 | printf("%s: uticom_param: %s\n", |
683 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
684 | return (err); |
685 | } |
686 | |
687 | if (ISSET(t->c_cflag, CRTSCTS)((t->c_cflag) & (0x00010000))) { |
688 | err = uticom_set_crtscts(sc); |
689 | if (err) |
690 | return (EIO5); |
691 | } |
692 | |
693 | return (0); |
694 | } |
695 | |
696 | static int |
697 | uticom_open(void *addr, int portno) |
698 | { |
699 | struct uticom_softc *sc = addr; |
700 | usbd_status err; |
701 | |
702 | if (usbd_is_dying(sc->sc_udev)) |
703 | return (ENXIO6); |
704 | |
705 | DPRINTF(("%s: uticom_open\n", sc->sc_dev.dv_xname)); |
706 | |
707 | sc->sc_status = 0; /* clear status bit */ |
708 | |
709 | if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL((void *)0)) { |
710 | sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV102, M_WAITOK0x0001); |
711 | err = usbd_open_pipe_intr(sc->sc_intr_iface, sc->sc_intr_number, |
712 | USBD_SHORT_XFER_OK0x04, &sc->sc_intr_pipe, sc, sc->sc_intr_buf, |
713 | sc->sc_isize, uticom_intr, UTICOM_INTR_INTERVAL100); |
714 | if (err) { |
715 | printf("%s: cannot open interrupt pipe (addr %d)\n", |
716 | sc->sc_dev.dv_xname, sc->sc_intr_number); |
717 | return (EIO5); |
718 | } |
719 | } |
720 | |
721 | DPRINTF(("%s: uticom_open: port opened\n", sc->sc_dev.dv_xname)); |
722 | return (0); |
723 | } |
724 | |
725 | static void |
726 | uticom_close(void *addr, int portno) |
727 | { |
728 | struct uticom_softc *sc = addr; |
729 | usb_device_request_t req; |
730 | usbd_status err; |
731 | |
732 | if (usbd_is_dying(sc->sc_udev)) |
733 | return; |
734 | |
735 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
736 | req.bRequest = UTICOM_RQ_SON2; |
737 | USETW(req.wValue, 0)(*(u_int16_t *)(req.wValue) = (0)); |
738 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
739 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
740 | |
741 | /* Try to reset UART part of chip. */ |
742 | err = usbd_do_request(sc->sc_udev, &req, NULL((void *)0)); |
743 | if (err) { |
744 | printf("%s: uticom_close: %s\n", |
745 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
746 | return; |
747 | } |
748 | |
749 | DPRINTF(("%s: uticom_close: close\n", sc->sc_dev.dv_xname)); |
750 | |
751 | if (sc->sc_intr_pipe != NULL((void *)0)) { |
752 | err = usbd_close_pipe(sc->sc_intr_pipe); |
753 | if (err) |
754 | printf("%s: close interrupt pipe failed: %s\n", |
755 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
756 | free(sc->sc_intr_buf, M_USBDEV102, sc->sc_isize); |
757 | sc->sc_intr_pipe = NULL((void *)0); |
758 | } |
759 | } |
760 | |
761 | static void |
762 | uticom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status) |
763 | { |
764 | struct uticom_softc *sc = priv; |
765 | u_char *buf = sc->sc_intr_buf; |
766 | |
767 | if (usbd_is_dying(sc->sc_udev)) |
768 | return; |
769 | |
770 | if (status != USBD_NORMAL_COMPLETION) { |
771 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { |
772 | DPRINTF(("%s: uticom_intr: int status: %s\n", |
773 | sc->sc_dev.dv_xname, usbd_errstr(status))); |
774 | return; |
775 | } |
776 | |
777 | DPRINTF(("%s: uticom_intr: abnormal status: %s\n", |
778 | sc->sc_dev.dv_xname, usbd_errstr(status))); |
779 | usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); |
780 | return; |
781 | } |
782 | |
783 | if (!xfer->actlen) |
784 | return; |
785 | |
786 | DPRINTF(("%s: xfer_length = %d\n", sc->sc_dev.dv_xname, |
787 | xfer->actlen)); |
788 | |
789 | sc->sc_lsr = sc->sc_msr = 0; |
790 | |
791 | if (buf[0] == 0) { |
792 | /* msr registers */ |
793 | if (buf[1] & UTICOM_MCR_CTS0x1) |
794 | sc->sc_msr |= UMSR_CTS0x10; |
795 | if (buf[1] & UTICOM_MCR_DSR0x2) |
796 | sc->sc_msr |= UMSR_DSR0x20; |
797 | if (buf[1] & UTICOM_MCR_CD0x4) |
798 | sc->sc_msr |= UMSR_DCD0x80; |
799 | if (buf[1] & UTICOM_MCR_RI0x8) |
800 | sc->sc_msr |= UMSR_RI0x40; |
801 | } else { |
802 | /* lsr registers */ |
803 | if (buf[0] & UTICOM_LCR_OVR0x1) |
804 | sc->sc_lsr |= ULSR_OE0x02; |
805 | if (buf[0] & UTICOM_LCR_PTE0x2) |
806 | sc->sc_lsr |= ULSR_PE0x04; |
807 | if (buf[0] & UTICOM_LCR_FRE0x4) |
808 | sc->sc_lsr |= ULSR_FE0x08; |
809 | if (buf[0] & UTICOM_LCR_BRK0x8) |
810 | sc->sc_lsr |= ULSR_BI0x10; |
811 | } |
812 | |
813 | // if (uticomstickdsr) |
814 | // sc->sc_msr |= UMSR_DSR; |
815 | |
816 | ucom_status_change((struct ucom_softc *)sc->sc_subdev); |
817 | } |
818 | |
819 | static void |
820 | uticom_get_status(void *addr, int portno, u_char *lsr, u_char *msr) |
821 | { |
822 | #if 0 /* TODO */ |
823 | struct uticom_softc *sc = addr; |
824 | |
825 | DPRINTF(("uticom_get_status:\n")); |
826 | |
827 | if (lsr != NULL((void *)0)) |
828 | *lsr = sc->sc_lsr; |
829 | if (msr != NULL((void *)0)) |
830 | *msr = sc->sc_msr; |
831 | #endif |
832 | return; |
833 | } |
834 | |
835 | static int |
836 | uticom_download_fw(struct uticom_softc *sc, int pipeno, |
837 | struct usbd_device *dev) |
838 | { |
839 | u_char *obuf, *firmware; |
840 | size_t firmware_size; |
841 | int buffer_size, pos; |
842 | uint8_t cs = 0, *buffer; |
843 | usbd_status err; |
844 | struct uticom_fw_header *header; |
845 | struct usbd_xfer *oxfer = 0; |
846 | usbd_status error = 0; |
847 | struct usbd_pipe *pipe; |
848 | |
849 | error = loadfirmware("tusb3410", &firmware, &firmware_size); |
850 | if (error) |
851 | return (error); |
852 | |
853 | buffer_size = UTICOM_FW_BUFSZ16284 + sizeof(struct uticom_fw_header); |
854 | buffer = malloc(buffer_size, M_USBDEV102, M_WAITOK0x0001 | M_CANFAIL0x0004); |
855 | |
856 | if (!buffer) { |
857 | printf("%s: uticom_download_fw: out of memory\n", |
858 | sc->sc_dev.dv_xname); |
859 | free(firmware, M_DEVBUF2, firmware_size); |
860 | return ENOMEM12; |
861 | } |
862 | |
863 | memcpy(buffer, firmware, firmware_size)__builtin_memcpy((buffer), (firmware), (firmware_size)); |
864 | memset(buffer + firmware_size, 0xff, buffer_size - firmware_size)__builtin_memset((buffer + firmware_size), (0xff), (buffer_size - firmware_size)); |
865 | |
866 | for (pos = sizeof(struct uticom_fw_header); pos < buffer_size; pos++) |
867 | cs = (uint8_t)(cs + buffer[pos]); |
868 | |
869 | header = (struct uticom_fw_header*)buffer; |
870 | header->length = (uint16_t)(buffer_size - |
871 | sizeof(struct uticom_fw_header)); |
872 | header->checkSum = cs; |
873 | |
874 | DPRINTF(("%s: downloading firmware ...\n", |
875 | sc->sc_dev.dv_xname)); |
876 | |
877 | err = usbd_open_pipe(sc->sc_iface, pipeno, USBD_EXCLUSIVE_USE0x01, |
878 | &pipe); |
879 | if (err) { |
880 | printf("%s: open bulk out error (addr %d): %s\n", |
881 | sc->sc_dev.dv_xname, pipeno, usbd_errstr(err)); |
882 | error = EIO5; |
883 | goto finish; |
884 | } |
885 | |
886 | oxfer = usbd_alloc_xfer(dev); |
887 | if (oxfer == NULL((void *)0)) { |
888 | error = ENOMEM12; |
889 | goto finish; |
890 | } |
891 | |
892 | obuf = usbd_alloc_buffer(oxfer, buffer_size); |
893 | if (obuf == NULL((void *)0)) { |
894 | error = ENOMEM12; |
895 | goto finish; |
896 | } |
897 | |
898 | memcpy(obuf, buffer, buffer_size)__builtin_memcpy((obuf), (buffer), (buffer_size)); |
899 | |
900 | usbd_setup_xfer(oxfer, pipe, (void *)sc, obuf, buffer_size, |
901 | USBD_NO_COPY0x01 | USBD_SYNCHRONOUS0x02, USBD_NO_TIMEOUT0, 0); |
902 | err = usbd_transfer(oxfer); |
903 | |
904 | if (err != USBD_NORMAL_COMPLETION) |
905 | printf("%s: uticom_download_fw: error: %s\n", |
906 | sc->sc_dev.dv_xname, usbd_errstr(err)); |
907 | |
908 | finish: |
909 | free(firmware, M_DEVBUF2, firmware_size); |
910 | usbd_free_buffer(oxfer); |
911 | usbd_free_xfer(oxfer); |
912 | oxfer = NULL((void *)0); |
913 | usbd_close_pipe(pipe); |
914 | free(buffer, M_USBDEV102, buffer_size); |
915 | return err; |
916 | } |