]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/usb/serial/uchcom.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / dev / usb / serial / uchcom.c
1 /*      $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $      */
2
3 /*-
4  * Copyright (c) 2007, Takanori Watanabe
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 /*
30  * Copyright (c) 2007 The NetBSD Foundation, Inc.
31  * All rights reserved.
32  *
33  * This code is derived from software contributed to The NetBSD Foundation
34  * by Takuya SHIOZAKI (tshiozak@netbsd.org).
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
46  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
47  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
49  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
50  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
52  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
53  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
55  * POSSIBILITY OF SUCH DAMAGE.
56  */
57
58 #include <sys/cdefs.h>
59 __FBSDID("$FreeBSD$");
60
61 /*
62  * Driver for WinChipHead CH341/340, the worst USB-serial chip in the
63  * world.
64  */
65
66 #include <sys/stdint.h>
67 #include <sys/stddef.h>
68 #include <sys/param.h>
69 #include <sys/queue.h>
70 #include <sys/types.h>
71 #include <sys/systm.h>
72 #include <sys/kernel.h>
73 #include <sys/bus.h>
74 #include <sys/module.h>
75 #include <sys/lock.h>
76 #include <sys/mutex.h>
77 #include <sys/condvar.h>
78 #include <sys/sysctl.h>
79 #include <sys/sx.h>
80 #include <sys/unistd.h>
81 #include <sys/callout.h>
82 #include <sys/malloc.h>
83 #include <sys/priv.h>
84
85 #include <dev/usb/usb.h>
86 #include <dev/usb/usbdi.h>
87 #include <dev/usb/usbdi_util.h>
88 #include "usbdevs.h"
89
90 #define USB_DEBUG_VAR uchcom_debug
91 #include <dev/usb/usb_debug.h>
92 #include <dev/usb/usb_process.h>
93
94 #include <dev/usb/serial/usb_serial.h>
95
96 #ifdef USB_DEBUG
97 static int uchcom_debug = 0;
98
99 static SYSCTL_NODE(_hw_usb, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom");
100 SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RW,
101     &uchcom_debug, 0, "uchcom debug level");
102 #endif
103
104 #define UCHCOM_IFACE_INDEX      0
105 #define UCHCOM_CONFIG_INDEX     0
106
107 #define UCHCOM_REV_CH340        0x0250
108 #define UCHCOM_INPUT_BUF_SIZE   8
109
110 #define UCHCOM_REQ_GET_VERSION  0x5F
111 #define UCHCOM_REQ_READ_REG     0x95
112 #define UCHCOM_REQ_WRITE_REG    0x9A
113 #define UCHCOM_REQ_RESET        0xA1
114 #define UCHCOM_REQ_SET_DTRRTS   0xA4
115
116 #define UCHCOM_REG_STAT1        0x06
117 #define UCHCOM_REG_STAT2        0x07
118 #define UCHCOM_REG_BPS_PRE      0x12
119 #define UCHCOM_REG_BPS_DIV      0x13
120 #define UCHCOM_REG_BPS_MOD      0x14
121 #define UCHCOM_REG_BPS_PAD      0x0F
122 #define UCHCOM_REG_BREAK1       0x05
123 #define UCHCOM_REG_BREAK2       0x18
124 #define UCHCOM_REG_LCR1         0x18
125 #define UCHCOM_REG_LCR2         0x25
126
127 #define UCHCOM_VER_20           0x20
128
129 #define UCHCOM_BASE_UNKNOWN     0
130 #define UCHCOM_BPS_MOD_BASE     20000000
131 #define UCHCOM_BPS_MOD_BASE_OFS 1100
132
133 #define UCHCOM_DTR_MASK         0x20
134 #define UCHCOM_RTS_MASK         0x40
135
136 #define UCHCOM_BRK1_MASK        0x01
137 #define UCHCOM_BRK2_MASK        0x40
138
139 #define UCHCOM_LCR1_MASK        0xAF
140 #define UCHCOM_LCR2_MASK        0x07
141 #define UCHCOM_LCR1_PARENB      0x80
142 #define UCHCOM_LCR2_PAREVEN     0x07
143 #define UCHCOM_LCR2_PARODD      0x06
144 #define UCHCOM_LCR2_PARMARK     0x05
145 #define UCHCOM_LCR2_PARSPACE    0x04
146
147 #define UCHCOM_INTR_STAT1       0x02
148 #define UCHCOM_INTR_STAT2       0x03
149 #define UCHCOM_INTR_LEAST       4
150
151 #define UCHCOM_BULK_BUF_SIZE 1024       /* bytes */
152
153 enum {
154         UCHCOM_BULK_DT_WR,
155         UCHCOM_BULK_DT_RD,
156         UCHCOM_INTR_DT_RD,
157         UCHCOM_N_TRANSFER,
158 };
159
160 struct uchcom_softc {
161         struct ucom_super_softc sc_super_ucom;
162         struct ucom_softc sc_ucom;
163
164         struct usb_xfer *sc_xfer[UCHCOM_N_TRANSFER];
165         struct usb_device *sc_udev;
166         struct mtx sc_mtx;
167
168         uint8_t sc_dtr;                 /* local copy */
169         uint8_t sc_rts;                 /* local copy */
170         uint8_t sc_version;
171         uint8_t sc_msr;
172         uint8_t sc_lsr;                 /* local status register */
173 };
174
175 struct uchcom_divider {
176         uint8_t dv_prescaler;
177         uint8_t dv_div;
178         uint8_t dv_mod;
179 };
180
181 struct uchcom_divider_record {
182         uint32_t dvr_high;
183         uint32_t dvr_low;
184         uint32_t dvr_base_clock;
185         struct uchcom_divider dvr_divider;
186 };
187
188 static const struct uchcom_divider_record dividers[] =
189 {
190         {307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}},
191         {921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}},
192         {2999999, 23530, 6000000, {3, 0, 0}},
193         {23529, 2942, 750000, {2, 0, 0}},
194         {2941, 368, 93750, {1, 0, 0}},
195         {367, 1, 11719, {0, 0, 0}},
196 };
197
198 #define NUM_DIVIDERS    (sizeof (dividers) / sizeof (dividers[0]))
199
200 static const STRUCT_USB_HOST_ID uchcom_devs[] = {
201         {USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)},
202         {USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER, 0)},
203         {USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER_2, 0)},
204 };
205
206 /* protypes */
207
208 static void     uchcom_free(struct ucom_softc *);
209 static int      uchcom_pre_param(struct ucom_softc *, struct termios *);
210 static void     uchcom_cfg_get_status(struct ucom_softc *, uint8_t *,
211                     uint8_t *);
212 static void     uchcom_cfg_open(struct ucom_softc *ucom);
213 static void     uchcom_cfg_param(struct ucom_softc *, struct termios *);
214 static void     uchcom_cfg_set_break(struct ucom_softc *, uint8_t);
215 static void     uchcom_cfg_set_dtr(struct ucom_softc *, uint8_t);
216 static void     uchcom_cfg_set_rts(struct ucom_softc *, uint8_t);
217 static void     uchcom_start_read(struct ucom_softc *);
218 static void     uchcom_start_write(struct ucom_softc *);
219 static void     uchcom_stop_read(struct ucom_softc *);
220 static void     uchcom_stop_write(struct ucom_softc *);
221 static void     uchcom_update_version(struct uchcom_softc *);
222 static void     uchcom_convert_status(struct uchcom_softc *, uint8_t);
223 static void     uchcom_update_status(struct uchcom_softc *);
224 static void     uchcom_set_dtr_rts(struct uchcom_softc *);
225 static int      uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
226 static void     uchcom_set_baudrate(struct uchcom_softc *, uint32_t);
227 static void     uchcom_poll(struct ucom_softc *ucom);
228
229 static device_probe_t uchcom_probe;
230 static device_attach_t uchcom_attach;
231 static device_detach_t uchcom_detach;
232 static void uchcom_free_softc(struct uchcom_softc *);
233
234 static usb_callback_t uchcom_intr_callback;
235 static usb_callback_t uchcom_write_callback;
236 static usb_callback_t uchcom_read_callback;
237
238 static const struct usb_config uchcom_config_data[UCHCOM_N_TRANSFER] = {
239
240         [UCHCOM_BULK_DT_WR] = {
241                 .type = UE_BULK,
242                 .endpoint = UE_ADDR_ANY,
243                 .direction = UE_DIR_OUT,
244                 .bufsize = UCHCOM_BULK_BUF_SIZE,
245                 .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
246                 .callback = &uchcom_write_callback,
247         },
248
249         [UCHCOM_BULK_DT_RD] = {
250                 .type = UE_BULK,
251                 .endpoint = UE_ADDR_ANY,
252                 .direction = UE_DIR_IN,
253                 .bufsize = UCHCOM_BULK_BUF_SIZE,
254                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
255                 .callback = &uchcom_read_callback,
256         },
257
258         [UCHCOM_INTR_DT_RD] = {
259                 .type = UE_INTERRUPT,
260                 .endpoint = UE_ADDR_ANY,
261                 .direction = UE_DIR_IN,
262                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
263                 .bufsize = 0,   /* use wMaxPacketSize */
264                 .callback = &uchcom_intr_callback,
265         },
266 };
267
268 static struct ucom_callback uchcom_callback = {
269         .ucom_cfg_get_status = &uchcom_cfg_get_status,
270         .ucom_cfg_set_dtr = &uchcom_cfg_set_dtr,
271         .ucom_cfg_set_rts = &uchcom_cfg_set_rts,
272         .ucom_cfg_set_break = &uchcom_cfg_set_break,
273         .ucom_cfg_open = &uchcom_cfg_open,
274         .ucom_cfg_param = &uchcom_cfg_param,
275         .ucom_pre_param = &uchcom_pre_param,
276         .ucom_start_read = &uchcom_start_read,
277         .ucom_stop_read = &uchcom_stop_read,
278         .ucom_start_write = &uchcom_start_write,
279         .ucom_stop_write = &uchcom_stop_write,
280         .ucom_poll = &uchcom_poll,
281         .ucom_free = &uchcom_free,
282 };
283
284 /* ----------------------------------------------------------------------
285  * driver entry points
286  */
287
288 static int
289 uchcom_probe(device_t dev)
290 {
291         struct usb_attach_arg *uaa = device_get_ivars(dev);
292
293         DPRINTFN(11, "\n");
294
295         if (uaa->usb_mode != USB_MODE_HOST) {
296                 return (ENXIO);
297         }
298         if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) {
299                 return (ENXIO);
300         }
301         if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) {
302                 return (ENXIO);
303         }
304         return (usbd_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa));
305 }
306
307 static int
308 uchcom_attach(device_t dev)
309 {
310         struct uchcom_softc *sc = device_get_softc(dev);
311         struct usb_attach_arg *uaa = device_get_ivars(dev);
312         int error;
313         uint8_t iface_index;
314
315         DPRINTFN(11, "\n");
316
317         device_set_usb_desc(dev);
318         mtx_init(&sc->sc_mtx, "uchcom", NULL, MTX_DEF);
319         ucom_ref(&sc->sc_super_ucom);
320
321         sc->sc_udev = uaa->device;
322
323         switch (uaa->info.bcdDevice) {
324         case UCHCOM_REV_CH340:
325                 device_printf(dev, "CH340 detected\n");
326                 break;
327         default:
328                 device_printf(dev, "CH341 detected\n");
329                 break;
330         }
331
332         iface_index = UCHCOM_IFACE_INDEX;
333         error = usbd_transfer_setup(uaa->device,
334             &iface_index, sc->sc_xfer, uchcom_config_data,
335             UCHCOM_N_TRANSFER, sc, &sc->sc_mtx);
336
337         if (error) {
338                 DPRINTF("one or more missing USB endpoints, "
339                     "error=%s\n", usbd_errstr(error));
340                 goto detach;
341         }
342
343         /* clear stall at first run */
344         mtx_lock(&sc->sc_mtx);
345         usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
346         usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
347         mtx_unlock(&sc->sc_mtx);
348
349         error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
350             &uchcom_callback, &sc->sc_mtx);
351         if (error) {
352                 goto detach;
353         }
354         ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
355
356         return (0);
357
358 detach:
359         uchcom_detach(dev);
360         return (ENXIO);
361 }
362
363 static int
364 uchcom_detach(device_t dev)
365 {
366         struct uchcom_softc *sc = device_get_softc(dev);
367
368         DPRINTFN(11, "\n");
369
370         ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
371         usbd_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER);
372
373         device_claim_softc(dev);
374
375         uchcom_free_softc(sc);
376
377         return (0);
378 }
379
380 UCOM_UNLOAD_DRAIN(uchcom);
381
382 static void
383 uchcom_free_softc(struct uchcom_softc *sc)
384 {
385         if (ucom_unref(&sc->sc_super_ucom)) {
386                 mtx_destroy(&sc->sc_mtx);
387                 device_free_softc(sc);
388         }
389 }
390
391 static void
392 uchcom_free(struct ucom_softc *ucom)
393 {
394         uchcom_free_softc(ucom->sc_parent);
395 }
396
397 /* ----------------------------------------------------------------------
398  * low level i/o
399  */
400
401 static void
402 uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno,
403     uint16_t value, uint16_t index)
404 {
405         struct usb_device_request req;
406
407         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
408         req.bRequest = reqno;
409         USETW(req.wValue, value);
410         USETW(req.wIndex, index);
411         USETW(req.wLength, 0);
412
413         ucom_cfg_do_request(sc->sc_udev,
414             &sc->sc_ucom, &req, NULL, 0, 1000);
415 }
416
417 static void
418 uchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno,
419     uint16_t value, uint16_t index, void *buf, uint16_t buflen)
420 {
421         struct usb_device_request req;
422
423         req.bmRequestType = UT_READ_VENDOR_DEVICE;
424         req.bRequest = reqno;
425         USETW(req.wValue, value);
426         USETW(req.wIndex, index);
427         USETW(req.wLength, buflen);
428
429         ucom_cfg_do_request(sc->sc_udev,
430             &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000);
431 }
432
433 static void
434 uchcom_write_reg(struct uchcom_softc *sc,
435     uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
436 {
437         DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
438             (unsigned)reg1, (unsigned)val1,
439             (unsigned)reg2, (unsigned)val2);
440         uchcom_ctrl_write(
441             sc, UCHCOM_REQ_WRITE_REG,
442             reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8));
443 }
444
445 static void
446 uchcom_read_reg(struct uchcom_softc *sc,
447     uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
448 {
449         uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
450
451         uchcom_ctrl_read(
452             sc, UCHCOM_REQ_READ_REG,
453             reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf));
454
455         DPRINTF("0x%02X->0x%02X, 0x%02X->0x%02X\n",
456             (unsigned)reg1, (unsigned)buf[0],
457             (unsigned)reg2, (unsigned)buf[1]);
458
459         if (rval1)
460                 *rval1 = buf[0];
461         if (rval2)
462                 *rval2 = buf[1];
463 }
464
465 static void
466 uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver)
467 {
468         uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
469
470         uchcom_ctrl_read(sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf));
471
472         if (rver)
473                 *rver = buf[0];
474 }
475
476 static void
477 uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval)
478 {
479         uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL);
480 }
481
482 static void
483 uchcom_set_dtr_rts_10(struct uchcom_softc *sc, uint8_t val)
484 {
485         uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val);
486 }
487
488 static void
489 uchcom_set_dtr_rts_20(struct uchcom_softc *sc, uint8_t val)
490 {
491         uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
492 }
493
494
495 /* ----------------------------------------------------------------------
496  * middle layer
497  */
498
499 static void
500 uchcom_update_version(struct uchcom_softc *sc)
501 {
502         uchcom_get_version(sc, &sc->sc_version);
503 }
504
505 static void
506 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
507 {
508         sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
509         sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
510
511         cur = ~cur & 0x0F;
512         sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
513 }
514
515 static void
516 uchcom_update_status(struct uchcom_softc *sc)
517 {
518         uint8_t cur;
519
520         uchcom_get_status(sc, &cur);
521         uchcom_convert_status(sc, cur);
522 }
523
524
525 static void
526 uchcom_set_dtr_rts(struct uchcom_softc *sc)
527 {
528         uint8_t val = 0;
529
530         if (sc->sc_dtr)
531                 val |= UCHCOM_DTR_MASK;
532         if (sc->sc_rts)
533                 val |= UCHCOM_RTS_MASK;
534
535         if (sc->sc_version < UCHCOM_VER_20)
536                 uchcom_set_dtr_rts_10(sc, ~val);
537         else
538                 uchcom_set_dtr_rts_20(sc, ~val);
539 }
540
541 static void
542 uchcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
543 {
544         struct uchcom_softc *sc = ucom->sc_parent;
545         uint8_t brk1;
546         uint8_t brk2;
547
548         uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2);
549         if (onoff) {
550                 /* on - clear bits */
551                 brk1 &= ~UCHCOM_BRK1_MASK;
552                 brk2 &= ~UCHCOM_BRK2_MASK;
553         } else {
554                 /* off - set bits */
555                 brk1 |= UCHCOM_BRK1_MASK;
556                 brk2 |= UCHCOM_BRK2_MASK;
557         }
558         uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2);
559 }
560
561 static int
562 uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
563 {
564         const struct uchcom_divider_record *rp;
565         uint32_t div;
566         uint32_t rem;
567         uint32_t mod;
568         uint8_t i;
569
570         /* find record */
571         for (i = 0; i != NUM_DIVIDERS; i++) {
572                 if (dividers[i].dvr_high >= rate &&
573                     dividers[i].dvr_low <= rate) {
574                         rp = &dividers[i];
575                         goto found;
576                 }
577         }
578         return (-1);
579
580 found:
581         dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
582         if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
583                 dp->dv_div = rp->dvr_divider.dv_div;
584         else {
585                 div = rp->dvr_base_clock / rate;
586                 rem = rp->dvr_base_clock % rate;
587                 if (div == 0 || div >= 0xFF)
588                         return (-1);
589                 if ((rem << 1) >= rate)
590                         div += 1;
591                 dp->dv_div = (uint8_t)-div;
592         }
593
594         mod = (UCHCOM_BPS_MOD_BASE / rate) + UCHCOM_BPS_MOD_BASE_OFS;
595         mod = mod + (mod / 2);
596
597         dp->dv_mod = (mod + 0xFF) / 0x100;
598
599         return (0);
600 }
601
602 static void
603 uchcom_set_baudrate(struct uchcom_softc *sc, uint32_t rate)
604 {
605         struct uchcom_divider dv;
606
607         if (uchcom_calc_divider_settings(&dv, rate))
608                 return;
609
610         uchcom_write_reg(sc,
611             UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
612             UCHCOM_REG_BPS_DIV, dv.dv_div);
613         uchcom_write_reg(sc,
614             UCHCOM_REG_BPS_MOD, dv.dv_mod,
615             UCHCOM_REG_BPS_PAD, 0);
616 }
617
618 /* ----------------------------------------------------------------------
619  * methods for ucom
620  */
621 static void
622 uchcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
623 {
624         struct uchcom_softc *sc = ucom->sc_parent;
625
626         DPRINTF("\n");
627
628         *lsr = sc->sc_lsr;
629         *msr = sc->sc_msr;
630 }
631
632 static void
633 uchcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
634 {
635         struct uchcom_softc *sc = ucom->sc_parent;
636
637         DPRINTF("onoff = %d\n", onoff);
638
639         sc->sc_dtr = onoff;
640         uchcom_set_dtr_rts(sc);
641 }
642
643 static void
644 uchcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
645 {
646         struct uchcom_softc *sc = ucom->sc_parent;
647
648         DPRINTF("onoff = %d\n", onoff);
649
650         sc->sc_rts = onoff;
651         uchcom_set_dtr_rts(sc);
652 }
653
654 static void
655 uchcom_cfg_open(struct ucom_softc *ucom)
656 {
657         struct uchcom_softc *sc = ucom->sc_parent;
658
659         DPRINTF("\n");
660
661         uchcom_update_version(sc);
662         uchcom_update_status(sc);
663 }
664
665 static int
666 uchcom_pre_param(struct ucom_softc *ucom, struct termios *t)
667 {
668         struct uchcom_divider dv;
669
670         switch (t->c_cflag & CSIZE) {
671         case CS8:
672                 break;
673         default:
674                 return (EIO);
675         }
676
677         if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) {
678                 return (EIO);
679         }
680         return (0);                     /* success */
681 }
682
683 static void
684 uchcom_cfg_param(struct ucom_softc *ucom, struct termios *t)
685 {
686         struct uchcom_softc *sc = ucom->sc_parent;
687
688         uchcom_get_version(sc, 0);
689         uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0);
690         uchcom_set_baudrate(sc, t->c_ospeed);
691         uchcom_read_reg(sc, 0x18, 0, 0x25, 0);
692         uchcom_write_reg(sc, 0x18, 0x50, 0x25, 0x00);
693         uchcom_update_status(sc);
694         uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0x501f, 0xd90a);
695         uchcom_set_baudrate(sc, t->c_ospeed);
696         uchcom_set_dtr_rts(sc);
697         uchcom_update_status(sc);
698 }
699
700 static void
701 uchcom_start_read(struct ucom_softc *ucom)
702 {
703         struct uchcom_softc *sc = ucom->sc_parent;
704
705         /* start interrupt endpoint */
706         usbd_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
707
708         /* start read endpoint */
709         usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
710 }
711
712 static void
713 uchcom_stop_read(struct ucom_softc *ucom)
714 {
715         struct uchcom_softc *sc = ucom->sc_parent;
716
717         /* stop interrupt endpoint */
718         usbd_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
719
720         /* stop read endpoint */
721         usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
722 }
723
724 static void
725 uchcom_start_write(struct ucom_softc *ucom)
726 {
727         struct uchcom_softc *sc = ucom->sc_parent;
728
729         usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
730 }
731
732 static void
733 uchcom_stop_write(struct ucom_softc *ucom)
734 {
735         struct uchcom_softc *sc = ucom->sc_parent;
736
737         usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
738 }
739
740 /* ----------------------------------------------------------------------
741  * callback when the modem status is changed.
742  */
743 static void
744 uchcom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
745 {
746         struct uchcom_softc *sc = usbd_xfer_softc(xfer);
747         struct usb_page_cache *pc;
748         uint8_t buf[UCHCOM_INTR_LEAST];
749         int actlen;
750
751         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
752
753         switch (USB_GET_STATE(xfer)) {
754         case USB_ST_TRANSFERRED:
755
756                 DPRINTF("actlen = %u\n", actlen);
757
758                 if (actlen >= UCHCOM_INTR_LEAST) {
759                         pc = usbd_xfer_get_frame(xfer, 0);
760                         usbd_copy_out(pc, 0, buf, UCHCOM_INTR_LEAST);
761
762                         DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n",
763                             (unsigned)buf[0], (unsigned)buf[1],
764                             (unsigned)buf[2], (unsigned)buf[3]);
765
766                         uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
767                         ucom_status_change(&sc->sc_ucom);
768                 }
769         case USB_ST_SETUP:
770 tr_setup:
771                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
772                 usbd_transfer_submit(xfer);
773                 break;
774
775         default:                        /* Error */
776                 if (error != USB_ERR_CANCELLED) {
777                         /* try to clear stall first */
778                         usbd_xfer_set_stall(xfer);
779                         goto tr_setup;
780                 }
781                 break;
782         }
783 }
784
785 static void
786 uchcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
787 {
788         struct uchcom_softc *sc = usbd_xfer_softc(xfer);
789         struct usb_page_cache *pc;
790         uint32_t actlen;
791
792         switch (USB_GET_STATE(xfer)) {
793         case USB_ST_SETUP:
794         case USB_ST_TRANSFERRED:
795 tr_setup:
796                 pc = usbd_xfer_get_frame(xfer, 0);
797                 if (ucom_get_data(&sc->sc_ucom, pc, 0,
798                     usbd_xfer_max_len(xfer), &actlen)) {
799
800                         DPRINTF("actlen = %d\n", actlen);
801
802                         usbd_xfer_set_frame_len(xfer, 0, actlen);
803                         usbd_transfer_submit(xfer);
804                 }
805                 break;
806
807         default:                        /* Error */
808                 if (error != USB_ERR_CANCELLED) {
809                         /* try to clear stall first */
810                         usbd_xfer_set_stall(xfer);
811                         goto tr_setup;
812                 }
813                 break;
814         }
815 }
816
817 static void
818 uchcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
819 {
820         struct uchcom_softc *sc = usbd_xfer_softc(xfer);
821         struct usb_page_cache *pc;
822         int actlen;
823
824         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
825
826         switch (USB_GET_STATE(xfer)) {
827         case USB_ST_TRANSFERRED:
828
829                 if (actlen > 0) {
830                         pc = usbd_xfer_get_frame(xfer, 0);
831                         ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
832                 }
833
834         case USB_ST_SETUP:
835 tr_setup:
836                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
837                 usbd_transfer_submit(xfer);
838                 break;
839
840         default:                        /* Error */
841                 if (error != USB_ERR_CANCELLED) {
842                         /* try to clear stall first */
843                         usbd_xfer_set_stall(xfer);
844                         goto tr_setup;
845                 }
846                 break;
847         }
848 }
849
850 static void
851 uchcom_poll(struct ucom_softc *ucom)
852 {
853         struct uchcom_softc *sc = ucom->sc_parent;
854         usbd_transfer_poll(sc->sc_xfer, UCHCOM_N_TRANSFER);
855 }
856
857 static device_method_t uchcom_methods[] = {
858         /* Device interface */
859         DEVMETHOD(device_probe, uchcom_probe),
860         DEVMETHOD(device_attach, uchcom_attach),
861         DEVMETHOD(device_detach, uchcom_detach),
862         DEVMETHOD_END
863 };
864
865 static driver_t uchcom_driver = {
866         .name = "uchcom",
867         .methods = uchcom_methods,
868         .size = sizeof(struct uchcom_softc)
869 };
870
871 static devclass_t uchcom_devclass;
872
873 DRIVER_MODULE(uchcom, uhub, uchcom_driver, uchcom_devclass, NULL, 0);
874 MODULE_DEPEND(uchcom, ucom, 1, 1, 1);
875 MODULE_DEPEND(uchcom, usb, 1, 1, 1);
876 MODULE_VERSION(uchcom, 1);