Skip to content

Commit 173dbbf

Browse files
committed
Merge branch 'wwan-improvements'
Sergey Ryazanov says: ==================== net: WWAN subsystem improvements While working on WWAN netdev creation support, I notice a few things that could be done to make the wwan subsystem more developer and user friendly. This series implements them. The series begins with a WWAN HW simulator designed simplify testing and make the WWAN subsystem available for a wider audience. The next two patches are intended to make the code a bit more clearer. This is followed by a few patches to make the port device naming more user-friendly. The series is finishes with a set of changes that allow the WWAN AT port to be used with terminal emulation software. All changes were tested with the HW simulator that was introduced in this series, as well as with a Huawei E3372 LTE modem (a CDC-NCM device), which I finally found on my desk. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 9584809 + 5046720 commit 173dbbf

File tree

5 files changed

+738
-24
lines changed

5 files changed

+738
-24
lines changed

drivers/net/wwan/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ config WWAN_CORE
2020
To compile this driver as a module, choose M here: the module will be
2121
called wwan.
2222

23+
config WWAN_HWSIM
24+
tristate "Simulated WWAN device"
25+
depends on WWAN_CORE
26+
help
27+
This driver is a developer testing tool that can be used to test WWAN
28+
framework.
29+
30+
To compile this driver as a module, choose M here: the module will be
31+
called wwan_hwsim. If unsure, say N.
32+
2333
config MHI_WWAN_CTRL
2434
tristate "MHI WWAN control driver for QCOM-based PCIe modems"
2535
select WWAN_CORE

drivers/net/wwan/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@
66
obj-$(CONFIG_WWAN_CORE) += wwan.o
77
wwan-objs += wwan_core.o
88

9+
obj-$(CONFIG_WWAN_HWSIM) += wwan_hwsim.o
10+
911
obj-$(CONFIG_MHI_WWAN_CTRL) += mhi_wwan_ctrl.o

drivers/net/wwan/wwan_core.c

Lines changed: 217 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
#include <linux/skbuff.h>
1313
#include <linux/slab.h>
1414
#include <linux/types.h>
15+
#include <linux/termios.h>
1516
#include <linux/wwan.h>
1617

17-
#define WWAN_MAX_MINORS 256 /* 256 minors allowed with register_chrdev() */
18+
/* Maximum number of minors in use */
19+
#define WWAN_MAX_MINORS (1 << MINORBITS)
1820

1921
static DEFINE_MUTEX(wwan_register_lock); /* WWAN device create|remove lock */
2022
static DEFINE_IDA(minors); /* minors for WWAN port chardevs */
@@ -33,12 +35,10 @@ static int wwan_major;
3335
*
3436
* @id: WWAN device unique ID.
3537
* @dev: Underlying device.
36-
* @port_id: Current available port ID to pick.
3738
*/
3839
struct wwan_device {
3940
unsigned int id;
4041
struct device dev;
41-
atomic_t port_id;
4242
};
4343

