]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/serial/uchcom.c
Update ena-com HAL to v1.1.4.3 and update driver accordingly
[FreeBSD/FreeBSD.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_RWTUN,
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    nitems(dividers)
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         /* XXX Note: sc_lsr is always zero */
629         *lsr = sc->sc_lsr;
630         *msr = sc->sc_msr;
631 }
632
633 static void
634 uchcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
635 {
636         struct uchcom_softc *sc = ucom->sc_parent;
637
638         DPRINTF("onoff = %d\n", onoff);
639
640         sc->sc_dtr = onoff;
641         uchcom_set_dtr_rts(sc);
642 }
643
644 static void
645 uchcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
646 {
647         struct uchcom_softc *sc = ucom->sc_parent;
648
649         DPRINTF("onoff = %d\n", onoff);
650
651         sc->sc_rts = onoff;
652         uchcom_set_dtr_rts(sc);
653 }
654
655 static void
656 uchcom_cfg_open(struct ucom_softc *ucom)
657 {
658         struct uchcom_softc *sc = ucom->sc_parent;
659
660         DPRINTF("\n");
661
662         uchcom_update_version(sc);
663         uchcom_update_status(sc);
664 }
665
666 static int
667 uchcom_pre_param(struct ucom_softc *ucom, struct termios *t)
668 {
669         struct uchcom_divider dv;
670
671         switch (t->c_cflag & CSIZE) {
672         case CS8:
673                 break;
674         default:
675                 return (EIO);
676         }
677
678         if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) {
679                 return (EIO);
680         }
681         return (0);                     /* success */
682 }
683
684 static void
685 uchcom_cfg_param(struct ucom_softc *ucom, struct termios *t)
686 {
687         struct uchcom_softc *sc = ucom->sc_parent;
688
689         uchcom_get_version(sc, 0);
690         uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0);
691         uchcom_set_baudrate(sc, t->c_ospeed);
692         uchcom_read_reg(sc, 0x18, 0, 0x25, 0);
693         uchcom_write_reg(sc, 0x18, 0x50, 0x25, 0x00);
694         uchcom_update_status(sc);
695         uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0x501f, 0xd90a);
696         uchcom_set_baudrate(sc, t->c_ospeed);
697         uchcom_set_dtr_rts(sc);
698         uchcom_update_status(sc);
699 }
700
701 static void
702 uchcom_start_read(struct ucom_softc *ucom)
703 {
704         struct uchcom_softc *sc = ucom->sc_parent;
705
706         /* start interrupt endpoint */
707         usbd_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
708
709         /* start read endpoint */
710         usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
711 }
712
713 static void
714 uchcom_stop_read(struct ucom_softc *ucom)
715 {
716         struct uchcom_softc *sc = ucom->sc_parent;
717
718         /* stop interrupt endpoint */
719         usbd_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
720
721         /* stop read endpoint */
722         usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
723 }
724
725 static void
726 uchcom_start_write(struct ucom_softc *ucom)
727 {
728         struct uchcom_softc *sc = ucom->sc_parent;
729
730         usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
731 }
732
733 static void
734 uchcom_stop_write(struct ucom_softc *ucom)
735 {
736         struct uchcom_softc *sc = ucom->sc_parent;
737
738         usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
739 }
740
741 /* ----------------------------------------------------------------------
742  * callback when the modem status is changed.
743  */
744 static void
745 uchcom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
746 {
747         struct uchcom_softc *sc = usbd_xfer_softc(xfer);
748         struct usb_page_cache *pc;
749         uint8_t buf[UCHCOM_INTR_LEAST];
750         int actlen;
751
752         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
753
754         switch (USB_GET_STATE(xfer)) {
755         case USB_ST_TRANSFERRED:
756
757                 DPRINTF("actlen = %u\n", actlen);
758
759                 if (actlen >= UCHCOM_INTR_LEAST) {
760                         pc = usbd_xfer_get_frame(xfer, 0);
761                         usbd_copy_out(pc, 0, buf, UCHCOM_INTR_LEAST);
762
763                         DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n",
764                             (unsigned)buf[0], (unsigned)buf[1],
765                             (unsigned)buf[2], (unsigned)buf[3]);
766
767                         uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
768                         ucom_status_change(&sc->sc_ucom);
769                 }
770         case USB_ST_SETUP:
771 tr_setup:
772                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
773                 usbd_transfer_submit(xfer);
774                 break;
775
776         default:                        /* Error */
777                 if (error != USB_ERR_CANCELLED) {
778                         /* try to clear stall first */
779                         usbd_xfer_set_stall(xfer);
780                         goto tr_setup;
781                 }
782                 break;
783         }
784 }
785
786 static void
787 uchcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
788 {
789         struct uchcom_softc *sc = usbd_xfer_softc(xfer);
790         struct usb_page_cache *pc;
791         uint32_t actlen;
792
793         switch (USB_GET_STATE(xfer)) {
794         case USB_ST_SETUP:
795         case USB_ST_TRANSFERRED:
796 tr_setup:
797                 pc = usbd_xfer_get_frame(xfer, 0);
798                 if (ucom_get_data(&sc->sc_ucom, pc, 0,
799                     usbd_xfer_max_len(xfer), &actlen)) {
800
801                         DPRINTF("actlen = %d\n", actlen);
802
803                         usbd_xfer_set_frame_len(xfer, 0, actlen);
804                         usbd_transfer_submit(xfer);
805                 }
806                 break;
807
808         default:                        /* Error */
809                 if (error != USB_ERR_CANCELLED) {
810                         /* try to clear stall first */
811                         usbd_xfer_set_stall(xfer);
812                         goto tr_setup;
813                 }
814                 break;
815         }
816 }
817
818 static void
819 uchcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
820 {
821         struct uchcom_softc *sc = usbd_xfer_softc(xfer);
822         struct usb_page_cache *pc;
823         int actlen;
824
825         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
826
827         switch (USB_GET_STATE(xfer)) {
828         case USB_ST_TRANSFERRED:
829
830                 if (actlen > 0) {
831                         pc = usbd_xfer_get_frame(xfer, 0);
832                         ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
833                 }
834
835         case USB_ST_SETUP:
836 tr_setup:
837                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
838                 usbd_transfer_submit(xfer);
839                 break;
840
841         default:                        /* Error */
842                 if (error != USB_ERR_CANCELLED) {
843                         /* try to clear stall first */
844                         usbd_xfer_set_stall(xfer);
845                         goto tr_setup;
846                 }
847                 break;
848         }
849 }
850
851 static void
852 uchcom_poll(struct ucom_softc *ucom)
853 {
854         struct uchcom_softc *sc = ucom->sc_parent;
855         usbd_transfer_poll(sc->sc_xfer, UCHCOM_N_TRANSFER);
856 }
857
858 static device_method_t uchcom_methods[] = {
859         /* Device interface */
860         DEVMETHOD(device_probe, uchcom_probe),
861         DEVMETHOD(device_attach, uchcom_attach),
862         DEVMETHOD(device_detach, uchcom_detach),
863         DEVMETHOD_END
864 };
865
866 static driver_t uchcom_driver = {
867         .name = "uchcom",
868         .methods = uchcom_methods,
869         .size = sizeof(struct uchcom_softc)
870 };
871
872 static devclass_t uchcom_devclass;
873
874 DRIVER_MODULE(uchcom, uhub, uchcom_driver, uchcom_devclass, NULL, 0);
875 MODULE_DEPEND(uchcom, ucom, 1, 1, 1);
876 MODULE_DEPEND(uchcom, usb, 1, 1, 1);
877 MODULE_VERSION(uchcom, 1);
878 USB_PNP_HOST_INFO(uchcom_devs);