summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>2006-06-16 06:28:26 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>2006-06-16 06:28:26 +0000
commita929bdbb76bb276eab989ec5a8b4f77282b873ca (patch)
treea80ec1b104696564b36107b34747b809391323c4 /sys
parent2ebc659c7dab3d02a32775877d5177623cf40ef8 (diff)
algorithmically handle baudrate requests, allowing all sorts of
strange rates one might need; mrd@alkemio.org
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/usb/uftdi.c86
-rw-r--r--sys/dev/usb/uftdireg.h24
2 files changed, 73 insertions, 37 deletions
diff --git a/sys/dev/usb/uftdi.c b/sys/dev/usb/uftdi.c
index dabb9200fdf..62317bb3426 100644
--- a/sys/dev/usb/uftdi.c
+++ b/sys/dev/usb/uftdi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uftdi.c,v 1.28 2006/05/11 14:53:11 jason Exp $ */
+/* $OpenBSD: uftdi.c,v 1.29 2006/06/16 06:28:25 deraadt Exp $ */
/* $NetBSD: uftdi.c,v 1.14 2003/02/23 04:20:07 simonb Exp $ */
/*
@@ -111,6 +111,7 @@ Static void uftdi_read(void *sc, int portno, u_char **ptr,
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_methods uftdi_methods = {
uftdi_get_status,
@@ -564,25 +565,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;
@@ -694,3 +678,67 @@ uftdi_break(void *vsc, int portno, int onoff)
USETW(req.wLength, 0);
(void)usbd_do_request(sc->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);
+}
diff --git a/sys/dev/usb/uftdireg.h b/sys/dev/usb/uftdireg.h
index 0696fc23c8d..f0ccf100438 100644
--- a/sys/dev/usb/uftdireg.h
+++ b/sys/dev/usb/uftdireg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: uftdireg.h,v 1.10 2005/03/12 21:38:48 tdeval Exp $ */
+/* $OpenBSD: uftdireg.h,v 1.11 2006/06/16 06:28:25 deraadt Exp $ */
/* $NetBSD: uftdireg.h,v 1.6 2002/07/11 21:14:28 augustss Exp $ */
/*
@@ -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