pl2303: Fix mode switching regression

Cleaning out all the incorrect 'no change made' checks for termios
settings showed up a problem with the PL2303. The hardware here seems to
lose sync and bits if you tell it to make no changes. This shows up with
a real world application.

To fix this the driver check for meaningful hardware changes is restored
but doing the tests correctly and as a tty layer function so it doesn't
get duplicated wrongly everywhere if other drivers turn out to need it.

Signed-off-by: Alan Cox <alan@redhat.com>
Tested-by: Mirko Parthey <mirko.parthey@informatik.tu-chemnitz.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by Alan Cox and committed by Linus Torvalds bf5e5834 cf059462

+27
+19
drivers/char/tty_ioctl.c
··· 365 EXPORT_SYMBOL(tty_termios_copy_hw); 366 367 /** 368 * change_termios - update termios values 369 * @tty: tty to update 370 * @new_termios: desired new value
··· 365 EXPORT_SYMBOL(tty_termios_copy_hw); 366 367 /** 368 + * tty_termios_hw_change - check for setting change 369 + * @a: termios 370 + * @b: termios to compare 371 + * 372 + * Check if any of the bits that affect a dumb device have changed 373 + * between the two termios structures, or a speed change is needed. 374 + */ 375 + 376 + int tty_termios_hw_change(struct ktermios *a, struct ktermios *b) 377 + { 378 + if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed) 379 + return 1; 380 + if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL)) 381 + return 1; 382 + return 0; 383 + } 384 + EXPORT_SYMBOL(tty_termios_hw_change); 385 + 386 + /** 387 * change_termios - update termios values 388 * @tty: tty to update 389 * @new_termios: desired new value
+7
drivers/usb/serial/pl2303.c
··· 483 } 484 spin_unlock_irqrestore(&priv->lock, flags); 485 486 cflag = port->tty->termios->c_cflag; 487 488 buf = kzalloc(7, GFP_KERNEL);
··· 483 } 484 spin_unlock_irqrestore(&priv->lock, flags); 485 486 + /* The PL2303 is reported to lose bytes if you change 487 + serial settings even to the same values as before. Thus 488 + we actually need to filter in this specific case */ 489 + 490 + if (!tty_termios_hw_change(port->tty->termios, old_termios)) 491 + return; 492 + 493 cflag = port->tty->termios->c_cflag; 494 495 buf = kzalloc(7, GFP_KERNEL);
+1
include/linux/tty.h
··· 319 extern void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud); 320 extern void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud); 321 extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old); 322 323 extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *); 324 extern void tty_ldisc_deref(struct tty_ldisc *);
··· 319 extern void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud); 320 extern void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud); 321 extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old); 322 + extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b); 323 324 extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *); 325 extern void tty_ldisc_deref(struct tty_ldisc *);