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