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> 15*e263c5b2SSergey Ryazanov #include <linux/termios.h> 169a44c1ccSLoic Poulain #include <linux/wwan.h> 179a44c1ccSLoic Poulain 1872eedfc4SSergey Ryazanov /* Maximum number of minors in use */ 1972eedfc4SSergey Ryazanov #define WWAN_MAX_MINORS (1 << MINORBITS) 209a44c1ccSLoic Poulain 219a44c1ccSLoic Poulain static DEFINE_MUTEX(wwan_register_lock); /* WWAN device create|remove lock */ 229a44c1ccSLoic Poulain static DEFINE_IDA(minors); /* minors for WWAN port chardevs */ 239a44c1ccSLoic Poulain static DEFINE_IDA(wwan_dev_ids); /* for unique WWAN device IDs */ 249a44c1ccSLoic Poulain static struct class *wwan_class; 259a44c1ccSLoic Poulain static int wwan_major; 269a44c1ccSLoic Poulain 279a44c1ccSLoic Poulain #define to_wwan_dev(d) container_of(d, struct wwan_device, dev) 289a44c1ccSLoic Poulain #define to_wwan_port(d) container_of(d, struct wwan_port, dev) 299a44c1ccSLoic Poulain 309a44c1ccSLoic Poulain /* WWAN port flags */ 31b8c55ce2SLoic Poulain #define WWAN_PORT_TX_OFF 0 329a44c1ccSLoic Poulain 339a44c1ccSLoic Poulain /** 349a44c1ccSLoic Poulain * struct wwan_device - The structure that defines a WWAN device 359a44c1ccSLoic Poulain * 369a44c1ccSLoic Poulain * @id: WWAN device unique ID. 379a44c1ccSLoic Poulain * @dev: Underlying device. 389a44c1ccSLoic Poulain */ 399a44c1ccSLoic Poulain struct wwan_device { 409a44c1ccSLoic Poulain unsigned int id; 419a44c1ccSLoic Poulain struct device dev; 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 66e4e92ee7SLoic Poulain static ssize_t index_show(struct device *dev, struct device_attribute *attr, char *buf) 67e4e92ee7SLoic Poulain { 68e4e92ee7SLoic Poulain struct wwan_device *wwan = to_wwan_dev(dev); 69e4e92ee7SLoic Poulain 70e4e92ee7SLoic Poulain return sprintf(buf, "%d\n", wwan->id); 71e4e92ee7SLoic Poulain } 72e4e92ee7SLoic Poulain static DEVICE_ATTR_RO(index); 73e4e92ee7SLoic Poulain 74e4e92ee7SLoic Poulain static struct attribute *wwan_dev_attrs[] = { 75e4e92ee7SLoic Poulain &dev_attr_index.attr, 76e4e92ee7SLoic Poulain NULL, 77e4e92ee7SLoic Poulain }; 78e4e92ee7SLoic Poulain ATTRIBUTE_GROUPS(wwan_dev); 79e4e92ee7SLoic Poulain 809a44c1ccSLoic Poulain static void wwan_dev_destroy(struct device *dev) 819a44c1ccSLoic Poulain { 829a44c1ccSLoic Poulain struct wwan_device *wwandev = to_wwan_dev(dev); 839a44c1ccSLoic Poulain 849a44c1ccSLoic Poulain ida_free(&wwan_dev_ids, wwandev->id); 859a44c1ccSLoic Poulain kfree(wwandev); 869a44c1ccSLoic Poulain } 879a44c1ccSLoic Poulain 889a44c1ccSLoic Poulain static const struct device_type wwan_dev_type = { 899a44c1ccSLoic Poulain .name = "wwan_dev", 909a44c1ccSLoic Poulain .release = wwan_dev_destroy, 91e4e92ee7SLoic Poulain .groups = wwan_dev_groups, 929a44c1ccSLoic Poulain }; 939a44c1ccSLoic Poulain 949a44c1ccSLoic Poulain static int wwan_dev_parent_match(struct device *dev, const void *parent) 959a44c1ccSLoic Poulain { 969a44c1ccSLoic Poulain return (dev->type == &wwan_dev_type && dev->parent == parent); 979a44c1ccSLoic Poulain } 989a44c1ccSLoic Poulain 999a44c1ccSLoic Poulain static struct wwan_device *wwan_dev_get_by_parent(struct device *parent) 1009a44c1ccSLoic Poulain { 1019a44c1ccSLoic Poulain struct device *dev; 1029a44c1ccSLoic Poulain 1039a44c1ccSLoic Poulain dev = class_find_device(wwan_class, NULL, parent, wwan_dev_parent_match); 1049a44c1ccSLoic Poulain if (!dev) 1059a44c1ccSLoic Poulain return ERR_PTR(-ENODEV); 1069a44c1ccSLoic Poulain 1079a44c1ccSLoic Poulain return to_wwan_dev(dev); 1089a44c1ccSLoic Poulain } 1099a44c1ccSLoic Poulain 1109a44c1ccSLoic Poulain /* This function allocates and registers a new WWAN device OR if a WWAN device 1119a44c1ccSLoic Poulain * already exist for the given parent, it gets a reference and return it. 1129a44c1ccSLoic Poulain * This function is not exported (for now), it is called indirectly via 1139a44c1ccSLoic Poulain * wwan_create_port(). 1149a44c1ccSLoic Poulain */ 1159a44c1ccSLoic Poulain static struct wwan_device *wwan_create_dev(struct device *parent) 1169a44c1ccSLoic Poulain { 1179a44c1ccSLoic Poulain struct wwan_device *wwandev; 1189a44c1ccSLoic Poulain int err, id; 1199a44c1ccSLoic Poulain 1209a44c1ccSLoic Poulain /* The 'find-alloc-register' operation must be protected against 1219a44c1ccSLoic Poulain * concurrent execution, a WWAN device is possibly shared between 1229a44c1ccSLoic Poulain * multiple callers or concurrently unregistered from wwan_remove_dev(). 1239a44c1ccSLoic Poulain */ 1249a44c1ccSLoic Poulain mutex_lock(&wwan_register_lock); 1259a44c1ccSLoic Poulain 1269a44c1ccSLoic Poulain /* If wwandev already exists, return it */ 1279a44c1ccSLoic Poulain wwandev = wwan_dev_get_by_parent(parent); 1289a44c1ccSLoic Poulain if (!IS_ERR(wwandev)) 1299a44c1ccSLoic Poulain goto done_unlock; 1309a44c1ccSLoic Poulain 1319a44c1ccSLoic Poulain id = ida_alloc(&wwan_dev_ids, GFP_KERNEL); 1329a44c1ccSLoic Poulain if (id < 0) 1339a44c1ccSLoic Poulain goto done_unlock; 1349a44c1ccSLoic Poulain 1359a44c1ccSLoic Poulain wwandev = kzalloc(sizeof(*wwandev), GFP_KERNEL); 1369a44c1ccSLoic Poulain if (!wwandev) { 1379a44c1ccSLoic Poulain ida_free(&wwan_dev_ids, id); 1389a44c1ccSLoic Poulain goto done_unlock; 1399a44c1ccSLoic Poulain } 1409a44c1ccSLoic Poulain 1419a44c1ccSLoic Poulain wwandev->dev.parent = parent; 1429a44c1ccSLoic Poulain wwandev->dev.class = wwan_class; 1439a44c1ccSLoic Poulain wwandev->dev.type = &wwan_dev_type; 1449a44c1ccSLoic Poulain wwandev->id = id; 1459a44c1ccSLoic Poulain dev_set_name(&wwandev->dev, "wwan%d", wwandev->id); 1469a44c1ccSLoic Poulain 1479a44c1ccSLoic Poulain err = device_register(&wwandev->dev); 1489a44c1ccSLoic Poulain if (err) { 1499a44c1ccSLoic Poulain put_device(&wwandev->dev); 1509a44c1ccSLoic Poulain wwandev = NULL; 1519a44c1ccSLoic Poulain } 1529a44c1ccSLoic Poulain 1539a44c1ccSLoic Poulain done_unlock: 1549a44c1ccSLoic Poulain mutex_unlock(&wwan_register_lock); 1559a44c1ccSLoic Poulain 1569a44c1ccSLoic Poulain return wwandev; 1579a44c1ccSLoic Poulain } 1589a44c1ccSLoic Poulain 1599a44c1ccSLoic Poulain static int is_wwan_child(struct device *dev, void *data) 1609a44c1ccSLoic Poulain { 1619a44c1ccSLoic Poulain return dev->class == wwan_class; 1629a44c1ccSLoic Poulain } 1639a44c1ccSLoic Poulain 1649a44c1ccSLoic Poulain static void wwan_remove_dev(struct wwan_device *wwandev) 1659a44c1ccSLoic Poulain { 1669a44c1ccSLoic Poulain int ret; 1679a44c1ccSLoic Poulain 1689a44c1ccSLoic Poulain /* Prevent concurrent picking from wwan_create_dev */ 1699a44c1ccSLoic Poulain mutex_lock(&wwan_register_lock); 1709a44c1ccSLoic Poulain 1719a44c1ccSLoic Poulain /* WWAN device is created and registered (get+add) along with its first 1729a44c1ccSLoic Poulain * child port, and subsequent port registrations only grab a reference 1739a44c1ccSLoic Poulain * (get). The WWAN device must then be unregistered (del+put) along with 1749a44c1ccSLoic Poulain * its latest port, and reference simply dropped (put) otherwise. 1759a44c1ccSLoic Poulain */ 1769a44c1ccSLoic Poulain ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child); 1779a44c1ccSLoic Poulain if (!ret) 1789a44c1ccSLoic Poulain device_unregister(&wwandev->dev); 1799a44c1ccSLoic Poulain else 1809a44c1ccSLoic Poulain put_device(&wwandev->dev); 1819a44c1ccSLoic Poulain 1829a44c1ccSLoic Poulain mutex_unlock(&wwan_register_lock); 1839a44c1ccSLoic Poulain } 1849a44c1ccSLoic Poulain 1859a44c1ccSLoic Poulain /* ------- WWAN port management ------- */ 1869a44c1ccSLoic Poulain 187392c26f7SSergey Ryazanov static const struct { 188392c26f7SSergey Ryazanov const char * const name; /* Port type name */ 189392c26f7SSergey Ryazanov const char * const devsuf; /* Port devce name suffix */ 190392c26f7SSergey Ryazanov } wwan_port_types[WWAN_PORT_MAX + 1] = { 191392c26f7SSergey Ryazanov [WWAN_PORT_AT] = { 192392c26f7SSergey Ryazanov .name = "AT", 193392c26f7SSergey Ryazanov .devsuf = "at", 194392c26f7SSergey Ryazanov }, 195392c26f7SSergey Ryazanov [WWAN_PORT_MBIM] = { 196392c26f7SSergey Ryazanov .name = "MBIM", 197392c26f7SSergey Ryazanov .devsuf = "mbim", 198392c26f7SSergey Ryazanov }, 199392c26f7SSergey Ryazanov [WWAN_PORT_QMI] = { 200392c26f7SSergey Ryazanov .name = "QMI", 201392c26f7SSergey Ryazanov .devsuf = "qmi", 202392c26f7SSergey Ryazanov }, 203392c26f7SSergey Ryazanov [WWAN_PORT_QCDM] = { 204392c26f7SSergey Ryazanov .name = "QCDM", 205392c26f7SSergey Ryazanov .devsuf = "qcdm", 206392c26f7SSergey Ryazanov }, 207392c26f7SSergey Ryazanov [WWAN_PORT_FIREHOSE] = { 208392c26f7SSergey Ryazanov .name = "FIREHOSE", 209392c26f7SSergey Ryazanov .devsuf = "firehose", 210392c26f7SSergey Ryazanov }, 211b3e22e10SLoic Poulain }; 212b3e22e10SLoic Poulain 213b3e22e10SLoic Poulain static ssize_t type_show(struct device *dev, struct device_attribute *attr, 214b3e22e10SLoic Poulain char *buf) 215b3e22e10SLoic Poulain { 216b3e22e10SLoic Poulain struct wwan_port *port = to_wwan_port(dev); 217b3e22e10SLoic Poulain 218392c26f7SSergey Ryazanov return sprintf(buf, "%s\n", wwan_port_types[port->type].name); 219b3e22e10SLoic Poulain } 220b3e22e10SLoic Poulain static DEVICE_ATTR_RO(type); 221b3e22e10SLoic Poulain 222b3e22e10SLoic Poulain static struct attribute *wwan_port_attrs[] = { 223b3e22e10SLoic Poulain &dev_attr_type.attr, 224b3e22e10SLoic Poulain NULL, 225b3e22e10SLoic Poulain }; 226b3e22e10SLoic Poulain ATTRIBUTE_GROUPS(wwan_port); 227b3e22e10SLoic Poulain 2289a44c1ccSLoic Poulain static void wwan_port_destroy(struct device *dev) 2299a44c1ccSLoic Poulain { 2309a44c1ccSLoic Poulain struct wwan_port *port = to_wwan_port(dev); 2319a44c1ccSLoic Poulain 2329a44c1ccSLoic Poulain ida_free(&minors, MINOR(port->dev.devt)); 2339a44c1ccSLoic Poulain skb_queue_purge(&port->rxq); 2349a44c1ccSLoic Poulain mutex_destroy(&port->ops_lock); 2359a44c1ccSLoic Poulain kfree(port); 2369a44c1ccSLoic Poulain } 2379a44c1ccSLoic Poulain 2389a44c1ccSLoic Poulain static const struct device_type wwan_port_dev_type = { 2399a44c1ccSLoic Poulain .name = "wwan_port", 2409a44c1ccSLoic Poulain .release = wwan_port_destroy, 241b3e22e10SLoic Poulain .groups = wwan_port_groups, 2429a44c1ccSLoic Poulain }; 2439a44c1ccSLoic Poulain 2449a44c1ccSLoic Poulain static int wwan_port_minor_match(struct device *dev, const void *minor) 2459a44c1ccSLoic Poulain { 2469a44c1ccSLoic Poulain return (dev->type == &wwan_port_dev_type && 2479a44c1ccSLoic Poulain MINOR(dev->devt) == *(unsigned int *)minor); 2489a44c1ccSLoic Poulain } 2499a44c1ccSLoic Poulain 2509a44c1ccSLoic Poulain static struct wwan_port *wwan_port_get_by_minor(unsigned int minor) 2519a44c1ccSLoic Poulain { 2529a44c1ccSLoic Poulain struct device *dev; 2539a44c1ccSLoic Poulain 2549a44c1ccSLoic Poulain dev = class_find_device(wwan_class, NULL, &minor, wwan_port_minor_match); 2559a44c1ccSLoic Poulain if (!dev) 2569a44c1ccSLoic Poulain return ERR_PTR(-ENODEV); 2579a44c1ccSLoic Poulain 2589a44c1ccSLoic Poulain return to_wwan_port(dev); 2599a44c1ccSLoic Poulain } 2609a44c1ccSLoic Poulain 261f458709fSSergey Ryazanov /* Allocate and set unique name based on passed format 262f458709fSSergey Ryazanov * 263f458709fSSergey Ryazanov * Name allocation approach is highly inspired by the __dev_alloc_name() 264f458709fSSergey Ryazanov * function. 265f458709fSSergey Ryazanov * 266f458709fSSergey Ryazanov * To avoid names collision, the caller must prevent the new port device 267f458709fSSergey Ryazanov * registration as well as concurrent invocation of this function. 268f458709fSSergey Ryazanov */ 269f458709fSSergey Ryazanov static int __wwan_port_dev_assign_name(struct wwan_port *port, const char *fmt) 270f458709fSSergey Ryazanov { 271f458709fSSergey Ryazanov struct wwan_device *wwandev = to_wwan_dev(port->dev.parent); 272f458709fSSergey Ryazanov const unsigned int max_ports = PAGE_SIZE * 8; 273f458709fSSergey Ryazanov struct class_dev_iter iter; 274f458709fSSergey Ryazanov unsigned long *idmap; 275f458709fSSergey Ryazanov struct device *dev; 276f458709fSSergey Ryazanov char buf[0x20]; 277f458709fSSergey Ryazanov int id; 278f458709fSSergey Ryazanov 279f458709fSSergey Ryazanov idmap = (unsigned long *)get_zeroed_page(GFP_KERNEL); 280f458709fSSergey Ryazanov if (!idmap) 281f458709fSSergey Ryazanov return -ENOMEM; 282f458709fSSergey Ryazanov 283f458709fSSergey Ryazanov /* Collect ids of same name format ports */ 284f458709fSSergey Ryazanov class_dev_iter_init(&iter, wwan_class, NULL, &wwan_port_dev_type); 285f458709fSSergey Ryazanov while ((dev = class_dev_iter_next(&iter))) { 286f458709fSSergey Ryazanov if (dev->parent != &wwandev->dev) 287f458709fSSergey Ryazanov continue; 288f458709fSSergey Ryazanov if (sscanf(dev_name(dev), fmt, &id) != 1) 289f458709fSSergey Ryazanov continue; 290f458709fSSergey Ryazanov if (id < 0 || id >= max_ports) 291f458709fSSergey Ryazanov continue; 292f458709fSSergey Ryazanov set_bit(id, idmap); 293f458709fSSergey Ryazanov } 294f458709fSSergey Ryazanov class_dev_iter_exit(&iter); 295f458709fSSergey Ryazanov 296f458709fSSergey Ryazanov /* Allocate unique id */ 297f458709fSSergey Ryazanov id = find_first_zero_bit(idmap, max_ports); 298f458709fSSergey Ryazanov free_page((unsigned long)idmap); 299f458709fSSergey Ryazanov 300f458709fSSergey Ryazanov snprintf(buf, sizeof(buf), fmt, id); /* Name generation */ 301f458709fSSergey Ryazanov 302f458709fSSergey Ryazanov dev = device_find_child_by_name(&wwandev->dev, buf); 303f458709fSSergey Ryazanov if (dev) { 304f458709fSSergey Ryazanov put_device(dev); 305f458709fSSergey Ryazanov return -ENFILE; 306f458709fSSergey Ryazanov } 307f458709fSSergey Ryazanov 308f458709fSSergey Ryazanov return dev_set_name(&port->dev, buf); 309f458709fSSergey Ryazanov } 310f458709fSSergey Ryazanov 3119a44c1ccSLoic Poulain struct wwan_port *wwan_create_port(struct device *parent, 3129a44c1ccSLoic Poulain enum wwan_port_type type, 3139a44c1ccSLoic Poulain const struct wwan_port_ops *ops, 3149a44c1ccSLoic Poulain void *drvdata) 3159a44c1ccSLoic Poulain { 3169a44c1ccSLoic Poulain struct wwan_device *wwandev; 3179a44c1ccSLoic Poulain struct wwan_port *port; 3189a44c1ccSLoic Poulain int minor, err = -ENOMEM; 319f458709fSSergey Ryazanov char namefmt[0x20]; 3209a44c1ccSLoic Poulain 321b64d76b7SSergey Ryazanov if (type > WWAN_PORT_MAX || !ops) 3229a44c1ccSLoic Poulain return ERR_PTR(-EINVAL); 3239a44c1ccSLoic Poulain 3249a44c1ccSLoic Poulain /* A port is always a child of a WWAN device, retrieve (allocate or 3259a44c1ccSLoic Poulain * pick) the WWAN device based on the provided parent device. 3269a44c1ccSLoic Poulain */ 3279a44c1ccSLoic Poulain wwandev = wwan_create_dev(parent); 3289a44c1ccSLoic Poulain if (IS_ERR(wwandev)) 3299a44c1ccSLoic Poulain return ERR_CAST(wwandev); 3309a44c1ccSLoic Poulain 3319a44c1ccSLoic Poulain /* A port is exposed as character device, get a minor */ 3329a44c1ccSLoic Poulain minor = ida_alloc_range(&minors, 0, WWAN_MAX_MINORS - 1, GFP_KERNEL); 3339a44c1ccSLoic Poulain if (minor < 0) 3349a44c1ccSLoic Poulain goto error_wwandev_remove; 3359a44c1ccSLoic Poulain 3369a44c1ccSLoic Poulain port = kzalloc(sizeof(*port), GFP_KERNEL); 3379a44c1ccSLoic Poulain if (!port) { 3389a44c1ccSLoic Poulain ida_free(&minors, minor); 3399a44c1ccSLoic Poulain goto error_wwandev_remove; 3409a44c1ccSLoic Poulain } 3419a44c1ccSLoic Poulain 3429a44c1ccSLoic Poulain port->type = type; 3439a44c1ccSLoic Poulain port->ops = ops; 3449a44c1ccSLoic Poulain mutex_init(&port->ops_lock); 3459a44c1ccSLoic Poulain skb_queue_head_init(&port->rxq); 3469a44c1ccSLoic Poulain init_waitqueue_head(&port->waitqueue); 3479a44c1ccSLoic Poulain 3489a44c1ccSLoic Poulain port->dev.parent = &wwandev->dev; 3499a44c1ccSLoic Poulain port->dev.class = wwan_class; 3509a44c1ccSLoic Poulain port->dev.type = &wwan_port_dev_type; 3519a44c1ccSLoic Poulain port->dev.devt = MKDEV(wwan_major, minor); 3529a44c1ccSLoic Poulain dev_set_drvdata(&port->dev, drvdata); 3539a44c1ccSLoic Poulain 354f458709fSSergey Ryazanov /* allocate unique name based on wwan device id, port type and number */ 355f458709fSSergey Ryazanov snprintf(namefmt, sizeof(namefmt), "wwan%u%s%%d", wwandev->id, 356392c26f7SSergey Ryazanov wwan_port_types[port->type].devsuf); 3579a44c1ccSLoic Poulain 358f458709fSSergey Ryazanov /* Serialize ports registration */ 359f458709fSSergey Ryazanov mutex_lock(&wwan_register_lock); 360f458709fSSergey Ryazanov 361f458709fSSergey Ryazanov __wwan_port_dev_assign_name(port, namefmt); 3629a44c1ccSLoic Poulain err = device_register(&port->dev); 363f458709fSSergey Ryazanov 364f458709fSSergey Ryazanov mutex_unlock(&wwan_register_lock); 365f458709fSSergey Ryazanov 3669a44c1ccSLoic Poulain if (err) 3679a44c1ccSLoic Poulain goto error_put_device; 3689a44c1ccSLoic Poulain 3699a44c1ccSLoic Poulain return port; 3709a44c1ccSLoic Poulain 3719a44c1ccSLoic Poulain error_put_device: 3729a44c1ccSLoic Poulain put_device(&port->dev); 3739a44c1ccSLoic Poulain error_wwandev_remove: 3749a44c1ccSLoic Poulain wwan_remove_dev(wwandev); 3759a44c1ccSLoic Poulain 3769a44c1ccSLoic Poulain return ERR_PTR(err); 3779a44c1ccSLoic Poulain } 3789a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_create_port); 3799a44c1ccSLoic Poulain 3809a44c1ccSLoic Poulain void wwan_remove_port(struct wwan_port *port) 3819a44c1ccSLoic Poulain { 3829a44c1ccSLoic Poulain struct wwan_device *wwandev = to_wwan_dev(port->dev.parent); 3839a44c1ccSLoic Poulain 3849a44c1ccSLoic Poulain mutex_lock(&port->ops_lock); 3859a44c1ccSLoic Poulain if (port->start_count) 3869a44c1ccSLoic Poulain port->ops->stop(port); 3879a44c1ccSLoic Poulain port->ops = NULL; /* Prevent any new port operations (e.g. from fops) */ 3889a44c1ccSLoic Poulain mutex_unlock(&port->ops_lock); 3899a44c1ccSLoic Poulain 3909a44c1ccSLoic Poulain wake_up_interruptible(&port->waitqueue); 3919a44c1ccSLoic Poulain 3929a44c1ccSLoic Poulain skb_queue_purge(&port->rxq); 3939a44c1ccSLoic Poulain dev_set_drvdata(&port->dev, NULL); 3949a44c1ccSLoic Poulain device_unregister(&port->dev); 3959a44c1ccSLoic Poulain 3969a44c1ccSLoic Poulain /* Release related wwan device */ 3979a44c1ccSLoic Poulain wwan_remove_dev(wwandev); 3989a44c1ccSLoic Poulain } 3999a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_remove_port); 4009a44c1ccSLoic Poulain 4019a44c1ccSLoic Poulain void wwan_port_rx(struct wwan_port *port, struct sk_buff *skb) 4029a44c1ccSLoic Poulain { 4039a44c1ccSLoic Poulain skb_queue_tail(&port->rxq, skb); 4049a44c1ccSLoic Poulain wake_up_interruptible(&port->waitqueue); 4059a44c1ccSLoic Poulain } 4069a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_rx); 4079a44c1ccSLoic Poulain 4089a44c1ccSLoic Poulain void wwan_port_txon(struct wwan_port *port) 4099a44c1ccSLoic Poulain { 4109a44c1ccSLoic Poulain clear_bit(WWAN_PORT_TX_OFF, &port->flags); 4119a44c1ccSLoic Poulain wake_up_interruptible(&port->waitqueue); 4129a44c1ccSLoic Poulain } 4139a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_txon); 4149a44c1ccSLoic Poulain 4159a44c1ccSLoic Poulain void wwan_port_txoff(struct wwan_port *port) 4169a44c1ccSLoic Poulain { 4179a44c1ccSLoic Poulain set_bit(WWAN_PORT_TX_OFF, &port->flags); 4189a44c1ccSLoic Poulain } 4199a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_txoff); 4209a44c1ccSLoic Poulain 4219a44c1ccSLoic Poulain void *wwan_port_get_drvdata(struct wwan_port *port) 4229a44c1ccSLoic Poulain { 4239a44c1ccSLoic Poulain return dev_get_drvdata(&port->dev); 4249a44c1ccSLoic Poulain } 4259a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_get_drvdata); 4269a44c1ccSLoic Poulain 4279a44c1ccSLoic Poulain static int wwan_port_op_start(struct wwan_port *port) 4289a44c1ccSLoic Poulain { 4299a44c1ccSLoic Poulain int ret = 0; 4309a44c1ccSLoic Poulain 4319a44c1ccSLoic Poulain mutex_lock(&port->ops_lock); 4329a44c1ccSLoic Poulain if (!port->ops) { /* Port got unplugged */ 4339a44c1ccSLoic Poulain ret = -ENODEV; 4349a44c1ccSLoic Poulain goto out_unlock; 4359a44c1ccSLoic Poulain } 4369a44c1ccSLoic Poulain 4379a44c1ccSLoic Poulain /* If port is already started, don't start again */ 4389a44c1ccSLoic Poulain if (!port->start_count) 4399a44c1ccSLoic Poulain ret = port->ops->start(port); 4409a44c1ccSLoic Poulain 4419a44c1ccSLoic Poulain if (!ret) 4429a44c1ccSLoic Poulain port->start_count++; 4439a44c1ccSLoic Poulain 4449a44c1ccSLoic Poulain out_unlock: 4459a44c1ccSLoic Poulain mutex_unlock(&port->ops_lock); 4469a44c1ccSLoic Poulain 4479a44c1ccSLoic Poulain return ret; 4489a44c1ccSLoic Poulain } 4499a44c1ccSLoic Poulain 4509a44c1ccSLoic Poulain static void wwan_port_op_stop(struct wwan_port *port) 4519a44c1ccSLoic Poulain { 4529a44c1ccSLoic Poulain mutex_lock(&port->ops_lock); 4539a44c1ccSLoic Poulain port->start_count--; 4549a44c1ccSLoic Poulain if (port->ops && !port->start_count) 4559a44c1ccSLoic Poulain port->ops->stop(port); 4569a44c1ccSLoic Poulain mutex_unlock(&port->ops_lock); 4579a44c1ccSLoic Poulain } 4589a44c1ccSLoic Poulain 4599a44c1ccSLoic Poulain static int wwan_port_op_tx(struct wwan_port *port, struct sk_buff *skb) 4609a44c1ccSLoic Poulain { 4619a44c1ccSLoic Poulain int ret; 4629a44c1ccSLoic Poulain 4639a44c1ccSLoic Poulain mutex_lock(&port->ops_lock); 4649a44c1ccSLoic Poulain if (!port->ops) { /* Port got unplugged */ 4659a44c1ccSLoic Poulain ret = -ENODEV; 4669a44c1ccSLoic Poulain goto out_unlock; 4679a44c1ccSLoic Poulain } 4689a44c1ccSLoic Poulain 4699a44c1ccSLoic Poulain ret = port->ops->tx(port, skb); 4709a44c1ccSLoic Poulain 4719a44c1ccSLoic Poulain out_unlock: 4729a44c1ccSLoic Poulain mutex_unlock(&port->ops_lock); 4739a44c1ccSLoic Poulain 4749a44c1ccSLoic Poulain return ret; 4759a44c1ccSLoic Poulain } 4769a44c1ccSLoic Poulain 4779a44c1ccSLoic Poulain static bool is_read_blocked(struct wwan_port *port) 4789a44c1ccSLoic Poulain { 4799a44c1ccSLoic Poulain return skb_queue_empty(&port->rxq) && port->ops; 4809a44c1ccSLoic Poulain } 4819a44c1ccSLoic Poulain 4829a44c1ccSLoic Poulain static bool is_write_blocked(struct wwan_port *port) 4839a44c1ccSLoic Poulain { 4849a44c1ccSLoic Poulain return test_bit(WWAN_PORT_TX_OFF, &port->flags) && port->ops; 4859a44c1ccSLoic Poulain } 4869a44c1ccSLoic Poulain 4879a44c1ccSLoic Poulain static int wwan_wait_rx(struct wwan_port *port, bool nonblock) 4889a44c1ccSLoic Poulain { 4899a44c1ccSLoic Poulain if (!is_read_blocked(port)) 4909a44c1ccSLoic Poulain return 0; 4919a44c1ccSLoic Poulain 4929a44c1ccSLoic Poulain if (nonblock) 4939a44c1ccSLoic Poulain return -EAGAIN; 4949a44c1ccSLoic Poulain 4959a44c1ccSLoic Poulain if (wait_event_interruptible(port->waitqueue, !is_read_blocked(port))) 4969a44c1ccSLoic Poulain return -ERESTARTSYS; 4979a44c1ccSLoic Poulain 4989a44c1ccSLoic Poulain return 0; 4999a44c1ccSLoic Poulain } 5009a44c1ccSLoic Poulain 5019a44c1ccSLoic Poulain static int wwan_wait_tx(struct wwan_port *port, bool nonblock) 5029a44c1ccSLoic Poulain { 5039a44c1ccSLoic Poulain if (!is_write_blocked(port)) 5049a44c1ccSLoic Poulain return 0; 5059a44c1ccSLoic Poulain 5069a44c1ccSLoic Poulain if (nonblock) 5079a44c1ccSLoic Poulain return -EAGAIN; 5089a44c1ccSLoic Poulain 5099a44c1ccSLoic Poulain if (wait_event_interruptible(port->waitqueue, !is_write_blocked(port))) 5109a44c1ccSLoic Poulain return -ERESTARTSYS; 5119a44c1ccSLoic Poulain 5129a44c1ccSLoic Poulain return 0; 5139a44c1ccSLoic Poulain } 5149a44c1ccSLoic Poulain 5159a44c1ccSLoic Poulain static int wwan_port_fops_open(struct inode *inode, struct file *file) 5169a44c1ccSLoic Poulain { 5179a44c1ccSLoic Poulain struct wwan_port *port; 5189a44c1ccSLoic Poulain int err = 0; 5199a44c1ccSLoic Poulain 5209a44c1ccSLoic Poulain port = wwan_port_get_by_minor(iminor(inode)); 5219a44c1ccSLoic Poulain if (IS_ERR(port)) 5229a44c1ccSLoic Poulain return PTR_ERR(port); 5239a44c1ccSLoic Poulain 5249a44c1ccSLoic Poulain file->private_data = port; 5259a44c1ccSLoic Poulain stream_open(inode, file); 5269a44c1ccSLoic Poulain 5279a44c1ccSLoic Poulain err = wwan_port_op_start(port); 5289a44c1ccSLoic Poulain if (err) 5299a44c1ccSLoic Poulain put_device(&port->dev); 5309a44c1ccSLoic Poulain 5319a44c1ccSLoic Poulain return err; 5329a44c1ccSLoic Poulain } 5339a44c1ccSLoic Poulain 5349a44c1ccSLoic Poulain static int wwan_port_fops_release(struct inode *inode, struct file *filp) 5359a44c1ccSLoic Poulain { 5369a44c1ccSLoic Poulain struct wwan_port *port = filp->private_data; 5379a44c1ccSLoic Poulain 5389a44c1ccSLoic Poulain wwan_port_op_stop(port); 5399a44c1ccSLoic Poulain put_device(&port->dev); 5409a44c1ccSLoic Poulain 5419a44c1ccSLoic Poulain return 0; 5429a44c1ccSLoic Poulain } 5439a44c1ccSLoic Poulain 5449a44c1ccSLoic Poulain static ssize_t wwan_port_fops_read(struct file *filp, char __user *buf, 5459a44c1ccSLoic Poulain size_t count, loff_t *ppos) 5469a44c1ccSLoic Poulain { 5479a44c1ccSLoic Poulain struct wwan_port *port = filp->private_data; 5489a44c1ccSLoic Poulain struct sk_buff *skb; 5499a44c1ccSLoic Poulain size_t copied; 5509a44c1ccSLoic Poulain int ret; 5519a44c1ccSLoic Poulain 5529a44c1ccSLoic Poulain ret = wwan_wait_rx(port, !!(filp->f_flags & O_NONBLOCK)); 5539a44c1ccSLoic Poulain if (ret) 5549a44c1ccSLoic Poulain return ret; 5559a44c1ccSLoic Poulain 5569a44c1ccSLoic Poulain skb = skb_dequeue(&port->rxq); 5579a44c1ccSLoic Poulain if (!skb) 5589a44c1ccSLoic Poulain return -EIO; 5599a44c1ccSLoic Poulain 5609a44c1ccSLoic Poulain copied = min_t(size_t, count, skb->len); 5619a44c1ccSLoic Poulain if (copy_to_user(buf, skb->data, copied)) { 5629a44c1ccSLoic Poulain kfree_skb(skb); 5639a44c1ccSLoic Poulain return -EFAULT; 5649a44c1ccSLoic Poulain } 5659a44c1ccSLoic Poulain skb_pull(skb, copied); 5669a44c1ccSLoic Poulain 5679a44c1ccSLoic Poulain /* skb is not fully consumed, keep it in the queue */ 5689a44c1ccSLoic Poulain if (skb->len) 5699a44c1ccSLoic Poulain skb_queue_head(&port->rxq, skb); 5709a44c1ccSLoic Poulain else 5719a44c1ccSLoic Poulain consume_skb(skb); 5729a44c1ccSLoic Poulain 5739a44c1ccSLoic Poulain return copied; 5749a44c1ccSLoic Poulain } 5759a44c1ccSLoic Poulain 5769a44c1ccSLoic Poulain static ssize_t wwan_port_fops_write(struct file *filp, const char __user *buf, 5779a44c1ccSLoic Poulain size_t count, loff_t *offp) 5789a44c1ccSLoic Poulain { 5799a44c1ccSLoic Poulain struct wwan_port *port = filp->private_data; 5809a44c1ccSLoic Poulain struct sk_buff *skb; 5819a44c1ccSLoic Poulain int ret; 5829a44c1ccSLoic Poulain 5839a44c1ccSLoic Poulain ret = wwan_wait_tx(port, !!(filp->f_flags & O_NONBLOCK)); 5849a44c1ccSLoic Poulain if (ret) 5859a44c1ccSLoic Poulain return ret; 5869a44c1ccSLoic Poulain 5879a44c1ccSLoic Poulain skb = alloc_skb(count, GFP_KERNEL); 5889a44c1ccSLoic Poulain if (!skb) 5899a44c1ccSLoic Poulain return -ENOMEM; 5909a44c1ccSLoic Poulain 5919a44c1ccSLoic Poulain if (copy_from_user(skb_put(skb, count), buf, count)) { 5929a44c1ccSLoic Poulain kfree_skb(skb); 5939a44c1ccSLoic Poulain return -EFAULT; 5949a44c1ccSLoic Poulain } 5959a44c1ccSLoic Poulain 5969a44c1ccSLoic Poulain ret = wwan_port_op_tx(port, skb); 5979a44c1ccSLoic Poulain if (ret) { 5989a44c1ccSLoic Poulain kfree_skb(skb); 5999a44c1ccSLoic Poulain return ret; 6009a44c1ccSLoic Poulain } 6019a44c1ccSLoic Poulain 6029a44c1ccSLoic Poulain return count; 6039a44c1ccSLoic Poulain } 6049a44c1ccSLoic Poulain 6059a44c1ccSLoic Poulain static __poll_t wwan_port_fops_poll(struct file *filp, poll_table *wait) 6069a44c1ccSLoic Poulain { 6079a44c1ccSLoic Poulain struct wwan_port *port = filp->private_data; 6089a44c1ccSLoic Poulain __poll_t mask = 0; 6099a44c1ccSLoic Poulain 6109a44c1ccSLoic Poulain poll_wait(filp, &port->waitqueue, wait); 6119a44c1ccSLoic Poulain 6129a44c1ccSLoic Poulain if (!is_write_blocked(port)) 6139a44c1ccSLoic Poulain mask |= EPOLLOUT | EPOLLWRNORM; 6149a44c1ccSLoic Poulain if (!is_read_blocked(port)) 6159a44c1ccSLoic Poulain mask |= EPOLLIN | EPOLLRDNORM; 61657e22247SLoic Poulain if (!port->ops) 61757e22247SLoic Poulain mask |= EPOLLHUP | EPOLLERR; 6189a44c1ccSLoic Poulain 6199a44c1ccSLoic Poulain return mask; 6209a44c1ccSLoic Poulain } 6219a44c1ccSLoic Poulain 622*e263c5b2SSergey Ryazanov static long wwan_port_fops_ioctl(struct file *filp, unsigned int cmd, 623*e263c5b2SSergey Ryazanov unsigned long arg) 624*e263c5b2SSergey Ryazanov { 625*e263c5b2SSergey Ryazanov struct wwan_port *port = filp->private_data; 626*e263c5b2SSergey Ryazanov 627*e263c5b2SSergey Ryazanov switch (cmd) { 628*e263c5b2SSergey Ryazanov case TIOCINQ: { /* aka SIOCINQ aka FIONREAD */ 629*e263c5b2SSergey Ryazanov unsigned long flags; 630*e263c5b2SSergey Ryazanov struct sk_buff *skb; 631*e263c5b2SSergey Ryazanov int amount = 0; 632*e263c5b2SSergey Ryazanov 633*e263c5b2SSergey Ryazanov spin_lock_irqsave(&port->rxq.lock, flags); 634*e263c5b2SSergey Ryazanov skb_queue_walk(&port->rxq, skb) 635*e263c5b2SSergey Ryazanov amount += skb->len; 636*e263c5b2SSergey Ryazanov spin_unlock_irqrestore(&port->rxq.lock, flags); 637*e263c5b2SSergey Ryazanov 638*e263c5b2SSergey Ryazanov return put_user(amount, (int __user *)arg); 639*e263c5b2SSergey Ryazanov } 640*e263c5b2SSergey Ryazanov 641*e263c5b2SSergey Ryazanov default: 642*e263c5b2SSergey Ryazanov return -ENOIOCTLCMD; 643*e263c5b2SSergey Ryazanov } 644*e263c5b2SSergey Ryazanov } 645*e263c5b2SSergey Ryazanov 6469a44c1ccSLoic Poulain static const struct file_operations wwan_port_fops = { 6479a44c1ccSLoic Poulain .owner = THIS_MODULE, 6489a44c1ccSLoic Poulain .open = wwan_port_fops_open, 6499a44c1ccSLoic Poulain .release = wwan_port_fops_release, 6509a44c1ccSLoic Poulain .read = wwan_port_fops_read, 6519a44c1ccSLoic Poulain .write = wwan_port_fops_write, 6529a44c1ccSLoic Poulain .poll = wwan_port_fops_poll, 653*e263c5b2SSergey Ryazanov .unlocked_ioctl = wwan_port_fops_ioctl, 654*e263c5b2SSergey Ryazanov #ifdef CONFIG_COMPAT 655*e263c5b2SSergey Ryazanov .compat_ioctl = compat_ptr_ioctl, 656*e263c5b2SSergey Ryazanov #endif 6579a44c1ccSLoic Poulain .llseek = noop_llseek, 6589a44c1ccSLoic Poulain }; 6599a44c1ccSLoic Poulain 6609a44c1ccSLoic Poulain static int __init wwan_init(void) 6619a44c1ccSLoic Poulain { 6629a44c1ccSLoic Poulain wwan_class = class_create(THIS_MODULE, "wwan"); 6639a44c1ccSLoic Poulain if (IS_ERR(wwan_class)) 6649a44c1ccSLoic Poulain return PTR_ERR(wwan_class); 6659a44c1ccSLoic Poulain 6669a44c1ccSLoic Poulain /* chrdev used for wwan ports */ 66772eedfc4SSergey Ryazanov wwan_major = __register_chrdev(0, 0, WWAN_MAX_MINORS, "wwan_port", 66872eedfc4SSergey Ryazanov &wwan_port_fops); 6699a44c1ccSLoic Poulain if (wwan_major < 0) { 6709a44c1ccSLoic Poulain class_destroy(wwan_class); 6719a44c1ccSLoic Poulain return wwan_major; 6729a44c1ccSLoic Poulain } 6739a44c1ccSLoic Poulain 6749a44c1ccSLoic Poulain return 0; 6759a44c1ccSLoic Poulain } 6769a44c1ccSLoic Poulain 6779a44c1ccSLoic Poulain static void __exit wwan_exit(void) 6789a44c1ccSLoic Poulain { 67972eedfc4SSergey Ryazanov __unregister_chrdev(wwan_major, 0, WWAN_MAX_MINORS, "wwan_port"); 6809a44c1ccSLoic Poulain class_destroy(wwan_class); 6819a44c1ccSLoic Poulain } 6829a44c1ccSLoic Poulain 6839a44c1ccSLoic Poulain module_init(wwan_init); 6849a44c1ccSLoic Poulain module_exit(wwan_exit); 6859a44c1ccSLoic Poulain 6869a44c1ccSLoic Poulain MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>"); 6879a44c1ccSLoic Poulain MODULE_DESCRIPTION("WWAN core"); 6889a44c1ccSLoic Poulain MODULE_LICENSE("GPL v2"); 689