Skip to contentSkip to conversation

media: radio: Replace strlcat() with seq_buf in TEA drivers

PATCH NAK Mariia Nikitash 4 participants 10 messages Nov 6 – Nov 10

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 media: radio: Replace strlcat() with seq_buf in TEA drivers

In preparation for removing the deprecated strlcat() API[1], replace the strscpy() and strlcat() calls used to construct V4L2 card names in the TEA5777 and TEA575x radio drivers.

seq_buf tracks the current write position and remaining capacity across appends, avoiding repeated scans of the destination buffer. This preserves the resulting strings, including truncation behavior.

---
 drivers/media/radio/radio-tea5777.c | 10 ++++++++--
 drivers/media/radio/tea575x.c       | 10 ++++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)
drivers/media/radio/radio-tea5777.c +8 -2
index fb9de7bbcd19..2b1c7f6ddd60 100644
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/seq_buf.h>
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>
@@ -255,10 +256,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct radio_tea5777 *tea = video_drvdata(file);
+	struct seq_buf s;
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, " TEA5777", sizeof(v->card));
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, " TEA5777");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
drivers/media/radio/tea575x.c +8 -2
index c37315226c42..e49228eb7c46 100644
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/seq_buf.h>
 #include <linux/sched.h>
 #include <asm/io.h>
 #include <media/v4l2-device.h>
@@ -221,10 +222,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct snd_tea575x *tea = video_drvdata(file);
+	struct seq_buf s;
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, tea->tea5759 ? " TEA5759" : " TEA5757");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
Signature · 1 line
2.43.0
#1 lore raw

Hi Mariia,

Quoted 35 lines

Date: 2026-11-05 23:46:39-0800
From: Mariia Nikitash <[email protected]>

In preparation for removing the deprecated strlcat() API[1], replace the strscpy() and strlcat() calls used to construct V4L2 card names in the TEA5777 and TEA575x radio drivers.

seq_buf tracks the current write position and remaining capacity across appends, avoiding repeated scans of the destination buffer. This preserves the resulting strings, including truncation behavior.

---
 drivers/media/radio/radio-tea5777.c | 10 ++++++++--
 drivers/media/radio/tea575x.c       | 10 ++++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)
drivers/media/radio/radio-tea5777.c +2 -0
index fb9de7bbcd19..2b1c7f6ddd60 100644
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/seq_buf.h>
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>
@@ -255,10 +256,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct radio_tea5777 *tea = video_drvdata(file);
+	struct seq_buf s;

drivers/media/radio/radio-tea5777.c static int vidioc_querycap(struct file *file, void *priv, · line 267

Quoted 10 lines
drivers/media/radio/radio-tea5777.c +6 -2
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, " TEA5777", sizeof(v->card));
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, " TEA5777");
+	seq_buf_terminate(&s);
+

Why are the casts needed?

Have a lovely day!
Alex

Quoted 37 lines
drivers/media/radio/radio-tea5777.c +0 -0
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
drivers/media/radio/tea575x.c +8 -2
index c37315226c42..e49228eb7c46 100644
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/seq_buf.h>
 #include <linux/sched.h>
 #include <asm/io.h>
 #include <media/v4l2-device.h>
@@ -221,10 +222,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct snd_tea575x *tea = video_drvdata(file);
+	struct seq_buf s;
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, tea->tea5759 ? " TEA5759" : " TEA5757");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
Signature · 1 line
2.43.0
Signature · 1 line
<https://www.alejandro-colomar.es>
#2 lore raw
Quoted 39 lines · Alejandro Colomar

Hi Mariia,

Quoted 36 lines

Date: 2026-11-05 23:46:39-0800
From: Mariia Nikitash <[email protected]>

In preparation for removing the deprecated strlcat() API[1], replace the strscpy() and strlcat() calls used to construct V4L2 card names in the TEA5777 and TEA575x radio drivers.

seq_buf tracks the current write position and remaining capacity across appends, avoiding repeated scans of the destination buffer. This preserves the resulting strings, including truncation behavior.

---
 drivers/media/radio/radio-tea5777.c | 10 ++++++++--
 drivers/media/radio/tea575x.c       | 10 ++++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)
