From 21f337aade9e7c20212387266a5a09b6dc313faa Mon Sep 17 00:00:00 2001 From: Hasso Tepper Date: Fri, 31 Aug 2007 13:39:35 +0000 Subject: [PATCH] Handle baudrate requests algorithmically with newer chips (not old SIO), allowing all sorts of strange rates one might need. Obtained-from: OpenBSD --- sys/dev/usbmisc/uftdi/uftdi.c | 85 +++++++++++++++++++++++++++++++--------- sys/dev/usbmisc/uftdi/uftdireg.h | 24 +++--------- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/sys/dev/usbmisc/uftdi/uftdi.c b/sys/dev/usbmisc/uftdi/uftdi.c index 9a476233fd..69f4cd001b 100644 --- a/sys/dev/usbmisc/uftdi/uftdi.c +++ b/sys/dev/usbmisc/uftdi/uftdi.c @@ -1,7 +1,7 @@ /* * $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ * $FreeBSD: src/sys/dev/usb/uftdi.c,v 1.37 2007/06/22 05:53:05 imp Exp $ - * $DragonFly: src/sys/dev/usbmisc/uftdi/uftdi.c,v 1.16 2007/08/31 13:25:32 hasso Exp $ + * $DragonFly: src/sys/dev/usbmisc/uftdi/uftdi.c,v 1.17 2007/08/31 13:39:35 hasso Exp $ */ /*- @@ -121,6 +121,7 @@ static void uftdi_read(void *sc, int portno, u_char **ptr,u_int32_t *count); static void uftdi_write(void *sc, int portno, u_char *to, u_char *from, u_int32_t *count); static void uftdi_break(void *sc, int portno, int onoff); +static int uftdi_8u232am_getrate(speed_t speed, int *rate); struct ucom_callback uftdi_callback = { uftdi_get_status, @@ -503,25 +504,8 @@ uftdi_param(void *vsc, int portno, struct termios *t) break; case UFTDI_TYPE_8U232AM: - switch(t->c_ospeed) { - case 300: rate = ftdi_8u232am_b300; break; - case 600: rate = ftdi_8u232am_b600; break; - case 1200: rate = ftdi_8u232am_b1200; break; - case 2400: rate = ftdi_8u232am_b2400; break; - case 4800: rate = ftdi_8u232am_b4800; break; - case 9600: rate = ftdi_8u232am_b9600; break; - case 19200: rate = ftdi_8u232am_b19200; break; - case 38400: rate = ftdi_8u232am_b38400; break; - case 57600: rate = ftdi_8u232am_b57600; break; - case 115200: rate = ftdi_8u232am_b115200; break; - case 230400: rate = ftdi_8u232am_b230400; break; - case 460800: rate = ftdi_8u232am_b460800; break; - case 921600: rate = ftdi_8u232am_b921600; break; - case 2000000: rate = ftdi_8u232am_b2000000; break; - case 3000000: rate = ftdi_8u232am_b3000000; break; - default: + if (uftdi_8u232am_getrate(t->c_ospeed, &rate) == -1) return (EINVAL); - } break; } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; @@ -636,6 +620,69 @@ uftdi_break(void *vsc, int portno, int onoff) (void)usbd_do_request(ucom->sc_udev, &req, NULL); } +static int +uftdi_8u232am_getrate(speed_t speed, int *rate) +{ + /* Table of the nearest even powers-of-2 for values 0..15. */ + static const unsigned char roundoff[16] = { + 0, 2, 2, 4, 4, 4, 8, 8, + 8, 8, 8, 8, 16, 16, 16, 16, + }; + + unsigned int d, freq; + int result; + + if (speed <= 0) + return (-1); + + /* Special cases for 2M and 3M. */ + if (speed >= 3000000 * 100 / 103 && + speed <= 3000000 * 100 / 97) { + result = 0; + goto done; + } + if (speed >= 2000000 * 100 / 103 && + speed <= 2000000 * 100 / 97) { + result = 1; + goto done; + } + + d = (FTDI_8U232AM_FREQ << 4) / speed; + d = (d & ~15) + roundoff[d & 15]; + + if (d < FTDI_8U232AM_MIN_DIV) + d = FTDI_8U232AM_MIN_DIV; + else if (d > FTDI_8U232AM_MAX_DIV) + d = FTDI_8U232AM_MAX_DIV; + + /* + * Calculate the frequency needed for d to exactly divide down + * to our target speed, and check that the actual frequency is + * within 3% of this. + */ + freq = speed * d; + if (freq < (quad_t)(FTDI_8U232AM_FREQ << 4) * 100 / 103 || + freq > (quad_t)(FTDI_8U232AM_FREQ << 4) * 100 / 97) + return (-1); + + /* + * Pack the divisor into the resultant value. The lower + * 14-bits hold the integral part, while the upper 2 bits + * encode the fractional component: either 0, 0.5, 0.25, or + * 0.125. + */ + result = d >> 4; + if (d & 8) + result |= 0x4000; + else if (d & 4) + result |= 0x8000; + else if (d & 2) + result |= 0xc000; + +done: + *rate = result; + return (0); +} static device_method_t uftdi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uftdi_match), diff --git a/sys/dev/usbmisc/uftdi/uftdireg.h b/sys/dev/usbmisc/uftdi/uftdireg.h index b8195719d5..411abb04ec 100644 --- a/sys/dev/usbmisc/uftdi/uftdireg.h +++ b/sys/dev/usbmisc/uftdi/uftdireg.h @@ -1,7 +1,7 @@ /* * $NetBSD: uftdireg.h,v 1.6 2002/07/11 21:14:28 augustss Exp $ * $FreeBSD: src/sys/dev/usb/uftdireg.h,v 1.2 2004/07/01 17:16:20 brooks Exp $ - * $DragonFly: src/sys/dev/usbmisc/uftdi/uftdireg.h,v 1.4 2007/08/19 19:45:39 hasso Exp $ + * $DragonFly: src/sys/dev/usbmisc/uftdi/uftdireg.h,v 1.5 2007/08/31 13:39:35 hasso Exp $ */ /* @@ -94,23 +94,11 @@ enum { ftdi_sio_b115200 = 9 }; -enum { - ftdi_8u232am_b300 = 0x2710, - ftdi_8u232am_b600 = 0x1388, - ftdi_8u232am_b1200 = 0x09c4, - ftdi_8u232am_b2400 = 0x04e2, - ftdi_8u232am_b4800 = 0x0271, - ftdi_8u232am_b9600 = 0x4138, - ftdi_8u232am_b19200 = 0x809c, - ftdi_8u232am_b38400 = 0xc04e, - ftdi_8u232am_b57600 = 0x0034, - ftdi_8u232am_b115200 = 0x001a, - ftdi_8u232am_b230400 = 0x000d, - ftdi_8u232am_b460800 = 0x4006, - ftdi_8u232am_b921600 = 0x8003, - ftdi_8u232am_b2000000 = 0x0001, /* special case for 2M baud */ - ftdi_8u232am_b3000000 = 0x0000, /* special case for 3M baud */ -}; +#define FTDI_8U232AM_FREQ 3000000 + +/* Bounds for normal divisors as 4-bit fixed precision ints. */ +#define FTDI_8U232AM_MIN_DIV 0x20 +#define FTDI_8U232AM_MAX_DIV 0x3fff8 /* * BmRequestType: 0100 0000B -- 2.11.4.GIT