[PATCH 2/8] pl2303: fix+improve the divsor based baud rate encoding method

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Based on the formula in the code description, Reinhard Max and me have
investigated the devices behavior / functional principle of the divisor based
baud rate encoding method.

It turned out, that (although beeing a good starting point) the current code has
some flaws. It doesn't work correctly for a wide range of baud rates and the
divisor resolution can be improved. It also doesn't report the actually set baud
rate.

This patch fixes and improves the code for the divisor based baud rate encoding
method a lot. It can now be used for the whole range of baud rates from 46 baud
to 24M baud with a very good divisor resolution and userspace can read back the
resulting baud rate.

It also documents the formula used for encoding and the hardware behavior
(including special cases).

The basic algorithm, rounding and several code comments/explanations are
provided by Reinhard Max.
I've added some minor fixes, the handling of the special cases and further
code/algorithm descriptions.

Signed-off-by: Frank Schäfer <fschaefer.oss@xxxxxxxxxxxxxx>
Signed-off-by: Reinhard Max <max@xxxxxxx>
---
 drivers/usb/serial/pl2303.c |   59 +++++++++++++++++++++++++++++++++++--------
 1 Datei geändert, 49 Zeilen hinzugefügt(+), 10 Zeilen entfernt(-)

diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 2448201..323dfa6 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -3,6 +3,8 @@
  *
  * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@xxxxxxxxx)
  * Copyright (C) 2003 IBM Corp.
+ * Copyright (C) 2009, 2013 Frank Schäfer <fschaefer.oss@xxxxxxxxxxxxxx>
+ * Copyright (C) 2013 Reinhard max <max@xxxxxxx>
  *
  * Original driver for 2.2.x by anonymous
  *
@@ -310,21 +312,58 @@ static void pl2303_encode_baudrate(struct tty_struct *tty,
 		put_unaligned_le32(baud, buf);
 	} else {
 		/*
+		 * Divisor based baud rate encoding method
+		 *
 		 * NOTE: it's not clear if the type_0/1 chips
 		 * support this method
 		 *
-		 * Apparently the formula for higher speeds is:
-		 * baudrate = 12M * 32 / (2^buf[1]) / buf[0]
+		 * divisor = 12MHz * 32 / baudrate = 2^A * B
+		 *
+		 * with
+		 *
+		 * A = buf[1] & 0x0e
+		 * B = buf[0]  +  (buf[1] & 0x01) << 8
+		 *
+		 * Special cases:
+		 * => 8 < B < 16: device seems to work not properly
+		 * => B <= 8: device uses the max. value B = 512 instead
 		 */
-		unsigned tmp = 12000000 * 32 / baud;
-		buf[3] = 0x80;
-		buf[2] = 0;
-		buf[1] = (tmp >= 256);
-		while (tmp >= 256) {
-			tmp >>= 2;
-			buf[1] <<= 1;
+
+		/* Determine factors A and B */
+		unsigned int A = 0;
+		unsigned int B = 12000000 * 32 / baud;  /* 12MHz */
+		B <<= 1; /* Add one bit for rounding */
+		while (B > (512 << 1) && A <= 14) {
+			A += 2;
+			B >>= 2;
+		}
+		if (A > 14) { /* max. divisor = min. baudrate reached */
+			A = 14;
+			B = 512;
+			/* => ~45.78 baud */
+		} else {
+			B = (B + 1) >> 1; /* Round the last bit */
 		}
-		buf[0] = tmp;
+		/* Handle special cases */
+		if (B == 512)
+			B = 0; /* also: 1 to 8 */
+		else if (B < 16)
+			/*
+			 * NOTE: with the current algorithm this happens
+			 * only for A=0 and means that the min. divisor
+			 * (respectively: the max. baudrate) is reached.
+			 */
+			B = 16;		/* => 24 MBaud */
+		/* Encode the baud rate */
+		buf[3] = 0x80;     /* Select divisor encoding method */
+		buf[2] = 0;
+		buf[1] = (A & 0x0e);		/* A */
+		buf[1] |= ((B & 0x100) >> 8);	/* MSB of B */
+		buf[0] = B & 0xff;		/* 8 LSBs of B */
+		/* Calculate the actual/resulting baud rate */
+		if (B <= 8)
+			B = 512;
+		baud = 12000000 * 32 / ((1 << A) * B);
 	}
 
 	/* Save resulting baud rate */
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux