xref: /openbmc/linux/drivers/net/wwan/wwan_hwsim.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1f36a111aSSergey Ryazanov // SPDX-License-Identifier: GPL-2.0-only
2f36a111aSSergey Ryazanov /*
3f36a111aSSergey Ryazanov  * WWAN device simulator for WWAN framework testing.
4f36a111aSSergey Ryazanov  *
5f36a111aSSergey Ryazanov  * Copyright (c) 2021, Sergey Ryazanov <ryazanov.s.a@gmail.com>
6f36a111aSSergey Ryazanov  */
7f36a111aSSergey Ryazanov 
8f36a111aSSergey Ryazanov #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9f36a111aSSergey Ryazanov 
10f36a111aSSergey Ryazanov #include <linux/kernel.h>
11f36a111aSSergey Ryazanov #include <linux/module.h>
12f36a111aSSergey Ryazanov #include <linux/slab.h>
13f36a111aSSergey Ryazanov #include <linux/device.h>
14f36a111aSSergey Ryazanov #include <linux/spinlock.h>
15f36a111aSSergey Ryazanov #include <linux/list.h>
16f36a111aSSergey Ryazanov #include <linux/skbuff.h>
17f842f488SSergey Ryazanov #include <linux/netdevice.h>
18f36a111aSSergey Ryazanov #include <linux/wwan.h>
199ee23f48SSergey Ryazanov #include <linux/debugfs.h>
209ee23f48SSergey Ryazanov #include <linux/workqueue.h>
21f36a111aSSergey Ryazanov 
22f842f488SSergey Ryazanov #include <net/arp.h>
23f842f488SSergey Ryazanov 
24f36a111aSSergey Ryazanov static int wwan_hwsim_devsnum = 2;
25f36a111aSSergey Ryazanov module_param_named(devices, wwan_hwsim_devsnum, int, 0444);
26f36a111aSSergey Ryazanov MODULE_PARM_DESC(devices, "Number of simulated devices");
27f36a111aSSergey Ryazanov 
28f36a111aSSergey Ryazanov static struct class *wwan_hwsim_class;
29f36a111aSSergey Ryazanov 
309ee23f48SSergey Ryazanov static struct dentry *wwan_hwsim_debugfs_topdir;
319ee23f48SSergey Ryazanov static struct dentry *wwan_hwsim_debugfs_devcreate;
329ee23f48SSergey Ryazanov 
33f36a111aSSergey Ryazanov static DEFINE_SPINLOCK(wwan_hwsim_devs_lock);
34f36a111aSSergey Ryazanov static LIST_HEAD(wwan_hwsim_devs);
35f36a111aSSergey Ryazanov static unsigned int wwan_hwsim_dev_idx;
36cc271ab8STetsuo Handa static struct workqueue_struct *wwan_wq;
37f36a111aSSergey Ryazanov 
38f36a111aSSergey Ryazanov struct wwan_hwsim_dev {
39f36a111aSSergey Ryazanov 	struct list_head list;
40f36a111aSSergey Ryazanov 	unsigned int id;
41f36a111aSSergey Ryazanov 	struct device dev;
429ee23f48SSergey Ryazanov 	struct work_struct del_work;
439ee23f48SSergey Ryazanov 	struct dentry *debugfs_topdir;
449ee23f48SSergey Ryazanov 	struct dentry *debugfs_portcreate;
45f36a111aSSergey Ryazanov 	spinlock_t ports_lock;	/* Serialize ports creation/deletion */
46f36a111aSSergey Ryazanov 	unsigned int port_idx;
47f36a111aSSergey Ryazanov 	struct list_head ports;
48f36a111aSSergey Ryazanov };
49f36a111aSSergey Ryazanov 
50f36a111aSSergey Ryazanov struct wwan_hwsim_port {
51f36a111aSSergey Ryazanov 	struct list_head list;
52f36a111aSSergey Ryazanov 	unsigned int id;
53f36a111aSSergey Ryazanov 	struct wwan_hwsim_dev *dev;
54f36a111aSSergey Ryazanov 	struct wwan_port *wwan;
559ee23f48SSergey Ryazanov 	struct work_struct del_work;
569ee23f48SSergey Ryazanov 	struct dentry *debugfs_topdir;
57f36a111aSSergey Ryazanov 	enum {			/* AT command parser state */
58f36a111aSSergey Ryazanov 		AT_PARSER_WAIT_A,
59f36a111aSSergey Ryazanov 		AT_PARSER_WAIT_T,
60f36a111aSSergey Ryazanov 		AT_PARSER_WAIT_TERM,
61f36a111aSSergey Ryazanov 		AT_PARSER_SKIP_LINE,
62f36a111aSSergey Ryazanov 	} pstate;
63f36a111aSSergey Ryazanov };
64f36a111aSSergey Ryazanov 
659ee23f48SSergey Ryazanov static const struct file_operations wwan_hwsim_debugfs_portdestroy_fops;
669ee23f48SSergey Ryazanov static const struct file_operations wwan_hwsim_debugfs_portcreate_fops;
679ee23f48SSergey Ryazanov static const struct file_operations wwan_hwsim_debugfs_devdestroy_fops;
689ee23f48SSergey Ryazanov static void wwan_hwsim_port_del_work(struct work_struct *work);
699ee23f48SSergey Ryazanov static void wwan_hwsim_dev_del_work(struct work_struct *work);
709ee23f48SSergey Ryazanov 
wwan_hwsim_netdev_xmit(struct sk_buff * skb,struct net_device * ndev)71f842f488SSergey Ryazanov static netdev_tx_t wwan_hwsim_netdev_xmit(struct sk_buff *skb,
72f842f488SSergey Ryazanov 					  struct net_device *ndev)
73f842f488SSergey Ryazanov {
74f842f488SSergey Ryazanov 	ndev->stats.tx_packets++;
75f842f488SSergey Ryazanov 	ndev->stats.tx_bytes += skb->len;
76f842f488SSergey Ryazanov 	consume_skb(skb);
77f842f488SSergey Ryazanov 	return NETDEV_TX_OK;
78f842f488SSergey Ryazanov }
79f842f488SSergey Ryazanov 
80f842f488SSergey Ryazanov static const struct net_device_ops wwan_hwsim_netdev_ops = {
81f842f488SSergey Ryazanov 	.ndo_start_xmit = wwan_hwsim_netdev_xmit,
82f842f488SSergey Ryazanov };
83f842f488SSergey Ryazanov 
wwan_hwsim_netdev_setup(struct net_device * ndev)84f842f488SSergey Ryazanov static void wwan_hwsim_netdev_setup(struct net_device *ndev)
85f842f488SSergey Ryazanov {
86f842f488SSergey Ryazanov 	ndev->netdev_ops = &wwan_hwsim_netdev_ops;
87f842f488SSergey Ryazanov 	ndev->needs_free_netdev = true;
88f842f488SSergey Ryazanov 
89f842f488SSergey Ryazanov 	ndev->mtu = ETH_DATA_LEN;
90f842f488SSergey Ryazanov 	ndev->min_mtu = ETH_MIN_MTU;
91f842f488SSergey Ryazanov 	ndev->max_mtu = ETH_MAX_MTU;
92f842f488SSergey Ryazanov 
93f842f488SSergey Ryazanov 	ndev->type = ARPHRD_NONE;
94f842f488SSergey Ryazanov 	ndev->flags = IFF_POINTOPOINT | IFF_NOARP;
95f842f488SSergey Ryazanov }
96f842f488SSergey Ryazanov 
97f842f488SSergey Ryazanov static const struct wwan_ops wwan_hwsim_wwan_rtnl_ops = {
98f842f488SSergey Ryazanov 	.priv_size = 0,			/* No private data */
99f842f488SSergey Ryazanov 	.setup = wwan_hwsim_netdev_setup,
100f842f488SSergey Ryazanov };
101f842f488SSergey Ryazanov 
wwan_hwsim_port_start(struct wwan_port * wport)102f36a111aSSergey Ryazanov static int wwan_hwsim_port_start(struct wwan_port *wport)
103f36a111aSSergey Ryazanov {
104f36a111aSSergey Ryazanov 	struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport);
105f36a111aSSergey Ryazanov 
106f36a111aSSergey Ryazanov 	port->pstate = AT_PARSER_WAIT_A;
107f36a111aSSergey Ryazanov 
108f36a111aSSergey Ryazanov 	return 0;
109f36a111aSSergey Ryazanov }
110f36a111aSSergey Ryazanov 
wwan_hwsim_port_stop(struct wwan_port * wport)111f36a111aSSergey Ryazanov static void wwan_hwsim_port_stop(struct wwan_port *wport)
112f36a111aSSergey Ryazanov {
113f36a111aSSergey Ryazanov }
114f36a111aSSergey Ryazanov 
115f36a111aSSergey Ryazanov /* Implements a minimalistic AT commands parser that echo input back and
116f36a111aSSergey Ryazanov  * reply with 'OK' to each input command. See AT command protocol details in the
117f36a111aSSergey Ryazanov  * ITU-T V.250 recomendations document.
118f36a111aSSergey Ryazanov  *
119f36a111aSSergey Ryazanov  * Be aware that this processor is not fully V.250 compliant.
120f36a111aSSergey Ryazanov  */
wwan_hwsim_port_tx(struct wwan_port * wport,struct sk_buff * in)121f36a111aSSergey Ryazanov static int wwan_hwsim_port_tx(struct wwan_port *wport, struct sk_buff *in)
122f36a111aSSergey Ryazanov {
123f36a111aSSergey Ryazanov 	struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport);
124f36a111aSSergey Ryazanov 	struct sk_buff *out;
125f36a111aSSergey Ryazanov 	int i, n, s;
126f36a111aSSergey Ryazanov 
127f36a111aSSergey Ryazanov 	/* Estimate a max possible number of commands by counting the number of
128f36a111aSSergey Ryazanov 	 * termination chars (S3 param, CR by default). And then allocate the
129f36a111aSSergey Ryazanov 	 * output buffer that will be enough to fit the echo and result codes of
130f36a111aSSergey Ryazanov 	 * all commands.
131f36a111aSSergey Ryazanov 	 */
132f36a111aSSergey Ryazanov 	for (i = 0, n = 0; i < in->len; ++i)
133f36a111aSSergey Ryazanov 		if (in->data[i] == '\r')
134f36a111aSSergey Ryazanov 			n++;
135f36a111aSSergey Ryazanov 	n = in->len + n * (2 + 2 + 2);	/* Output buffer size */
136f36a111aSSergey Ryazanov 	out = alloc_skb(n, GFP_KERNEL);
137f36a111aSSergey Ryazanov 	if (!out)
138f36a111aSSergey Ryazanov 		return -ENOMEM;
139f36a111aSSergey Ryazanov 
140f36a111aSSergey Ryazanov 	for (i = 0, s = 0; i < in->len; ++i) {
141f36a111aSSergey Ryazanov 		char c = in->data[i];
142f36a111aSSergey Ryazanov 
143f36a111aSSergey Ryazanov 		if (port->pstate == AT_PARSER_WAIT_A) {
144f36a111aSSergey Ryazanov 			if (c == 'A' || c == 'a')
145f36a111aSSergey Ryazanov 				port->pstate = AT_PARSER_WAIT_T;
146f36a111aSSergey Ryazanov 			else if (c != '\n')	/* Ignore formating char */
147f36a111aSSergey Ryazanov 				port->pstate = AT_PARSER_SKIP_LINE;
148f36a111aSSergey Ryazanov 		} else if (port->pstate == AT_PARSER_WAIT_T) {
149f36a111aSSergey Ryazanov 			if (c == 'T' || c == 't')
150f36a111aSSergey Ryazanov 				port->pstate = AT_PARSER_WAIT_TERM;
151f36a111aSSergey Ryazanov 			else
152f36a111aSSergey Ryazanov 				port->pstate = AT_PARSER_SKIP_LINE;
153f36a111aSSergey Ryazanov 		} else if (port->pstate == AT_PARSER_WAIT_TERM) {
154f36a111aSSergey Ryazanov 			if (c != '\r')
155f36a111aSSergey Ryazanov 				continue;
156f36a111aSSergey Ryazanov 			/* Consume the trailing formatting char as well */
157f36a111aSSergey Ryazanov 			if ((i + 1) < in->len && in->data[i + 1] == '\n')
158f36a111aSSergey Ryazanov 				i++;
159f36a111aSSergey Ryazanov 			n = i - s + 1;
1606db239f0SShang XiaoJing 			skb_put_data(out, &in->data[s], n);/* Echo */
1616db239f0SShang XiaoJing 			skb_put_data(out, "\r\nOK\r\n", 6);
162f36a111aSSergey Ryazanov 			s = i + 1;
163f36a111aSSergey Ryazanov 			port->pstate = AT_PARSER_WAIT_A;
164f36a111aSSergey Ryazanov 		} else if (port->pstate == AT_PARSER_SKIP_LINE) {
165f36a111aSSergey Ryazanov 			if (c != '\r')
166f36a111aSSergey Ryazanov 				continue;
167f36a111aSSergey Ryazanov 			port->pstate = AT_PARSER_WAIT_A;
168f36a111aSSergey Ryazanov 		}
169f36a111aSSergey Ryazanov 	}
170f36a111aSSergey Ryazanov 
171f36a111aSSergey Ryazanov 	if (i > s) {
172f36a111aSSergey Ryazanov 		/* Echo the processed portion of a not yet completed command */
173f36a111aSSergey Ryazanov 		n = i - s;
1746db239f0SShang XiaoJing 		skb_put_data(out, &in->data[s], n);
175f36a111aSSergey Ryazanov 	}
176f36a111aSSergey Ryazanov 
177f36a111aSSergey Ryazanov 	consume_skb(in);
178f36a111aSSergey Ryazanov 
179f36a111aSSergey Ryazanov 	wwan_port_rx(wport, out);
180f36a111aSSergey Ryazanov 
181f36a111aSSergey Ryazanov 	return 0;
182f36a111aSSergey Ryazanov }
183f36a111aSSergey Ryazanov 
184f36a111aSSergey Ryazanov static const struct wwan_port_ops wwan_hwsim_port_ops = {
185f36a111aSSergey Ryazanov 	.start = wwan_hwsim_port_start,
186f36a111aSSergey Ryazanov 	.stop = wwan_hwsim_port_stop,
187f36a111aSSergey Ryazanov 	.tx = wwan_hwsim_port_tx,
188f36a111aSSergey Ryazanov };
189f36a111aSSergey Ryazanov 
wwan_hwsim_port_new(struct wwan_hwsim_dev * dev)190f36a111aSSergey Ryazanov static struct wwan_hwsim_port *wwan_hwsim_port_new(struct wwan_hwsim_dev *dev)
191f36a111aSSergey Ryazanov {
192f36a111aSSergey Ryazanov 	struct wwan_hwsim_port *port;
1939ee23f48SSergey Ryazanov 	char name[0x10];
194f36a111aSSergey Ryazanov 	int err;
195f36a111aSSergey Ryazanov 
196f36a111aSSergey Ryazanov 	port = kzalloc(sizeof(*port), GFP_KERNEL);
197f36a111aSSergey Ryazanov 	if (!port)
198f36a111aSSergey Ryazanov 		return ERR_PTR(-ENOMEM);
199f36a111aSSergey Ryazanov 
200f36a111aSSergey Ryazanov 	port->dev = dev;
201f36a111aSSergey Ryazanov 
202f36a111aSSergey Ryazanov 	spin_lock(&dev->ports_lock);
203f36a111aSSergey Ryazanov 	port->id = dev->port_idx++;
204f36a111aSSergey Ryazanov 	spin_unlock(&dev->ports_lock);
205f36a111aSSergey Ryazanov 
206f36a111aSSergey Ryazanov 	port->wwan = wwan_create_port(&dev->dev, WWAN_PORT_AT,
207f36a111aSSergey Ryazanov 				      &wwan_hwsim_port_ops,
208*36bd28c1Shaozhe chang 				      NULL, port);
209f36a111aSSergey Ryazanov 	if (IS_ERR(port->wwan)) {
210f36a111aSSergey Ryazanov 		err = PTR_ERR(port->wwan);
211f36a111aSSergey Ryazanov 		goto err_free_port;
212f36a111aSSergey Ryazanov 	}
213f36a111aSSergey Ryazanov 
2149ee23f48SSergey Ryazanov 	INIT_WORK(&port->del_work, wwan_hwsim_port_del_work);
2159ee23f48SSergey Ryazanov 
2169ee23f48SSergey Ryazanov 	snprintf(name, sizeof(name), "port%u", port->id);
2179ee23f48SSergey Ryazanov 	port->debugfs_topdir = debugfs_create_dir(name, dev->debugfs_topdir);
2189ee23f48SSergey Ryazanov 	debugfs_create_file("destroy", 0200, port->debugfs_topdir, port,
2199ee23f48SSergey Ryazanov 			    &wwan_hwsim_debugfs_portdestroy_fops);
2209ee23f48SSergey Ryazanov 
221f36a111aSSergey Ryazanov 	return port;
222f36a111aSSergey Ryazanov 
223f36a111aSSergey Ryazanov err_free_port:
224f36a111aSSergey Ryazanov 	kfree(port);
225f36a111aSSergey Ryazanov 
226f36a111aSSergey Ryazanov 	return ERR_PTR(err);
227f36a111aSSergey Ryazanov }
228f36a111aSSergey Ryazanov 
wwan_hwsim_port_del(struct wwan_hwsim_port * port)229f36a111aSSergey Ryazanov static void wwan_hwsim_port_del(struct wwan_hwsim_port *port)
230f36a111aSSergey Ryazanov {
2319ee23f48SSergey Ryazanov 	debugfs_remove(port->debugfs_topdir);
2329ee23f48SSergey Ryazanov 
2339ee23f48SSergey Ryazanov 	/* Make sure that there is no pending deletion work */
2349ee23f48SSergey Ryazanov 	if (current_work() != &port->del_work)
2359ee23f48SSergey Ryazanov 		cancel_work_sync(&port->del_work);
2369ee23f48SSergey Ryazanov 
237f36a111aSSergey Ryazanov 	wwan_remove_port(port->wwan);
238f36a111aSSergey Ryazanov 	kfree(port);
239f36a111aSSergey Ryazanov }
240f36a111aSSergey Ryazanov 
wwan_hwsim_port_del_work(struct work_struct * work)2419ee23f48SSergey Ryazanov static void wwan_hwsim_port_del_work(struct work_struct *work)
2429ee23f48SSergey Ryazanov {
2439ee23f48SSergey Ryazanov 	struct wwan_hwsim_port *port =
2449ee23f48SSergey Ryazanov 				container_of(work, typeof(*port), del_work);
2459ee23f48SSergey Ryazanov 	struct wwan_hwsim_dev *dev = port->dev;
2469ee23f48SSergey Ryazanov 
2479ee23f48SSergey Ryazanov 	spin_lock(&dev->ports_lock);
2489ee23f48SSergey Ryazanov 	if (list_empty(&port->list)) {
2499ee23f48SSergey Ryazanov 		/* Someone else deleting port at the moment */
2509ee23f48SSergey Ryazanov 		spin_unlock(&dev->ports_lock);
2519ee23f48SSergey Ryazanov 		return;
2529ee23f48SSergey Ryazanov 	}
2539ee23f48SSergey Ryazanov 	list_del_init(&port->list);
2549ee23f48SSergey Ryazanov 	spin_unlock(&dev->ports_lock);
2559ee23f48SSergey Ryazanov 
2569ee23f48SSergey Ryazanov 	wwan_hwsim_port_del(port);
2579ee23f48SSergey Ryazanov }
2589ee23f48SSergey Ryazanov 
wwan_hwsim_dev_release(struct device * sysdev)259f36a111aSSergey Ryazanov static void wwan_hwsim_dev_release(struct device *sysdev)
260f36a111aSSergey Ryazanov {
261f36a111aSSergey Ryazanov 	struct wwan_hwsim_dev *dev = container_of(sysdev, typeof(*dev), dev);
262f36a111aSSergey Ryazanov 
263f36a111aSSergey Ryazanov 	kfree(dev);
264f36a111aSSergey Ryazanov }
265f36a111aSSergey Ryazanov 
wwan_hwsim_dev_new(void)266f36a111aSSergey Ryazanov static struct wwan_hwsim_dev *wwan_hwsim_dev_new(void)
267f36a111aSSergey Ryazanov {
268f36a111aSSergey Ryazanov 	struct wwan_hwsim_dev *dev;
269f36a111aSSergey Ryazanov 	int err;
270f36a111aSSergey Ryazanov 
271f36a111aSSergey Ryazanov 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
272f36a111aSSergey Ryazanov 	if (!dev)
273f36a111aSSergey Ryazanov 		return ERR_PTR(-ENOMEM);
274f36a111aSSergey Ryazanov 
275f36a111aSSergey Ryazanov 	spin_lock(&wwan_hwsim_devs_lock);
276f36a111aSSergey Ryazanov 	dev->id = wwan_hwsim_dev_idx++;
277f36a111aSSergey Ryazanov 	spin_unlock(&wwan_hwsim_devs_lock);
278f36a111aSSergey Ryazanov 
279f36a111aSSergey Ryazanov 	dev->dev.release = wwan_hwsim_dev_release;
280f36a111aSSergey Ryazanov 	dev->dev.class = wwan_hwsim_class;
281f36a111aSSergey Ryazanov 	dev_set_name(&dev->dev, "hwsim%u", dev->id);
282f36a111aSSergey Ryazanov 
283f36a111aSSergey Ryazanov 	spin_lock_init(&dev->ports_lock);
284f36a111aSSergey Ryazanov 	INIT_LIST_HEAD(&dev->ports);
285f36a111aSSergey Ryazanov 
286f36a111aSSergey Ryazanov 	err = device_register(&dev->dev);
287f36a111aSSergey Ryazanov 	if (err)
288f36a111aSSergey Ryazanov 		goto err_free_dev;
289f36a111aSSergey Ryazanov 
2909ee23f48SSergey Ryazanov 	INIT_WORK(&dev->del_work, wwan_hwsim_dev_del_work);
2919ee23f48SSergey Ryazanov 
292ca374290SSergey Ryazanov 	err = wwan_register_ops(&dev->dev, &wwan_hwsim_wwan_rtnl_ops, dev, 1);
293f842f488SSergey Ryazanov 	if (err)
294f842f488SSergey Ryazanov 		goto err_unreg_dev;
295f842f488SSergey Ryazanov 
2969ee23f48SSergey Ryazanov 	dev->debugfs_topdir = debugfs_create_dir(dev_name(&dev->dev),
2979ee23f48SSergey Ryazanov 						 wwan_hwsim_debugfs_topdir);
2989ee23f48SSergey Ryazanov 	debugfs_create_file("destroy", 0200, dev->debugfs_topdir, dev,
2999ee23f48SSergey Ryazanov 			    &wwan_hwsim_debugfs_devdestroy_fops);
3009ee23f48SSergey Ryazanov 	dev->debugfs_portcreate =
3019ee23f48SSergey Ryazanov 		debugfs_create_file("portcreate", 0200,
3029ee23f48SSergey Ryazanov 				    dev->debugfs_topdir, dev,
3039ee23f48SSergey Ryazanov 				    &wwan_hwsim_debugfs_portcreate_fops);
3049ee23f48SSergey Ryazanov 
305f36a111aSSergey Ryazanov 	return dev;
306f36a111aSSergey Ryazanov 
307f842f488SSergey Ryazanov err_unreg_dev:
308f842f488SSergey Ryazanov 	device_unregister(&dev->dev);
309f842f488SSergey Ryazanov 	/* Memory will be freed in the device release callback */
310f842f488SSergey Ryazanov 
311f842f488SSergey Ryazanov 	return ERR_PTR(err);
312f842f488SSergey Ryazanov 
313f36a111aSSergey Ryazanov err_free_dev:
314258ad2feSYang Yingliang 	put_device(&dev->dev);
315f36a111aSSergey Ryazanov 
316f36a111aSSergey Ryazanov 	return ERR_PTR(err);
317f36a111aSSergey Ryazanov }
318f36a111aSSergey Ryazanov 
wwan_hwsim_dev_del(struct wwan_hwsim_dev * dev)319f36a111aSSergey Ryazanov static void wwan_hwsim_dev_del(struct wwan_hwsim_dev *dev)
320f36a111aSSergey Ryazanov {
3219ee23f48SSergey Ryazanov 	debugfs_remove(dev->debugfs_portcreate);	/* Avoid new ports */
3229ee23f48SSergey Ryazanov 
323f36a111aSSergey Ryazanov 	spin_lock(&dev->ports_lock);
324f36a111aSSergey Ryazanov 	while (!list_empty(&dev->ports)) {
325f36a111aSSergey Ryazanov 		struct wwan_hwsim_port *port;
326f36a111aSSergey Ryazanov 
327f36a111aSSergey Ryazanov 		port = list_first_entry(&dev->ports, struct wwan_hwsim_port,
328f36a111aSSergey Ryazanov 					list);
3299ee23f48SSergey Ryazanov 		list_del_init(&port->list);
330f36a111aSSergey Ryazanov 		spin_unlock(&dev->ports_lock);
331f36a111aSSergey Ryazanov 		wwan_hwsim_port_del(port);
332f36a111aSSergey Ryazanov 		spin_lock(&dev->ports_lock);
333f36a111aSSergey Ryazanov 	}
334f36a111aSSergey Ryazanov 	spin_unlock(&dev->ports_lock);
335f36a111aSSergey Ryazanov 
3369ee23f48SSergey Ryazanov 	debugfs_remove(dev->debugfs_topdir);
3379ee23f48SSergey Ryazanov 
338f842f488SSergey Ryazanov 	/* This will remove all child netdev(s) */
339f842f488SSergey Ryazanov 	wwan_unregister_ops(&dev->dev);
340f842f488SSergey Ryazanov 
3419ee23f48SSergey Ryazanov 	/* Make sure that there is no pending deletion work */
3429ee23f48SSergey Ryazanov 	if (current_work() != &dev->del_work)
3439ee23f48SSergey Ryazanov 		cancel_work_sync(&dev->del_work);
3449ee23f48SSergey Ryazanov 
345f36a111aSSergey Ryazanov 	device_unregister(&dev->dev);
346f36a111aSSergey Ryazanov 	/* Memory will be freed in the device release callback */
347f36a111aSSergey Ryazanov }
348f36a111aSSergey Ryazanov 
wwan_hwsim_dev_del_work(struct work_struct * work)3499ee23f48SSergey Ryazanov static void wwan_hwsim_dev_del_work(struct work_struct *work)
3509ee23f48SSergey Ryazanov {
3519ee23f48SSergey Ryazanov 	struct wwan_hwsim_dev *dev = container_of(work, typeof(*dev), del_work);
3529ee23f48SSergey Ryazanov 
3539ee23f48SSergey Ryazanov 	spin_lock(&wwan_hwsim_devs_lock);
3549ee23f48SSergey Ryazanov 	if (list_empty(&dev->list)) {
3559ee23f48SSergey Ryazanov 		/* Someone else deleting device at the moment */
3569ee23f48SSergey Ryazanov 		spin_unlock(&wwan_hwsim_devs_lock);
3579ee23f48SSergey Ryazanov 		return;
3589ee23f48SSergey Ryazanov 	}
3599ee23f48SSergey Ryazanov 	list_del_init(&dev->list);
3609ee23f48SSergey Ryazanov 	spin_unlock(&wwan_hwsim_devs_lock);
3619ee23f48SSergey Ryazanov 
3629ee23f48SSergey Ryazanov 	wwan_hwsim_dev_del(dev);
3639ee23f48SSergey Ryazanov }
3649ee23f48SSergey Ryazanov 
wwan_hwsim_debugfs_portdestroy_write(struct file * file,const char __user * usrbuf,size_t count,loff_t * ppos)3659ee23f48SSergey Ryazanov static ssize_t wwan_hwsim_debugfs_portdestroy_write(struct file *file,
3669ee23f48SSergey Ryazanov 						    const char __user *usrbuf,
3679ee23f48SSergey Ryazanov 						    size_t count, loff_t *ppos)
3689ee23f48SSergey Ryazanov {
3699ee23f48SSergey Ryazanov 	struct wwan_hwsim_port *port = file->private_data;
3709ee23f48SSergey Ryazanov 
3719ee23f48SSergey Ryazanov 	/* We can not delete port here since it will cause a deadlock due to
3729ee23f48SSergey Ryazanov 	 * waiting this callback to finish in the debugfs_remove() call. So,
3739ee23f48SSergey Ryazanov 	 * use workqueue.
3749ee23f48SSergey Ryazanov 	 */
375cc271ab8STetsuo Handa 	queue_work(wwan_wq, &port->del_work);
3769ee23f48SSergey Ryazanov 
3779ee23f48SSergey Ryazanov 	return count;
3789ee23f48SSergey Ryazanov }
3799ee23f48SSergey Ryazanov 
3809ee23f48SSergey Ryazanov static const struct file_operations wwan_hwsim_debugfs_portdestroy_fops = {
3819ee23f48SSergey Ryazanov 	.write = wwan_hwsim_debugfs_portdestroy_write,
3829ee23f48SSergey Ryazanov 	.open = simple_open,
3839ee23f48SSergey Ryazanov 	.llseek = noop_llseek,
3849ee23f48SSergey Ryazanov };
3859ee23f48SSergey Ryazanov 
wwan_hwsim_debugfs_portcreate_write(struct file * file,const char __user * usrbuf,size_t count,loff_t * ppos)3869ee23f48SSergey Ryazanov static ssize_t wwan_hwsim_debugfs_portcreate_write(struct file *file,
3879ee23f48SSergey Ryazanov 						   const char __user *usrbuf,
3889ee23f48SSergey Ryazanov 						   size_t count, loff_t *ppos)
3899ee23f48SSergey Ryazanov {
3909ee23f48SSergey Ryazanov 	struct wwan_hwsim_dev *dev = file->private_data;
3919ee23f48SSergey Ryazanov 	struct wwan_hwsim_port *port;
3929ee23f48SSergey Ryazanov 
3939ee23f48SSergey Ryazanov 	port = wwan_hwsim_port_new(dev);
3949ee23f48SSergey Ryazanov 	if (IS_ERR(port))
3959ee23f48SSergey Ryazanov 		return PTR_ERR(port);
3969ee23f48SSergey Ryazanov 
3979ee23f48SSergey Ryazanov 	spin_lock(&dev->ports_lock);
3989ee23f48SSergey Ryazanov 	list_add_tail(&port->list, &dev->ports);
3999ee23f48SSergey Ryazanov 	spin_unlock(&dev->ports_lock);
4009ee23f48SSergey Ryazanov 
4019ee23f48SSergey Ryazanov 	return count;
4029ee23f48SSergey Ryazanov }
4039ee23f48SSergey Ryazanov 
4049ee23f48SSergey Ryazanov static const struct file_operations wwan_hwsim_debugfs_portcreate_fops = {
4059ee23f48SSergey Ryazanov 	.write = wwan_hwsim_debugfs_portcreate_write,
4069ee23f48SSergey Ryazanov 	.open = simple_open,
4079ee23f48SSergey Ryazanov 	.llseek = noop_llseek,
4089ee23f48SSergey Ryazanov };
4099ee23f48SSergey Ryazanov 
wwan_hwsim_debugfs_devdestroy_write(struct file * file,const char __user * usrbuf,size_t count,loff_t * ppos)4109ee23f48SSergey Ryazanov static ssize_t wwan_hwsim_debugfs_devdestroy_write(struct file *file,
4119ee23f48SSergey Ryazanov 						   const char __user *usrbuf,
4129ee23f48SSergey Ryazanov 						   size_t count, loff_t *ppos)
4139ee23f48SSergey Ryazanov {
4149ee23f48SSergey Ryazanov 	struct wwan_hwsim_dev *dev = file->private_data;
4159ee23f48SSergey Ryazanov 
4169ee23f48SSergey Ryazanov 	/* We can not delete device here since it will cause a deadlock due to
4179ee23f48SSergey Ryazanov 	 * waiting this callback to finish in the debugfs_remove() call. So,
4189ee23f48SSergey Ryazanov 	 * use workqueue.
4199ee23f48SSergey Ryazanov 	 */
420cc271ab8STetsuo Handa 	queue_work(wwan_wq, &dev->del_work);
4219ee23f48SSergey Ryazanov 
4229ee23f48SSergey Ryazanov 	return count;
4239ee23f48SSergey Ryazanov }
4249ee23f48SSergey Ryazanov 
4259ee23f48SSergey Ryazanov static const struct file_operations wwan_hwsim_debugfs_devdestroy_fops = {
4269ee23f48SSergey Ryazanov 	.write = wwan_hwsim_debugfs_devdestroy_write,
4279ee23f48SSergey Ryazanov 	.open = simple_open,
4289ee23f48SSergey Ryazanov 	.llseek = noop_llseek,
4299ee23f48SSergey Ryazanov };
4309ee23f48SSergey Ryazanov 
wwan_hwsim_debugfs_devcreate_write(struct file * file,const char __user * usrbuf,size_t count,loff_t * ppos)4319ee23f48SSergey Ryazanov static ssize_t wwan_hwsim_debugfs_devcreate_write(struct file *file,
4329ee23f48SSergey Ryazanov 						  const char __user *usrbuf,
4339ee23f48SSergey Ryazanov 						  size_t count, loff_t *ppos)
4349ee23f48SSergey Ryazanov {
4359ee23f48SSergey Ryazanov 	struct wwan_hwsim_dev *dev;
4369ee23f48SSergey Ryazanov 
4379ee23f48SSergey Ryazanov 	dev = wwan_hwsim_dev_new();
4389ee23f48SSergey Ryazanov 	if (IS_ERR(dev))
4399ee23f48SSergey Ryazanov 		return PTR_ERR(dev);
4409ee23f48SSergey Ryazanov 
4419ee23f48SSergey Ryazanov 	spin_lock(&wwan_hwsim_devs_lock);
4429ee23f48SSergey Ryazanov 	list_add_tail(&dev->list, &wwan_hwsim_devs);
4439ee23f48SSergey Ryazanov 	spin_unlock(&wwan_hwsim_devs_lock);
4449ee23f48SSergey Ryazanov 
4459ee23f48SSergey Ryazanov 	return count;
4469ee23f48SSergey Ryazanov }
4479ee23f48SSergey Ryazanov 
4489ee23f48SSergey Ryazanov static const struct file_operations wwan_hwsim_debugfs_devcreate_fops = {
4499ee23f48SSergey Ryazanov 	.write = wwan_hwsim_debugfs_devcreate_write,
4509ee23f48SSergey Ryazanov 	.open = simple_open,
4519ee23f48SSergey Ryazanov 	.llseek = noop_llseek,
4529ee23f48SSergey Ryazanov };
4539ee23f48SSergey Ryazanov 
wwan_hwsim_init_devs(void)454f36a111aSSergey Ryazanov static int __init wwan_hwsim_init_devs(void)
455f36a111aSSergey Ryazanov {
456f36a111aSSergey Ryazanov 	struct wwan_hwsim_dev *dev;
457f36a111aSSergey Ryazanov 	int i, j;
458f36a111aSSergey Ryazanov 
459f36a111aSSergey Ryazanov 	for (i = 0; i < wwan_hwsim_devsnum; ++i) {
460f36a111aSSergey Ryazanov 		dev = wwan_hwsim_dev_new();
461f36a111aSSergey Ryazanov 		if (IS_ERR(dev))
462f36a111aSSergey Ryazanov 			return PTR_ERR(dev);
463f36a111aSSergey Ryazanov 
464f36a111aSSergey Ryazanov 		spin_lock(&wwan_hwsim_devs_lock);
465f36a111aSSergey Ryazanov 		list_add_tail(&dev->list, &wwan_hwsim_devs);
466f36a111aSSergey Ryazanov 		spin_unlock(&wwan_hwsim_devs_lock);
467f36a111aSSergey Ryazanov 
468f36a111aSSergey Ryazanov 		/* Create a couple of ports per each device to accelerate
469f36a111aSSergey Ryazanov 		 * the simulator readiness time.
470f36a111aSSergey Ryazanov 		 */
471f36a111aSSergey Ryazanov 		for (j = 0; j < 2; ++j) {
472f36a111aSSergey Ryazanov 			struct wwan_hwsim_port *port;
473f36a111aSSergey Ryazanov 
474f36a111aSSergey Ryazanov 			port = wwan_hwsim_port_new(dev);
475f36a111aSSergey Ryazanov 			if (IS_ERR(port))
476f36a111aSSergey Ryazanov 				return PTR_ERR(port);
477f36a111aSSergey Ryazanov 
478f36a111aSSergey Ryazanov 			spin_lock(&dev->ports_lock);
479f36a111aSSergey Ryazanov 			list_add_tail(&port->list, &dev->ports);
480f36a111aSSergey Ryazanov 			spin_unlock(&dev->ports_lock);
481f36a111aSSergey Ryazanov 		}
482f36a111aSSergey Ryazanov 	}
483f36a111aSSergey Ryazanov 
484f36a111aSSergey Ryazanov 	return 0;
485f36a111aSSergey Ryazanov }
486f36a111aSSergey Ryazanov 
wwan_hwsim_free_devs(void)487f36a111aSSergey Ryazanov static void wwan_hwsim_free_devs(void)
488f36a111aSSergey Ryazanov {
489f36a111aSSergey Ryazanov 	struct wwan_hwsim_dev *dev;
490f36a111aSSergey Ryazanov 
491f36a111aSSergey Ryazanov 	spin_lock(&wwan_hwsim_devs_lock);
492f36a111aSSergey Ryazanov 	while (!list_empty(&wwan_hwsim_devs)) {
493f36a111aSSergey Ryazanov 		dev = list_first_entry(&wwan_hwsim_devs, struct wwan_hwsim_dev,
494f36a111aSSergey Ryazanov 				       list);
4959ee23f48SSergey Ryazanov 		list_del_init(&dev->list);
496f36a111aSSergey Ryazanov 		spin_unlock(&wwan_hwsim_devs_lock);
497f36a111aSSergey Ryazanov 		wwan_hwsim_dev_del(dev);
498f36a111aSSergey Ryazanov 		spin_lock(&wwan_hwsim_devs_lock);
499f36a111aSSergey Ryazanov 	}
500f36a111aSSergey Ryazanov 	spin_unlock(&wwan_hwsim_devs_lock);
501f36a111aSSergey Ryazanov }
502f36a111aSSergey Ryazanov 
wwan_hwsim_init(void)503f36a111aSSergey Ryazanov static int __init wwan_hwsim_init(void)
504f36a111aSSergey Ryazanov {
505f36a111aSSergey Ryazanov 	int err;
506f36a111aSSergey Ryazanov 
507f36a111aSSergey Ryazanov 	if (wwan_hwsim_devsnum < 0 || wwan_hwsim_devsnum > 128)
508f36a111aSSergey Ryazanov 		return -EINVAL;
509f36a111aSSergey Ryazanov 
510cc271ab8STetsuo Handa 	wwan_wq = alloc_workqueue("wwan_wq", 0, 0);
511cc271ab8STetsuo Handa 	if (!wwan_wq)
512cc271ab8STetsuo Handa 		return -ENOMEM;
513cc271ab8STetsuo Handa 
5141aaba11dSGreg Kroah-Hartman 	wwan_hwsim_class = class_create("wwan_hwsim");
515cc271ab8STetsuo Handa 	if (IS_ERR(wwan_hwsim_class)) {
516cc271ab8STetsuo Handa 		err = PTR_ERR(wwan_hwsim_class);
517cc271ab8STetsuo Handa 		goto err_wq_destroy;
518cc271ab8STetsuo Handa 	}
519f36a111aSSergey Ryazanov 
5209ee23f48SSergey Ryazanov 	wwan_hwsim_debugfs_topdir = debugfs_create_dir("wwan_hwsim", NULL);
5219ee23f48SSergey Ryazanov 	wwan_hwsim_debugfs_devcreate =
5229ee23f48SSergey Ryazanov 			debugfs_create_file("devcreate", 0200,
5239ee23f48SSergey Ryazanov 					    wwan_hwsim_debugfs_topdir, NULL,
5249ee23f48SSergey Ryazanov 					    &wwan_hwsim_debugfs_devcreate_fops);
5259ee23f48SSergey Ryazanov 
526f36a111aSSergey Ryazanov 	err = wwan_hwsim_init_devs();
527f36a111aSSergey Ryazanov 	if (err)
528f36a111aSSergey Ryazanov 		goto err_clean_devs;
529f36a111aSSergey Ryazanov 
530f36a111aSSergey Ryazanov 	return 0;
531f36a111aSSergey Ryazanov 
532f36a111aSSergey Ryazanov err_clean_devs:
533cc271ab8STetsuo Handa 	debugfs_remove(wwan_hwsim_debugfs_devcreate);	/* Avoid new devs */
534f36a111aSSergey Ryazanov 	wwan_hwsim_free_devs();
535cc271ab8STetsuo Handa 	flush_workqueue(wwan_wq);	/* Wait deletion works completion */
5369ee23f48SSergey Ryazanov 	debugfs_remove(wwan_hwsim_debugfs_topdir);
537f36a111aSSergey Ryazanov 	class_destroy(wwan_hwsim_class);
538cc271ab8STetsuo Handa err_wq_destroy:
539cc271ab8STetsuo Handa 	destroy_workqueue(wwan_wq);
540f36a111aSSergey Ryazanov 
541f36a111aSSergey Ryazanov 	return err;
542f36a111aSSergey Ryazanov }
543f36a111aSSergey Ryazanov 
wwan_hwsim_exit(void)544f36a111aSSergey Ryazanov static void __exit wwan_hwsim_exit(void)
545f36a111aSSergey Ryazanov {
5469ee23f48SSergey Ryazanov 	debugfs_remove(wwan_hwsim_debugfs_devcreate);	/* Avoid new devs */
547f36a111aSSergey Ryazanov 	wwan_hwsim_free_devs();
548cc271ab8STetsuo Handa 	flush_workqueue(wwan_wq);	/* Wait deletion works completion */
5499ee23f48SSergey Ryazanov 	debugfs_remove(wwan_hwsim_debugfs_topdir);
550f36a111aSSergey Ryazanov 	class_destroy(wwan_hwsim_class);
551cc271ab8STetsuo Handa 	destroy_workqueue(wwan_wq);
552f36a111aSSergey Ryazanov }
553f36a111aSSergey Ryazanov 
554f36a111aSSergey Ryazanov module_init(wwan_hwsim_init);
555f36a111aSSergey Ryazanov module_exit(wwan_hwsim_exit);
556f36a111aSSergey Ryazanov 
557f36a111aSSergey Ryazanov MODULE_AUTHOR("Sergey Ryazanov");
558f36a111aSSergey Ryazanov MODULE_DESCRIPTION("Device simulator for WWAN framework");
559f36a111aSSergey Ryazanov MODULE_LICENSE("GPL");
560