4444
/**
@@ -51,6 +51,8 @@ struct wwan_device {
5151
* @dev: Underlying device
5252
* @rxq: Buffer inbound queue
5353
* @waitqueue: The waitqueue for port fops (read/write/poll)
54+
* @data_lock: Port specific data access serialization
55+
* @at_data: AT port specific data
5456
*/
5557
struct wwan_port {
5658
enum wwan_port_type type;
@@ -61,6 +63,13 @@ struct wwan_port {
6163
struct device dev;
6264
struct sk_buff_head rxq;
6365
wait_queue_head_t waitqueue;
66+
struct mutex data_lock; /* Port specific data access serialization */
67+
union {
68+
struct {
69+
struct ktermios termios;
70+
int mdmbits;
71+
} at_data;
72+
};
6473
};
6574

6675
static ssize_t index_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -184,21 +193,38 @@ static void wwan_remove_dev(struct wwan_device *wwandev)
184193

185194
/* ------- WWAN port management ------- */
186195

187-
/* Keep aligned with wwan_port_type enum */
188-
static const char * const wwan_port_type_str[] = {
189-
"AT",
190-
"MBIM",
191-
"QMI",
192-
"QCDM",
193-
"FIREHOSE"
196+
static const struct {
197+
const char * const name; /* Port type name */
198+
const char * const devsuf; /* Port devce name suffix */
199+
} wwan_port_types[WWAN_PORT_MAX + 1] = {
200+
[WWAN_PORT_AT] = {
201+
.name = "AT",
202+
.devsuf = "at",
203+
},
204+
[WWAN_PORT_MBIM] = {
205+
.name = "MBIM",
206+
.devsuf = "mbim",
207+
},
208+
[WWAN_PORT_QMI] = {
209+
.name = "QMI",
210+
.devsuf = "qmi",
211+
},
212+
[WWAN_PORT_QCDM] = {
213+
.name = "QCDM",
214+
.devsuf = "qcdm",
215+
},
216+
[WWAN_PORT_FIREHOSE] = {
217+
.name = "FIREHOSE",
218+
.devsuf = "firehose",
219+
},
194220
};
195221

196222
static ssize_t type_show(struct device *dev, struct device_attribute *attr,
197223
char *buf)
198224
{
199225
struct wwan_port *port = to_wwan_port(dev);
200226

201-
return sprintf(buf, "%s\n", wwan_port_type_str[port->type]);
227+
return sprintf(buf, "%s\n", wwan_port_types[port->type].name);
202228
}
203229
static DEVICE_ATTR_RO(type);
204230

@@ -213,7 +239,7 @@ static void wwan_port_destroy(struct device *dev)
213239
struct wwan_port *port = to_wwan_port(dev);
214240

215241
ida_free(&minors, MINOR(port->dev.devt));
216-
skb_queue_purge(&port->rxq);
242+
mutex_destroy(&port->data_lock);
217243
mutex_destroy(&port->ops_lock);
218244
kfree(port);
219245
}
@@ -241,6 +267,56 @@ static struct wwan_port *wwan_port_get_by_minor(unsigned int minor)
241267
return to_wwan_port(dev);
242268
}
243269

