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> 6c4804670SM Chetan Kumar #include <linux/debugfs.h> 79a44c1ccSLoic Poulain #include <linux/fs.h> 89a44c1ccSLoic Poulain #include <linux/init.h> 99a44c1ccSLoic Poulain #include <linux/idr.h> 109a44c1ccSLoic Poulain #include <linux/kernel.h> 119a44c1ccSLoic Poulain #include <linux/module.h> 129a44c1ccSLoic Poulain #include <linux/poll.h> 139a44c1ccSLoic Poulain #include <linux/skbuff.h> 149a44c1ccSLoic Poulain #include <linux/slab.h> 159a44c1ccSLoic Poulain #include <linux/types.h> 16*89bbeb7eSAl Viro #include <linux/uaccess.h> 17e263c5b2SSergey Ryazanov #include <linux/termios.h> 189a44c1ccSLoic Poulain #include <linux/wwan.h> 1988b71053SJohannes Berg #include <net/rtnetlink.h> 2088b71053SJohannes Berg #include <uapi/linux/wwan.h> 219a44c1ccSLoic Poulain 2272eedfc4SSergey Ryazanov /* Maximum number of minors in use */ 2372eedfc4SSergey Ryazanov #define WWAN_MAX_MINORS (1 << MINORBITS) 249a44c1ccSLoic Poulain 259a44c1ccSLoic Poulain static DEFINE_MUTEX(wwan_register_lock); /* WWAN device create|remove lock */ 269a44c1ccSLoic Poulain static DEFINE_IDA(minors); /* minors for WWAN port chardevs */ 279a44c1ccSLoic Poulain static DEFINE_IDA(wwan_dev_ids); /* for unique WWAN device IDs */ 289a44c1ccSLoic Poulain static struct class *wwan_class; 299a44c1ccSLoic Poulain static int wwan_major; 30c4804670SM Chetan Kumar static struct dentry *wwan_debugfs_dir; 319a44c1ccSLoic Poulain 329a44c1ccSLoic Poulain #define to_wwan_dev(d) container_of(d, struct wwan_device, dev) 339a44c1ccSLoic Poulain #define to_wwan_port(d) container_of(d, struct wwan_port, dev) 349a44c1ccSLoic Poulain 359a44c1ccSLoic Poulain /* WWAN port flags */ 36b8c55ce2SLoic Poulain #define WWAN_PORT_TX_OFF 0 379a44c1ccSLoic Poulain 389a44c1ccSLoic Poulain /** 399a44c1ccSLoic Poulain * struct wwan_device - The structure that defines a WWAN device 409a44c1ccSLoic Poulain * 419a44c1ccSLoic Poulain * @id: WWAN device unique ID. 429a44c1ccSLoic Poulain * @dev: Underlying device. 4388b71053SJohannes Berg * @port_id: Current available port ID to pick. 4488b71053SJohannes Berg * @ops: wwan device ops 4588b71053SJohannes Berg * @ops_ctxt: context to pass to ops 46c4804670SM Chetan Kumar * @debugfs_dir: WWAN device debugfs dir 479a44c1ccSLoic Poulain */ 489a44c1ccSLoic Poulain struct wwan_device { 499a44c1ccSLoic Poulain unsigned int id; 509a44c1ccSLoic Poulain struct device dev; 5188b71053SJohannes Berg atomic_t port_id; 5288b71053SJohannes Berg const struct wwan_ops *ops; 5388b71053SJohannes Berg void *ops_ctxt; 54283e6f5aSSergey Ryazanov #ifdef CONFIG_WWAN_DEBUGFS 55c4804670SM Chetan Kumar struct dentry *debugfs_dir; 56283e6f5aSSergey Ryazanov #endif 579a44c1ccSLoic Poulain }; 589a44c1ccSLoic Poulain 599a44c1ccSLoic Poulain /** 609a44c1ccSLoic Poulain * struct wwan_port - The structure that defines a WWAN port 619a44c1ccSLoic Poulain * @type: Port type 629a44c1ccSLoic Poulain * @start_count: Port start counter 639a44c1ccSLoic Poulain * @flags: Store port state and capabilities 649a44c1ccSLoic Poulain * @ops: Pointer to WWAN port operations 659a44c1ccSLoic Poulain * @ops_lock: Protect port ops 669a44c1ccSLoic Poulain * @dev: Underlying device 679a44c1ccSLoic Poulain * @rxq: Buffer inbound queue 689a44c1ccSLoic Poulain * @waitqueue: The waitqueue for port fops (read/write/poll) 69c230035cSSergey Ryazanov * @data_lock: Port specific data access serialization 70c230035cSSergey Ryazanov * @at_data: AT port specific data 719a44c1ccSLoic Poulain */ 729a44c1ccSLoic Poulain struct wwan_port { 739a44c1ccSLoic Poulain enum wwan_port_type type; 749a44c1ccSLoic Poulain unsigned int start_count; 759a44c1ccSLoic Poulain unsigned long flags; 769a44c1ccSLoic Poulain const struct wwan_port_ops *ops; 779a44c1ccSLoic Poulain struct mutex ops_lock; /* Serialize ops + protect against removal */ 789a44c1ccSLoic Poulain struct device dev; 799a44c1ccSLoic Poulain struct sk_buff_head rxq; 809a44c1ccSLoic Poulain wait_queue_head_t waitqueue; 81c230035cSSergey Ryazanov struct mutex data_lock; /* Port specific data access serialization */ 82c230035cSSergey Ryazanov union { 83c230035cSSergey Ryazanov struct { 84c230035cSSergey Ryazanov struct ktermios termios; 85c230035cSSergey Ryazanov int mdmbits; 86c230035cSSergey Ryazanov } at_data; 87c230035cSSergey Ryazanov }; 889a44c1ccSLoic Poulain }; 899a44c1ccSLoic Poulain 90e4e92ee7SLoic Poulain static ssize_t index_show(struct device *dev, struct device_attribute *attr, char *buf) 91e4e92ee7SLoic Poulain { 92e4e92ee7SLoic Poulain struct wwan_device *wwan = to_wwan_dev(dev); 93e4e92ee7SLoic Poulain 94e4e92ee7SLoic Poulain return sprintf(buf, "%d\n", wwan->id); 95e4e92ee7SLoic Poulain } 96e4e92ee7SLoic Poulain static DEVICE_ATTR_RO(index); 97e4e92ee7SLoic Poulain 98e4e92ee7SLoic Poulain static struct attribute *wwan_dev_attrs[] = { 99e4e92ee7SLoic Poulain &dev_attr_index.attr, 100e4e92ee7SLoic Poulain NULL, 101e4e92ee7SLoic Poulain }; 102e4e92ee7SLoic Poulain ATTRIBUTE_GROUPS(wwan_dev); 103e4e92ee7SLoic Poulain 1049a44c1ccSLoic Poulain static void wwan_dev_destroy(struct device *dev) 1059a44c1ccSLoic Poulain { 1069a44c1ccSLoic Poulain struct wwan_device *wwandev = to_wwan_dev(dev); 1079a44c1ccSLoic Poulain 1089a44c1ccSLoic Poulain ida_free(&wwan_dev_ids, wwandev->id); 1099a44c1ccSLoic Poulain kfree(wwandev); 1109a44c1ccSLoic Poulain } 1119a44c1ccSLoic Poulain 1129a44c1ccSLoic Poulain static const struct device_type wwan_dev_type = { 1139a44c1ccSLoic Poulain .name = "wwan_dev", 1149a44c1ccSLoic Poulain .release = wwan_dev_destroy, 115e4e92ee7SLoic Poulain .groups = wwan_dev_groups, 1169a44c1ccSLoic Poulain }; 1179a44c1ccSLoic Poulain 1189a44c1ccSLoic Poulain static int wwan_dev_parent_match(struct device *dev, const void *parent) 1199a44c1ccSLoic Poulain { 12088b71053SJohannes Berg return (dev->type == &wwan_dev_type && 12188b71053SJohannes Berg (dev->parent == parent || dev == parent)); 1229a44c1ccSLoic Poulain } 1239a44c1ccSLoic Poulain 1249a44c1ccSLoic Poulain static struct wwan_device *wwan_dev_get_by_parent(struct device *parent) 1259a44c1ccSLoic Poulain { 1269a44c1ccSLoic Poulain struct device *dev; 1279a44c1ccSLoic Poulain 1289a44c1ccSLoic Poulain dev = class_find_device(wwan_class, NULL, parent, wwan_dev_parent_match); 1299a44c1ccSLoic Poulain if (!dev) 1309a44c1ccSLoic Poulain return ERR_PTR(-ENODEV); 1319a44c1ccSLoic Poulain 1329a44c1ccSLoic Poulain return to_wwan_dev(dev); 1339a44c1ccSLoic Poulain } 1349a44c1ccSLoic Poulain 13588b71053SJohannes Berg static int wwan_dev_name_match(struct device *dev, const void *name) 13688b71053SJohannes Berg { 13788b71053SJohannes Berg return dev->type == &wwan_dev_type && 13888b71053SJohannes Berg strcmp(dev_name(dev), name) == 0; 13988b71053SJohannes Berg } 14088b71053SJohannes Berg 14188b71053SJohannes Berg static struct wwan_device *wwan_dev_get_by_name(const char *name) 14288b71053SJohannes Berg { 14388b71053SJohannes Berg struct device *dev; 14488b71053SJohannes Berg 14588b71053SJohannes Berg dev = class_find_device(wwan_class, NULL, name, wwan_dev_name_match); 14688b71053SJohannes Berg if (!dev) 14788b71053SJohannes Berg return ERR_PTR(-ENODEV); 14888b71053SJohannes Berg 14988b71053SJohannes Berg return to_wwan_dev(dev); 15088b71053SJohannes Berg } 15188b71053SJohannes Berg 152283e6f5aSSergey Ryazanov #ifdef CONFIG_WWAN_DEBUGFS 153c4804670SM Chetan Kumar struct dentry *wwan_get_debugfs_dir(struct device *parent) 154c4804670SM Chetan Kumar { 155c4804670SM Chetan Kumar struct wwan_device *wwandev; 156c4804670SM Chetan Kumar 157c4804670SM Chetan Kumar wwandev = wwan_dev_get_by_parent(parent); 158c4804670SM Chetan Kumar if (IS_ERR(wwandev)) 159c4804670SM Chetan Kumar return ERR_CAST(wwandev); 160c4804670SM Chetan Kumar 161c4804670SM Chetan Kumar return wwandev->debugfs_dir; 162c4804670SM Chetan Kumar } 163c4804670SM Chetan Kumar EXPORT_SYMBOL_GPL(wwan_get_debugfs_dir); 16476f05d88SM Chetan Kumar 16576f05d88SM Chetan Kumar static int wwan_dev_debugfs_match(struct device *dev, const void *dir) 16676f05d88SM Chetan Kumar { 16776f05d88SM Chetan Kumar struct wwan_device *wwandev; 16876f05d88SM Chetan Kumar 16976f05d88SM Chetan Kumar if (dev->type != &wwan_dev_type) 17076f05d88SM Chetan Kumar return 0; 17176f05d88SM Chetan Kumar 17276f05d88SM Chetan Kumar wwandev = to_wwan_dev(dev); 17376f05d88SM Chetan Kumar 17476f05d88SM Chetan Kumar return wwandev->debugfs_dir == dir; 17576f05d88SM Chetan Kumar } 17676f05d88SM Chetan Kumar 17776f05d88SM Chetan Kumar static struct wwan_device *wwan_dev_get_by_debugfs(struct dentry *dir) 17876f05d88SM Chetan Kumar { 17976f05d88SM Chetan Kumar struct device *dev; 18076f05d88SM Chetan Kumar 18176f05d88SM Chetan Kumar dev = class_find_device(wwan_class, NULL, dir, wwan_dev_debugfs_match); 18276f05d88SM Chetan Kumar if (!dev) 18376f05d88SM Chetan Kumar return ERR_PTR(-ENODEV); 18476f05d88SM Chetan Kumar 18576f05d88SM Chetan Kumar return to_wwan_dev(dev); 18676f05d88SM Chetan Kumar } 18776f05d88SM Chetan Kumar 18876f05d88SM Chetan Kumar void wwan_put_debugfs_dir(struct dentry *dir) 18976f05d88SM Chetan Kumar { 19076f05d88SM Chetan Kumar struct wwan_device *wwandev = wwan_dev_get_by_debugfs(dir); 19176f05d88SM Chetan Kumar 19276f05d88SM Chetan Kumar if (WARN_ON(IS_ERR(wwandev))) 19376f05d88SM Chetan Kumar return; 19476f05d88SM Chetan Kumar 19576f05d88SM Chetan Kumar /* wwan_dev_get_by_debugfs() also got a reference */ 19676f05d88SM Chetan Kumar put_device(&wwandev->dev); 19776f05d88SM Chetan Kumar put_device(&wwandev->dev); 19876f05d88SM Chetan Kumar } 19976f05d88SM Chetan Kumar EXPORT_SYMBOL_GPL(wwan_put_debugfs_dir); 200283e6f5aSSergey Ryazanov #endif 201c4804670SM Chetan Kumar 2029a44c1ccSLoic Poulain /* This function allocates and registers a new WWAN device OR if a WWAN device 2039a44c1ccSLoic Poulain * already exist for the given parent, it gets a reference and return it. 2049a44c1ccSLoic Poulain * This function is not exported (for now), it is called indirectly via 2059a44c1ccSLoic Poulain * wwan_create_port(). 2069a44c1ccSLoic Poulain */ 2079a44c1ccSLoic Poulain static struct wwan_device *wwan_create_dev(struct device *parent) 2089a44c1ccSLoic Poulain { 2099a44c1ccSLoic Poulain struct wwan_device *wwandev; 2109a44c1ccSLoic Poulain int err, id; 2119a44c1ccSLoic Poulain 2129a44c1ccSLoic Poulain /* The 'find-alloc-register' operation must be protected against 2139a44c1ccSLoic Poulain * concurrent execution, a WWAN device is possibly shared between 2149a44c1ccSLoic Poulain * multiple callers or concurrently unregistered from wwan_remove_dev(). 2159a44c1ccSLoic Poulain */ 2169a44c1ccSLoic Poulain mutex_lock(&wwan_register_lock); 2179a44c1ccSLoic Poulain 2189a44c1ccSLoic Poulain /* If wwandev already exists, return it */ 2199a44c1ccSLoic Poulain wwandev = wwan_dev_get_by_parent(parent); 2209a44c1ccSLoic Poulain if (!IS_ERR(wwandev)) 2219a44c1ccSLoic Poulain goto done_unlock; 2229a44c1ccSLoic Poulain 2239a44c1ccSLoic Poulain id = ida_alloc(&wwan_dev_ids, GFP_KERNEL); 224d9d5b896SAndy Shevchenko if (id < 0) { 225d9d5b896SAndy Shevchenko wwandev = ERR_PTR(id); 2269a44c1ccSLoic Poulain goto done_unlock; 227d9d5b896SAndy Shevchenko } 2289a44c1ccSLoic Poulain 2299a44c1ccSLoic Poulain wwandev = kzalloc(sizeof(*wwandev), GFP_KERNEL); 2309a44c1ccSLoic Poulain if (!wwandev) { 231d9d5b896SAndy Shevchenko wwandev = ERR_PTR(-ENOMEM); 2329a44c1ccSLoic Poulain ida_free(&wwan_dev_ids, id); 2339a44c1ccSLoic Poulain goto done_unlock; 2349a44c1ccSLoic Poulain } 2359a44c1ccSLoic Poulain 2369a44c1ccSLoic Poulain wwandev->dev.parent = parent; 2379a44c1ccSLoic Poulain wwandev->dev.class = wwan_class; 2389a44c1ccSLoic Poulain wwandev->dev.type = &wwan_dev_type; 2399a44c1ccSLoic Poulain wwandev->id = id; 2409a44c1ccSLoic Poulain dev_set_name(&wwandev->dev, "wwan%d", wwandev->id); 2419a44c1ccSLoic Poulain 2429a44c1ccSLoic Poulain err = device_register(&wwandev->dev); 2439a44c1ccSLoic Poulain if (err) { 2449a44c1ccSLoic Poulain put_device(&wwandev->dev); 245d9d5b896SAndy Shevchenko wwandev = ERR_PTR(err); 246d9d5b896SAndy Shevchenko goto done_unlock; 2479a44c1ccSLoic Poulain } 2489a44c1ccSLoic Poulain 249283e6f5aSSergey Ryazanov #ifdef CONFIG_WWAN_DEBUGFS 250283e6f5aSSergey Ryazanov wwandev->debugfs_dir = 251283e6f5aSSergey Ryazanov debugfs_create_dir(kobject_name(&wwandev->dev.kobj), 252c4804670SM Chetan Kumar wwan_debugfs_dir); 253283e6f5aSSergey Ryazanov #endif 254c4804670SM Chetan Kumar 2559a44c1ccSLoic Poulain done_unlock: 2569a44c1ccSLoic Poulain mutex_unlock(&wwan_register_lock); 2579a44c1ccSLoic Poulain 2589a44c1ccSLoic Poulain return wwandev; 2599a44c1ccSLoic Poulain } 2609a44c1ccSLoic Poulain 2619a44c1ccSLoic Poulain static int is_wwan_child(struct device *dev, void *data) 2629a44c1ccSLoic Poulain { 2639a44c1ccSLoic Poulain return dev->class == wwan_class; 2649a44c1ccSLoic Poulain } 2659a44c1ccSLoic Poulain 2669a44c1ccSLoic Poulain static void wwan_remove_dev(struct wwan_device *wwandev) 2679a44c1ccSLoic Poulain { 2689a44c1ccSLoic Poulain int ret; 2699a44c1ccSLoic Poulain 2709a44c1ccSLoic Poulain /* Prevent concurrent picking from wwan_create_dev */ 2719a44c1ccSLoic Poulain mutex_lock(&wwan_register_lock); 2729a44c1ccSLoic Poulain 2739a44c1ccSLoic Poulain /* WWAN device is created and registered (get+add) along with its first 2749a44c1ccSLoic Poulain * child port, and subsequent port registrations only grab a reference 2759a44c1ccSLoic Poulain * (get). The WWAN device must then be unregistered (del+put) along with 27688b71053SJohannes Berg * its last port, and reference simply dropped (put) otherwise. In the 27788b71053SJohannes Berg * same fashion, we must not unregister it when the ops are still there. 2789a44c1ccSLoic Poulain */ 27988b71053SJohannes Berg if (wwandev->ops) 28088b71053SJohannes Berg ret = 1; 28188b71053SJohannes Berg else 2829a44c1ccSLoic Poulain ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child); 28388b71053SJohannes Berg 284c4804670SM Chetan Kumar if (!ret) { 285283e6f5aSSergey Ryazanov #ifdef CONFIG_WWAN_DEBUGFS 286c4804670SM Chetan Kumar debugfs_remove_recursive(wwandev->debugfs_dir); 287283e6f5aSSergey Ryazanov #endif 2889a44c1ccSLoic Poulain device_unregister(&wwandev->dev); 289c4804670SM Chetan Kumar } else { 2909a44c1ccSLoic Poulain put_device(&wwandev->dev); 291c4804670SM Chetan Kumar } 2929a44c1ccSLoic Poulain 2939a44c1ccSLoic Poulain mutex_unlock(&wwan_register_lock); 2949a44c1ccSLoic Poulain } 2959a44c1ccSLoic Poulain 2969a44c1ccSLoic Poulain /* ------- WWAN port management ------- */ 2979a44c1ccSLoic Poulain 298392c26f7SSergey Ryazanov static const struct { 299392c26f7SSergey Ryazanov const char * const name; /* Port type name */ 300392c26f7SSergey Ryazanov const char * const devsuf; /* Port devce name suffix */ 301392c26f7SSergey Ryazanov } wwan_port_types[WWAN_PORT_MAX + 1] = { 302392c26f7SSergey Ryazanov [WWAN_PORT_AT] = { 303392c26f7SSergey Ryazanov .name = "AT", 304392c26f7SSergey Ryazanov .devsuf = "at", 305392c26f7SSergey Ryazanov }, 306392c26f7SSergey Ryazanov [WWAN_PORT_MBIM] = { 307392c26f7SSergey Ryazanov .name = "MBIM", 308392c26f7SSergey Ryazanov .devsuf = "mbim", 309392c26f7SSergey Ryazanov }, 310392c26f7SSergey Ryazanov [WWAN_PORT_QMI] = { 311392c26f7SSergey Ryazanov .name = "QMI", 312392c26f7SSergey Ryazanov .devsuf = "qmi", 313392c26f7SSergey Ryazanov }, 314392c26f7SSergey Ryazanov [WWAN_PORT_QCDM] = { 315392c26f7SSergey Ryazanov .name = "QCDM", 316392c26f7SSergey Ryazanov .devsuf = "qcdm", 317392c26f7SSergey Ryazanov }, 318392c26f7SSergey Ryazanov [WWAN_PORT_FIREHOSE] = { 319392c26f7SSergey Ryazanov .name = "FIREHOSE", 320392c26f7SSergey Ryazanov .devsuf = "firehose", 321392c26f7SSergey Ryazanov }, 322b3e22e10SLoic Poulain }; 323b3e22e10SLoic Poulain 324b3e22e10SLoic Poulain static ssize_t type_show(struct device *dev, struct device_attribute *attr, 325b3e22e10SLoic Poulain char *buf) 326b3e22e10SLoic Poulain { 327b3e22e10SLoic Poulain struct wwan_port *port = to_wwan_port(dev); 328b3e22e10SLoic Poulain 329392c26f7SSergey Ryazanov return sprintf(buf, "%s\n", wwan_port_types[port->type].name); 330b3e22e10SLoic Poulain } 331b3e22e10SLoic Poulain static DEVICE_ATTR_RO(type); 332b3e22e10SLoic Poulain 333b3e22e10SLoic Poulain static struct attribute *wwan_port_attrs[] = { 334b3e22e10SLoic Poulain &dev_attr_type.attr, 335b3e22e10SLoic Poulain NULL, 336b3e22e10SLoic Poulain }; 337b3e22e10SLoic Poulain ATTRIBUTE_GROUPS(wwan_port); 338b3e22e10SLoic Poulain 3399a44c1ccSLoic Poulain static void wwan_port_destroy(struct device *dev) 3409a44c1ccSLoic Poulain { 3419a44c1ccSLoic Poulain struct wwan_port *port = to_wwan_port(dev); 3429a44c1ccSLoic Poulain 3439a44c1ccSLoic Poulain ida_free(&minors, MINOR(port->dev.devt)); 344c230035cSSergey Ryazanov mutex_destroy(&port->data_lock); 3459a44c1ccSLoic Poulain mutex_destroy(&port->ops_lock); 3469a44c1ccSLoic Poulain kfree(port); 3479a44c1ccSLoic Poulain } 3489a44c1ccSLoic Poulain 3499a44c1ccSLoic Poulain static const struct device_type wwan_port_dev_type = { 3509a44c1ccSLoic Poulain .name = "wwan_port", 3519a44c1ccSLoic Poulain .release = wwan_port_destroy, 352b3e22e10SLoic Poulain .groups = wwan_port_groups, 3539a44c1ccSLoic Poulain }; 3549a44c1ccSLoic Poulain 3559a44c1ccSLoic Poulain static int wwan_port_minor_match(struct device *dev, const void *minor) 3569a44c1ccSLoic Poulain { 3579a44c1ccSLoic Poulain return (dev->type == &wwan_port_dev_type && 3589a44c1ccSLoic Poulain MINOR(dev->devt) == *(unsigned int *)minor); 3599a44c1ccSLoic Poulain } 3609a44c1ccSLoic Poulain 3619a44c1ccSLoic Poulain static struct wwan_port *wwan_port_get_by_minor(unsigned int minor) 3629a44c1ccSLoic Poulain { 3639a44c1ccSLoic Poulain struct device *dev; 3649a44c1ccSLoic Poulain 3659a44c1ccSLoic Poulain dev = class_find_device(wwan_class, NULL, &minor, wwan_port_minor_match); 3669a44c1ccSLoic Poulain if (!dev) 3679a44c1ccSLoic Poulain return ERR_PTR(-ENODEV); 3689a44c1ccSLoic Poulain 3699a44c1ccSLoic Poulain return to_wwan_port(dev); 3709a44c1ccSLoic Poulain } 3719a44c1ccSLoic Poulain 372f458709fSSergey Ryazanov /* Allocate and set unique name based on passed format 373f458709fSSergey Ryazanov * 374f458709fSSergey Ryazanov * Name allocation approach is highly inspired by the __dev_alloc_name() 375f458709fSSergey Ryazanov * function. 376f458709fSSergey Ryazanov * 377f458709fSSergey Ryazanov * To avoid names collision, the caller must prevent the new port device 378f458709fSSergey Ryazanov * registration as well as concurrent invocation of this function. 379f458709fSSergey Ryazanov */ 380f458709fSSergey Ryazanov static int __wwan_port_dev_assign_name(struct wwan_port *port, const char *fmt) 381f458709fSSergey Ryazanov { 382f458709fSSergey Ryazanov struct wwan_device *wwandev = to_wwan_dev(port->dev.parent); 383f458709fSSergey Ryazanov const unsigned int max_ports = PAGE_SIZE * 8; 384f458709fSSergey Ryazanov struct class_dev_iter iter; 385f458709fSSergey Ryazanov unsigned long *idmap; 386f458709fSSergey Ryazanov struct device *dev; 387f458709fSSergey Ryazanov char buf[0x20]; 388f458709fSSergey Ryazanov int id; 389f458709fSSergey Ryazanov 390f458709fSSergey Ryazanov idmap = (unsigned long *)get_zeroed_page(GFP_KERNEL); 391f458709fSSergey Ryazanov if (!idmap) 392f458709fSSergey Ryazanov return -ENOMEM; 393f458709fSSergey Ryazanov 394f458709fSSergey Ryazanov /* Collect ids of same name format ports */ 395f458709fSSergey Ryazanov class_dev_iter_init(&iter, wwan_class, NULL, &wwan_port_dev_type); 396f458709fSSergey Ryazanov while ((dev = class_dev_iter_next(&iter))) { 397f458709fSSergey Ryazanov if (dev->parent != &wwandev->dev) 398f458709fSSergey Ryazanov continue; 399f458709fSSergey Ryazanov if (sscanf(dev_name(dev), fmt, &id) != 1) 400f458709fSSergey Ryazanov continue; 401f458709fSSergey Ryazanov if (id < 0 || id >= max_ports) 402f458709fSSergey Ryazanov continue; 403f458709fSSergey Ryazanov set_bit(id, idmap); 404f458709fSSergey Ryazanov } 405f458709fSSergey Ryazanov class_dev_iter_exit(&iter); 406f458709fSSergey Ryazanov 407f458709fSSergey Ryazanov /* Allocate unique id */ 408f458709fSSergey Ryazanov id = find_first_zero_bit(idmap, max_ports); 409f458709fSSergey Ryazanov free_page((unsigned long)idmap); 410f458709fSSergey Ryazanov 411f458709fSSergey Ryazanov snprintf(buf, sizeof(buf), fmt, id); /* Name generation */ 412f458709fSSergey Ryazanov 413f458709fSSergey Ryazanov dev = device_find_child_by_name(&wwandev->dev, buf); 414f458709fSSergey Ryazanov if (dev) { 415f458709fSSergey Ryazanov put_device(dev); 416f458709fSSergey Ryazanov return -ENFILE; 417f458709fSSergey Ryazanov } 418f458709fSSergey Ryazanov 419f458709fSSergey Ryazanov return dev_set_name(&port->dev, buf); 420f458709fSSergey Ryazanov } 421f458709fSSergey Ryazanov 4229a44c1ccSLoic Poulain struct wwan_port *wwan_create_port(struct device *parent, 4239a44c1ccSLoic Poulain enum wwan_port_type type, 4249a44c1ccSLoic Poulain const struct wwan_port_ops *ops, 4259a44c1ccSLoic Poulain void *drvdata) 4269a44c1ccSLoic Poulain { 4279a44c1ccSLoic Poulain struct wwan_device *wwandev; 4289a44c1ccSLoic Poulain struct wwan_port *port; 429f458709fSSergey Ryazanov char namefmt[0x20]; 4300de6fd5fSAndy Shevchenko int minor, err; 4319a44c1ccSLoic Poulain 432b64d76b7SSergey Ryazanov if (type > WWAN_PORT_MAX || !ops) 4339a44c1ccSLoic Poulain return ERR_PTR(-EINVAL); 4349a44c1ccSLoic Poulain 4359a44c1ccSLoic Poulain /* A port is always a child of a WWAN device, retrieve (allocate or 4369a44c1ccSLoic Poulain * pick) the WWAN device based on the provided parent device. 4379a44c1ccSLoic Poulain */ 4389a44c1ccSLoic Poulain wwandev = wwan_create_dev(parent); 4399a44c1ccSLoic Poulain if (IS_ERR(wwandev)) 4409a44c1ccSLoic Poulain return ERR_CAST(wwandev); 4419a44c1ccSLoic Poulain 4429a44c1ccSLoic Poulain /* A port is exposed as character device, get a minor */ 4439a44c1ccSLoic Poulain minor = ida_alloc_range(&minors, 0, WWAN_MAX_MINORS - 1, GFP_KERNEL); 4440de6fd5fSAndy Shevchenko if (minor < 0) { 4450de6fd5fSAndy Shevchenko err = minor; 4469a44c1ccSLoic Poulain goto error_wwandev_remove; 4470de6fd5fSAndy Shevchenko } 4489a44c1ccSLoic Poulain 4499a44c1ccSLoic Poulain port = kzalloc(sizeof(*port), GFP_KERNEL); 4509a44c1ccSLoic Poulain if (!port) { 4510de6fd5fSAndy Shevchenko err = -ENOMEM; 4529a44c1ccSLoic Poulain ida_free(&minors, minor); 4539a44c1ccSLoic Poulain goto error_wwandev_remove; 4549a44c1ccSLoic Poulain } 4559a44c1ccSLoic Poulain 4569a44c1ccSLoic Poulain port->type = type; 4579a44c1ccSLoic Poulain port->ops = ops; 4589a44c1ccSLoic Poulain mutex_init(&port->ops_lock); 4599a44c1ccSLoic Poulain skb_queue_head_init(&port->rxq); 4609a44c1ccSLoic Poulain init_waitqueue_head(&port->waitqueue); 461c230035cSSergey Ryazanov mutex_init(&port->data_lock); 4629a44c1ccSLoic Poulain 4639a44c1ccSLoic Poulain port->dev.parent = &wwandev->dev; 4649a44c1ccSLoic Poulain port->dev.class = wwan_class; 4659a44c1ccSLoic Poulain port->dev.type = &wwan_port_dev_type; 4669a44c1ccSLoic Poulain port->dev.devt = MKDEV(wwan_major, minor); 4679a44c1ccSLoic Poulain dev_set_drvdata(&port->dev, drvdata); 4689a44c1ccSLoic Poulain 469f458709fSSergey Ryazanov /* allocate unique name based on wwan device id, port type and number */ 470f458709fSSergey Ryazanov snprintf(namefmt, sizeof(namefmt), "wwan%u%s%%d", wwandev->id, 471392c26f7SSergey Ryazanov wwan_port_types[port->type].devsuf); 4729a44c1ccSLoic Poulain 473f458709fSSergey Ryazanov /* Serialize ports registration */ 474f458709fSSergey Ryazanov mutex_lock(&wwan_register_lock); 475f458709fSSergey Ryazanov 476f458709fSSergey Ryazanov __wwan_port_dev_assign_name(port, namefmt); 4779a44c1ccSLoic Poulain err = device_register(&port->dev); 478f458709fSSergey Ryazanov 479f458709fSSergey Ryazanov mutex_unlock(&wwan_register_lock); 480f458709fSSergey Ryazanov 4819a44c1ccSLoic Poulain if (err) 4829a44c1ccSLoic Poulain goto error_put_device; 4839a44c1ccSLoic Poulain 4849a44c1ccSLoic Poulain return port; 4859a44c1ccSLoic Poulain 4869a44c1ccSLoic Poulain error_put_device: 4879a44c1ccSLoic Poulain put_device(&port->dev); 4889a44c1ccSLoic Poulain error_wwandev_remove: 4899a44c1ccSLoic Poulain wwan_remove_dev(wwandev); 4909a44c1ccSLoic Poulain 4919a44c1ccSLoic Poulain return ERR_PTR(err); 4929a44c1ccSLoic Poulain } 4939a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_create_port); 4949a44c1ccSLoic Poulain 4959a44c1ccSLoic Poulain void wwan_remove_port(struct wwan_port *port) 4969a44c1ccSLoic Poulain { 4979a44c1ccSLoic Poulain struct wwan_device *wwandev = to_wwan_dev(port->dev.parent); 4989a44c1ccSLoic Poulain 4999a44c1ccSLoic Poulain mutex_lock(&port->ops_lock); 5009a44c1ccSLoic Poulain if (port->start_count) 5019a44c1ccSLoic Poulain port->ops->stop(port); 5029a44c1ccSLoic Poulain port->ops = NULL; /* Prevent any new port operations (e.g. from fops) */ 5039a44c1ccSLoic Poulain mutex_unlock(&port->ops_lock); 5049a44c1ccSLoic Poulain 5059a44c1ccSLoic Poulain wake_up_interruptible(&port->waitqueue); 5069a44c1ccSLoic Poulain 5079a44c1ccSLoic Poulain skb_queue_purge(&port->rxq); 5089a44c1ccSLoic Poulain dev_set_drvdata(&port->dev, NULL); 5099a44c1ccSLoic Poulain device_unregister(&port->dev); 5109a44c1ccSLoic Poulain 5119a44c1ccSLoic Poulain /* Release related wwan device */ 5129a44c1ccSLoic Poulain wwan_remove_dev(wwandev); 5139a44c1ccSLoic Poulain } 5149a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_remove_port); 5159a44c1ccSLoic Poulain 5169a44c1ccSLoic Poulain void wwan_port_rx(struct wwan_port *port, struct sk_buff *skb) 5179a44c1ccSLoic Poulain { 5189a44c1ccSLoic Poulain skb_queue_tail(&port->rxq, skb); 5199a44c1ccSLoic Poulain wake_up_interruptible(&port->waitqueue); 5209a44c1ccSLoic Poulain } 5219a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_rx); 5229a44c1ccSLoic Poulain 5239a44c1ccSLoic Poulain void wwan_port_txon(struct wwan_port *port) 5249a44c1ccSLoic Poulain { 5259a44c1ccSLoic Poulain clear_bit(WWAN_PORT_TX_OFF, &port->flags); 5269a44c1ccSLoic Poulain wake_up_interruptible(&port->waitqueue); 5279a44c1ccSLoic Poulain } 5289a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_txon); 5299a44c1ccSLoic Poulain 5309a44c1ccSLoic Poulain void wwan_port_txoff(struct wwan_port *port) 5319a44c1ccSLoic Poulain { 5329a44c1ccSLoic Poulain set_bit(WWAN_PORT_TX_OFF, &port->flags); 5339a44c1ccSLoic Poulain } 5349a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_txoff); 5359a44c1ccSLoic Poulain 5369a44c1ccSLoic Poulain void *wwan_port_get_drvdata(struct wwan_port *port) 5379a44c1ccSLoic Poulain { 5389a44c1ccSLoic Poulain return dev_get_drvdata(&port->dev); 5399a44c1ccSLoic Poulain } 5409a44c1ccSLoic Poulain EXPORT_SYMBOL_GPL(wwan_port_get_drvdata); 5419a44c1ccSLoic Poulain 5429a44c1ccSLoic Poulain static int wwan_port_op_start(struct wwan_port *port) 5439a44c1ccSLoic Poulain { 5449a44c1ccSLoic Poulain int ret = 0; 5459a44c1ccSLoic Poulain 5469a44c1ccSLoic Poulain mutex_lock(&port->ops_lock); 5479a44c1ccSLoic Poulain if (!port->ops) { /* Port got unplugged */ 5489a44c1ccSLoic Poulain ret = -ENODEV; 5499a44c1ccSLoic Poulain goto out_unlock; 5509a44c1ccSLoic Poulain } 5519a44c1ccSLoic Poulain 5529a44c1ccSLoic Poulain /* If port is already started, don't start again */ 5539a44c1ccSLoic Poulain if (!port->start_count) 5549a44c1ccSLoic Poulain ret = port->ops->start(port); 5559a44c1ccSLoic Poulain 5569a44c1ccSLoic Poulain if (!ret) 5579a44c1ccSLoic Poulain port->start_count++; 5589a44c1ccSLoic Poulain 5599a44c1ccSLoic Poulain out_unlock: 5609a44c1ccSLoic Poulain mutex_unlock(&port->ops_lock); 5619a44c1ccSLoic Poulain 5629a44c1ccSLoic Poulain return ret; 5639a44c1ccSLoic Poulain } 5649a44c1ccSLoic Poulain 5659a44c1ccSLoic Poulain static void wwan_port_op_stop(struct wwan_port *port) 5669a44c1ccSLoic Poulain { 5679a44c1ccSLoic Poulain mutex_lock(&port->ops_lock); 5689a44c1ccSLoic Poulain port->start_count--; 56950467203SSergey Ryazanov if (!port->start_count) { 57050467203SSergey Ryazanov if (port->ops) 5719a44c1ccSLoic Poulain port->ops->stop(port); 57250467203SSergey Ryazanov skb_queue_purge(&port->rxq); 57350467203SSergey Ryazanov } 5749a44c1ccSLoic Poulain mutex_unlock(&port->ops_lock); 5759a44c1ccSLoic Poulain } 5769a44c1ccSLoic Poulain 57731c143f7SStephan Gerhold static int wwan_port_op_tx(struct wwan_port *port, struct sk_buff *skb, 57831c143f7SStephan Gerhold bool nonblock) 5799a44c1ccSLoic Poulain { 5809a44c1ccSLoic Poulain int ret; 5819a44c1ccSLoic Poulain 5829a44c1ccSLoic Poulain mutex_lock(&port->ops_lock); 5839a44c1ccSLoic Poulain if (!port->ops) { /* Port got unplugged */ 5849a44c1ccSLoic Poulain ret = -ENODEV; 5859a44c1ccSLoic Poulain goto out_unlock; 5869a44c1ccSLoic Poulain } 5879a44c1ccSLoic Poulain 58831c143f7SStephan Gerhold if (nonblock || !port->ops->tx_blocking) 5899a44c1ccSLoic Poulain ret = port->ops->tx(port, skb); 59031c143f7SStephan Gerhold else 59131c143f7SStephan Gerhold ret = port->ops->tx_blocking(port, skb); 5929a44c1ccSLoic Poulain 5939a44c1ccSLoic Poulain out_unlock: 5949a44c1ccSLoic Poulain mutex_unlock(&port->ops_lock); 5959a44c1ccSLoic Poulain 5969a44c1ccSLoic Poulain return ret; 5979a44c1ccSLoic Poulain } 5989a44c1ccSLoic Poulain 5999a44c1ccSLoic Poulain static bool is_read_blocked(struct wwan_port *port) 6009a44c1ccSLoic Poulain { 6019a44c1ccSLoic Poulain return skb_queue_empty(&port->rxq) && port->ops; 6029a44c1ccSLoic Poulain } 6039a44c1ccSLoic Poulain 6049a44c1ccSLoic Poulain static bool is_write_blocked(struct wwan_port *port) 6059a44c1ccSLoic Poulain { 6069a44c1ccSLoic Poulain return test_bit(WWAN_PORT_TX_OFF, &port->flags) && port->ops; 6079a44c1ccSLoic Poulain } 6089a44c1ccSLoic Poulain 6099a44c1ccSLoic Poulain static int wwan_wait_rx(struct wwan_port *port, bool nonblock) 6109a44c1ccSLoic Poulain { 6119a44c1ccSLoic Poulain if (!is_read_blocked(port)) 6129a44c1ccSLoic Poulain return 0; 6139a44c1ccSLoic Poulain 6149a44c1ccSLoic Poulain if (nonblock) 6159a44c1ccSLoic Poulain return -EAGAIN; 6169a44c1ccSLoic Poulain 6179a44c1ccSLoic Poulain if (wait_event_interruptible(port->waitqueue, !is_read_blocked(port))) 6189a44c1ccSLoic Poulain return -ERESTARTSYS; 6199a44c1ccSLoic Poulain 6209a44c1ccSLoic Poulain return 0; 6219a44c1ccSLoic Poulain } 6229a44c1ccSLoic Poulain 6239a44c1ccSLoic Poulain static int wwan_wait_tx(struct wwan_port *port, bool nonblock) 6249a44c1ccSLoic Poulain { 6259a44c1ccSLoic Poulain if (!is_write_blocked(port)) 6269a44c1ccSLoic Poulain return 0; 6279a44c1ccSLoic Poulain 6289a44c1ccSLoic Poulain if (nonblock) 6299a44c1ccSLoic Poulain return -EAGAIN; 6309a44c1ccSLoic Poulain 6319a44c1ccSLoic Poulain if (wait_event_interruptible(port->waitqueue, !is_write_blocked(port))) 6329a44c1ccSLoic Poulain return -ERESTARTSYS; 6339a44c1ccSLoic Poulain 6349a44c1ccSLoic Poulain return 0; 6359a44c1ccSLoic Poulain } 6369a44c1ccSLoic Poulain 6379a44c1ccSLoic Poulain static int wwan_port_fops_open(struct inode *inode, struct file *file) 6389a44c1ccSLoic Poulain { 6399a44c1ccSLoic Poulain struct wwan_port *port; 6409a44c1ccSLoic Poulain int err = 0; 6419a44c1ccSLoic Poulain 6429a44c1ccSLoic Poulain port = wwan_port_get_by_minor(iminor(inode)); 6439a44c1ccSLoic Poulain if (IS_ERR(port)) 6449a44c1ccSLoic Poulain return PTR_ERR(port); 6459a44c1ccSLoic Poulain 6469a44c1ccSLoic Poulain file->private_data = port; 6479a44c1ccSLoic Poulain stream_open(inode, file); 6489a44c1ccSLoic Poulain 6499a44c1ccSLoic Poulain err = wwan_port_op_start(port); 6509a44c1ccSLoic Poulain if (err) 6519a44c1ccSLoic Poulain put_device(&port->dev); 6529a44c1ccSLoic Poulain 6539a44c1ccSLoic Poulain return err; 6549a44c1ccSLoic Poulain } 6559a44c1ccSLoic Poulain 6569a44c1ccSLoic Poulain static int wwan_port_fops_release(struct inode *inode, struct file *filp) 6579a44c1ccSLoic Poulain { 6589a44c1ccSLoic Poulain struct wwan_port *port = filp->private_data; 6599a44c1ccSLoic Poulain 6609a44c1ccSLoic Poulain wwan_port_op_stop(port); 6619a44c1ccSLoic Poulain put_device(&port->dev); 6629a44c1ccSLoic Poulain 6639a44c1ccSLoic Poulain return 0; 6649a44c1ccSLoic Poulain } 6659a44c1ccSLoic Poulain 6669a44c1ccSLoic Poulain static ssize_t wwan_port_fops_read(struct file *filp, char __user *buf, 6679a44c1ccSLoic Poulain size_t count, loff_t *ppos) 6689a44c1ccSLoic Poulain { 6699a44c1ccSLoic Poulain struct wwan_port *port = filp->private_data; 6709a44c1ccSLoic Poulain struct sk_buff *skb; 6719a44c1ccSLoic Poulain size_t copied; 6729a44c1ccSLoic Poulain int ret; 6739a44c1ccSLoic Poulain 6749a44c1ccSLoic Poulain ret = wwan_wait_rx(port, !!(filp->f_flags & O_NONBLOCK)); 6759a44c1ccSLoic Poulain if (ret) 6769a44c1ccSLoic Poulain return ret; 6779a44c1ccSLoic Poulain 6789a44c1ccSLoic Poulain skb = skb_dequeue(&port->rxq); 6799a44c1ccSLoic Poulain if (!skb) 6809a44c1ccSLoic Poulain return -EIO; 6819a44c1ccSLoic Poulain 6829a44c1ccSLoic Poulain copied = min_t(size_t, count, skb->len); 6839a44c1ccSLoic Poulain if (copy_to_user(buf, skb->data, copied)) { 6849a44c1ccSLoic Poulain kfree_skb(skb); 6859a44c1ccSLoic Poulain return -EFAULT; 6869a44c1ccSLoic Poulain } 6879a44c1ccSLoic Poulain skb_pull(skb, copied); 6889a44c1ccSLoic Poulain 6899a44c1ccSLoic Poulain /* skb is not fully consumed, keep it in the queue */ 6909a44c1ccSLoic Poulain if (skb->len) 6919a44c1ccSLoic Poulain skb_queue_head(&port->rxq, skb); 6929a44c1ccSLoic Poulain else 6939a44c1ccSLoic Poulain consume_skb(skb); 6949a44c1ccSLoic Poulain 6959a44c1ccSLoic Poulain return copied; 6969a44c1ccSLoic Poulain } 6979a44c1ccSLoic Poulain 6989a44c1ccSLoic Poulain static ssize_t wwan_port_fops_write(struct file *filp, const char __user *buf, 6999a44c1ccSLoic Poulain size_t count, loff_t *offp) 7009a44c1ccSLoic Poulain { 7019a44c1ccSLoic Poulain struct wwan_port *port = filp->private_data; 7029a44c1ccSLoic Poulain struct sk_buff *skb; 7039a44c1ccSLoic Poulain int ret; 7049a44c1ccSLoic Poulain 7059a44c1ccSLoic Poulain ret = wwan_wait_tx(port, !!(filp->f_flags & O_NONBLOCK)); 7069a44c1ccSLoic Poulain if (ret) 7079a44c1ccSLoic Poulain return ret; 7089a44c1ccSLoic Poulain 7099a44c1ccSLoic Poulain skb = alloc_skb(count, GFP_KERNEL); 7109a44c1ccSLoic Poulain if (!skb) 7119a44c1ccSLoic Poulain return -ENOMEM; 7129a44c1ccSLoic Poulain 7139a44c1ccSLoic Poulain if (copy_from_user(skb_put(skb, count), buf, count)) { 7149a44c1ccSLoic Poulain kfree_skb(skb); 7159a44c1ccSLoic Poulain return -EFAULT; 7169a44c1ccSLoic Poulain } 7179a44c1ccSLoic Poulain 71831c143f7SStephan Gerhold ret = wwan_port_op_tx(port, skb, !!(filp->f_flags & O_NONBLOCK)); 7199a44c1ccSLoic Poulain if (ret) { 7209a44c1ccSLoic Poulain kfree_skb(skb); 7219a44c1ccSLoic Poulain return ret; 7229a44c1ccSLoic Poulain } 7239a44c1ccSLoic Poulain 7249a44c1ccSLoic Poulain return count; 7259a44c1ccSLoic Poulain } 7269a44c1ccSLoic Poulain 7279a44c1ccSLoic Poulain static __poll_t wwan_port_fops_poll(struct file *filp, poll_table *wait) 7289a44c1ccSLoic Poulain { 7299a44c1ccSLoic Poulain struct wwan_port *port = filp->private_data; 7309a44c1ccSLoic Poulain __poll_t mask = 0; 7319a44c1ccSLoic Poulain 7329a44c1ccSLoic Poulain poll_wait(filp, &port->waitqueue, wait); 7339a44c1ccSLoic Poulain 73431c143f7SStephan Gerhold mutex_lock(&port->ops_lock); 73531c143f7SStephan Gerhold if (port->ops && port->ops->tx_poll) 73631c143f7SStephan Gerhold mask |= port->ops->tx_poll(port, filp, wait); 73731c143f7SStephan Gerhold else if (!is_write_blocked(port)) 7389a44c1ccSLoic Poulain mask |= EPOLLOUT | EPOLLWRNORM; 7399a44c1ccSLoic Poulain if (!is_read_blocked(port)) 7409a44c1ccSLoic Poulain mask |= EPOLLIN | EPOLLRDNORM; 74157e22247SLoic Poulain if (!port->ops) 74257e22247SLoic Poulain mask |= EPOLLHUP | EPOLLERR; 74331c143f7SStephan Gerhold mutex_unlock(&port->ops_lock); 7449a44c1ccSLoic Poulain 7459a44c1ccSLoic Poulain return mask; 7469a44c1ccSLoic Poulain } 7479a44c1ccSLoic Poulain 748c230035cSSergey Ryazanov /* Implements minimalistic stub terminal IOCTLs support */ 749c230035cSSergey Ryazanov static long wwan_port_fops_at_ioctl(struct wwan_port *port, unsigned int cmd, 750c230035cSSergey Ryazanov unsigned long arg) 751c230035cSSergey Ryazanov { 752c230035cSSergey Ryazanov int ret = 0; 753c230035cSSergey Ryazanov 754c230035cSSergey Ryazanov mutex_lock(&port->data_lock); 755c230035cSSergey Ryazanov 756c230035cSSergey Ryazanov switch (cmd) { 757c230035cSSergey Ryazanov case TCFLSH: 758c230035cSSergey Ryazanov break; 759c230035cSSergey Ryazanov 760c230035cSSergey Ryazanov case TCGETS: 761c230035cSSergey Ryazanov if (copy_to_user((void __user *)arg, &port->at_data.termios, 762c230035cSSergey Ryazanov sizeof(struct termios))) 763c230035cSSergey Ryazanov ret = -EFAULT; 764c230035cSSergey Ryazanov break; 765c230035cSSergey Ryazanov 766c230035cSSergey Ryazanov case TCSETS: 767c230035cSSergey Ryazanov case TCSETSW: 768c230035cSSergey Ryazanov case TCSETSF: 769c230035cSSergey Ryazanov if (copy_from_user(&port->at_data.termios, (void __user *)arg, 770c230035cSSergey Ryazanov sizeof(struct termios))) 771c230035cSSergey Ryazanov ret = -EFAULT; 772c230035cSSergey Ryazanov break; 773c230035cSSergey Ryazanov 774c230035cSSergey Ryazanov #ifdef TCGETS2 775c230035cSSergey Ryazanov case TCGETS2: 776c230035cSSergey Ryazanov if (copy_to_user((void __user *)arg, &port->at_data.termios, 777c230035cSSergey Ryazanov sizeof(struct termios2))) 778c230035cSSergey Ryazanov ret = -EFAULT; 779c230035cSSergey Ryazanov break; 780c230035cSSergey Ryazanov 781c230035cSSergey Ryazanov case TCSETS2: 782c230035cSSergey Ryazanov case TCSETSW2: 783c230035cSSergey Ryazanov case TCSETSF2: 784c230035cSSergey Ryazanov if (copy_from_user(&port->at_data.termios, (void __user *)arg, 785c230035cSSergey Ryazanov sizeof(struct termios2))) 786c230035cSSergey Ryazanov ret = -EFAULT; 787c230035cSSergey Ryazanov break; 788c230035cSSergey Ryazanov #endif 789c230035cSSergey Ryazanov 790c230035cSSergey Ryazanov case TIOCMGET: 791c230035cSSergey Ryazanov ret = put_user(port->at_data.mdmbits, (int __user *)arg); 792c230035cSSergey Ryazanov break; 793c230035cSSergey Ryazanov 794c230035cSSergey Ryazanov case TIOCMSET: 795c230035cSSergey Ryazanov case TIOCMBIC: 796c230035cSSergey Ryazanov case TIOCMBIS: { 797c230035cSSergey Ryazanov int mdmbits; 798c230035cSSergey Ryazanov 799c230035cSSergey Ryazanov if (copy_from_user(&mdmbits, (int __user *)arg, sizeof(int))) { 800c230035cSSergey Ryazanov ret = -EFAULT; 801c230035cSSergey Ryazanov break; 802c230035cSSergey Ryazanov } 803c230035cSSergey Ryazanov if (cmd == TIOCMBIC) 804c230035cSSergey Ryazanov port->at_data.mdmbits &= ~mdmbits; 805c230035cSSergey Ryazanov else if (cmd == TIOCMBIS) 806c230035cSSergey Ryazanov port->at_data.mdmbits |= mdmbits; 807c230035cSSergey Ryazanov else 808c230035cSSergey Ryazanov port->at_data.mdmbits = mdmbits; 809c230035cSSergey Ryazanov break; 810c230035cSSergey Ryazanov } 811c230035cSSergey Ryazanov 812c230035cSSergey Ryazanov default: 813c230035cSSergey Ryazanov ret = -ENOIOCTLCMD; 814c230035cSSergey Ryazanov } 815c230035cSSergey Ryazanov 816c230035cSSergey Ryazanov mutex_unlock(&port->data_lock); 817c230035cSSergey Ryazanov 818c230035cSSergey Ryazanov return ret; 819c230035cSSergey Ryazanov } 820c230035cSSergey Ryazanov 821e263c5b2SSergey Ryazanov static long wwan_port_fops_ioctl(struct file *filp, unsigned int cmd, 822e263c5b2SSergey Ryazanov unsigned long arg) 823e263c5b2SSergey Ryazanov { 824e263c5b2SSergey Ryazanov struct wwan_port *port = filp->private_data; 825c230035cSSergey Ryazanov int res; 826c230035cSSergey Ryazanov 827c230035cSSergey Ryazanov if (port->type == WWAN_PORT_AT) { /* AT port specific IOCTLs */ 828c230035cSSergey Ryazanov res = wwan_port_fops_at_ioctl(port, cmd, arg); 829c230035cSSergey Ryazanov if (res != -ENOIOCTLCMD) 830c230035cSSergey Ryazanov return res; 831c230035cSSergey Ryazanov } 832e263c5b2SSergey Ryazanov 833e263c5b2SSergey Ryazanov switch (cmd) { 834e263c5b2SSergey Ryazanov case TIOCINQ: { /* aka SIOCINQ aka FIONREAD */ 835e263c5b2SSergey Ryazanov unsigned long flags; 836e263c5b2SSergey Ryazanov struct sk_buff *skb; 837e263c5b2SSergey Ryazanov int amount = 0; 838e263c5b2SSergey Ryazanov 839e263c5b2SSergey Ryazanov spin_lock_irqsave(&port->rxq.lock, flags); 840e263c5b2SSergey Ryazanov skb_queue_walk(&port->rxq, skb) 841e263c5b2SSergey Ryazanov amount += skb->len; 842e263c5b2SSergey Ryazanov spin_unlock_irqrestore(&port->rxq.lock, flags); 843e263c5b2SSergey Ryazanov 844e263c5b2SSergey Ryazanov return put_user(amount, (int __user *)arg); 845e263c5b2SSergey Ryazanov } 846e263c5b2SSergey Ryazanov 847e263c5b2SSergey Ryazanov default: 848e263c5b2SSergey Ryazanov return -ENOIOCTLCMD; 849e263c5b2SSergey Ryazanov } 850e263c5b2SSergey Ryazanov } 851e263c5b2SSergey Ryazanov 8529a44c1ccSLoic Poulain static const struct file_operations wwan_port_fops = { 8539a44c1ccSLoic Poulain .owner = THIS_MODULE, 8549a44c1ccSLoic Poulain .open = wwan_port_fops_open, 8559a44c1ccSLoic Poulain .release = wwan_port_fops_release, 8569a44c1ccSLoic Poulain .read = wwan_port_fops_read, 8579a44c1ccSLoic Poulain .write = wwan_port_fops_write, 8589a44c1ccSLoic Poulain .poll = wwan_port_fops_poll, 859e263c5b2SSergey Ryazanov .unlocked_ioctl = wwan_port_fops_ioctl, 860e263c5b2SSergey Ryazanov #ifdef CONFIG_COMPAT 861e263c5b2SSergey Ryazanov .compat_ioctl = compat_ptr_ioctl, 862e263c5b2SSergey Ryazanov #endif 8639a44c1ccSLoic Poulain .llseek = noop_llseek, 8649a44c1ccSLoic Poulain }; 8659a44c1ccSLoic Poulain 86688b71053SJohannes Berg static int wwan_rtnl_validate(struct nlattr *tb[], struct nlattr *data[], 86788b71053SJohannes Berg struct netlink_ext_ack *extack) 86888b71053SJohannes Berg { 86988b71053SJohannes Berg if (!data) 87088b71053SJohannes Berg return -EINVAL; 87188b71053SJohannes Berg 87288b71053SJohannes Berg if (!tb[IFLA_PARENT_DEV_NAME]) 87388b71053SJohannes Berg return -EINVAL; 87488b71053SJohannes Berg 87588b71053SJohannes Berg if (!data[IFLA_WWAN_LINK_ID]) 87688b71053SJohannes Berg return -EINVAL; 87788b71053SJohannes Berg 87888b71053SJohannes Berg return 0; 87988b71053SJohannes Berg } 88088b71053SJohannes Berg 88188b71053SJohannes Berg static struct device_type wwan_type = { .name = "wwan" }; 88288b71053SJohannes Berg 88388b71053SJohannes Berg static struct net_device *wwan_rtnl_alloc(struct nlattr *tb[], 88488b71053SJohannes Berg const char *ifname, 88588b71053SJohannes Berg unsigned char name_assign_type, 88688b71053SJohannes Berg unsigned int num_tx_queues, 88788b71053SJohannes Berg unsigned int num_rx_queues) 88888b71053SJohannes Berg { 88988b71053SJohannes Berg const char *devname = nla_data(tb[IFLA_PARENT_DEV_NAME]); 89088b71053SJohannes Berg struct wwan_device *wwandev = wwan_dev_get_by_name(devname); 89188b71053SJohannes Berg struct net_device *dev; 89269940924SSergey Ryazanov unsigned int priv_size; 89388b71053SJohannes Berg 89488b71053SJohannes Berg if (IS_ERR(wwandev)) 89588b71053SJohannes Berg return ERR_CAST(wwandev); 89688b71053SJohannes Berg 89788b71053SJohannes Berg /* only supported if ops were registered (not just ports) */ 89888b71053SJohannes Berg if (!wwandev->ops) { 89988b71053SJohannes Berg dev = ERR_PTR(-EOPNOTSUPP); 90088b71053SJohannes Berg goto out; 90188b71053SJohannes Berg } 90288b71053SJohannes Berg 90369940924SSergey Ryazanov priv_size = sizeof(struct wwan_netdev_priv) + wwandev->ops->priv_size; 90469940924SSergey Ryazanov dev = alloc_netdev_mqs(priv_size, ifname, name_assign_type, 90588b71053SJohannes Berg wwandev->ops->setup, num_tx_queues, num_rx_queues); 90688b71053SJohannes Berg 90788b71053SJohannes Berg if (dev) { 90888b71053SJohannes Berg SET_NETDEV_DEV(dev, &wwandev->dev); 90988b71053SJohannes Berg SET_NETDEV_DEVTYPE(dev, &wwan_type); 91088b71053SJohannes Berg } 91188b71053SJohannes Berg 91288b71053SJohannes Berg out: 91388b71053SJohannes Berg /* release the reference */ 91488b71053SJohannes Berg put_device(&wwandev->dev); 91588b71053SJohannes Berg return dev; 91688b71053SJohannes Berg } 91788b71053SJohannes Berg 91888b71053SJohannes Berg static int wwan_rtnl_newlink(struct net *src_net, struct net_device *dev, 91988b71053SJohannes Berg struct nlattr *tb[], struct nlattr *data[], 92088b71053SJohannes Berg struct netlink_ext_ack *extack) 92188b71053SJohannes Berg { 92288b71053SJohannes Berg struct wwan_device *wwandev = wwan_dev_get_by_parent(dev->dev.parent); 92388b71053SJohannes Berg u32 link_id = nla_get_u32(data[IFLA_WWAN_LINK_ID]); 92469940924SSergey Ryazanov struct wwan_netdev_priv *priv = netdev_priv(dev); 92588b71053SJohannes Berg int ret; 92688b71053SJohannes Berg 92788b71053SJohannes Berg if (IS_ERR(wwandev)) 92888b71053SJohannes Berg return PTR_ERR(wwandev); 92988b71053SJohannes Berg 93088b71053SJohannes Berg /* shouldn't have a netdev (left) with us as parent so WARN */ 93188b71053SJohannes Berg if (WARN_ON(!wwandev->ops)) { 93288b71053SJohannes Berg ret = -EOPNOTSUPP; 93388b71053SJohannes Berg goto out; 93488b71053SJohannes Berg } 93588b71053SJohannes Berg 93669940924SSergey Ryazanov priv->link_id = link_id; 93788b71053SJohannes Berg if (wwandev->ops->newlink) 93888b71053SJohannes Berg ret = wwandev->ops->newlink(wwandev->ops_ctxt, dev, 93988b71053SJohannes Berg link_id, extack); 94088b71053SJohannes Berg else 94188b71053SJohannes Berg ret = register_netdevice(dev); 94288b71053SJohannes Berg 94388b71053SJohannes Berg out: 94488b71053SJohannes Berg /* release the reference */ 94588b71053SJohannes Berg put_device(&wwandev->dev); 94688b71053SJohannes Berg return ret; 94788b71053SJohannes Berg } 94888b71053SJohannes Berg 94988b71053SJohannes Berg static void wwan_rtnl_dellink(struct net_device *dev, struct list_head *head) 95088b71053SJohannes Berg { 95188b71053SJohannes Berg struct wwan_device *wwandev = wwan_dev_get_by_parent(dev->dev.parent); 95288b71053SJohannes Berg 95388b71053SJohannes Berg if (IS_ERR(wwandev)) 95488b71053SJohannes Berg return; 95588b71053SJohannes Berg 95688b71053SJohannes Berg /* shouldn't have a netdev (left) with us as parent so WARN */ 95788b71053SJohannes Berg if (WARN_ON(!wwandev->ops)) 95888b71053SJohannes Berg goto out; 95988b71053SJohannes Berg 96088b71053SJohannes Berg if (wwandev->ops->dellink) 96188b71053SJohannes Berg wwandev->ops->dellink(wwandev->ops_ctxt, dev, head); 96288b71053SJohannes Berg else 963f492fccfSSergey Ryazanov unregister_netdevice_queue(dev, head); 96488b71053SJohannes Berg 96588b71053SJohannes Berg out: 96688b71053SJohannes Berg /* release the reference */ 96788b71053SJohannes Berg put_device(&wwandev->dev); 96888b71053SJohannes Berg } 96988b71053SJohannes Berg 97069940924SSergey Ryazanov static size_t wwan_rtnl_get_size(const struct net_device *dev) 97169940924SSergey Ryazanov { 97269940924SSergey Ryazanov return 97369940924SSergey Ryazanov nla_total_size(4) + /* IFLA_WWAN_LINK_ID */ 97469940924SSergey Ryazanov 0; 97569940924SSergey Ryazanov } 97669940924SSergey Ryazanov 97769940924SSergey Ryazanov static int wwan_rtnl_fill_info(struct sk_buff *skb, 97869940924SSergey Ryazanov const struct net_device *dev) 97969940924SSergey Ryazanov { 98069940924SSergey Ryazanov struct wwan_netdev_priv *priv = netdev_priv(dev); 98169940924SSergey Ryazanov 98269940924SSergey Ryazanov if (nla_put_u32(skb, IFLA_WWAN_LINK_ID, priv->link_id)) 98369940924SSergey Ryazanov goto nla_put_failure; 98469940924SSergey Ryazanov 98569940924SSergey Ryazanov return 0; 98669940924SSergey Ryazanov 98769940924SSergey Ryazanov nla_put_failure: 98869940924SSergey Ryazanov return -EMSGSIZE; 98969940924SSergey Ryazanov } 99069940924SSergey Ryazanov 99188b71053SJohannes Berg static const struct nla_policy wwan_rtnl_policy[IFLA_WWAN_MAX + 1] = { 99288b71053SJohannes Berg [IFLA_WWAN_LINK_ID] = { .type = NLA_U32 }, 99388b71053SJohannes Berg }; 99488b71053SJohannes Berg 99588b71053SJohannes Berg static struct rtnl_link_ops wwan_rtnl_link_ops __read_mostly = { 99688b71053SJohannes Berg .kind = "wwan", 99788b71053SJohannes Berg .maxtype = __IFLA_WWAN_MAX, 99888b71053SJohannes Berg .alloc = wwan_rtnl_alloc, 99988b71053SJohannes Berg .validate = wwan_rtnl_validate, 100088b71053SJohannes Berg .newlink = wwan_rtnl_newlink, 100188b71053SJohannes Berg .dellink = wwan_rtnl_dellink, 100269940924SSergey Ryazanov .get_size = wwan_rtnl_get_size, 100369940924SSergey Ryazanov .fill_info = wwan_rtnl_fill_info, 100488b71053SJohannes Berg .policy = wwan_rtnl_policy, 100588b71053SJohannes Berg }; 100688b71053SJohannes Berg 1007ca374290SSergey Ryazanov static void wwan_create_default_link(struct wwan_device *wwandev, 1008ca374290SSergey Ryazanov u32 def_link_id) 1009ca374290SSergey Ryazanov { 1010ca374290SSergey Ryazanov struct nlattr *tb[IFLA_MAX + 1], *linkinfo[IFLA_INFO_MAX + 1]; 1011ca374290SSergey Ryazanov struct nlattr *data[IFLA_WWAN_MAX + 1]; 1012ca374290SSergey Ryazanov struct net_device *dev; 1013ca374290SSergey Ryazanov struct nlmsghdr *nlh; 1014ca374290SSergey Ryazanov struct sk_buff *msg; 1015ca374290SSergey Ryazanov 1016ca374290SSergey Ryazanov /* Forge attributes required to create a WWAN netdev. We first 1017ca374290SSergey Ryazanov * build a netlink message and then parse it. This looks 1018ca374290SSergey Ryazanov * odd, but such approach is less error prone. 1019ca374290SSergey Ryazanov */ 1020ca374290SSergey Ryazanov msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 1021ca374290SSergey Ryazanov if (WARN_ON(!msg)) 1022ca374290SSergey Ryazanov return; 1023ca374290SSergey Ryazanov nlh = nlmsg_put(msg, 0, 0, RTM_NEWLINK, 0, 0); 1024ca374290SSergey Ryazanov if (WARN_ON(!nlh)) 1025ca374290SSergey Ryazanov goto free_attrs; 1026ca374290SSergey Ryazanov 1027ca374290SSergey Ryazanov if (nla_put_string(msg, IFLA_PARENT_DEV_NAME, dev_name(&wwandev->dev))) 1028ca374290SSergey Ryazanov goto free_attrs; 1029ca374290SSergey Ryazanov tb[IFLA_LINKINFO] = nla_nest_start(msg, IFLA_LINKINFO); 1030ca374290SSergey Ryazanov if (!tb[IFLA_LINKINFO]) 1031ca374290SSergey Ryazanov goto free_attrs; 1032ca374290SSergey Ryazanov linkinfo[IFLA_INFO_DATA] = nla_nest_start(msg, IFLA_INFO_DATA); 1033ca374290SSergey Ryazanov if (!linkinfo[IFLA_INFO_DATA]) 1034ca374290SSergey Ryazanov goto free_attrs; 1035ca374290SSergey Ryazanov if (nla_put_u32(msg, IFLA_WWAN_LINK_ID, def_link_id)) 1036ca374290SSergey Ryazanov goto free_attrs; 1037ca374290SSergey Ryazanov nla_nest_end(msg, linkinfo[IFLA_INFO_DATA]); 1038ca374290SSergey Ryazanov nla_nest_end(msg, tb[IFLA_LINKINFO]); 1039ca374290SSergey Ryazanov 1040ca374290SSergey Ryazanov nlmsg_end(msg, nlh); 1041ca374290SSergey Ryazanov 1042ca374290SSergey Ryazanov /* The next three parsing calls can not fail */ 1043ca374290SSergey Ryazanov nlmsg_parse_deprecated(nlh, 0, tb, IFLA_MAX, NULL, NULL); 1044ca374290SSergey Ryazanov nla_parse_nested_deprecated(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO], 1045ca374290SSergey Ryazanov NULL, NULL); 1046ca374290SSergey Ryazanov nla_parse_nested_deprecated(data, IFLA_WWAN_MAX, 1047ca374290SSergey Ryazanov linkinfo[IFLA_INFO_DATA], NULL, NULL); 1048ca374290SSergey Ryazanov 1049ca374290SSergey Ryazanov rtnl_lock(); 1050ca374290SSergey Ryazanov 1051ca374290SSergey Ryazanov dev = rtnl_create_link(&init_net, "wwan%d", NET_NAME_ENUM, 1052ca374290SSergey Ryazanov &wwan_rtnl_link_ops, tb, NULL); 1053ca374290SSergey Ryazanov if (WARN_ON(IS_ERR(dev))) 1054ca374290SSergey Ryazanov goto unlock; 1055ca374290SSergey Ryazanov 1056ca374290SSergey Ryazanov if (WARN_ON(wwan_rtnl_newlink(&init_net, dev, tb, data, NULL))) { 1057ca374290SSergey Ryazanov free_netdev(dev); 1058ca374290SSergey Ryazanov goto unlock; 1059ca374290SSergey Ryazanov } 1060ca374290SSergey Ryazanov 10618cc236dbSLoic Poulain rtnl_configure_link(dev, NULL); /* Link initialized, notify new link */ 10628cc236dbSLoic Poulain 1063ca374290SSergey Ryazanov unlock: 1064ca374290SSergey Ryazanov rtnl_unlock(); 1065ca374290SSergey Ryazanov 1066ca374290SSergey Ryazanov free_attrs: 1067ca374290SSergey Ryazanov nlmsg_free(msg); 1068ca374290SSergey Ryazanov } 1069ca374290SSergey Ryazanov 1070355a4e7eSSergey Ryazanov /** 1071355a4e7eSSergey Ryazanov * wwan_register_ops - register WWAN device ops 1072355a4e7eSSergey Ryazanov * @parent: Device to use as parent and shared by all WWAN ports and 1073355a4e7eSSergey Ryazanov * created netdevs 1074355a4e7eSSergey Ryazanov * @ops: operations to register 1075355a4e7eSSergey Ryazanov * @ctxt: context to pass to operations 1076ca374290SSergey Ryazanov * @def_link_id: id of the default link that will be automatically created by 1077ca374290SSergey Ryazanov * the WWAN core for the WWAN device. The default link will not be created 1078ca374290SSergey Ryazanov * if the passed value is WWAN_NO_DEFAULT_LINK. 1079355a4e7eSSergey Ryazanov * 1080355a4e7eSSergey Ryazanov * Returns: 0 on success, a negative error code on failure 1081355a4e7eSSergey Ryazanov */ 1082355a4e7eSSergey Ryazanov int wwan_register_ops(struct device *parent, const struct wwan_ops *ops, 1083ca374290SSergey Ryazanov void *ctxt, u32 def_link_id) 1084355a4e7eSSergey Ryazanov { 1085355a4e7eSSergey Ryazanov struct wwan_device *wwandev; 1086355a4e7eSSergey Ryazanov 108758c3b421SSergey Ryazanov if (WARN_ON(!parent || !ops || !ops->setup)) 1088355a4e7eSSergey Ryazanov return -EINVAL; 1089355a4e7eSSergey Ryazanov 1090355a4e7eSSergey Ryazanov wwandev = wwan_create_dev(parent); 1091d9d5b896SAndy Shevchenko if (IS_ERR(wwandev)) 1092d9d5b896SAndy Shevchenko return PTR_ERR(wwandev); 1093355a4e7eSSergey Ryazanov 1094355a4e7eSSergey Ryazanov if (WARN_ON(wwandev->ops)) { 1095355a4e7eSSergey Ryazanov wwan_remove_dev(wwandev); 1096355a4e7eSSergey Ryazanov return -EBUSY; 1097355a4e7eSSergey Ryazanov } 1098355a4e7eSSergey Ryazanov 1099355a4e7eSSergey Ryazanov wwandev->ops = ops; 1100355a4e7eSSergey Ryazanov wwandev->ops_ctxt = ctxt; 1101355a4e7eSSergey Ryazanov 1102ca374290SSergey Ryazanov /* NB: we do not abort ops registration in case of default link 1103ca374290SSergey Ryazanov * creation failure. Link ops is the management interface, while the 1104ca374290SSergey Ryazanov * default link creation is a service option. And we should not prevent 1105ca374290SSergey Ryazanov * a user from manually creating a link latter if service option failed 1106ca374290SSergey Ryazanov * now. 1107ca374290SSergey Ryazanov */ 1108ca374290SSergey Ryazanov if (def_link_id != WWAN_NO_DEFAULT_LINK) 1109ca374290SSergey Ryazanov wwan_create_default_link(wwandev, def_link_id); 1110ca374290SSergey Ryazanov 1111355a4e7eSSergey Ryazanov return 0; 1112355a4e7eSSergey Ryazanov } 1113355a4e7eSSergey Ryazanov EXPORT_SYMBOL_GPL(wwan_register_ops); 1114355a4e7eSSergey Ryazanov 11152f752380SSergey Ryazanov /* Enqueue child netdev deletion */ 11162f752380SSergey Ryazanov static int wwan_child_dellink(struct device *dev, void *data) 11172f752380SSergey Ryazanov { 11182f752380SSergey Ryazanov struct list_head *kill_list = data; 11192f752380SSergey Ryazanov 11202f752380SSergey Ryazanov if (dev->type == &wwan_type) 11212f752380SSergey Ryazanov wwan_rtnl_dellink(to_net_dev(dev), kill_list); 11222f752380SSergey Ryazanov 11232f752380SSergey Ryazanov return 0; 11242f752380SSergey Ryazanov } 11252f752380SSergey Ryazanov 1126355a4e7eSSergey Ryazanov /** 1127355a4e7eSSergey Ryazanov * wwan_unregister_ops - remove WWAN device ops 1128355a4e7eSSergey Ryazanov * @parent: Device to use as parent and shared by all WWAN ports and 1129355a4e7eSSergey Ryazanov * created netdevs 1130355a4e7eSSergey Ryazanov */ 1131355a4e7eSSergey Ryazanov void wwan_unregister_ops(struct device *parent) 1132355a4e7eSSergey Ryazanov { 1133355a4e7eSSergey Ryazanov struct wwan_device *wwandev = wwan_dev_get_by_parent(parent); 11342f752380SSergey Ryazanov LIST_HEAD(kill_list); 1135355a4e7eSSergey Ryazanov 1136355a4e7eSSergey Ryazanov if (WARN_ON(IS_ERR(wwandev))) 1137355a4e7eSSergey Ryazanov return; 11382f752380SSergey Ryazanov if (WARN_ON(!wwandev->ops)) { 11392f752380SSergey Ryazanov put_device(&wwandev->dev); 11402f752380SSergey Ryazanov return; 11412f752380SSergey Ryazanov } 1142355a4e7eSSergey Ryazanov 1143355a4e7eSSergey Ryazanov /* put the reference obtained by wwan_dev_get_by_parent(), 1144355a4e7eSSergey Ryazanov * we should still have one (that the owner is giving back 11452f752380SSergey Ryazanov * now) due to the ops being assigned. 1146355a4e7eSSergey Ryazanov */ 1147355a4e7eSSergey Ryazanov put_device(&wwandev->dev); 1148355a4e7eSSergey Ryazanov 11492f752380SSergey Ryazanov rtnl_lock(); /* Prevent concurent netdev(s) creation/destroying */ 1150355a4e7eSSergey Ryazanov 11512f752380SSergey Ryazanov /* Remove all child netdev(s), using batch removing */ 11522f752380SSergey Ryazanov device_for_each_child(&wwandev->dev, &kill_list, 11532f752380SSergey Ryazanov wwan_child_dellink); 11542f752380SSergey Ryazanov unregister_netdevice_many(&kill_list); 11552f752380SSergey Ryazanov 11562f752380SSergey Ryazanov wwandev->ops = NULL; /* Finally remove ops */ 11572f752380SSergey Ryazanov 11582f752380SSergey Ryazanov rtnl_unlock(); 11592f752380SSergey Ryazanov 1160355a4e7eSSergey Ryazanov wwandev->ops_ctxt = NULL; 1161355a4e7eSSergey Ryazanov wwan_remove_dev(wwandev); 1162355a4e7eSSergey Ryazanov } 1163355a4e7eSSergey Ryazanov EXPORT_SYMBOL_GPL(wwan_unregister_ops); 1164355a4e7eSSergey Ryazanov 11659a44c1ccSLoic Poulain static int __init wwan_init(void) 11669a44c1ccSLoic Poulain { 116788b71053SJohannes Berg int err; 116888b71053SJohannes Berg 116988b71053SJohannes Berg err = rtnl_link_register(&wwan_rtnl_link_ops); 117088b71053SJohannes Berg if (err) 117188b71053SJohannes Berg return err; 117288b71053SJohannes Berg 11739a44c1ccSLoic Poulain wwan_class = class_create(THIS_MODULE, "wwan"); 117488b71053SJohannes Berg if (IS_ERR(wwan_class)) { 117588b71053SJohannes Berg err = PTR_ERR(wwan_class); 117688b71053SJohannes Berg goto unregister; 117788b71053SJohannes Berg } 11789a44c1ccSLoic Poulain 11799a44c1ccSLoic Poulain /* chrdev used for wwan ports */ 118072eedfc4SSergey Ryazanov wwan_major = __register_chrdev(0, 0, WWAN_MAX_MINORS, "wwan_port", 118172eedfc4SSergey Ryazanov &wwan_port_fops); 11829a44c1ccSLoic Poulain if (wwan_major < 0) { 118388b71053SJohannes Berg err = wwan_major; 118488b71053SJohannes Berg goto destroy; 11859a44c1ccSLoic Poulain } 11869a44c1ccSLoic Poulain 1187283e6f5aSSergey Ryazanov #ifdef CONFIG_WWAN_DEBUGFS 1188c4804670SM Chetan Kumar wwan_debugfs_dir = debugfs_create_dir("wwan", NULL); 1189283e6f5aSSergey Ryazanov #endif 1190c4804670SM Chetan Kumar 11919a44c1ccSLoic Poulain return 0; 119288b71053SJohannes Berg 119388b71053SJohannes Berg destroy: 119488b71053SJohannes Berg class_destroy(wwan_class); 119588b71053SJohannes Berg unregister: 119688b71053SJohannes Berg rtnl_link_unregister(&wwan_rtnl_link_ops); 119788b71053SJohannes Berg return err; 11989a44c1ccSLoic Poulain } 11999a44c1ccSLoic Poulain 12009a44c1ccSLoic Poulain static void __exit wwan_exit(void) 12019a44c1ccSLoic Poulain { 1202c4804670SM Chetan Kumar debugfs_remove_recursive(wwan_debugfs_dir); 120372eedfc4SSergey Ryazanov __unregister_chrdev(wwan_major, 0, WWAN_MAX_MINORS, "wwan_port"); 120488b71053SJohannes Berg rtnl_link_unregister(&wwan_rtnl_link_ops); 12059a44c1ccSLoic Poulain class_destroy(wwan_class); 12069a44c1ccSLoic Poulain } 12079a44c1ccSLoic Poulain 12089a44c1ccSLoic Poulain module_init(wwan_init); 12099a44c1ccSLoic Poulain module_exit(wwan_exit); 12109a44c1ccSLoic Poulain 12119a44c1ccSLoic Poulain MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>"); 12129a44c1ccSLoic Poulain MODULE_DESCRIPTION("WWAN core"); 12139a44c1ccSLoic Poulain MODULE_LICENSE("GPL v2"); 1214