drivers/media/radio/radio-tea5777.c +2 -0
index fb9de7bbcd19..2b1c7f6ddd60 100644
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/seq_buf.h>
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>
@@ -255,10 +256,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
                                      struct v4l2_capability *v)
 {
      struct radio_tea5777 *tea = video_drvdata(file);
+     struct seq_buf s;
 
Quoted 11 lines

drivers/media/radio/radio-tea5777.c static int vidioc_querycap(struct file *file, void *priv, · line 267

Quoted 9 lines
drivers/media/radio/radio-tea5777.c +6 -2
      strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-     strscpy(v->card, tea->card, sizeof(v->card));
-     strlcat(v->card, " TEA5777", sizeof(v->card));
+
+     seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+     seq_buf_puts(&s, (const char *)tea->card);
+     seq_buf_puts(&s, " TEA5777");
+     seq_buf_terminate(&s);
+

Why are the casts needed?

Hi Alex,

I added the casts to make conversions from u8/__u8 to character-pointer types expected by the seq_buf helpers explicit.

I'm also considering simplifying these two-part string constructions using scnprintf() instead. Would this approach be preferred?

Quoted 2 lines

Have a lovely day!
Alex

Thanks,
Mariia

Quoted 41 lines
Quoted 35 lines
drivers/media/radio/radio-tea5777.c +0 -0
      strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
      return 0;
 }
drivers/media/radio/tea575x.c +8 -3
index c37315226c42..e49228eb7c46 100644
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/seq_buf.h>
 #include <linux/sched.h>
 #include <asm/io.h>
 #include <media/v4l2-device.h>
@@ -221,10 +222,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
                                      struct v4l2_capability *v)
 {
      struct snd_tea575x *tea = video_drvdata(file);
+     struct seq_buf s;
 
      strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-     strscpy(v->card, tea->card, sizeof(v->card));
-     strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+
+     seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+     seq_buf_puts(&s, (const char *)tea->card);
+     seq_buf_puts(&s, tea->tea5759 ? " TEA5759" : " TEA5757");
+     seq_buf_terminate(&s);
+
      strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
      return 0;
 }
--
Quoted 3 lines

2.43.0

--
<https://www.alejandro-colomar.es>

#3 lore raw

Hi Mariia,

Quoted 3 lines

Date: 2026-11-06 20:31:53-0800
From: Mariia Nikitash <[email protected]>

[...]

Quoted 18 lines
Quoted 6 lines
Quoted 6 lines
drivers/media/radio/radio-tea5777.c +1 -0
@@ -255,10 +256,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
                                      struct v4l2_capability *v)
 {
      struct radio_tea5777 *tea = video_drvdata(file);
+     struct seq_buf s;
 
Quoted 11 lines

drivers/media/radio/radio-tea5777.c static int vidioc_querycap(struct file *file, void *priv, · line 267

Quoted 9 lines
drivers/media/radio/radio-tea5777.c +6 -2
      strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-     strscpy(v->card, tea->card, sizeof(v->card));
-     strlcat(v->card, " TEA5777", sizeof(v->card));
+
+     seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+     seq_buf_puts(&s, (const char *)tea->card);
+     seq_buf_puts(&s, " TEA5777");
+     seq_buf_terminate(&s);
+

Why are the casts needed?

Quoted 4 lines

Hi Alex,

I added the casts to make conversions from u8/__u8 to character-pointer types expected by the seq_buf helpers explicit.

I was surprised that it wasn't needed before. In theory, strscpy() should be expecting the same types, so if it wasn't needed before, it shouldn't be needed now (and conversely, if it is needed now, it would have been needed before).

Quoted 3 lines

I'm also considering simplifying these two-part string constructions using scnprintf() instead. Would this approach be preferred?

I certainly prefer scnprintf() over seq_buf. :)

Have a lovely day!
Alex

Signature · 1 line
<https://www.alejandro-colomar.es>
#4 lore raw
In reply to Mariia Nikitash
Quoted 7 lines · On Thu, 5 Nov 2026 23:46:39 -0800 Mariia Nikitash…

In preparation for removing the deprecated strlcat() API[1], replace the strscpy() and strlcat() calls used to construct V4L2 card names in the TEA5777 and TEA575x radio drivers.

seq_buf tracks the current write position and remaining capacity across appends, avoiding repeated scans of the destination buffer. This preserves the resulting strings, including truncation behavior.

seq_buf is just OTT.
Using to replace a single strcat() is just silly.
There is no point doing these patches until there is a sensible alternative.

Quoted 21 lines
---
 drivers/media/radio/radio-tea5777.c | 10 ++++++++--
 drivers/media/radio/tea575x.c       | 10 ++++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)
drivers/media/radio/radio-tea5777.c +1 -0
index fb9de7bbcd19..2b1c7f6ddd60 100644
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/seq_buf.h>
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>

drivers/media/radio/radio-tea5777.c static int vidioc_querycap(struct file *file, void *priv, · line 261

Quoted 8 lines
drivers/media/radio/radio-tea5777.c +1 -2
@@ -255,10 +256,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct radio_tea5777 *tea = video_drvdata(file);
+	struct seq_buf s;
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, " TEA5777", sizeof(v->card));

What is wrong with (and almost OTT):

#define TEA5777 " TEA5777"
	BUILD_BUG_ON(sizeof(TEA5777) <= sizeof(v->card))

	len = strscpy(v->card, tea->card, sizeof(v->card) - sizeof(TEA5777) + 1);
	if (len < 0)
		goto fail;
	memcpy(v->card + len, TEA5777, sizeof(TEA5777);

What might be useful is a helper that would let you write:

	len = strscpy(v->card, tea->card);
	len = strscpy_at(v->card, len, " TEA5777" [, implied_length]);
	if (len < 0)
		goto fail;

David

Quoted 39 lines
drivers/media/radio/radio-tea5777.c +6 -0
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, " TEA5777");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
drivers/media/radio/tea575x.c +8 -2
index c37315226c42..e49228eb7c46 100644
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/seq_buf.h>
 #include <linux/sched.h>
 #include <asm/io.h>
 #include <media/v4l2-device.h>
@@ -221,10 +222,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct snd_tea575x *tea = video_drvdata(file);
+	struct seq_buf s;
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, tea->tea5759 ? " TEA5759" : " TEA5757");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
#5 lore raw

Hi David,

Quoted 3 lines

Date: 2026-11-08 21:02:32+0000
From: David Laight <[email protected]>

[...]

Quoted 4 lines

seq_buf is just OTT.
Using to replace a single strcat() is just silly.
There is no point doing these patches until there is a sensible alternative.

Agree.

[...]

Quoted 9 lines

drivers/media/radio/radio-tea5777.c static int vidioc_querycap(struct file *file, void *priv, · line 261

Quoted 3 lines
drivers/media/radio/radio-tea5777.c +0 -2
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, " TEA5777", sizeof(v->card));

What is wrong with (and almost OTT):

#define TEA5777 " TEA5777"
	BUILD_BUG_ON(sizeof(TEA5777) <= sizeof(v->card))

	len = strscpy(v->card, tea->card, sizeof(v->card) - sizeof(TEA5777) + 1);

I can answer this one: bugs (particularly, off by one) are quite prone in such code.

From the Authors of strlcat(3), you can look at OpenBSD's manual page:

	Since it is known how many characters were copied the first
	time, things can be sped up a bit by using a copy instead of an
	append:

		char *dir, *file, pname[PATH_MAX];
		size_t n;

		...

		n = strlcpy(pname, dir, sizeof(pname));
		if (n >= sizeof(pname))
			goto toolong;
		if (strlcpy(pname + n, file, sizeof(pname) - n) >= sizeof(pname) - n)
			goto toolong;

	However, one may question the validity of such optimizations, as
	they defeat the whole purpose of strlcpy() and strlcat().  As a
	matter of fact, the first version of this manual page got it
	wrong.
Quoted 9 lines
	if (len < 0)
		goto fail;
	memcpy(v->card + len, TEA5777, sizeof(TEA5777);

What might be useful is a helper that would let you write:

	len = strscpy(v->card, tea->card);
	len = strscpy_at(v->card, len, " TEA5777" [, implied_length]);
	if (len < 0)
		goto fail;

Indeed, as said in other threads, I have such an API:

	p = v->card;
	e = ARRAY_END(v->card);
	p = stpecpy(p, e, tea->card);
	p = stpecpy(p, e, " TEA5777");
	if (p == NULL)
		goto fail;

Linus didn't like my function name. We could indeed call it strscpy_at().

Have a lovely night!
Alex

Quoted 44 lines

David

Quoted 39 lines
drivers/media/radio/radio-tea5777.c +6 -0
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, " TEA5777");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
drivers/media/radio/tea575x.c +8 -2
index c37315226c42..e49228eb7c46 100644
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/seq_buf.h>
 #include <linux/sched.h>
 #include <asm/io.h>
 #include <media/v4l2-device.h>
@@ -221,10 +222,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct snd_tea575x *tea = video_drvdata(file);
+	struct seq_buf s;
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, tea->tea5759 ? " TEA5759" : " TEA5757");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
Signature · 1 line
<https://www.alejandro-colomar.es>
Nacked-by on v1 1/1 NAK
#6 lore raw
In reply to Mariia Nikitash
Quoted 10 lines · Mariia Nikitash

In preparation for removing the deprecated strlcat() API[1], replace the strscpy() and strlcat() calls used to construct V4L2 card names in the TEA5777 and TEA575x radio drivers.

seq_buf tracks the current write position and remaining capacity across appends, avoiding repeated scans of the destination buffer. This preserves the resulting strings, including truncation behavior.

This is silly. Just use scnprintf().

Regards,

Hans

Quoted 65 lines
---
 drivers/media/radio/radio-tea5777.c | 10 ++++++++--
 drivers/media/radio/tea575x.c       | 10 ++++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)
drivers/media/radio/radio-tea5777.c +8 -2
index fb9de7bbcd19..2b1c7f6ddd60 100644
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/seq_buf.h>
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>
@@ -255,10 +256,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct radio_tea5777 *tea = video_drvdata(file);
+	struct seq_buf s;
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, " TEA5777", sizeof(v->card));
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, " TEA5777");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
drivers/media/radio/tea575x.c +8 -2
index c37315226c42..e49228eb7c46 100644
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/seq_buf.h>
 #include <linux/sched.h>
 #include <asm/io.h>
 #include <media/v4l2-device.h>
@@ -221,10 +222,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct snd_tea575x *tea = video_drvdata(file);
+	struct seq_buf s;
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, tea->tea5759 ? " TEA5759" : " TEA5757");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
#7 lore raw
Quoted 9 lines · On Tue, 10 Nov 2026 11:08:30 +0100 Hans Verkuil <[email protected]>…
Quoted 8 lines · Mariia Nikitash

In preparation for removing the deprecated strlcat() API[1], replace the strscpy() and strlcat() calls used to construct V4L2 card names in the TEA5777 and TEA575x radio drivers.

seq_buf tracks the current write position and remaining capacity across appends, avoiding repeated scans of the destination buffer. This preserves the resulting strings, including truncation behavior.

Quoted 6 lines
Quoted 2 lines

This is silly. Just use scnprintf().

Or wait for someone to add a call that just does what is needed. In this case the first stscpy() returns the length (and it is often known).
So you could have:

	len = strscpy(buf, tea->card[, buflen]);
	len = strscpy_at(buf, len, " TEA5777"[, buflen]);
or:
	len = strscat(buf, buflen, tea->card, " TEA5777", ....);

or some variant that will do a strnlen(buf, buflen) to get the offset.

But it really is best to leave the strlcat() calls in there for now.

David

Quoted 72 lines

Regards,

Hans

Quoted 65 lines
---
 drivers/media/radio/radio-tea5777.c | 10 ++++++++--
 drivers/media/radio/tea575x.c       | 10 ++++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)
drivers/media/radio/radio-tea5777.c +8 -2
index fb9de7bbcd19..2b1c7f6ddd60 100644
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/seq_buf.h>
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>
@@ -255,10 +256,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct radio_tea5777 *tea = video_drvdata(file);
+	struct seq_buf s;
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, " TEA5777", sizeof(v->card));
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, " TEA5777");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }
drivers/media/radio/tea575x.c +8 -2
index c37315226c42..e49228eb7c46 100644
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/seq_buf.h>
 #include <linux/sched.h>
 #include <asm/io.h>
 #include <media/v4l2-device.h>
@@ -221,10 +222,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
 					struct v4l2_capability *v)
 {
 	struct snd_tea575x *tea = video_drvdata(file);
+	struct seq_buf s;
 
 	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-	strscpy(v->card, tea->card, sizeof(v->card));
-	strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+
+	seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+	seq_buf_puts(&s, (const char *)tea->card);
+	seq_buf_puts(&s, tea->tea5759 ? " TEA5759" : " TEA5757");
+	seq_buf_terminate(&s);
+
 	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
 	return 0;
 }  
#8 lore raw
Quoted 13 lines · David Laight
Quoted 9 lines · On Tue, 10 Nov 2026 11:08:30 +0100 Hans Verkuil <[email protected]>…
Quoted 8 lines · Mariia Nikitash

In preparation for removing the deprecated strlcat() API[1], replace the strscpy() and strlcat() calls used to construct V4L2 card names in the TEA5777 and TEA575x radio drivers.

seq_buf tracks the current write position and remaining capacity across appends, avoiding repeated scans of the destination buffer. This preserves the resulting strings, including truncation behavior.

Quoted 5 lines
Quoted 5 lines
Quoted 2 lines

Hi,

Quoted 6 lines
Quoted 1 line

This is silly. Just use scnprintf().

Or wait for someone to add a call that just does what is needed. In this case the first stscpy() returns the length (and it is often known).
So you could have:

Quoted 8 lines
        len = strscpy(buf, tea->card[, buflen]);
        len = strscpy_at(buf, len, " TEA5777"[, buflen]);
or:
        len = strscat(buf, buflen, tea->card, " TEA5777", ....);

or some variant that will do a strnlen(buf, buflen) to get the offset.

But it really is best to leave the strlcat() calls in there for now.

Thanks Hans and David for the feedback.

I agree that seq_buf is too complex here. I'll look into using scnprintf() as Hans suggested.

David, I also see your point about leaving strlcat() in place until a more suitable append helper is available. I'll revisit the approach before deciding on a v2.

Best,
Mariia

Quoted 75 lines

David

Quoted 72 lines

Regards,

Hans

Quoted 65 lines
---
 drivers/media/radio/radio-tea5777.c | 10 ++++++++--
 drivers/media/radio/tea575x.c       | 10 ++++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)
drivers/media/radio/radio-tea5777.c +8 -2
index fb9de7bbcd19..2b1c7f6ddd60 100644
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/seq_buf.h>
 #include <linux/slab.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>
@@ -255,10 +256,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
                                    struct v4l2_capability *v)
 {
    struct radio_tea5777 *tea = video_drvdata(file);
+   struct seq_buf s;
 
    strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-   strscpy(v->card, tea->card, sizeof(v->card));
-   strlcat(v->card, " TEA5777", sizeof(v->card));
+
+   seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+   seq_buf_puts(&s, (const char *)tea->card);
+   seq_buf_puts(&s, " TEA5777");
+   seq_buf_terminate(&s);
+
    strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
    return 0;
 }
drivers/media/radio/tea575x.c +8 -2
index c37315226c42..e49228eb7c46 100644
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/seq_buf.h>
 #include <linux/sched.h>
 #include <asm/io.h>
 #include <media/v4l2-device.h>
@@ -221,10 +222,15 @@ static int vidioc_querycap(struct file *file, void  *priv,
                                    struct v4l2_capability *v)
 {
    struct snd_tea575x *tea = video_drvdata(file);
+   struct seq_buf s;
 
    strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
-   strscpy(v->card, tea->card, sizeof(v->card));
-   strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+
+   seq_buf_init(&s, (char *)v->card, sizeof(v->card));
+   seq_buf_puts(&s, (const char *)tea->card);
+   seq_buf_puts(&s, tea->tea5759 ? " TEA5759" : " TEA5757");
+   seq_buf_terminate(&s);
+
    strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
    return 0;
 }
#9 lore raw
Quoted 3 lines · On Tue, 10 Nov 2026 10:41:06 -0800 Mariia Nikitash…

David, I also see your point about leaving strlcat() in place until a more suitable append helper is available. I'll revisit the approach before deciding on a v2.

I really don't understand why strlcat() got marked deprecated without a suitable replacement.
The performance of any form of strcpy(t + strlen(t), s) is horrid. The only extra issue strlcat() has is to do with it doing strlen(s) to generated the return value that most code doesn't want (and in pretty much all reasonable cases is actually fine).

David