xref: /openbmc/linux/drivers/net/wwan/wwan_core.c (revision b8c55ce2)
19a44c1ccSLoic Poulain // SPDX-License-Identifier: GPL-2.0-only
29a44c1ccSLoic Poulain /* Copyright (c) 2021, Linaro Ltd <loic.poulain@linaro.org> */
39a44c1ccSLoic Poulain 
49a44c1ccSLoic Poulain #include <linux/err.h>
59a44c1ccSLoic Poulain #include <linux/errno.h>
69a44c1ccSLoic Poulain #include <linux/fs.h>
79a44c1ccSLoic Poulain #include <linux/init.h>
89a44c1ccSLoic Poulain #include <linux/idr.h>
99a44c1ccSLoic Poulain #include <linux/kernel.h>
109a44c1ccSLoic Poulain #include <linux/module.h>
119a44c1ccSLoic Poulain #include <linux/poll.h>
129a44c1ccSLoic Poulain #include <linux/skbuff.h>
139a44c1ccSLoic Poulain #include <linux/slab.h>
149a44c1ccSLoic Poulain #include <linux/types.h>
159a44c1ccSLoic Poulain #include <linux/wwan.h>
169a44c1ccSLoic Poulain 
179a44c1ccSLoic Poulain #define WWAN_MAX_MINORS 256 /* 256 minors allowed with register_chrdev() */
189a44c1ccSLoic Poulain 
199a44c1ccSLoic Poulain static DEFINE_MUTEX(wwan_register_lock); /* WWAN device create|remove lock */
209a44c1ccSLoic Poulain static DEFINE_IDA(minors); /* minors for WWAN port chardevs */
219a44c1ccSLoic Poulain static DEFINE_IDA(wwan_dev_ids); /* for unique WWAN device IDs */
229a44c1ccSLoic Poulain static struct class *wwan_class;
239a44c1ccSLoic Poulain static int wwan_major;
249a44c1ccSLoic Poulain 
259a44c1ccSLoic Poulain #define to_wwan_dev(d) container_of(d, struct wwan_device, dev)
269a44c1ccSLoic Poulain #define to_wwan_port(d) container_of(d, struct wwan_port, dev)
279a44c1ccSLoic Poulain 
289a44c1ccSLoic Poulain /* WWAN port flags */
29*b8c55ce2SLoic Poulain #define WWAN_PORT_TX_OFF	0
309a44c1ccSLoic Poulain 
319a44c1ccSLoic Poulain /**
329a44c1ccSLoic Poulain  * struct wwan_device - The structure that defines a WWAN device
339a44c1ccSLoic Poulain  *
349a44c1ccSLoic Poulain  * @id: WWAN device unique ID.
359a44c1ccSLoic Poulain  * @dev: Underlying device.
369a44c1ccSLoic Poulain  * @port_id: Current available port ID to pick.
379a44c1ccSLoic Poulain  */
389a44c1ccSLoic Poulain struct wwan_device {
399a44c1ccSLoic Poulain 	unsigned int id;
409a44c1ccSLoic Poulain 	struct device dev;
419a44c1ccSLoic Poulain 	atomic_t port_id;
429a44c1ccSLoic Poulain };
439a44c1ccSLoic Poulain 
449a44c1ccSLoic Poulain /**
459a44c1ccSLoic Poulain  * struct wwan_port - The structure that defines a WWAN port
469a44c1ccSLoic Poulain  * @type: Port type
479a44c1ccSLoic Poulain  * @start_count: Port start counter
489a44c1ccSLoic Poulain  * @flags: Store port state and capabilities
499a44c1ccSLoic Poulain  * @ops: Pointer to WWAN port operations
509a44c1ccSLoic Poulain  * @ops_lock: Protect port ops
519a44c1ccSLoic Poulain  * @dev: Underlying device
529a44c1ccSLoic Poulain  * @rxq: Buffer inbound queue
539a44c1ccSLoic Poulain  * @waitqueue: The waitqueue for port fops (read/write/poll)
549a44c1ccSLoic Poulain  */
559a44c1ccSLoic Poulain struct wwan_port {
569a44c1ccSLoic Poulain 	enum wwan_port_type type;
579a44c1ccSLoic Poulain 	unsigned int start_count;
589a44c1ccSLoic Poulain 	unsigned long flags;
599a44c1ccSLoic Poulain 	const struct wwan_port_ops *ops;
609a44c1ccSLoic Poulain 	struct mutex ops_lock; /* Serialize ops + protect against removal */
619a44c1ccSLoic Poulain 	struct device dev;
629a44c1ccSLoic Poulain 	struct sk_buff_head rxq;
639a44c1ccSLoic Poulain 	wait_queue_head_t waitqueue;
649a44c1ccSLoic Poulain };
659a44c1ccSLoic Poulain 
669a44c1ccSLoic Poulain static void wwan_dev_destroy(struct device *dev)
679a44c1ccSLoic Poulain {
689a44c1ccSLoic Poulain 	struct wwan_device *wwandev = to_wwan_dev(dev);
699a44c1ccSLoic Poulain 
709a44c1ccSLoic Poulain 	ida_free(&wwan_dev_ids, wwandev->id);
719a44c1ccSLoic Poulain 	kfree(wwandev);
729a44c1ccSLoic Poulain }
739a44c1ccSLoic Poulain 
749a44c1ccSLoic Poulain static const struct device_type wwan_dev_type = {
759a44c1ccSLoic Poulain 	.name    = "wwan_dev",
769a44c1ccSLoic Poulain 	.release = wwan_dev_destroy,
779a44c1ccSLoic Poulain };
789a44c1ccSLoic Poulain 
799a44c1ccSLoic Poulain static int wwan_dev_parent_match(struct device *dev, const void *parent)
809a44c1ccSLoic Poulain {
819a44c1ccSLoic Poulain 	return (dev->type == &wwan_dev_type && dev->parent == parent);
829a44c1ccSLoic Poulain }
839a44c1ccSLoic Poulain 
849a44c1ccSLoic Poulain static struct wwan_device *wwan_dev_get_by_parent(struct device *parent)
859a44c1ccSLoic Poulain {
869a44c1ccSLoic Poulain 	struct device *dev;
879a44c1ccSLoic Poulain 
889a44c1ccSLoic Poulain 	dev = class_find_device(wwan_class, NULL, parent, wwan_dev_parent_match);
899a44c1ccSLoic Poulain 	if (!dev)
909a44c1ccSLoic Poulain 		return ERR_PTR(-ENODEV);
919a44c1ccSLoic Poulain 
929a44c1ccSLoic Poulain 	return to_wwan_dev(dev);
939a44c1ccSLoic Poulain }
949a44c1ccSLoic Poulain 
959a44c1ccSLoic Poulain /* This function allocates and registers a new WWAN device OR if a WWAN device
969a44c1ccSLoic Poulain  * already exist for the given parent, it gets a reference and return it.
979a44c1ccSLoic Poulain  * This function is not exported (for now), it is called indirectly via
989a44c1ccSLoic Poulain  * wwan_create_port().
999a44c1ccSLoic Poulain  */
1009a44c1ccSLoic Poulain static struct wwan_device *wwan_create_dev(struct device *parent)
1019a44c1ccSLoic Poulain {
1029a44c1ccSLoic Poulain 	struct wwan_device *wwandev;
1039a44c1ccSLoic Poulain 	int err, id;
1049a44c1ccSLoic Poulain 
1059a44c1ccSLoic Poulain 	/* The 'find-alloc-register' operation must be protected against
1069a44c1ccSLoic Poulain 	 * concurrent execution, a WWAN device is possibly shared between
1079a44c1ccSLoic Poulain 	 * multiple callers or concurrently unregistered from wwan_remove_dev().
1089a44c1ccSLoic Poulain 	 */
1099a44c1ccSLoic Poulain 	mutex_lock(&wwan_register_lock);
1109a44c1ccSLoic Poulain 
1119a44c1ccSLoic Poulain 	/* If wwandev already exists, return it */
1129a44c1ccSLoic Poulain 	wwandev = wwan_dev_get_by_parent(parent);
1139a44c1ccSLoic Poulain 	if (!IS_ERR(wwandev))
1149a44c1ccSLoic Poulain 		goto done_unlock;
1159a44c1ccSLoic Poulain 
1169a44c1ccSLoic Poulain 	id = ida_alloc(&wwan_dev_ids, GFP_KERNEL);
1179a44c1ccSLoic Poulain 	if (id < 0)
1189a44c1ccSLoic Poulain 		goto done_unlock;
1199a44c1ccSLoic Poulain 
1209a44c1ccSLoic Poulain 	wwandev = kzalloc(sizeof(*wwandev), GFP_KERNEL);
1219a44c1ccSLoic Poulain 	if (!wwandev) {
1229a44c1ccSLoic Poulain 		ida_free(&wwan_dev_ids, id);
1239a44c1ccSLoic Poulain 		goto done_unlock;
1249a44c1ccSLoic Poulain 	}
1259a44c1ccSLoic Poulain 
1269a44c1ccSLoic Poulain 	wwandev->dev.parent = parent;
1279a44c1ccSLoic Poulain 	wwandev->dev.class = wwan_class;
1289a44c1ccSLoic Poulain 	wwandev->dev.type = &wwan_dev_type;
1299a44c1ccSLoic Poulain 	wwandev->id = id;
1309a44c1ccSLoic Poulain 	dev_set_name(&wwandev->dev, "wwan%d", wwandev->id);
1319a44c1ccSLoic Poulain 
1329a44c1ccSLoic Poulain 	err = device_register(&wwandev->dev);
1339a44c1ccSLoic Poulain 	if (err) {
1349a44c1ccSLoic Poulain 		put_device(&wwandev->dev);
1359a44c1ccSLoic Poulain 		wwandev = NULL;
1369a44c1ccSLoic Poulain 	}
1379a44c1ccSLoic Poulain 
1389a44c1ccSLoic Poulain done_unlock:
1399a44c1ccSLoic Poulain 	mutex_unlock(&wwan_register_lock);
1409a44c1ccSLoic Poulain 
1419a44c1ccSLoic Poulain 	return wwandev;
1429a44c1ccSLoic Poulain }
1439a44c1ccSLoic Poulain 
1449a44c1ccSLoic Poulain static int is_wwan_child(struct device *dev, void *data)
1459a44c1ccSLoic Poulain {
1469a44c1ccSLoic Poulain 	return dev->class == wwan_class;
1479a44c1ccSLoic Poulain }
1489a44c1ccSLoic Poulain 
1499a44c1ccSLoic Poulain static void wwan_remove_dev(struct wwan_device *wwandev)
1509a44c1ccSLoic Poulain {
1519a44c1ccSLoic Poulain 	int ret;
1529a44c1ccSLoic Poulain 
1539a44c1ccSLoic Poulain 	/* Prevent concurrent picking from wwan_create_dev */
1549a44c1ccSLoic Poulain 	mutex_lock(&wwan_register_lock);
1559a44c1ccSLoic Poulain 
1569a44c1ccSLoic Poulain 	/* WWAN device is created and registered (get+add) along with its first
1579a44c1ccSLoic Poulain 	 * child port, and subsequent port registrations only grab a reference
1589a44c1ccSLoic Poulain 	 * (get). The WWAN device must then be unregistered (del+put) along with
1599a44c1ccSLoic Poulain 	 * its latest port, and reference simply dropped (put) otherwise.
1609a44c1ccSLoic Poulain 	 */
1619a44c1ccSLoic Poulain 	ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child);
1629a44c1ccSLoic Poulain 	if (!ret)
1639a44c1ccSLoic Poulain 		device_unregister(&wwandev->dev);
1649a44c1ccSLoic Poulain 	else
1659a44c1ccSLoic Poulain 		put_device(&wwandev->dev);
1669a44c1ccSLoic Poulain 
1679a44c1ccSLoic Poulain 	mutex_unlock(&wwan_register_lock);
1689a44c1ccSLoic Poulain }
1699a44c1ccSLoic Poulain 
1709a44c1ccSLoic Poulain /* ------- WWAN port management ------- */
1719a44c1ccSLoic Poulain 
1729a44c1ccSLoic Poulain static void wwan_port_destroy(struct device *dev)
1739a44c1ccSLoic Poulain {
1749a44c1ccSLoic Poulain 	struct wwan_port *port = to_wwan_port(dev);
1759a44c1ccSLoic Poulain 
1769a44c1ccSLoic Poulain 	ida_free(&minors, MINOR(port->dev.devt));
1779a44c1ccSLoic Poulain 	skb_queue_purge(&port->rxq);
1789a44c1ccSLoic Poulain 	mutex_destroy(&port->ops_lock);
1799a44c1ccSLoic Poulain 	kfree(port);
1809a44c1ccSLoic Poulain }
1819a44c1ccSLoic Poulain 
1829a44c1ccSLoic Poulain static const struct device_type wwan_port_dev_type = {
1839a44c1ccSLoic Poulain 	.name = "wwan_port",
1849a44c1ccSLoic Poulain 	.release = wwan_port_destroy,
1859a44c1ccSLoic Poulain };
1869a44c1ccSLoic Poulain 
1879a44c1ccSLoic Poulain static int wwan_port_minor_match(struct device *dev, const void *minor)
1889a44c1ccSLoic Poulain {
1899a44c1ccSLoic Poulain 	return (dev->type == &wwan_port_dev_type &&
1909a44c1ccSLoic Poulain 		MINOR(dev->devt) == *(unsigned int *)minor);
1919a44c1ccSLoic Poulain }
1929a44c1ccSLoic Poulain 
1939a44c1ccSLoic Poulain static struct wwan_port *wwan_port_get_by_minor(unsigned int minor)
1949a44c1ccSLoic Poulain {
1959a44c1ccSLoic Poulain 	struct device *dev;
1969a44c1ccSLoic Poulain 
1979a44c1ccSLoic Poulain 	dev = class_find_device(wwan_class, NULL, &minor, wwan_port_minor_match);
1989a44c1ccSLoic Poulain 	if (!dev)
1999a44c1ccSLoic Poulain 		return ERR_PTR(-ENODEV);
2009a44c1ccSLoic Poulain 
2019a44c1ccSLoic Poulain 	return to_wwan_port(dev);
2029a44c1ccSLoic Poulain }
2039a44c1ccSLoic Poulain 
2049a44c1ccSLoic Poulain /* Keep aligned with wwan_port_type enum */
2059a44c1ccSLoic Poulain static const char * const wwan_port_type_str[] = {
2069a44c1ccSLoic Poulain 	"AT",
2079a44c1ccSLoic Poulain 	"MBIM",
2089a44c1ccSLoic Poulain 	"QMI",
2099a44c1ccSLoic Poulain 	"QCDM",
2109a44c1ccSLoic Poulain 	"FIREHOSE"
2119a44c1ccSLoic Poulain };
2129a44c1ccSLoic Poulain 
2139a44c1ccSLoic Poulain struct wwan_port *wwan_create_port(struct device *parent,
2149a44c1ccSLoic Poulain 				   enum wwan_port_type type,
2159a44c1ccSLoic Poulain 				   const struct wwan_port_ops *ops,
2169a44c1ccSLoic Poulain 				   void *drvdata)
2179a44c1ccSLoic Poulain {
2189a44c1ccSLoic Poulain 	struct wwan_device *wwandev;
2199a44c1ccSLoic Poulain 	struct wwan_port *port;
2209a44c1ccSLoic Poulain 	int minor, err = -ENOMEM;
2219a44c1ccSLoic Poulain 
2229a44c1ccSLoic Poulain 	if (type >= WWAN_PORT_MAX || !ops)
2239a44c1ccSLoic Poulain 		return ERR_PTR(-EINVAL);
2249a44c1ccSLoic Poulain 
2259a44c1ccSLoic Poulain 	/* A port is always a child of a WWAN device, retrieve (allocate or
2269a44c1ccSLoic Poulain 	 * pick) the WWAN device based on the provided parent device.
2279a44c1ccSLoic Poulain 	 */
2289a44c1ccSLoic Poulain 	wwandev = wwan_create_dev(parent);
2299a44c1ccSLoic Poulain 	if (IS_ERR(wwandev))
2309a44c1ccSLoic Poulain 		return ERR_CAST(wwandev);
2319a44c1ccSLoic Poulain 
2329a44c1ccSLoic Poulain 	/* A port is exposed as character device, get a minor */
2339a44c1ccSLoic Poulain 	minor = ida_alloc_range(&minors, 0, WWAN_MAX_MINORS - 1, GFP_KERNEL);
2349a44c1ccSLoic Poulain 	if (minor < 0)
2359a44c1ccSLoic Poulain 		goto error_wwandev_remove;
2369a44c1ccSLoic Poulain 
2379a44c1ccSLoic Poulain 	port = kzalloc(sizeof(*port), GFP_KERNEL);
2389a44c1ccSLoic Poulain 	if (!port) {
2399a44c1ccSLoic Poulain 		ida_free(&minors, minor);
2409a44c1ccSLoic Poulain 		goto error_wwandev_remove;
2419a44c1ccSLoic Poulain 	}
2429a44c1ccSLoic Poulain 
2439a44c1ccSLoic Poulain 	port->type = type;
2449a44c1ccSLoic Poulain 	port->ops = ops;
2459a44c1ccSLoic Poulain 	mutex_init(&port->ops_lock);
2469a44c1ccSLoic Poulain 	skb_queue_head_init(&port->rxq);
2479a44c1ccSLoic Poulain 	init_waitqueue_head(&port->waitqueue);
2489a44c1ccSLoic Poulain 
2499a44c1ccSLoic Poulain 	port->dev.parent = &wwandev->dev;
2509a44c1ccSLoic Poulain 	port->dev.class = wwan_class;
2519a44c1ccSLoic Poulain 	port->dev.type = &wwan_port_dev_type;
2529a44c1ccSLoic Poulain 	port->dev.devt = MKDEV(wwan_major, minor);
2539a44c1ccSLoic Poulain 	dev_set_drvdata(&port->dev, drvdata);
2549a44c1ccSLoic Poulain 
2559a44c1ccSLoic Poulain 	/* create unique name based on wwan device id, port index and type */
2569a44c1ccSLoic Poulain 	dev_set_name(&port->dev, "wwan%up%u%s", wwandev->id,
2579a44c1ccSLoic Poulain 		     atomic_inc_return(&wwandev->port_id),
2589a44c1ccSLoic Poulain 		     wwan_port_type_str[port->type]);
2599a44c1ccSLoic Poulain 
2609a44c1ccSLoic Poulain 	err = device_register(&port->dev);
2619a44c1ccSLoic Poulain 	if (err)
2629a44c1ccSLoic Poulain 		goto error_put_device;
2639a44c1ccSLoic Poulain 
2649a44c1ccSLoic Poulain 	return port;
2659a44c1ccSLoic Poulain 
2669a44c1ccSLoic Poulain error_put_device:
2679a44c1ccSLoic Poulain 	put_device(&port->dev);
2689a44c1ccSLoic Poulain error_wwandev_remove:
2699a44c1ccSLoic Poulain 	wwan_remove_dev(wwandev);
2709a44c1ccSLoic Poulain 
2719a44c1ccSLoic Poulain 	return ERR_PTR(err);
2729a44c1ccSLoic Poulain }
2739a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_create_port);
2749a44c1ccSLoic Poulain 
2759a44c1ccSLoic Poulain void wwan_remove_port(struct wwan_port *port)
2769a44c1ccSLoic Poulain {
2779a44c1ccSLoic Poulain 	struct wwan_device *wwandev = to_wwan_dev(port->dev.parent);
2789a44c1ccSLoic Poulain 
2799a44c1ccSLoic Poulain 	mutex_lock(&port->ops_lock);
2809a44c1ccSLoic Poulain 	if (port->start_count)
2819a44c1ccSLoic Poulain 		port->ops->stop(port);
2829a44c1ccSLoic Poulain 	port->ops = NULL; /* Prevent any new port operations (e.g. from fops) */
2839a44c1ccSLoic Poulain 	mutex_unlock(&port->ops_lock);
2849a44c1ccSLoic Poulain 
2859a44c1ccSLoic Poulain 	wake_up_interruptible(&port->waitqueue);
2869a44c1ccSLoic Poulain 
2879a44c1ccSLoic Poulain 	skb_queue_purge(&port->rxq);
2889a44c1ccSLoic Poulain 	dev_set_drvdata(&port->dev, NULL);
2899a44c1ccSLoic Poulain 	device_unregister(&port->dev);
2909a44c1ccSLoic Poulain 
2919a44c1ccSLoic Poulain 	/* Release related wwan device */
2929a44c1ccSLoic Poulain 	wwan_remove_dev(wwandev);
2939a44c1ccSLoic Poulain }
2949a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_remove_port);
2959a44c1ccSLoic Poulain 
2969a44c1ccSLoic Poulain void wwan_port_rx(struct wwan_port *port, struct sk_buff *skb)
2979a44c1ccSLoic Poulain {
2989a44c1ccSLoic Poulain 	skb_queue_tail(&port->rxq, skb);
2999a44c1ccSLoic Poulain 	wake_up_interruptible(&port->waitqueue);
3009a44c1ccSLoic Poulain }
3019a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_rx);
3029a44c1ccSLoic Poulain 
3039a44c1ccSLoic Poulain void wwan_port_txon(struct wwan_port *port)
3049a44c1ccSLoic Poulain {
3059a44c1ccSLoic Poulain 	clear_bit(WWAN_PORT_TX_OFF, &port->flags);
3069a44c1ccSLoic Poulain 	wake_up_interruptible(&port->waitqueue);
3079a44c1ccSLoic Poulain }
3089a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_txon);
3099a44c1ccSLoic Poulain 
3109a44c1ccSLoic Poulain void wwan_port_txoff(struct wwan_port *port)
3119a44c1ccSLoic Poulain {
3129a44c1ccSLoic Poulain 	set_bit(WWAN_PORT_TX_OFF, &port->flags);
3139a44c1ccSLoic Poulain }
3149a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_txoff);
3159a44c1ccSLoic Poulain 
3169a44c1ccSLoic Poulain void *wwan_port_get_drvdata(struct wwan_port *port)
3179a44c1ccSLoic Poulain {
3189a44c1ccSLoic Poulain 	return dev_get_drvdata(&port->dev);
3199a44c1ccSLoic Poulain }
3209a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_get_drvdata);
3219a44c1ccSLoic Poulain 
3229a44c1ccSLoic Poulain static int wwan_port_op_start(struct wwan_port *port)
3239a44c1ccSLoic Poulain {
3249a44c1ccSLoic Poulain 	int ret = 0;
3259a44c1ccSLoic Poulain 
3269a44c1ccSLoic Poulain 	mutex_lock(&port->ops_lock);
3279a44c1ccSLoic Poulain 	if (!port->ops) { /* Port got unplugged */
3289a44c1ccSLoic Poulain 		ret = -ENODEV;
3299a44c1ccSLoic Poulain 		goto out_unlock;
3309a44c1ccSLoic Poulain 	}
3319a44c1ccSLoic Poulain 
3329a44c1ccSLoic Poulain 	/* If port is already started, don't start again */
3339a44c1ccSLoic Poulain 	if (!port->start_count)
3349a44c1ccSLoic Poulain 		ret = port->ops->start(port);
3359a44c1ccSLoic Poulain 
3369a44c1ccSLoic Poulain 	if (!ret)
3379a44c1ccSLoic Poulain 		port->start_count++;
3389a44c1ccSLoic Poulain 
3399a44c1ccSLoic Poulain out_unlock:
3409a44c1ccSLoic Poulain 	mutex_unlock(&port->ops_lock);
3419a44c1ccSLoic Poulain 
3429a44c1ccSLoic Poulain 	return ret;
3439a44c1ccSLoic Poulain }
3449a44c1ccSLoic Poulain 
3459a44c1ccSLoic Poulain static void wwan_port_op_stop(struct wwan_port *port)
3469a44c1ccSLoic Poulain {
3479a44c1ccSLoic Poulain 	mutex_lock(&port->ops_lock);
3489a44c1ccSLoic Poulain 	port->start_count--;
3499a44c1ccSLoic Poulain 	if (port->ops && !port->start_count)
3509a44c1ccSLoic Poulain 		port->ops->stop(port);
3519a44c1ccSLoic Poulain 	mutex_unlock(&port->ops_lock);
3529a44c1ccSLoic Poulain }
3539a44c1ccSLoic Poulain 
3549a44c1ccSLoic Poulain static int wwan_port_op_tx(struct wwan_port *port, struct sk_buff *skb)
3559a44c1ccSLoic Poulain {
3569a44c1ccSLoic Poulain 	int ret;
3579a44c1ccSLoic Poulain 
3589a44c1ccSLoic Poulain 	mutex_lock(&port->ops_lock);
3599a44c1ccSLoic Poulain 	if (!port->ops) { /* Port got unplugged */
3609a44c1ccSLoic Poulain 		ret = -ENODEV;
3619a44c1ccSLoic Poulain 		goto out_unlock;
3629a44c1ccSLoic Poulain 	}
3639a44c1ccSLoic Poulain 
3649a44c1ccSLoic Poulain 	ret = port->ops->tx(port, skb);
3659a44c1ccSLoic Poulain 
3669a44c1ccSLoic Poulain out_unlock:
3679a44c1ccSLoic Poulain 	mutex_unlock(&port->ops_lock);
3689a44c1ccSLoic Poulain 
3699a44c1ccSLoic Poulain 	return ret;
3709a44c1ccSLoic Poulain }
3719a44c1ccSLoic Poulain 
3729a44c1ccSLoic Poulain static bool is_read_blocked(struct wwan_port *port)
3739a44c1ccSLoic Poulain {
3749a44c1ccSLoic Poulain 	return skb_queue_empty(&port->rxq) && port->ops;
3759a44c1ccSLoic Poulain }
3769a44c1ccSLoic Poulain 
3779a44c1ccSLoic Poulain static bool is_write_blocked(struct wwan_port *port)
3789a44c1ccSLoic Poulain {
3799a44c1ccSLoic Poulain 	return test_bit(WWAN_PORT_TX_OFF, &port->flags) && port->ops;
3809a44c1ccSLoic Poulain }
3819a44c1ccSLoic Poulain 
3829a44c1ccSLoic Poulain static int wwan_wait_rx(struct wwan_port *port, bool nonblock)
3839a44c1ccSLoic Poulain {
3849a44c1ccSLoic Poulain 	if (!is_read_blocked(port))
3859a44c1ccSLoic Poulain 		return 0;
3869a44c1ccSLoic Poulain 
3879a44c1ccSLoic Poulain 	if (nonblock)
3889a44c1ccSLoic Poulain 		return -EAGAIN;
3899a44c1ccSLoic Poulain 
3909a44c1ccSLoic Poulain 	if (wait_event_interruptible(port->waitqueue, !is_read_blocked(port)))
3919a44c1ccSLoic Poulain 		return -ERESTARTSYS;
3929a44c1ccSLoic Poulain 
3939a44c1ccSLoic Poulain 	return 0;
3949a44c1ccSLoic Poulain }
3959a44c1ccSLoic Poulain 
3969a44c1ccSLoic Poulain static int wwan_wait_tx(struct wwan_port *port, bool nonblock)
3979a44c1ccSLoic Poulain {
3989a44c1ccSLoic Poulain 	if (!is_write_blocked(port))
3999a44c1ccSLoic Poulain 		return 0;
4009a44c1ccSLoic Poulain 
4019a44c1ccSLoic Poulain 	if (nonblock)
4029a44c1ccSLoic Poulain 		return -EAGAIN;
4039a44c1ccSLoic Poulain 
4049a44c1ccSLoic Poulain 	if (wait_event_interruptible(port->waitqueue, !is_write_blocked(port)))
4059a44c1ccSLoic Poulain 		return -ERESTARTSYS;
4069a44c1ccSLoic Poulain 
4079a44c1ccSLoic Poulain 	return 0;
4089a44c1ccSLoic Poulain }
4099a44c1ccSLoic Poulain 
4109a44c1ccSLoic Poulain static int wwan_port_fops_open(struct inode *inode, struct file *file)
4119a44c1ccSLoic Poulain {
4129a44c1ccSLoic Poulain 	struct wwan_port *port;
4139a44c1ccSLoic Poulain 	int err = 0;
4149a44c1ccSLoic Poulain 
4159a44c1ccSLoic Poulain 	port = wwan_port_get_by_minor(iminor(inode));
4169a44c1ccSLoic Poulain 	if (IS_ERR(port))
4179a44c1ccSLoic Poulain 		return PTR_ERR(port);
4189a44c1ccSLoic Poulain 
4199a44c1ccSLoic Poulain 	file->private_data = port;
4209a44c1ccSLoic Poulain 	stream_open(inode, file);
4219a44c1ccSLoic Poulain 
4229a44c1ccSLoic Poulain 	err = wwan_port_op_start(port);
4239a44c1ccSLoic Poulain 	if (err)
4249a44c1ccSLoic Poulain 		put_device(&port->dev);
4259a44c1ccSLoic Poulain 
4269a44c1ccSLoic Poulain 	return err;
4279a44c1ccSLoic Poulain }
4289a44c1ccSLoic Poulain 
4299a44c1ccSLoic Poulain static int wwan_port_fops_release(struct inode *inode, struct file *filp)
4309a44c1ccSLoic Poulain {
4319a44c1ccSLoic Poulain 	struct wwan_port *port = filp->private_data;
4329a44c1ccSLoic Poulain 
4339a44c1ccSLoic Poulain 	wwan_port_op_stop(port);
4349a44c1ccSLoic Poulain 	put_device(&port->dev);
4359a44c1ccSLoic Poulain 
4369a44c1ccSLoic Poulain 	return 0;
4379a44c1ccSLoic Poulain }
4389a44c1ccSLoic Poulain 
4399a44c1ccSLoic Poulain static ssize_t wwan_port_fops_read(struct file *filp, char __user *buf,
4409a44c1ccSLoic Poulain 				   size_t count, loff_t *ppos)
4419a44c1ccSLoic Poulain {
4429a44c1ccSLoic Poulain 	struct wwan_port *port = filp->private_data;
4439a44c1ccSLoic Poulain 	struct sk_buff *skb;
4449a44c1ccSLoic Poulain 	size_t copied;
4459a44c1ccSLoic Poulain 	int ret;
4469a44c1ccSLoic Poulain 
4479a44c1ccSLoic Poulain 	ret = wwan_wait_rx(port, !!(filp->f_flags & O_NONBLOCK));
4489a44c1ccSLoic Poulain 	if (ret)
4499a44c1ccSLoic Poulain 		return ret;
4509a44c1ccSLoic Poulain 
4519a44c1ccSLoic Poulain 	skb = skb_dequeue(&port->rxq);
4529a44c1ccSLoic Poulain 	if (!skb)
4539a44c1ccSLoic Poulain 		return -EIO;
4549a44c1ccSLoic Poulain 
4559a44c1ccSLoic Poulain 	copied = min_t(size_t, count, skb->len);
4569a44c1ccSLoic Poulain 	if (copy_to_user(buf, skb->data, copied)) {
4579a44c1ccSLoic Poulain 		kfree_skb(skb);
4589a44c1ccSLoic Poulain 		return -EFAULT;
4599a44c1ccSLoic Poulain 	}
4609a44c1ccSLoic Poulain 	skb_pull(skb, copied);
4619a44c1ccSLoic Poulain 
4629a44c1ccSLoic Poulain 	/* skb is not fully consumed, keep it in the queue */
4639a44c1ccSLoic Poulain 	if (skb->len)
4649a44c1ccSLoic Poulain 		skb_queue_head(&port->rxq, skb);
4659a44c1ccSLoic Poulain 	else
4669a44c1ccSLoic Poulain 		consume_skb(skb);
4679a44c1ccSLoic Poulain 
4689a44c1ccSLoic Poulain 	return copied;
4699a44c1ccSLoic Poulain }
4709a44c1ccSLoic Poulain 
4719a44c1ccSLoic Poulain static ssize_t wwan_port_fops_write(struct file *filp, const char __user *buf,
4729a44c1ccSLoic Poulain 				    size_t count, loff_t *offp)
4739a44c1ccSLoic Poulain {
4749a44c1ccSLoic Poulain 	struct wwan_port *port = filp->private_data;
4759a44c1ccSLoic Poulain 	struct sk_buff *skb;
4769a44c1ccSLoic Poulain 	int ret;
4779a44c1ccSLoic Poulain 
4789a44c1ccSLoic Poulain 	ret = wwan_wait_tx(port, !!(filp->f_flags & O_NONBLOCK));
4799a44c1ccSLoic Poulain 	if (ret)
4809a44c1ccSLoic Poulain 		return ret;
4819a44c1ccSLoic Poulain 
4829a44c1ccSLoic Poulain 	skb = alloc_skb(count, GFP_KERNEL);
4839a44c1ccSLoic Poulain 	if (!skb)
4849a44c1ccSLoic Poulain 		return -ENOMEM;
4859a44c1ccSLoic Poulain 
4869a44c1ccSLoic Poulain 	if (copy_from_user(skb_put(skb, count), buf, count)) {
4879a44c1ccSLoic Poulain 		kfree_skb(skb);
4889a44c1ccSLoic Poulain 		return -EFAULT;
4899a44c1ccSLoic Poulain 	}
4909a44c1ccSLoic Poulain 
4919a44c1ccSLoic Poulain 	ret = wwan_port_op_tx(port, skb);
4929a44c1ccSLoic Poulain 	if (ret) {
4939a44c1ccSLoic Poulain 		kfree_skb(skb);
4949a44c1ccSLoic Poulain 		return ret;
4959a44c1ccSLoic Poulain 	}
4969a44c1ccSLoic Poulain 
4979a44c1ccSLoic Poulain 	return count;
4989a44c1ccSLoic Poulain }
4999a44c1ccSLoic Poulain 
5009a44c1ccSLoic Poulain static __poll_t wwan_port_fops_poll(struct file *filp, poll_table *wait)
5019a44c1ccSLoic Poulain {
5029a44c1ccSLoic Poulain 	struct wwan_port *port = filp->private_data;
5039a44c1ccSLoic Poulain 	__poll_t mask = 0;
5049a44c1ccSLoic Poulain 
5059a44c1ccSLoic Poulain 	poll_wait(filp, &port->waitqueue, wait);
5069a44c1ccSLoic Poulain 
5079a44c1ccSLoic Poulain 	if (!is_write_blocked(port))
5089a44c1ccSLoic Poulain 		mask |= EPOLLOUT | EPOLLWRNORM;
5099a44c1ccSLoic Poulain 	if (!is_read_blocked(port))
5109a44c1ccSLoic Poulain 		mask |= EPOLLIN | EPOLLRDNORM;
5119a44c1ccSLoic Poulain 
5129a44c1ccSLoic Poulain 	return mask;
5139a44c1ccSLoic Poulain }
5149a44c1ccSLoic Poulain 
5159a44c1ccSLoic Poulain static const struct file_operations wwan_port_fops = {
5169a44c1ccSLoic Poulain 	.owner = THIS_MODULE,
5179a44c1ccSLoic Poulain 	.open = wwan_port_fops_open,
5189a44c1ccSLoic Poulain 	.release = wwan_port_fops_release,
5199a44c1ccSLoic Poulain 	.read = wwan_port_fops_read,
5209a44c1ccSLoic Poulain 	.write = wwan_port_fops_write,
5219a44c1ccSLoic Poulain 	.poll = wwan_port_fops_poll,
5229a44c1ccSLoic Poulain 	.llseek = noop_llseek,
5239a44c1ccSLoic Poulain };
5249a44c1ccSLoic Poulain 
5259a44c1ccSLoic Poulain static int __init wwan_init(void)
5269a44c1ccSLoic Poulain {
5279a44c1ccSLoic Poulain 	wwan_class = class_create(THIS_MODULE, "wwan");
5289a44c1ccSLoic Poulain 	if (IS_ERR(wwan_class))
5299a44c1ccSLoic Poulain 		return PTR_ERR(wwan_class);
5309a44c1ccSLoic Poulain 
5319a44c1ccSLoic Poulain 	/* chrdev used for wwan ports */
5329a44c1ccSLoic Poulain 	wwan_major = register_chrdev(0, "wwan_port", &wwan_port_fops);
5339a44c1ccSLoic Poulain 	if (wwan_major < 0) {
5349a44c1ccSLoic Poulain 		class_destroy(wwan_class);
5359a44c1ccSLoic Poulain 		return wwan_major;
5369a44c1ccSLoic Poulain 	}
5379a44c1ccSLoic Poulain 
5389a44c1ccSLoic Poulain 	return 0;
5399a44c1ccSLoic Poulain }
5409a44c1ccSLoic Poulain 
5419a44c1ccSLoic Poulain static void __exit wwan_exit(void)
5429a44c1ccSLoic Poulain {
5439a44c1ccSLoic Poulain 	unregister_chrdev(wwan_major, "wwan_port");
5449a44c1ccSLoic Poulain 	class_destroy(wwan_class);
5459a44c1ccSLoic Poulain }
5469a44c1ccSLoic Poulain 
5479a44c1ccSLoic Poulain module_init(wwan_init);
5489a44c1ccSLoic Poulain module_exit(wwan_exit);
5499a44c1ccSLoic Poulain 
5509a44c1ccSLoic Poulain MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
5519a44c1ccSLoic Poulain MODULE_DESCRIPTION("WWAN core");
5529a44c1ccSLoic Poulain MODULE_LICENSE("GPL v2");
553