270+
/* Allocate and set unique name based on passed format
271+
*
272+
* Name allocation approach is highly inspired by the __dev_alloc_name()
273+
* function.
274+
*
275+
* To avoid names collision, the caller must prevent the new port device
276+
* registration as well as concurrent invocation of this function.
277+
*/
278+
static int __wwan_port_dev_assign_name(struct wwan_port *port, const char *fmt)
279+
{
280+
struct wwan_device *wwandev = to_wwan_dev(port->dev.parent);
281+
const unsigned int max_ports = PAGE_SIZE * 8;
282+
struct class_dev_iter iter;
283+
unsigned long *idmap;
284+
struct device *dev;
285+
char buf[0x20];
286+
int id;
287+
288+
idmap = (unsigned long *)get_zeroed_page(GFP_KERNEL);
289+
if (!idmap)
290+
return -ENOMEM;
291+
292+
/* Collect ids of same name format ports */
293+
class_dev_iter_init(&iter, wwan_class, NULL, &wwan_port_dev_type);
294+
while ((dev = class_dev_iter_next(&iter))) {
295+
if (dev->parent != &wwandev->dev)
296+
continue;
297+
if (sscanf(dev_name(dev), fmt, &id) != 1)
298+
continue;
299+
if (id < 0 || id >= max_ports)
300+
continue;
301+
set_bit(id, idmap);
302+
}
303+
class_dev_iter_exit(&iter);
304+
305+
/* Allocate unique id */
306+
id = find_first_zero_bit(idmap, max_ports);
307+
free_page((unsigned long)idmap);
308+
309+
snprintf(buf, sizeof(buf), fmt, id); /* Name generation */
310+
311+
dev = device_find_child_by_name(&wwandev->dev, buf);
312+
if (dev) {
313+
put_device(dev);
314+
return -ENFILE;
315+
}
316+
317+
return dev_set_name(&port->dev, buf);
318+
}
319+
244320
struct wwan_port *wwan_create_port(struct device *parent,
245321
enum wwan_port_type type,
246322
const struct wwan_port_ops *ops,
@@ -249,8 +325,9 @@ struct wwan_port *wwan_create_port(struct device *parent,
249325
struct wwan_device *wwandev;
250326
struct wwan_port *port;
251327
int minor, err = -ENOMEM;
328+
char namefmt[0x20];
252329

253-
if (type >= WWAN_PORT_MAX || !ops)
330+
if (type > WWAN_PORT_MAX || !ops)
254331
return ERR_PTR(-EINVAL);
255332

256333
/* A port is always a child of a WWAN device, retrieve (allocate or
@@ -276,19 +353,26 @@ struct wwan_port *wwan_create_port(struct device *parent,
276353
mutex_init(&port->ops_lock);
277354
skb_queue_head_init(&port->rxq);
278355
init_waitqueue_head(&port->waitqueue);
356+
mutex_init(&port->data_lock);
279357

280358
port->dev.parent = &wwandev->dev;
281359
port->dev.class = wwan_class;
282360
port->dev.type = &wwan_port_dev_type;
283361
port->dev.devt = MKDEV(wwan_major, minor);
284362
dev_set_drvdata(&port->dev, drvdata);
285363

286-
/* create unique name based on wwan device id, port index and type */
287-
dev_set_name(&port->dev, "wwan%up%u%s", wwandev->id,
288-
atomic_inc_return(&wwandev->port_id),
289-
wwan_port_type_str[port->type]);
364+
/* allocate unique name based on wwan device id, port type and number */
365+
snprintf(namefmt, sizeof(namefmt), "wwan%u%s%%d", wwandev->id,
366+
wwan_port_types[port->type].devsuf);
290367

368+
/* Serialize ports registration */
369+
mutex_lock(&wwan_register_lock);
370+
371+
__wwan_port_dev_assign_name(port, namefmt);
291372
err = device_register(&port->dev);
373+
374+
mutex_unlock(&wwan_register_lock);
375+
292376
if (err)
293377
goto error_put_device;
294378

@@ -377,8 +461,11 @@ static void wwan_port_op_stop(struct wwan_port *port)
377461
{
378462
mutex_lock(&port->ops_lock);
379463
port->start_count--;
380-
if (port->ops && !port->start_count)
381-
port->ops->stop(port);
464+
if (!port->start_count) {
465+
if (port->ops)
466+
port->ops->stop(port);
467+
skb_queue_purge(&port->rxq);
468+
}
382469
mutex_unlock(&port->ops_lock);
383470
}
384471

@@ -545,13 +632,121 @@ static __poll_t wwan_port_fops_poll(struct file *filp, poll_table *wait)
545632
return mask;
546633
}
547634

635+
/* Implements minimalistic stub terminal IOCTLs support */
636+
static long wwan_port_fops_at_ioctl(struct wwan_port *port, unsigned int cmd,
637+
unsigned long arg)
638+
{
639+
int ret = 0;
640+
641+
mutex_lock(&port->data_lock);
642+
643+
switch (cmd) {
644+
case TCFLSH:
645+
break;
646+
647+
case TCGETS:
648+
if (copy_to_user((void __user *)arg, &port->at_data.termios,
649+
sizeof(struct termios)))
650+
ret = -EFAULT;
651+
break;
652+
653+
case TCSETS:
654+
case TCSETSW:
655+
case TCSETSF:
656+
if (copy_from_user(&port->at_data.termios, (void __user *)arg,
657+
sizeof(struct termios)))
658+
ret = -EFAULT;
659+
break;
660+
661+
#ifdef TCGETS2
662+
case TCGETS2:
663+
if (copy_to_user((void __user *)arg, &port->at_data.termios,
664+
sizeof(struct termios2)))
665+
ret = -EFAULT;
666+
break;
667+
668+
case TCSETS2:
669+
case TCSETSW2:
670+
case TCSETSF2:
671+
if (copy_from_user(&port->at_data.termios, (void __user *)arg,
672+
sizeof(struct termios2)))
673+
ret = -EFAULT;
674+
break;
675+
#endif
676+
677+
case TIOCMGET:
678+
ret = put_user(port->at_data.mdmbits, (int __user *)arg);
679+
break;
680+
681+
case TIOCMSET:
682+
case TIOCMBIC:
683+
case TIOCMBIS: {
684+
int mdmbits;
685+
686+
if (copy_from_user(&mdmbits, (int __user *)arg, sizeof(int))) {
687+
ret = -EFAULT;
688+
break;
689+
}
690+
if (cmd == TIOCMBIC)
691+
port->at_data.mdmbits &= ~mdmbits;
692+
else if (cmd == TIOCMBIS)
693+
port->at_data.mdmbits |= mdmbits;
694+
else
695+
port->at_data.mdmbits = mdmbits;
696+
break;
697+
}
698+
699+
default:
700+
ret = -ENOIOCTLCMD;
701+
}
702+
703+
mutex_unlock(&port->data_lock);
704+
705+
return ret;
706+
}
707+
708+
static long wwan_port_fops_ioctl(struct file *filp, unsigned int cmd,
709+
unsigned long arg)
710+
{
711+
struct wwan_port *port = filp->private_data;
712+
int res;
713+
714+
if (port->type == WWAN_PORT_AT) { /* AT port specific IOCTLs */
715+
res = wwan_port_fops_at_ioctl(port, cmd, arg);
716+
if (res != -ENOIOCTLCMD)
717+
return res;
718+
}
719+
720+
switch (cmd) {
721+
case TIOCINQ: { /* aka SIOCINQ aka FIONREAD */
722+
unsigned long flags;
723+
struct sk_buff *skb;
724+
int amount = 0;
725+
726+
spin_lock_irqsave(&port->rxq.lock, flags);
727+
skb_queue_walk(&port->rxq, skb)
728+
amount += skb->len;
729+
spin_unlock_irqrestore(&port->rxq.lock, flags);
730+
731+
return put_user(amount, (int __user *)arg);
732+
}
733+
734+
default:
735+
return -ENOIOCTLCMD;
736+
}
737+
}
738+
548739
static const struct file_operations wwan_port_fops = {
549740
.owner = THIS_MODULE,
550741
.open = wwan_port_fops_open,
551742
.release = wwan_port_fops_release,
552743
.read = wwan_port_fops_read,
553744
.write = wwan_port_fops_write,
554745
.poll = wwan_port_fops_poll,
746+
.unlocked_ioctl = wwan_port_fops_ioctl,
747+
#ifdef CONFIG_COMPAT
748+
.compat_ioctl = compat_ptr_ioctl,
749+
#endif
555750
.llseek = noop_llseek,
556751
};
557752

@@ -562,7 +757,8 @@ static int __init wwan_init(void)
562757
return PTR_ERR(wwan_class);
563758

564759
/* chrdev used for wwan ports */
565-
wwan_major = register_chrdev(0, "wwan_port", &wwan_port_fops);
760+
wwan_major = __register_chrdev(0, 0, WWAN_MAX_MINORS, "wwan_port",
761+
&wwan_port_fops);
566762
if (wwan_major < 0) {
567763
class_destroy(wwan_class);
568764
return wwan_major;
@@ -573,7 +769,7 @@ static int __init wwan_init(void)
573769

574770
static void __exit wwan_exit(void)
575771
{
576-
unregister_chrdev(wwan_major, "wwan_port");
772+
__unregister_chrdev(wwan_major, 0, WWAN_MAX_MINORS, "wwan_port");
577773
class_destroy(wwan_class);
578774
}
579775

0 commit comments

Comments
 (0)