summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoremaste <emaste@FreeBSD.org>2008-01-25 02:41:44 +0000
committeremaste <emaste@FreeBSD.org>2008-01-25 02:41:44 +0000
commitfb709bf5c600e28b14184f4ad02875dd9392639b (patch)
treedfdc9900b73af9806a53a42464255fa07c30cf6d
parent5d22bdedcfd8739b5897b476566777e3c347737a (diff)
downloadFreeBSD-src-fb709bf5c600e28b14184f4ad02875dd9392639b.zip
FreeBSD-src-fb709bf5c600e28b14184f4ad02875dd9392639b.tar.gz
Calculate baud rate divisor instead of allowing only a fixed set of
standard rates. Obtained from OpenBSD src/sys/dev/usb/uftdi.c 1.29 src/sys/dev/usb/uftdireg.h 1.11 OpenBSD revisions noted by: ticso, on hackers
-rw-r--r--sys/dev/usb/uftdi.c84
-rw-r--r--sys/dev/usb/uftdireg.h22
2 files changed, 71 insertions, 35 deletions
diff --git a/sys/dev/usb/uftdi.c b/sys/dev/usb/uftdi.c
index 73d0aa0..22036e4 100644
--- a/sys/dev/usb/uftdi.c
+++ b/sys/dev/usb/uftdi.c
@@ -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,
@@ -569,25 +570,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;
@@ -702,6 +686,70 @@ 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/usb/uftdireg.h b/sys/dev/usb/uftdireg.h
index a7c6edc..78c9349 100644
--- a/sys/dev/usb/uftdireg.h
+++ b/sys/dev/usb/uftdireg.h
@@ -91,23 +91,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
OpenPOWER on IntegriCloud