Skip to content

Commit c6adf77

Browse files
dnlplmdavem330
authored andcommitted
net: usb: qmi_wwan: add qmap mux protocol support
This patch adds support for qmap mux protocol available in recent Qualcomm based modems. The qmap mux protocol can be used for multiplexing data packets in order to have multiple ip streams through the same physical device. Two new sysfs files are added for adding/removing the qmap mux based interfaces (named qmimux): - /sys/class/net/<iface>/qmi/add_mux - /sys/class/net/<iface>/qmi/del_mux Main patch author is Bjørn Mork <[email protected]> Signed-off-by: Bjørn Mork <[email protected]> Signed-off-by: Daniele Palmas <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 1312444 commit c6adf77

File tree

1 file changed

+316
-1
lines changed

1 file changed

+316
-1
lines changed

drivers/net/usb/qmi_wwan.c

Lines changed: 316 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,198 @@ struct qmi_wwan_state {
5858

5959
enum qmi_wwan_flags {
6060
QMI_WWAN_FLAG_RAWIP = 1 << 0,
61+
QMI_WWAN_FLAG_MUX = 1 << 1,
6162
};
6263

6364
enum qmi_wwan_quirks {
6465
QMI_WWAN_QUIRK_DTR = 1 << 0, /* needs "set DTR" request */
6566
};
6667

68+
struct qmimux_hdr {
69+
u8 pad;
70+
u8 mux_id;
71+
__be16 pkt_len;
72+
};
73+
74+
struct qmimux_priv {
75+
struct net_device *real_dev;
76+
u8 mux_id;
77+
};
78+
79+
static int qmimux_open(struct net_device *dev)
80+
{
81+
struct qmimux_priv *priv = netdev_priv(dev);
82+
struct net_device *real_dev = priv->real_dev;
83+
84+
if (!(priv->real_dev->flags & IFF_UP))
85+
return -ENETDOWN;
86+
87+
if (netif_carrier_ok(real_dev))
88+
netif_carrier_on(dev);
89+
return 0;
90+
}
91+
92+
static int qmimux_stop(struct net_device *dev)
93+
{
94+
netif_carrier_off(dev);
95+
return 0;
96+
}
97+
98+
static netdev_tx_t qmimux_start_xmit(struct sk_buff *skb, struct net_device *dev)
99+
{
100+
struct qmimux_priv *priv = netdev_priv(dev);
101+
unsigned int len = skb->len;
102+
struct qmimux_hdr *hdr;
103+
104+
hdr = (struct qmimux_hdr *)skb_push(skb, sizeof(struct qmimux_hdr));
105+
hdr->pad = 0;
106+
hdr->mux_id = priv->mux_id;
107+
hdr->pkt_len = cpu_to_be16(len);
108+
skb->dev = priv->real_dev;
109+
return dev_queue_xmit(skb);
110+
}
111+
112+
static const struct net_device_ops qmimux_netdev_ops = {
113+
.ndo_open = qmimux_open,
114+
.ndo_stop = qmimux_stop,
115+
.ndo_start_xmit = qmimux_start_xmit,
116+
};
117+
118+
static void qmimux_setup(struct net_device *dev)
119+
{
120+
dev->header_ops = NULL; /* No header */
121+
dev->type = ARPHRD_NONE;
122+
dev->hard_header_len = 0;
123+
dev->addr_len = 0;
124+
dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
125+
dev->netdev_ops = &qmimux_netdev_ops;
126+
dev->destructor = free_netdev;
127+
}
128+
129+
static struct net_device *qmimux_find_dev(struct usbnet *dev, u8 mux_id)
130+
{
131+
struct qmimux_priv *priv;
132+
struct list_head *iter;
133+
struct net_device *ldev;
134+
135+
rcu_read_lock();
136+
netdev_for_each_upper_dev_rcu(dev->net, ldev, iter) {
137+
priv = netdev_priv(ldev);
138+
if (priv->mux_id == mux_id) {
139+
rcu_read_unlock();
140+
return ldev;
141+
}
142+
}
143+
rcu_read_unlock();
144+
return NULL;
145+
}
146+
147+
static bool qmimux_has_slaves(struct usbnet *dev)
148+
{
149+
return !list_empty(&dev->net->adj_list.upper);
150+
}
151+
152+
static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
153+
{
154+
unsigned int len, offset = sizeof(struct qmimux_hdr);
155+
struct qmimux_hdr *hdr;
156+
struct net_device *net;
157+
struct sk_buff *skbn;
158+
159+
while (offset < skb->len) {
160+
hdr = (struct qmimux_hdr *)skb->data;
161+
len = be16_to_cpu(hdr->pkt_len);
162+
163+
/* drop the packet, bogus length */
164+
if (offset + len > skb->len)
165+
return 0;
166+
167+
/* control packet, we do not know what to do */
168+
if (hdr->pad & 0x80)
169+
goto skip;
170+
171+
net = qmimux_find_dev(dev, hdr->mux_id);
172+
if (!net)
173+
goto skip;
174+
skbn = netdev_alloc_skb(net, len);
175+
if (!skbn)
176+
return 0;
177+
skbn->dev = net;
178+
179+
switch (skb->data[offset] & 0xf0) {
180+
case 0x40:
181+
skbn->protocol = htons(ETH_P_IP);
182+
break;
183+
case 0x60:
184+
skbn->protocol = htons(ETH_P_IPV6);
185+
break;
186+
default:
187+
/* not ip - do not know what to do */
188+
goto skip;
189+
}
190+
191+
memcpy(skb_put(skbn, len), skb->data + offset, len);
192+
if (netif_rx(skbn) != NET_RX_SUCCESS)
193+
return 0;
194+
195+
skip:
196+
offset += len + sizeof(struct qmimux_hdr);
197+
}
198+
return 1;
199+
}
200+
201+
static int qmimux_register_device(struct net_device *real_dev, u8 mux_id)
202+
{
203+
struct net_device *new_dev;
204+
struct qmimux_priv *priv;
205+
int err;
206+
207+
new_dev = alloc_netdev(sizeof(struct qmimux_priv),
208+
"qmimux%d", NET_NAME_UNKNOWN, qmimux_setup);
209+
if (!new_dev)
210+
return -ENOBUFS;
211+
212+
dev_net_set(new_dev, dev_net(real_dev));
213+
priv = netdev_priv(new_dev);
214+
priv->mux_id = mux_id;
215+
priv->real_dev = real_dev;
216+
217+
err = register_netdevice(new_dev);
218+
if (err < 0)
219+
goto out_free_newdev;
220+
221+
/* Account for reference in struct qmimux_priv_priv */
222+
dev_hold(real_dev);
223+
224+
err = netdev_upper_dev_link(real_dev, new_dev);
225+
if (err)
226+
goto out_unregister_netdev;
227+
228+
netif_stacked_transfer_operstate(real_dev, new_dev);
229+
230+
return 0;
231+
232+
out_unregister_netdev:
233+
unregister_netdevice(new_dev);
234+
dev_put(real_dev);
235+
236+
out_free_newdev:
237+
free_netdev(new_dev);
238+
return err;
239+
}
240+
241+
static void qmimux_unregister_device(struct net_device *dev)
242+
{
243+
struct qmimux_priv *priv = netdev_priv(dev);
244+
struct net_device *real_dev = priv->real_dev;
245+
246+
netdev_upper_dev_unlink(real_dev, dev);
247+
unregister_netdevice(dev);
248+
249+
/* Get rid of the reference to real_dev */
250+
dev_put(real_dev);
251+
}
252+
67253
static void qmi_wwan_netdev_setup(struct net_device *net)
68254
{
69255
struct usbnet *dev = netdev_priv(net);
@@ -137,10 +323,114 @@ static ssize_t raw_ip_store(struct device *d, struct device_attribute *attr, co
137323
return ret;
138324
}
139325

326+
static ssize_t add_mux_show(struct device *d, struct device_attribute *attr, char *buf)
327+
{
328+
struct net_device *dev = to_net_dev(d);
329+
struct qmimux_priv *priv;
330+
struct list_head *iter;
331+
struct net_device *ldev;
332+
ssize_t count = 0;
333+
334+
rcu_read_lock();
335+
netdev_for_each_upper_dev_rcu(dev, ldev, iter) {
336+
priv = netdev_priv(ldev);
337+
count += scnprintf(&buf[count], PAGE_SIZE - count,
338+
"0x%02x\n", priv->mux_id);
339+
}
340+
rcu_read_unlock();
341+
return count;
342+
}
343+
344+
static ssize_t add_mux_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len)
345+
{
346+
struct usbnet *dev = netdev_priv(to_net_dev(d));
347+
struct qmi_wwan_state *info = (void *)&dev->data;
348+
u8 mux_id;
349+
int ret;
350+
351+
if (kstrtou8(buf, 0, &mux_id))
352+
return -EINVAL;
353+
354+
/* mux_id [1 - 0x7f] range empirically found */
355+
if (mux_id < 1 || mux_id > 0x7f)
356+
return -EINVAL;
357+
358+
if (!rtnl_trylock())
359+
return restart_syscall();
360+
361+
if (qmimux_find_dev(dev, mux_id)) {
362+
netdev_err(dev->net, "mux_id already present\n");
363+
ret = -EINVAL;
364+
goto err;
365+
}
366+
367+
/* we don't want to modify a running netdev */
368+
if (netif_running(dev->net)) {
369+
netdev_err(dev->net, "Cannot change a running device\n");
370+
ret = -EBUSY;
371+
goto err;
372+
}
373+
374+
ret = qmimux_register_device(dev->net, mux_id);
375+
if (!ret) {
376+
info->flags |= QMI_WWAN_FLAG_MUX;
377+
ret = len;
378+
}
379+
err:
380+
rtnl_unlock();
381+
return ret;
382+
}
383+
384+
static ssize_t del_mux_show(struct device *d, struct device_attribute *attr, char *buf)
385+
{
386+
return add_mux_show(d, attr, buf);
387+
}
388+
389+
static ssize_t del_mux_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len)
390+
{
391+
struct usbnet *dev = netdev_priv(to_net_dev(d));
392+
struct qmi_wwan_state *info = (void *)&dev->data;
393+
struct net_device *del_dev;
394+
u8 mux_id;
395+
int ret = 0;
396+
397+
if (kstrtou8(buf, 0, &mux_id))
398+
return -EINVAL;
399+
400+
if (!rtnl_trylock())
401+
return restart_syscall();
402+
403+
/* we don't want to modify a running netdev */
404+
if (netif_running(dev->net)) {
405+
netdev_err(dev->net, "Cannot change a running device\n");
406+
ret = -EBUSY;
407+
goto err;
408+
}
409+
410+
del_dev = qmimux_find_dev(dev, mux_id);
411+
if (!del_dev) {
412+
netdev_err(dev->net, "mux_id not present\n");
413+
ret = -EINVAL;
414+
goto err;
415+
}
416+
qmimux_unregister_device(del_dev);
417+
418+
if (!qmimux_has_slaves(dev))
419+
info->flags &= ~QMI_WWAN_FLAG_MUX;
420+
ret = len;
421+
err:
422+
rtnl_unlock();
423+
return ret;
424+
}
425+
140426
static DEVICE_ATTR_RW(raw_ip);
427+
static DEVICE_ATTR_RW(add_mux);
428+
static DEVICE_ATTR_RW(del_mux);
141429

142430
static struct attribute *qmi_wwan_sysfs_attrs[] = {
143431
&dev_attr_raw_ip.attr,
432+
&dev_attr_add_mux.attr,
433+
&dev_attr_del_mux.attr,
144434
NULL,
145435
};
146436

@@ -184,6 +474,9 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
184474
if (skb->len < dev->net->hard_header_len)
185475
return 0;
186476

477+
if (info->flags & QMI_WWAN_FLAG_MUX)
478+
return qmimux_rx_fixup(dev, skb);
479+
187480
switch (skb->data[0] & 0xf0) {
188481
case 0x40:
189482
proto = htons(ETH_P_IP);
@@ -1036,11 +1329,33 @@ static int qmi_wwan_probe(struct usb_interface *intf,
10361329
return usbnet_probe(intf, id);
10371330
}
10381331

1332+
static void qmi_wwan_disconnect(struct usb_interface *intf)
1333+
{
1334+
struct usbnet *dev = usb_get_intfdata(intf);
1335+
struct qmi_wwan_state *info = (void *)&dev->data;
1336+
struct list_head *iter;
1337+
struct net_device *ldev;
1338+
1339+
if (info->flags & QMI_WWAN_FLAG_MUX) {
1340+
if (!rtnl_trylock()) {
1341+
restart_syscall();
1342+
return;
1343+
}
1344+
rcu_read_lock();
1345+
netdev_for_each_upper_dev_rcu(dev->net, ldev, iter)
1346+
qmimux_unregister_device(ldev);
1347+
rcu_read_unlock();
1348+
rtnl_unlock();
1349+
info->flags &= ~QMI_WWAN_FLAG_MUX;
1350+
}
1351+
usbnet_disconnect(intf);
1352+
}
1353+
10391354
static struct usb_driver qmi_wwan_driver = {
10401355
.name = "qmi_wwan",
10411356
.id_table = products,
10421357
.probe = qmi_wwan_probe,
1043-
.disconnect = usbnet_disconnect,
1358+
.disconnect = qmi_wwan_disconnect,
10441359
.suspend = qmi_wwan_suspend,
10451360
.resume = qmi_wwan_resume,
10461361
.reset_resume = qmi_wwan_resume,

0 commit comments

Comments
 (0)