9c9bc7e29cc4c2de9cb207c9362610afabb7b186
[dragonfly.git] / sys / dev / usbmisc / uchcom / uchcom.c
1 /* $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $ */
2
3 /*
4  * Copyright (c) 2007 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Takuya SHIOZAKI (tshiozak@netbsd.org).
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38
39 /*
40  * driver for WinChipHead CH341/340, the worst USB-serial chip in the world.
41  */
42
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/malloc.h>
47 #include <sys/conf.h>
48 #include <sys/tty.h>
49 #include <sys/device.h>
50 #include <sys/types.h>
51 #include <sys/bus.h>
52 #include <sys/module.h>
53
54 #include <bus/usb/usb.h>
55 #include <bus/usb/usbdi.h>
56 #include <bus/usb/usbdi_util.h>
57
58 #include <dev/usbmisc/ucom/ucomvar.h>
59
60 #ifdef UCHCOM_DEBUG
61 #define DPRINTFN(n, x)  do { if (uchcomdebug > (n)) kprintf x; } while (0)
62 int     uchcomdebug = 0;
63 #else
64 #define DPRINTFN(n, x)
65 #endif
66 #define DPRINTF(x) DPRINTFN(0, x)
67
68 #define UCHCOM_IFACE_INDEX      0
69 #define UCHCOM_CONFIG_INDEX     0
70
71 #define UCHCOM_REV_CH340        0x0250
72 #define UCHCOM_INPUT_BUF_SIZE   8
73
74 #define UCHCOM_REQ_GET_VERSION  0x5F
75 #define UCHCOM_REQ_READ_REG     0x95
76 #define UCHCOM_REQ_WRITE_REG    0x9A
77 #define UCHCOM_REQ_RESET        0xA1
78 #define UCHCOM_REQ_SET_DTRRTS   0xA4
79
80 #define UCHCOM_REG_STAT1        0x06
81 #define UCHCOM_REG_STAT2        0x07
82 #define UCHCOM_REG_BPS_PRE      0x12
83 #define UCHCOM_REG_BPS_DIV      0x13
84 #define UCHCOM_REG_BPS_MOD      0x14
85 #define UCHCOM_REG_BPS_PAD      0x0F
86 #define UCHCOM_REG_BREAK1       0x05
87 #define UCHCOM_REG_BREAK2       0x18
88 #define UCHCOM_REG_LCR1         0x18
89 #define UCHCOM_REG_LCR2         0x25
90
91 #define UCHCOM_VER_20           0x20
92
93 #define UCHCOM_BASE_UNKNOWN     0
94 #define UCHCOM_BPS_MOD_BASE     20000000
95 #define UCHCOM_BPS_MOD_BASE_OFS 1100
96
97 #define UCHCOM_DTR_MASK         0x20
98 #define UCHCOM_RTS_MASK         0x40
99
100 #define UCHCOM_BRK1_MASK        0x01
101 #define UCHCOM_BRK2_MASK        0x40
102
103 #define UCHCOM_LCR1_MASK        0xAF
104 #define UCHCOM_LCR2_MASK        0x07
105 #define UCHCOM_LCR1_PARENB      0x80
106 #define UCHCOM_LCR2_PAREVEN     0x07
107 #define UCHCOM_LCR2_PARODD      0x06
108 #define UCHCOM_LCR2_PARMARK     0x05
109 #define UCHCOM_LCR2_PARSPACE    0x04
110
111 #define UCHCOM_INTR_STAT1       0x02
112 #define UCHCOM_INTR_STAT2       0x03
113 #define UCHCOM_INTR_LEAST       4
114
115 #define UCHCOMIBUFSIZE 256
116 #define UCHCOMOBUFSIZE 256
117
118 struct uchcom_softc
119 {
120         struct ucom_softc       sc_ucom;
121
122         int                     sc_intr_endpoint;
123         int                     sc_intr_size;
124         usbd_pipe_handle        sc_intr_pipe;
125         u_char                  *sc_intr_buf;
126
127         uint8_t                 sc_version;
128         int                     sc_dtr;
129         int                     sc_rts;
130         u_char                  sc_lsr;
131         u_char                  sc_msr;
132         int                     sc_lcr1;
133         int                     sc_lcr2;
134 };
135
136 struct uchcom_endpoints
137 {
138         int             ep_bulkin;
139         int             ep_bulkout;
140         int             ep_intr;
141         int             ep_intr_size;
142 };
143
144 struct uchcom_divider
145 {
146         uint8_t         dv_prescaler;
147         uint8_t         dv_div;
148         uint8_t         dv_mod;
149 };
150
151 struct uchcom_divider_record
152 {
153         uint32_t                dvr_high;
154         uint32_t                dvr_low;
155         uint32_t                dvr_base_clock;
156         struct uchcom_divider   dvr_divider;
157 };
158
159 static const struct uchcom_divider_record dividers[] =
160 {
161         {  307200, 307200, UCHCOM_BASE_UNKNOWN, { 7, 0xD9, 0 } },
162         {  921600, 921600, UCHCOM_BASE_UNKNOWN, { 7, 0xF3, 0 } },
163         { 2999999,  23530,             6000000, { 3,    0, 0 } },
164         {   23529,   2942,              750000, { 2,    0, 0 } },
165         {    2941,    368,               93750, { 1,    0, 0 } },
166         {     367,      1,               11719, { 0,    0, 0 } },
167 };
168 #define NUM_DIVIDERS    (sizeof (dividers) / sizeof (dividers[0]))
169
170 static void     uchcom_get_status(void *, int, u_char *, u_char *);
171 static void     uchcom_set(void *, int, int, int);
172 static int      uchcom_param(void *, int, struct termios *);
173 static int      uchcom_open(void *, int);
174 static void     uchcom_close(void *, int);
175 static void     uchcom_intr(usbd_xfer_handle, usbd_private_handle,
176                     usbd_status);
177
178 static int      set_config(struct uchcom_softc *);
179 static int      find_ifaces(struct uchcom_softc *, usbd_interface_handle *);
180 static int      find_endpoints(struct uchcom_softc *,
181                     struct uchcom_endpoints *);
182 static void     close_intr_pipe(struct uchcom_softc *);
183
184 static device_probe_t uchcom_match;
185 static device_attach_t uchcom_attach;
186 static device_detach_t uchcom_detach;
187
188 static device_method_t uchcom_methods[] = {
189         DEVMETHOD(device_probe, uchcom_match),
190         DEVMETHOD(device_attach, uchcom_attach),
191         DEVMETHOD(device_detach, uchcom_detach),
192         { 0, 0 }
193 };
194
195 static driver_t uchcom_driver = {
196         "ucom",
197         uchcom_methods,
198         sizeof (struct uchcom_softc)
199 };
200
201 DRIVER_MODULE(uchcom, uhub, uchcom_driver, ucom_devclass, usbd_driver_load, NULL);
202 MODULE_DEPEND(uchcom, usb, 1, 1, 1);
203 MODULE_DEPEND(uchcom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
204 MODULE_VERSION(uchcom, 1);
205
206 struct ucom_callback uchcom_callback = {
207         .ucom_get_status        = uchcom_get_status,
208         .ucom_set               = uchcom_set,
209         .ucom_param             = uchcom_param,
210         .ucom_ioctl             = NULL,
211         .ucom_open              = uchcom_open,
212         .ucom_close             = uchcom_close,
213         .ucom_read              = NULL,
214         .ucom_write             = NULL,
215 };
216
217 static const struct usb_devno uchcom_devs[] = {
218         { USB_DEVICE(0x4348, 0x5523) }, /* QinHeng Electronics CH341/340 */
219 };
220 #define uchcom_lookup(v, p)     usb_lookup(uchcom_devs, v, p)
221
222 /* ----------------------------------------------------------------------
223  * driver entry points
224  */
225
226 static int
227 uchcom_match(device_t self)
228 {
229         struct usb_attach_arg *uaa = device_get_ivars(self);
230
231         if (uaa->iface != NULL)
232                 return UMATCH_NONE;
233
234         return (uchcom_lookup(uaa->vendor, uaa->product) != NULL ?
235                 UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
236 }
237
238 static int
239 uchcom_attach(device_t self)
240 {
241         struct uchcom_softc *sc = device_get_softc(self);
242         struct usb_attach_arg *uaa = device_get_ivars(self);
243         struct ucom_softc *ucom = &sc->sc_ucom;
244         usbd_device_handle dev = uaa->device;
245         struct uchcom_endpoints endpoints;
246
247         bzero(sc, sizeof (struct uchcom_softc));
248         ucom->sc_dev = self;
249         ucom->sc_udev = dev;
250         ucom->sc_dying = 0;
251         sc->sc_dtr = sc->sc_rts = -1;
252         sc->sc_lsr = sc->sc_msr = 0;
253
254         DPRINTF(("\n\nuchcom attach: sc=%p\n", sc));
255
256         if (set_config(sc))
257                 goto failed;
258
259         switch (uaa->release) {
260         case UCHCOM_REV_CH340:
261                 device_printf(ucom->sc_dev, "CH340\n");
262                 break;
263         default:
264                 device_printf(ucom->sc_dev, "CH341\n");
265                 break;
266         }
267
268         if (find_ifaces(sc, &ucom->sc_iface))
269                 goto failed;
270
271         if (find_endpoints(sc, &endpoints))
272                 goto failed;
273
274         sc->sc_intr_endpoint = endpoints.ep_intr;
275         sc->sc_intr_size = endpoints.ep_intr_size;
276
277         /* setup ucom layer */
278         ucom->sc_parent = sc;
279         ucom->sc_portno = UCOM_UNK_PORTNO;
280         ucom->sc_bulkin_no = endpoints.ep_bulkin;
281         ucom->sc_bulkout_no = endpoints.ep_bulkout;
282         ucom->sc_ibufsize = UCHCOMIBUFSIZE;
283         ucom->sc_obufsize = UCHCOMOBUFSIZE;
284         ucom->sc_ibufsizepad = UCHCOMIBUFSIZE;
285         ucom->sc_opkthdrlen = 0;
286         ucom->sc_callback = &uchcom_callback;
287
288         usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, ucom->sc_udev,
289             ucom->sc_dev);
290         ucom_attach(&sc->sc_ucom);
291
292         return 0;
293
294 failed:
295         ucom->sc_dying = 1;
296         return ENXIO;
297 }
298
299 static int
300 uchcom_detach(device_t self)
301 {
302         struct uchcom_softc *sc = device_get_softc(self);
303         int rv = 0;
304
305         DPRINTF(("uchcom_detach: sc=%p flags=%d\n", sc, flags));
306
307         close_intr_pipe(sc);
308         sc->sc_ucom.sc_dying = 1;
309         rv = ucom_detach(&sc->sc_ucom);
310         usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_ucom.sc_udev,
311                            sc->sc_ucom.sc_dev);
312
313         return rv;
314 }
315
316 #if 0 /* not yet */
317 int
318 uchcom_activate(struct device *self, enum devact act)
319 {
320         struct uchcom_softc *sc = (struct uchcom_softc *)self;
321         int rv = 0;
322
323         switch (act) {
324         case DVACT_ACTIVATE:
325                 rv = EOPNOTSUPP;
326                 break;
327         case DVACT_DEACTIVATE:
328                 uchcom_close_intr_pipe(sc);
329                 sc->sc_dying = 1;
330                 if (sc->sc_subdev != NULL)
331                         rv = config_deactivate(sc->sc_subdev);
332                 break;
333         }
334         return rv;
335 }
336 #endif
337
338 int
339 set_config(struct uchcom_softc *sc)
340 {
341         usbd_status err;
342
343         err = usbd_set_config_index(sc->sc_ucom.sc_udev,
344                                     UCHCOM_CONFIG_INDEX, 1);
345         if (err) {
346                 device_printf(sc->sc_ucom.sc_dev, "failed to set "
347                               "configuration: %s\n", usbd_errstr(err));
348                 return -1;
349         }
350
351         return 0;
352 }
353
354 int
355 find_ifaces(struct uchcom_softc *sc, usbd_interface_handle *riface)
356 {
357         usbd_status err;
358
359         err = usbd_device2interface_handle(sc->sc_ucom.sc_udev,
360                                            UCHCOM_IFACE_INDEX, riface);
361         if (err) {
362                 device_printf(sc->sc_ucom.sc_dev, "failed to get interface: "
363                               "%s\n", usbd_errstr(err));
364                 return -1;
365         }
366
367         return 0;
368 }
369
370 int
371 find_endpoints(struct uchcom_softc *sc,
372     struct uchcom_endpoints *endpoints)
373 {
374         int i, bin=-1, bout=-1, intr=-1, isize=0;
375         usb_interface_descriptor_t *id;
376         usb_endpoint_descriptor_t *ed;
377
378         id = usbd_get_interface_descriptor(sc->sc_ucom.sc_iface);
379
380         for (i = 0; i < id->bNumEndpoints; i++) {
381                 ed = usbd_interface2endpoint_descriptor(sc->sc_ucom.sc_iface,
382                                                         i);
383                 if (ed == NULL) {
384                         device_printf(sc->sc_ucom.sc_dev,
385                                       "no endpoint descriptor for %d\n", i);
386                         return -1;
387                 }
388
389                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
390                     UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
391                         intr = ed->bEndpointAddress;
392                         isize = UGETW(ed->wMaxPacketSize);
393                 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
394                            UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
395                         bin = ed->bEndpointAddress;
396                 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
397                            UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
398                         bout = ed->bEndpointAddress;
399                 }
400         }
401
402         if (intr == -1 || bin == -1 || bout == -1) {
403                 if (intr == -1) {
404                         device_printf(sc->sc_ucom.sc_dev,
405                                       "no interrupt end point\n");
406                 }
407                 if (bin == -1) {
408                         device_printf(sc->sc_ucom.sc_dev,
409                                       "no data bulk in end point\n");
410                 }
411                 if (bout == -1) {
412                         device_printf(sc->sc_ucom.sc_dev,
413                                       "no data bulk out end point\n");
414                 }
415                 return -1;
416         }
417         if (isize < UCHCOM_INTR_LEAST) {
418                 device_printf(sc->sc_ucom.sc_dev, "intr pipe is too short\n");
419                 return -1;
420         }
421
422         DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n",
423                  device_get_nameunit(sc->sc_ucom.sc_dev), bin, bout, intr,
424                  isize));
425
426         endpoints->ep_intr = intr;
427         endpoints->ep_intr_size = isize;
428         endpoints->ep_bulkin = bin;
429         endpoints->ep_bulkout = bout;
430
431         return 0;
432 }
433
434
435 /* ----------------------------------------------------------------------
436  * low level i/o
437  */
438
439 static __inline usbd_status
440 generic_control_out(struct uchcom_softc *sc, uint8_t reqno,
441     uint16_t value, uint16_t index)
442 {
443         usb_device_request_t req;
444
445         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
446         req.bRequest = reqno;
447         USETW(req.wValue, value);
448         USETW(req.wIndex, index);
449         USETW(req.wLength, 0);
450
451         return usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
452 }
453
454 static __inline usbd_status
455 generic_control_in(struct uchcom_softc *sc, uint8_t reqno,
456     uint16_t value, uint16_t index, void *buf, int buflen, int *actlen)
457 {
458         usb_device_request_t req;
459
460         req.bmRequestType = UT_READ_VENDOR_DEVICE;
461         req.bRequest = reqno;
462         USETW(req.wValue, value);
463         USETW(req.wIndex, index);
464         USETW(req.wLength, (uint16_t)buflen);
465
466         return usbd_do_request_flags(sc->sc_ucom.sc_udev, &req, buf,
467                                      USBD_SHORT_XFER_OK, actlen,
468                                      USBD_DEFAULT_TIMEOUT);
469 }
470
471 static __inline usbd_status
472 write_reg(struct uchcom_softc *sc,
473     uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
474 {
475         DPRINTF(("uchcom: write reg 0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
476                  (unsigned)reg1, (unsigned)val1,
477                  (unsigned)reg2, (unsigned)val2));
478         return generic_control_out(
479                 sc, UCHCOM_REQ_WRITE_REG,
480                 reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8));
481 }
482
483 static __inline usbd_status
484 read_reg(struct uchcom_softc *sc,
485     uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
486 {
487         uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
488         usbd_status err;
489         int actin;
490
491         err = generic_control_in(
492                 sc, UCHCOM_REQ_READ_REG,
493                 reg1|((uint16_t)reg2<<8), 0, buf, sizeof buf, &actin);
494         if (err)
495                 return err;
496
497         DPRINTF(("uchcom: read reg 0x%02X->0x%02X, 0x%02X->0x%02X\n",
498                  (unsigned)reg1, (unsigned)buf[0],
499                  (unsigned)reg2, (unsigned)buf[1]));
500
501         if (rval1) *rval1 = buf[0];
502         if (rval2) *rval2 = buf[1];
503
504         return USBD_NORMAL_COMPLETION;
505 }
506
507 static __inline usbd_status
508 get_version(struct uchcom_softc *sc, uint8_t *rver)
509 {
510         uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
511         usbd_status err;
512         int actin;
513
514         err = generic_control_in(
515                 sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof buf, &actin);
516         if (err)
517                 return err;
518
519         if (rver) *rver = buf[0];
520
521         return USBD_NORMAL_COMPLETION;
522 }
523
524 static __inline usbd_status
525 read_status(struct uchcom_softc *sc, uint8_t *rval)
526 {
527         return read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL);
528 }
529
530 static __inline usbd_status
531 set_dtrrts_10(struct uchcom_softc *sc, uint8_t val)
532 {
533         return write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val);
534 }
535
536 static __inline usbd_status
537 set_dtrrts_20(struct uchcom_softc *sc, uint8_t val)
538 {
539         return generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
540 }
541
542
543 /* ----------------------------------------------------------------------
544  * middle layer
545  */
546
547 static int
548 update_version(struct uchcom_softc *sc)
549 {
550         usbd_status err;
551
552         err = get_version(sc, &sc->sc_version);
553         if (err) {
554                 device_printf(sc->sc_ucom.sc_dev, "cannot get version: %s\n",
555                               usbd_errstr(err));
556                 return EIO;
557         }
558
559         return 0;
560 }
561
562 static void
563 convert_status(struct uchcom_softc *sc, uint8_t cur)
564 {
565         sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
566         sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
567
568         cur = ~cur & 0x0F;
569         sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
570 }
571
572 static int
573 update_status(struct uchcom_softc *sc)
574 {
575         usbd_status err;
576         uint8_t cur;
577
578         err = read_status(sc, &cur);
579         if (err) {
580                 device_printf(sc->sc_ucom.sc_dev, "cannot update status: %s\n",
581                               usbd_errstr(err));
582                 return EIO;
583         }
584         convert_status(sc, cur);
585
586         return 0;
587 }
588
589
590 static int
591 set_dtrrts(struct uchcom_softc *sc, int dtr, int rts)
592 {
593         usbd_status err;
594         uint8_t val = 0;
595
596         if (dtr) val |= UCHCOM_DTR_MASK;
597         if (rts) val |= UCHCOM_RTS_MASK;
598
599         if (sc->sc_version < UCHCOM_VER_20)
600                 err = set_dtrrts_10(sc, ~val);
601         else
602                 err = set_dtrrts_20(sc, ~val);
603
604         if (err) {
605                 device_printf(sc->sc_ucom.sc_dev, "cannot set DTR/RTS: %s\n",
606                               usbd_errstr(err));
607                 return EIO;
608         }
609
610         return 0;
611 }
612
613 static int
614 set_break(struct uchcom_softc *sc, int onoff)
615 {
616         usbd_status err;
617         uint8_t brk1, brk2;
618
619         err = read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2,
620             &brk2);
621         if (err)
622                 return EIO;
623         if (onoff) {
624                 /* on - clear bits */
625                 brk1 &= ~UCHCOM_BRK1_MASK;
626                 brk2 &= ~UCHCOM_BRK2_MASK;
627         } else {
628                 /* off - set bits */
629                 brk1 |= UCHCOM_BRK1_MASK;
630                 brk2 |= UCHCOM_BRK2_MASK;
631         }
632         err = write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2,
633             brk2);
634         if (err)
635                 return EIO;
636
637         return 0;
638 }
639
640 static int
641 calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
642 {
643         int i;
644         const struct uchcom_divider_record *rp;
645         uint32_t div, rem, mod;
646
647         /* find record */
648         for (i=0; i<NUM_DIVIDERS; i++) {
649                 if (dividers[i].dvr_high >= rate &&
650                     dividers[i].dvr_low <= rate) {
651                         rp = &dividers[i];
652                         goto found;
653                 }
654         }
655         return -1;
656
657 found:
658         dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
659         if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
660                 dp->dv_div = rp->dvr_divider.dv_div;
661         else {
662                 div = rp->dvr_base_clock / rate;
663                 rem = rp->dvr_base_clock % rate;
664                 if (div==0 || div>=0xFF)
665                         return -1;
666                 if ((rem<<1) >= rate)
667                         div += 1;
668                 dp->dv_div = (uint8_t)-div;
669         }
670
671         mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS;
672         mod = mod + mod/2;
673
674         dp->dv_mod = mod / 0x100;
675
676         return 0;
677 }
678
679 static int
680 set_dte_rate(struct uchcom_softc *sc, uint32_t rate)
681 {
682         usbd_status err;
683         struct uchcom_divider dv;
684
685         if (calc_divider_settings(&dv, rate))
686                 return EINVAL;
687
688         if ((err = write_reg(sc,
689                              UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
690                              UCHCOM_REG_BPS_DIV, dv.dv_div)) ||
691             (err = write_reg(sc,
692                              UCHCOM_REG_BPS_MOD, dv.dv_mod,
693                              UCHCOM_REG_BPS_PAD, 0))) {
694                 device_printf(sc->sc_ucom.sc_dev, "cannot set DTE rate: %s\n",
695                               usbd_errstr(err));
696                 return EIO;
697         }
698
699         return 0;
700 }
701
702 static int
703 set_line_control(struct uchcom_softc *sc, tcflag_t cflag)
704 {
705         usbd_status err;
706         uint8_t lcr1 = 0, lcr2 = 0;
707
708         err = read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2);
709         if (err) {
710                 device_printf(sc->sc_ucom.sc_dev, "cannot get LCR: %s\n",
711                               usbd_errstr(err));
712                 return EIO;
713         }
714
715         lcr1 &= ~UCHCOM_LCR1_MASK;
716         lcr2 &= ~UCHCOM_LCR2_MASK;
717
718         /*
719          * XXX: it is difficult to handle the line control appropriately:
720          *   - CS8, !CSTOPB and any parity mode seems ok, but
721          *   - the chip doesn't have the function to calculate parity
722          *     in !CS8 mode.
723          *   - it is unclear that the chip supports CS5,6 mode.
724          *   - it is unclear how to handle stop bits.
725          */
726
727         switch (ISSET(cflag, CSIZE)) {
728         case CS5:
729         case CS6:
730         case CS7:
731                 return EINVAL;
732         case CS8:
733                 break;
734         }
735
736         if (ISSET(cflag, PARENB)) {
737                 lcr1 |= UCHCOM_LCR1_PARENB;
738                 if (ISSET(cflag, PARODD))
739                         lcr2 |= UCHCOM_LCR2_PARODD;
740                 else
741                         lcr2 |= UCHCOM_LCR2_PAREVEN;
742         }
743
744         err = write_reg(sc, UCHCOM_REG_LCR1, lcr1, UCHCOM_REG_LCR2,
745             lcr2);
746         if (err) {
747                 device_printf(sc->sc_ucom.sc_dev, "cannot set LCR: %s\n",
748                               usbd_errstr(err));
749                 return EIO;
750         }
751
752         return 0;
753 }
754
755 static int
756 clear_chip(struct uchcom_softc *sc)
757 {
758         usbd_status err;
759
760         DPRINTF(("%s: clear\n", sc->sc_dev.dv_xname));
761         err = generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0);
762         if (err) {
763                 device_printf(sc->sc_ucom.sc_dev, "cannot clear: %s\n",
764                               usbd_errstr(err));
765                 return EIO;
766         }
767
768         return 0;
769 }
770
771 static int
772 reset_chip(struct uchcom_softc *sc)
773 {
774         usbd_status err;
775         uint8_t lcr1, lcr2, pre, div, mod;
776         uint16_t val=0, idx=0;
777
778         err = read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2);
779         if (err)
780                 goto failed;
781
782         err = read_reg(sc, UCHCOM_REG_BPS_PRE, &pre, UCHCOM_REG_BPS_DIV,
783             &div);
784         if (err)
785                 goto failed;
786
787         err = read_reg(sc, UCHCOM_REG_BPS_MOD, &mod, UCHCOM_REG_BPS_PAD,
788             NULL);
789         if (err)
790                 goto failed;
791
792         val |= (uint16_t)(lcr1&0xF0) << 8;
793         val |= 0x01;
794         val |= (uint16_t)(lcr2&0x0F) << 8;
795         val |= 0x02;
796         idx |= pre & 0x07;
797         val |= 0x04;
798         idx |= (uint16_t)div << 8;
799         val |= 0x08;
800         idx |= mod & 0xF8;
801         val |= 0x10;
802
803         DPRINTF(("%s: reset v=0x%04X, i=0x%04X\n",
804                  device_get_nameunit(sc->sc_ucom.sc_dev), val, idx));
805
806         err = generic_control_out(sc, UCHCOM_REQ_RESET, val, idx);
807         if (err)
808                 goto failed;
809
810         return 0;
811
812 failed:
813         device_printf(sc->sc_ucom.sc_dev, "cannot reset: %s\n",
814                       usbd_errstr(err));
815         return EIO;
816 }
817
818 static int
819 setup_comm(struct uchcom_softc *sc)
820 {
821         int ret;
822
823         ret = update_version(sc);
824         if (ret)
825                 return ret;
826
827         ret = clear_chip(sc);
828         if (ret)
829                 return ret;
830
831         ret = set_dte_rate(sc, TTYDEF_SPEED);
832         if (ret)
833                 return ret;
834
835         ret = set_line_control(sc, CS8);
836         if (ret)
837                 return ret;
838
839         ret = update_status(sc);
840         if (ret)
841                 return ret;
842
843         ret = reset_chip(sc);
844         if (ret)
845                 return ret;
846
847         ret = set_dte_rate(sc, TTYDEF_SPEED); /* XXX */
848         if (ret)
849                 return ret;
850
851         sc->sc_dtr = sc->sc_rts = 1;
852         ret = set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
853         if (ret)
854                 return ret;
855
856         return 0;
857 }
858
859 static int
860 setup_intr_pipe(struct uchcom_softc *sc)
861 {
862         usbd_status err;
863
864         if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) {
865                 sc->sc_intr_buf = kmalloc(sc->sc_intr_size, M_USBDEV, M_WAITOK);
866                 err = usbd_open_pipe_intr(sc->sc_ucom.sc_iface,
867                                           sc->sc_intr_endpoint,
868                                           USBD_SHORT_XFER_OK,
869                                           &sc->sc_intr_pipe, sc,
870                                           sc->sc_intr_buf,
871                                           sc->sc_intr_size,
872                                           uchcom_intr, USBD_DEFAULT_INTERVAL);
873                 if (err) {
874                         device_printf(sc->sc_ucom.sc_dev, "cannot open "
875                                       "interrupt pipe: %s\n", usbd_errstr(err));
876                         return EIO;
877                 }
878         }
879         return 0;
880 }
881
882 static void
883 close_intr_pipe(struct uchcom_softc *sc)
884 {
885         usbd_status err;
886
887         if (sc->sc_ucom.sc_dying)
888                 return;
889
890         if (sc->sc_intr_pipe != NULL) {
891                 err = usbd_abort_pipe(sc->sc_intr_pipe);
892                 if (err)
893                         device_printf(sc->sc_ucom.sc_dev, "abort interrupt "
894                                       "pipe failed: %s\n", usbd_errstr(err));
895                 err = usbd_close_pipe(sc->sc_intr_pipe);
896                 if (err)
897                         device_printf(sc->sc_ucom.sc_dev, "close interrupt "
898                                       "pipe failed: %s\n", usbd_errstr(err));
899                 kfree(sc->sc_intr_buf, M_USBDEV);
900                 sc->sc_intr_pipe = NULL;
901         }
902 }
903
904
905 /* ----------------------------------------------------------------------
906  * methods for ucom
907  */
908 void
909 uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr)
910 {
911         struct uchcom_softc *sc = arg;
912
913         if (sc->sc_ucom.sc_dying)
914                 return;
915
916         *rlsr = sc->sc_lsr;
917         *rmsr = sc->sc_msr;
918 }
919
920 void
921 uchcom_set(void *arg, int portno, int reg, int onoff)
922 {
923         struct uchcom_softc *sc = arg;
924
925         if (sc->sc_ucom.sc_dying)
926                 return;
927
928         switch (reg) {
929         case UCOM_SET_DTR:
930                 sc->sc_dtr = !!onoff;
931                 set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
932                 break;
933         case UCOM_SET_RTS:
934                 sc->sc_rts = !!onoff;
935                 set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
936                 break;
937         case UCOM_SET_BREAK:
938                 set_break(sc, onoff);
939                 break;
940         }
941 }
942
943 int
944 uchcom_param(void *arg, int portno, struct termios *t)
945 {
946         struct uchcom_softc *sc = arg;
947         int ret;
948
949         if (sc->sc_ucom.sc_dying)
950                 return 0;
951
952         ret = set_line_control(sc, t->c_cflag);
953         if (ret)
954                 return ret;
955
956         ret = set_dte_rate(sc, t->c_ospeed);
957         if (ret)
958                 return ret;
959
960         return 0;
961 }
962
963 int
964 uchcom_open(void *arg, int portno)
965 {
966         int ret;
967         struct uchcom_softc *sc = arg;
968
969         if (sc->sc_ucom.sc_dying)
970                 return EIO;
971
972         ret = setup_intr_pipe(sc);
973         if (ret)
974                 return ret;
975
976         ret = setup_comm(sc);
977         if (ret)
978                 return ret;
979
980         return 0;
981 }
982
983 void
984 uchcom_close(void *arg, int portno)
985 {
986         struct uchcom_softc *sc = arg;
987
988         if (sc->sc_ucom.sc_dying)
989                 return;
990
991         close_intr_pipe(sc);
992 }
993
994
995 /* ----------------------------------------------------------------------
996  * callback when the modem status is changed.
997  */
998 void
999 uchcom_intr(usbd_xfer_handle xfer, usbd_private_handle priv,
1000     usbd_status status)
1001 {
1002         struct uchcom_softc *sc = priv;
1003         u_char *buf = sc->sc_intr_buf;
1004
1005         if (sc->sc_ucom.sc_dying)
1006                 return;
1007
1008         if (status != USBD_NORMAL_COMPLETION) {
1009                 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
1010                         return;
1011
1012                 DPRINTF(("%s: abnormal status: %s\n",
1013                          device_get_nameunit(sc->sc_ucom.sc_dev),
1014                          usbd_errstr(status)));
1015                 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
1016                 return;
1017         }
1018         DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X "
1019                  "0x%02X 0x%02X 0x%02X 0x%02X\n",
1020                  device_get_nameunit(sc->sc_ucom.sc_dev),
1021                  (unsigned)buf[0], (unsigned)buf[1],
1022                  (unsigned)buf[2], (unsigned)buf[3],
1023                  (unsigned)buf[4], (unsigned)buf[5],
1024                  (unsigned)buf[6], (unsigned)buf[7]));
1025
1026         convert_status(sc, buf[UCHCOM_INTR_STAT1]);
1027         ucom_status_change(&sc->sc_ucom);
1028 }