* [PATCH 01/11] USB: serial: io_edgeport: fix NULL-deref on early status
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
2026-10-21 14:11 ` [PATCH 02/11] USB: serial: io_edgeport: fix potential fifo corruption Johan Hovold
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold
Cc: Greg Kroah-Hartman, linux-usb, linux-kernel,
syzbot+763813d65c7e18c1b6d5, stable
The driver submits the interrupt-in URB during probe or on first port
open, which on completion may submit the bulk-in URB before all ports
have been opened and their waitqueues have been initialised.
If a (malicious) device sends a status message for a port that has not
yet been opened this may lead to a NULL-pointer dereference in the
bulk-in completion handler when attempting to wakeup blocked tasks.
Move waitqueue initialisation from open() to port_probe() to address
this.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+763813d65c7e18c1b6d5@syzkaller.appspotmail.com
Link: https://lore.kernel.org/6a8818d8.ae6ddae5.3da009.0031.GAE@google.com
Cc: stable@vger.kernel.org
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index eaa8f00716c8..cdf41e86ba44 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -877,11 +877,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
}
}
- /* initialize our wait queues */
- init_waitqueue_head(&edge_port->wait_open);
- init_waitqueue_head(&edge_port->wait_chase);
- init_waitqueue_head(&edge_port->wait_command);
-
/* initialize our port settings */
edge_port->txCredits = 0; /* Can't send any data yet */
/* Must always set this bit to enable ints! */
@@ -2965,6 +2960,11 @@ static int edge_port_probe(struct usb_serial_port *port)
return -ENOMEM;
spin_lock_init(&edge_port->ep_lock);
+
+ init_waitqueue_head(&edge_port->wait_open);
+ init_waitqueue_head(&edge_port->wait_chase);
+ init_waitqueue_head(&edge_port->wait_command);
+
edge_port->port = port;
usb_set_serial_port_data(port, edge_port);
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 02/11] USB: serial: io_edgeport: fix potential fifo corruption
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
2026-10-21 14:11 ` [PATCH 01/11] USB: serial: io_edgeport: fix NULL-deref on early status Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
2026-10-21 14:11 ` [PATCH 03/11] USB: serial: io_edgeport: fix potential close() use-after-free Johan Hovold
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable
The device buffer size is determined during open() and the driver
allocates a TX FIFO of corresponding size.
Add the missing sanity check to prevent memory corruption in case a
malicious device sends a second open response with a different buffer
size.
This issue was flagged by Sashiko when reviewing a related fix.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Link: https://sashiko.dev/#/patchset/20260714101316.823942-1-shpark061104%40gmail.com?part=1
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index cdf41e86ba44..0cc4f41299e0 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -878,7 +878,11 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
}
/* initialize our port settings */
+ spin_lock_irq(&edge_port->ep_lock);
edge_port->txCredits = 0; /* Can't send any data yet */
+ edge_port->maxTxCredits = 0;
+ spin_unlock_irq(&edge_port->ep_lock);
+
/* Must always set this bit to enable ints! */
edge_port->shadowMCR = MCR_MASTER_IE;
edge_port->chaseResponsePending = false;
@@ -1750,6 +1754,7 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
struct tty_struct *tty;
struct device *dev;
__u8 code = edge_serial->rxStatusCode;
+ unsigned long flags;
/* switch the port pointer to the one being currently talked about */
if (edge_serial->rxPort >= edge_serial->serial->num_ports)
@@ -1793,10 +1798,17 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
}
if (code == IOSP_STATUS_OPEN_RSP) {
+ spin_lock_irqsave(&edge_port->ep_lock, flags);
+ /* reject stray open responses to avoid FIFO corruption */
+ if (edge_port->maxTxCredits != 0) {
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+ return;
+ }
edge_port->txCredits = GET_TX_BUFFER_SIZE(byte3);
edge_port->maxTxCredits = edge_port->txCredits;
dev_dbg(dev, "%s - Port %u Open Response Initial MSR = %02x TxBufferSize = %d\n",
__func__, edge_serial->rxPort, byte2, edge_port->txCredits);
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
handle_new_msr(edge_port, byte2);
/* send the current line settings to the port so we are
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 03/11] USB: serial: io_edgeport: fix potential close() use-after-free
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
2026-10-21 14:11 ` [PATCH 01/11] USB: serial: io_edgeport: fix NULL-deref on early status Johan Hovold
2026-10-21 14:11 ` [PATCH 02/11] USB: serial: io_edgeport: fix potential fifo corruption Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
2026-10-21 14:11 ` [PATCH 04/11] USB: serial: io_edgeport: fix potential NULL-deref at open Johan Hovold
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable
Add the missing locking when updating the port status during close() to
prevent a racing interrupt completion handler from trying to submit more
data for the port while its write URB and FIFO are being deallocated.
This issue was flagged by Sashiko when reviewing a related fix.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Link: https://sashiko.dev/#/patchset/20260714101316.823942-1-shpark061104%40gmail.com?part=1
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 0cc4f41299e0..a2410c063ffe 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -1089,7 +1089,9 @@ static void edge_close(struct usb_serial_port *port)
/* port->close = true; */
edge_port->closePending = false;
+ spin_lock_irq(&edge_port->ep_lock);
edge_port->open = false;
+ spin_unlock_irq(&edge_port->ep_lock);
edge_port->openPending = false;
usb_kill_urb(edge_port->write_urb);
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 04/11] USB: serial: io_edgeport: fix potential NULL-deref at open
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
` (2 preceding siblings ...)
2026-10-21 14:11 ` [PATCH 03/11] USB: serial: io_edgeport: fix potential close() use-after-free Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
2026-10-21 14:11 ` [PATCH 05/11] USB: serial: io_edgeport: serialise MCR accesses Johan Hovold
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable
Do not mark the port as open until after the write FIFO has been
allocated and initialised to prevent a racing interrupt completion from
attempting to send stale data and dereferencing the FIFO buffer pointer.
Note that the open flag defaults to false and is cleared on close() so
there is no need to reinitialise it on every open().
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index a2410c063ffe..38f58ca115eb 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -889,9 +889,8 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
/* send a open port command */
edge_port->openPending = true;
- edge_port->open = false;
- response = send_iosp_ext_cmd(edge_port, IOSP_CMD_OPEN_PORT, 0);
+ response = send_iosp_ext_cmd(edge_port, IOSP_CMD_OPEN_PORT, 0);
if (response < 0) {
dev_err(dev, "%s - error sending open port command\n", __func__);
edge_port->openPending = false;
@@ -901,8 +900,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
/* now wait for the port to be completely opened */
wait_event_timeout(edge_port->wait_open, !edge_port->openPending,
OPEN_TIMEOUT);
-
- if (!edge_port->open) {
+ if (edge_port->openPending) {
/* open timed out */
dev_dbg(dev, "%s - open timeout\n", __func__);
edge_port->openPending = false;
@@ -930,6 +928,10 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
return -ENOMEM;
}
+ spin_lock_irq(&edge_port->ep_lock);
+ edge_port->open = true;
+ spin_unlock_irq(&edge_port->ep_lock);
+
dev_dbg(dev, "%s - Initialize TX fifo to %d bytes\n",
__func__, edge_port->maxTxCredits);
@@ -1092,7 +1094,6 @@ static void edge_close(struct usb_serial_port *port)
spin_lock_irq(&edge_port->ep_lock);
edge_port->open = false;
spin_unlock_irq(&edge_port->ep_lock);
- edge_port->openPending = false;
usb_kill_urb(edge_port->write_urb);
@@ -1824,7 +1825,6 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
/* we have completed the open */
edge_port->openPending = false;
- edge_port->open = true;
wake_up(&edge_port->wait_open);
return;
}
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 05/11] USB: serial: io_edgeport: serialise MCR accesses
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
` (3 preceding siblings ...)
2026-10-21 14:11 ` [PATCH 04/11] USB: serial: io_edgeport: fix potential NULL-deref at open Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
2026-10-21 14:11 ` [PATCH 06/11] USB: serial: io_edgeport: drop redundant sanity checks Johan Hovold
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable
Add a mutex to serialise modem control register updates to prevent
concurrent tasks from interfering with each other.
Note that the register updates are currently asynchronous so a task may
read back a value which has not yet taken effect.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 38f58ca115eb..0b50c6b99b0a 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -168,6 +168,7 @@ struct edgeport_port {
struct urb *write_urb; /* write URB for this port */
bool write_in_progress; /* 'true' while a write URB is outstanding */
spinlock_t ep_lock;
+ struct mutex mutex;
__u8 shadowLCR; /* last LCR value received */
__u8 shadowMCR; /* last MCR value received */
@@ -884,7 +885,10 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
spin_unlock_irq(&edge_port->ep_lock);
/* Must always set this bit to enable ints! */
+ mutex_lock(&edge_port->mutex);
edge_port->shadowMCR = MCR_MASTER_IE;
+ mutex_unlock(&edge_port->mutex);
+
edge_port->chaseResponsePending = false;
/* send a open port command */
@@ -1397,9 +1401,11 @@ static void edge_throttle(struct tty_struct *tty)
/* if we are implementing RTS/CTS, toggle that line */
if (C_CRTSCTS(tty)) {
+ mutex_lock(&edge_port->mutex);
edge_port->shadowMCR &= ~MCR_RTS;
status = send_cmd_write_uart_register(edge_port, MCR,
edge_port->shadowMCR);
+ mutex_unlock(&edge_port->mutex);
if (status != 0)
return;
}
@@ -1434,9 +1440,11 @@ static void edge_unthrottle(struct tty_struct *tty)
}
/* if we are implementing RTS/CTS, toggle that line */
if (C_CRTSCTS(tty)) {
+ mutex_lock(&edge_port->mutex);
edge_port->shadowMCR |= MCR_RTS;
send_cmd_write_uart_register(edge_port, MCR,
edge_port->shadowMCR);
+ mutex_unlock(&edge_port->mutex);
}
}
@@ -1501,6 +1509,7 @@ static int edge_tiocmset(struct tty_struct *tty,
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
unsigned int mcr;
+ mutex_lock(&edge_port->mutex);
mcr = edge_port->shadowMCR;
if (set & TIOCM_RTS)
mcr |= MCR_RTS;
@@ -1519,6 +1528,7 @@ static int edge_tiocmset(struct tty_struct *tty,
edge_port->shadowMCR = mcr;
send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR);
+ mutex_unlock(&edge_port->mutex);
return 0;
}
@@ -1532,6 +1542,7 @@ static int edge_tiocmget(struct tty_struct *tty)
unsigned int mcr;
msr = edge_port->shadowMSR;
+ mutex_lock(&edge_port->mutex);
mcr = edge_port->shadowMCR;
result = ((mcr & MCR_DTR) ? TIOCM_DTR: 0) /* 0x002 */
| ((mcr & MCR_RTS) ? TIOCM_RTS: 0) /* 0x004 */
@@ -1539,6 +1550,7 @@ static int edge_tiocmget(struct tty_struct *tty)
| ((msr & EDGEPORT_MSR_CD) ? TIOCM_CAR: 0) /* 0x040 */
| ((msr & EDGEPORT_MSR_RI) ? TIOCM_RI: 0) /* 0x080 */
| ((msr & EDGEPORT_MSR_DSR) ? TIOCM_DSR: 0); /* 0x100 */
+ mutex_unlock(&edge_port->mutex);
return result;
}
@@ -2477,12 +2489,14 @@ static void change_port_settings(struct tty_struct *tty,
return;
/* set up the MCR register and send it to the EdgePort */
+ mutex_lock(&edge_port->mutex);
edge_port->shadowMCR = MCR_MASTER_IE;
if (cflag & CBAUD)
edge_port->shadowMCR |= (MCR_DTR | MCR_RTS);
status = send_cmd_write_uart_register(edge_port, MCR,
edge_port->shadowMCR);
+ mutex_unlock(&edge_port->mutex);
if (status != 0)
return;
@@ -2974,6 +2988,7 @@ static int edge_port_probe(struct usb_serial_port *port)
return -ENOMEM;
spin_lock_init(&edge_port->ep_lock);
+ mutex_init(&edge_port->mutex);
init_waitqueue_head(&edge_port->wait_open);
init_waitqueue_head(&edge_port->wait_chase);
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 06/11] USB: serial: io_edgeport: drop redundant sanity checks
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
` (4 preceding siblings ...)
2026-10-21 14:11 ` [PATCH 05/11] USB: serial: io_edgeport: serialise MCR accesses Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
2026-10-21 14:11 ` [PATCH 07/11] USB: serial: io_edgeport: drop redundant open " Johan Hovold
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel
The serial and port driver data is allocated at probe() before
registering the ports and will never be NULL when open(), close(),
write(), throttle() and unthrottle() are called.
Drop the redundant driver data sanity checks.
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 32 ++++----------------------------
1 file changed, 4 insertions(+), 28 deletions(-)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 0b50c6b99b0a..b8e78c749f9c 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -818,20 +818,13 @@ static void edge_bulk_out_cmd_callback(struct urb *urb)
static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
struct device *dev = &port->dev;
- struct usb_serial *serial;
- struct edgeport_serial *edge_serial;
int response;
- if (edge_port == NULL)
- return -ENODEV;
-
/* see if we've set up our endpoint info yet (can't set it up
in edge_startup as the structures were not set up at that time.) */
- serial = port->serial;
- edge_serial = usb_get_serial_data(serial);
- if (edge_serial == NULL)
- return -ENODEV;
if (edge_serial->interrupt_in_buffer == NULL) {
struct usb_serial_port *port0 = serial->port[0];
@@ -1058,15 +1051,10 @@ static void block_until_tx_empty(struct edgeport_port *edge_port)
*****************************************************************************/
static void edge_close(struct usb_serial_port *port)
{
- struct edgeport_serial *edge_serial;
- struct edgeport_port *edge_port;
+ struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial);
+ struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status;
- edge_serial = usb_get_serial_data(port->serial);
- edge_port = usb_get_serial_port_data(port);
- if (edge_serial == NULL || edge_port == NULL)
- return;
-
/* block until tx is empty */
block_until_tx_empty(edge_port);
@@ -1130,9 +1118,6 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
int secondhalf;
unsigned long flags;
- if (edge_port == NULL)
- return -ENODEV;
-
/* get a pointer to the Tx fifo */
fifo = &edge_port->txfifo;
@@ -1383,9 +1368,6 @@ static void edge_throttle(struct tty_struct *tty)
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status;
- if (edge_port == NULL)
- return;
-
if (!edge_port->open) {
dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
@@ -1423,9 +1405,6 @@ static void edge_unthrottle(struct tty_struct *tty)
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status;
- if (edge_port == NULL)
- return;
-
if (!edge_port->open) {
dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
@@ -1460,9 +1439,6 @@ static void edge_set_termios(struct tty_struct *tty,
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
- if (edge_port == NULL)
- return;
-
if (!edge_port->open) {
dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 07/11] USB: serial: io_edgeport: drop redundant open sanity checks
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
` (5 preceding siblings ...)
2026-10-21 14:11 ` [PATCH 06/11] USB: serial: io_edgeport: drop redundant sanity checks Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
2026-10-21 14:11 ` [PATCH 08/11] USB: serial: io_edgeport: move initial termios update to open() Johan Hovold
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel
The throttle(), unthrottle() and set_termios() callbacks will not be
called unless the port is open so drop the redundant sanity checks.
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index b8e78c749f9c..cccdcfb203e1 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -1368,11 +1368,6 @@ static void edge_throttle(struct tty_struct *tty)
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status;
- if (!edge_port->open) {
- dev_dbg(&port->dev, "%s - port not opened\n", __func__);
- return;
- }
-
/* if we are implementing XON/XOFF, send the stop character */
if (I_IXOFF(tty)) {
unsigned char stop_char = STOP_CHAR(tty);
@@ -1405,11 +1400,6 @@ static void edge_unthrottle(struct tty_struct *tty)
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status;
- if (!edge_port->open) {
- dev_dbg(&port->dev, "%s - port not opened\n", __func__);
- return;
- }
-
/* if we are implementing XON/XOFF, send the start character */
if (I_IXOFF(tty)) {
unsigned char start_char = START_CHAR(tty);
@@ -1439,11 +1429,6 @@ static void edge_set_termios(struct tty_struct *tty,
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
- if (!edge_port->open) {
- dev_dbg(&port->dev, "%s - port not opened\n", __func__);
- return;
- }
-
/* change the port settings to the new ones specified */
change_port_settings(tty, edge_port, old_termios);
}
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 08/11] USB: serial: io_edgeport: move initial termios update to open()
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
` (6 preceding siblings ...)
2026-10-21 14:11 ` [PATCH 07/11] USB: serial: io_edgeport: drop redundant open " Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
2026-10-21 14:11 ` [PATCH 09/11] USB: serial: io_edgeport: deassert DTR/RTS on close() and B0 Johan Hovold
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel
Explicitly update the terminal settings on open() instead of doing so
from the interrupt completion handler when receiving an open command
response, which in theory can be received after a failed open attempt.
Note that this will also allow for switching to synchronous commands.
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 56 ++++++--------------------------
1 file changed, 10 insertions(+), 46 deletions(-)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index cccdcfb203e1..acaac342f43f 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -282,8 +282,7 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData,
static int send_iosp_ext_cmd(struct edgeport_port *edge_port, __u8 command,
__u8 param);
static int calc_baud_rate_divisor(struct device *dev, int baud_rate, int *divisor);
-static void change_port_settings(struct tty_struct *tty,
- struct edgeport_port *edge_port,
+static void edge_set_termios(struct tty_struct *tty, struct usb_serial_port *port,
const struct ktermios *old_termios);
static int send_cmd_write_uart_register(struct edgeport_port *edge_port,
__u8 regNum, __u8 regValue);
@@ -925,6 +924,9 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
return -ENOMEM;
}
+ if (tty)
+ edge_set_termios(tty, port, NULL);
+
spin_lock_irq(&edge_port->ep_lock);
edge_port->open = true;
spin_unlock_irq(&edge_port->ep_lock);
@@ -1418,22 +1420,6 @@ static void edge_unthrottle(struct tty_struct *tty)
}
-/*****************************************************************************
- * SerialSetTermios
- * this function is called by the tty driver when it wants to change
- * the termios structure
- *****************************************************************************/
-static void edge_set_termios(struct tty_struct *tty,
- struct usb_serial_port *port,
- const struct ktermios *old_termios)
-{
- struct edgeport_port *edge_port = usb_get_serial_port_data(port);
-
- /* change the port settings to the new ones specified */
- change_port_settings(tty, edge_port, old_termios);
-}
-
-
/*****************************************************************************
* get_lsr_info - get line status register info
*
@@ -1727,7 +1713,6 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
{
struct usb_serial_port *port;
struct edgeport_port *edge_port;
- struct tty_struct *tty;
struct device *dev;
__u8 code = edge_serial->rxStatusCode;
unsigned long flags;
@@ -1787,15 +1772,6 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
handle_new_msr(edge_port, byte2);
- /* send the current line settings to the port so we are
- in sync with any further termios calls */
- tty = tty_port_tty_get(&edge_port->port->port);
- if (tty) {
- change_port_settings(tty,
- edge_port, &tty->termios);
- tty_kref_put(tty);
- }
-
/* we have completed the open */
edge_port->openPending = false;
wake_up(&edge_port->wait_open);
@@ -2307,18 +2283,12 @@ static int send_cmd_write_uart_register(struct edgeport_port *edge_port,
}
-/*****************************************************************************
- * change_port_settings
- * This routine is called to set the UART on the device to match the
- * specified new settings.
- *****************************************************************************/
-
-static void change_port_settings(struct tty_struct *tty,
- struct edgeport_port *edge_port, const struct ktermios *old_termios)
+static void edge_set_termios(struct tty_struct *tty, struct usb_serial_port *port,
+ const struct ktermios *old_termios)
{
- struct device *dev = &edge_port->port->dev;
- struct edgeport_serial *edge_serial =
- usb_get_serial_data(edge_port->port->serial);
+ struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial);
+ struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
int baud;
unsigned cflag;
__u8 mask = 0xff;
@@ -2329,12 +2299,6 @@ static void change_port_settings(struct tty_struct *tty,
__u8 txFlow;
int status;
- if (!edge_port->open &&
- !edge_port->openPending) {
- dev_dbg(dev, "%s - port not opened\n", __func__);
- return;
- }
-
cflag = tty->termios.c_cflag;
switch (cflag & CSIZE) {
@@ -2470,7 +2434,7 @@ static void change_port_settings(struct tty_struct *tty,
dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud);
status = send_cmd_write_baud_rate(edge_port, baud);
- if (status == -1) {
+ if (status == -1 && old_termios) {
/* Speed change was not possible - put back the old speed */
baud = tty_termios_baud_rate(old_termios);
tty_encode_baud_rate(tty, baud, baud);
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 09/11] USB: serial: io_edgeport: deassert DTR/RTS on close() and B0
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
` (7 preceding siblings ...)
2026-10-21 14:11 ` [PATCH 08/11] USB: serial: io_edgeport: move initial termios update to open() Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
2026-10-21 14:11 ` [PATCH 10/11] USB: serial: io_edgeport: replace atomic command allocations Johan Hovold
2026-10-21 14:11 ` [PATCH 11/11] USB: serial: io_edgeport: switch to synchronous commands Johan Hovold
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel
The modem control signals DTR and RTS should be dropped on close() if
HUPCL is set and when requesting B0, but the driver is currently only
asserting the signals on open().
Implement the missing dtr_rts() callback to handle open() and close()
while letting set_termios() handle B0 transitions.
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 30 +++++++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index acaac342f43f..fd983ebd1b6d 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -1567,6 +1567,23 @@ static int edge_break(struct tty_struct *tty, int break_state)
}
+static void edge_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+
+ mutex_lock(&edge_port->mutex);
+
+ if (on)
+ edge_port->shadowMCR |= MCR_DTR | MCR_RTS;
+ else
+ edge_port->shadowMCR &= ~(MCR_DTR | MCR_RTS);
+
+ send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR);
+
+ mutex_unlock(&edge_port->mutex);
+}
+
+
/*****************************************************************************
* process_rcvd_data
* this function handles the data received on the bulk in pipe.
@@ -2415,9 +2432,12 @@ static void edge_set_termios(struct tty_struct *tty, struct usb_serial_port *por
/* set up the MCR register and send it to the EdgePort */
mutex_lock(&edge_port->mutex);
- edge_port->shadowMCR = MCR_MASTER_IE;
- if (cflag & CBAUD)
- edge_port->shadowMCR |= (MCR_DTR | MCR_RTS);
+ if (cflag & CBAUD) {
+ if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+ edge_port->shadowMCR |= MCR_DTR | MCR_RTS;
+ } else {
+ edge_port->shadowMCR &= ~(MCR_DTR | MCR_RTS);
+ }
status = send_cmd_write_uart_register(edge_port, MCR,
edge_port->shadowMCR);
@@ -2959,6 +2979,7 @@ static struct usb_serial_driver edgeport_2port_device = {
.tiocmset = edge_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
+ .dtr_rts = edge_dtr_rts,
.write = edge_write,
.write_room = edge_write_room,
.chars_in_buffer = edge_chars_in_buffer,
@@ -2993,6 +3014,7 @@ static struct usb_serial_driver edgeport_4port_device = {
.tiocmset = edge_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
+ .dtr_rts = edge_dtr_rts,
.write = edge_write,
.write_room = edge_write_room,
.chars_in_buffer = edge_chars_in_buffer,
@@ -3027,6 +3049,7 @@ static struct usb_serial_driver edgeport_8port_device = {
.tiocmset = edge_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
+ .dtr_rts = edge_dtr_rts,
.write = edge_write,
.write_room = edge_write_room,
.chars_in_buffer = edge_chars_in_buffer,
@@ -3061,6 +3084,7 @@ static struct usb_serial_driver epic_device = {
.tiocmset = edge_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
+ .dtr_rts = edge_dtr_rts,
.write = edge_write,
.write_room = edge_write_room,
.chars_in_buffer = edge_chars_in_buffer,
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 10/11] USB: serial: io_edgeport: replace atomic command allocations
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
` (8 preceding siblings ...)
2026-10-21 14:11 ` [PATCH 09/11] USB: serial: io_edgeport: deassert DTR/RTS on close() and B0 Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
2026-10-21 14:11 ` [PATCH 11/11] USB: serial: io_edgeport: switch to synchronous commands Johan Hovold
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel
With the line settings no longer being updated from the interrupt
completion handler there is no need for command URBs and buffers to be
allocated atomically.
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index fd983ebd1b6d..fbb8249bb5b7 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2079,7 +2079,7 @@ static int send_iosp_ext_cmd(struct edgeport_port *edge_port,
int length = 0;
int status = 0;
- buffer = kmalloc(10, GFP_ATOMIC);
+ buffer = kmalloc(10, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
@@ -2114,7 +2114,7 @@ static int write_cmd_usb(struct edgeport_port *edge_port,
usb_serial_debug_data(dev, __func__, length, buffer);
/* Allocate our next urb */
- urb = usb_alloc_urb(0, GFP_ATOMIC);
+ urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return -ENOMEM;
@@ -2128,8 +2128,7 @@ static int write_cmd_usb(struct edgeport_port *edge_port,
buffer, length, edge_bulk_out_cmd_callback, edge_port);
edge_port->commandPending = true;
- status = usb_submit_urb(urb, GFP_ATOMIC);
-
+ status = usb_submit_urb(urb, GFP_KERNEL);
if (status) {
/* something went wrong */
dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n",
@@ -2186,7 +2185,7 @@ static int send_cmd_write_baud_rate(struct edgeport_port *edge_port,
}
/* Alloc memory for the string of commands. */
- cmdBuffer = kmalloc(0x100, GFP_ATOMIC);
+ cmdBuffer = kmalloc(0x100, GFP_KERNEL);
if (!cmdBuffer)
return -ENOMEM;
@@ -2280,7 +2279,7 @@ static int send_cmd_write_uart_register(struct edgeport_port *edge_port,
}
/* Alloc memory for the string of commands. */
- cmdBuffer = kmalloc(0x10, GFP_ATOMIC);
+ cmdBuffer = kmalloc(0x10, GFP_KERNEL);
if (cmdBuffer == NULL)
return -ENOMEM;
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 11/11] USB: serial: io_edgeport: switch to synchronous commands
2026-10-21 14:11 [PATCH 00/11] USB: serial: io_edgeport: fixes and sync commands Johan Hovold
` (9 preceding siblings ...)
2026-10-21 14:11 ` [PATCH 10/11] USB: serial: io_edgeport: replace atomic command allocations Johan Hovold
@ 2026-10-21 14:11 ` Johan Hovold
10 siblings, 0 replies; 12+ messages in thread
From: Johan Hovold @ 2026-10-21 14:11 UTC (permalink / raw)
To: Johan Hovold; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel
With the line settings no longer being updated from the interrupt
completion handler command requests can be made synchronous as is
generally expected.
Note that the five second timeout matches the timeout used by the
original (ifdeffed) synchronous handling (see commmit 421ae6e3db69
("USB: io_edgeport: remove unused defines")).
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/io_edgeport.c | 86 ++++----------------------------
1 file changed, 9 insertions(+), 77 deletions(-)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index fbb8249bb5b7..3f04898a7cf2 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -52,6 +52,7 @@
#define MAX_NAME_LEN 64
#define OPEN_TIMEOUT (5*HZ) /* 5 seconds */
+#define EDGE_CMD_TIMEOUT (5 * HZ)
static const struct usb_device_id edgeport_2port_id_table[] = {
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) },
@@ -181,7 +182,6 @@ struct edgeport_port {
bool open;
bool openPending;
- bool commandPending;
bool closePending;
bool chaseResponsePending;
@@ -262,9 +262,6 @@ static const struct divisor_table_entry divisor_table[] = {
{ 230400, 1},
};
-/* Number of outstanding Command Write Urbs */
-static atomic_t CmdUrbs = ATOMIC_INIT(0);
-
/* function prototypes */
@@ -766,44 +763,6 @@ static void edge_bulk_out_data_callback(struct urb *urb)
}
-/*****************************************************************************
- * BulkOutCmdCallback
- * this is the callback function for when we have finished sending a
- * command on the bulk out endpoint.
- *****************************************************************************/
-static void edge_bulk_out_cmd_callback(struct urb *urb)
-{
- struct edgeport_port *edge_port = urb->context;
- struct device *dev = &urb->dev->dev;
- int status = urb->status;
-
- atomic_dec(&CmdUrbs);
- dev_dbg(dev, "%s - FREE URB %p (outstanding %d)\n", __func__, urb,
- atomic_read(&CmdUrbs));
-
-
- /* clean up the transfer buffer */
- kfree(urb->transfer_buffer);
-
- /* Free the command urb */
- usb_free_urb(urb);
-
- if (status) {
- dev_dbg(dev, "%s - nonzero write bulk status received: %d\n",
- __func__, status);
- return;
- }
-
- /* tell the tty driver that something has changed */
- if (edge_port->open)
- tty_port_tty_wakeup(&edge_port->port->port);
-
- /* we have completed the command */
- edge_port->commandPending = false;
- wake_up(&edge_port->wait_command);
-}
-
-
/*****************************************************************************
* Driver tty interface functions
*****************************************************************************/
@@ -2105,49 +2064,22 @@ static int send_iosp_ext_cmd(struct edgeport_port *edge_port,
static int write_cmd_usb(struct edgeport_port *edge_port,
unsigned char *buffer, int length)
{
- struct edgeport_serial *edge_serial =
- usb_get_serial_data(edge_port->port->serial);
+ struct usb_serial *serial = edge_port->port->serial;
+ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
struct device *dev = &edge_port->port->dev;
- int status = 0;
- struct urb *urb;
+ int status;
usb_serial_debug_data(dev, __func__, length, buffer);
- /* Allocate our next urb */
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb)
- return -ENOMEM;
-
- atomic_inc(&CmdUrbs);
- dev_dbg(dev, "%s - ALLOCATE URB %p (outstanding %d)\n",
- __func__, urb, atomic_read(&CmdUrbs));
-
- usb_fill_bulk_urb(urb, edge_serial->serial->dev,
- usb_sndbulkpipe(edge_serial->serial->dev,
- edge_serial->bulk_out_endpoint),
- buffer, length, edge_bulk_out_cmd_callback, edge_port);
-
- edge_port->commandPending = true;
- status = usb_submit_urb(urb, GFP_KERNEL);
+ status = usb_bulk_msg(serial->dev,
+ usb_sndbulkpipe(serial->dev, edge_serial->bulk_out_endpoint),
+ buffer, length, NULL, EDGE_CMD_TIMEOUT);
if (status) {
- /* something went wrong */
- dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n",
- __func__, status);
- usb_free_urb(urb);
- atomic_dec(&CmdUrbs);
+ dev_err(dev, "failed to send command: %d\n", status);
return status;
}
-#if 0
- wait_event(&edge_port->wait_command, !edge_port->commandPending);
-
- if (edge_port->commandPending) {
- /* command timed out */
- dev_dbg(dev, "%s - command timed out\n", __func__);
- status = -EINVAL;
- }
-#endif
- return status;
+ return 0;
}
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread