197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29dcaec04SSubash Abhinov Kasiviswanathan /* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
3ceed73a2SSubash Abhinov Kasiviswanathan */
4ceed73a2SSubash Abhinov Kasiviswanathan
5ceed73a2SSubash Abhinov Kasiviswanathan #include <linux/netdevice.h>
6ceed73a2SSubash Abhinov Kasiviswanathan #include "rmnet_config.h"
7ceed73a2SSubash Abhinov Kasiviswanathan #include "rmnet_map.h"
8ceed73a2SSubash Abhinov Kasiviswanathan #include "rmnet_private.h"
9ceed73a2SSubash Abhinov Kasiviswanathan #include "rmnet_vnd.h"
10ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_map_do_flow_control(struct sk_buff * skb,struct rmnet_port * port,int enable)11ceed73a2SSubash Abhinov Kasiviswanathan static u8 rmnet_map_do_flow_control(struct sk_buff *skb,
123352e6c4SSubash Abhinov Kasiviswanathan struct rmnet_port *port,
13ceed73a2SSubash Abhinov Kasiviswanathan int enable)
14ceed73a2SSubash Abhinov Kasiviswanathan {
15*9d131d04SAlex Elder struct rmnet_map_header *map_header = (void *)skb->data;
16ceed73a2SSubash Abhinov Kasiviswanathan struct rmnet_endpoint *ep;
17ceed73a2SSubash Abhinov Kasiviswanathan struct net_device *vnd;
18ceed73a2SSubash Abhinov Kasiviswanathan u8 mux_id;
19ceed73a2SSubash Abhinov Kasiviswanathan int r;
20ceed73a2SSubash Abhinov Kasiviswanathan
21*9d131d04SAlex Elder mux_id = map_header->mux_id;
22ceed73a2SSubash Abhinov Kasiviswanathan
23ceed73a2SSubash Abhinov Kasiviswanathan if (mux_id >= RMNET_MAX_LOGICAL_EP) {
24ceed73a2SSubash Abhinov Kasiviswanathan kfree_skb(skb);
25ceed73a2SSubash Abhinov Kasiviswanathan return RX_HANDLER_CONSUMED;
26ceed73a2SSubash Abhinov Kasiviswanathan }
27ceed73a2SSubash Abhinov Kasiviswanathan
283352e6c4SSubash Abhinov Kasiviswanathan ep = rmnet_get_endpoint(port, mux_id);
29f57bbaaeSSubash Abhinov Kasiviswanathan if (!ep) {
30f57bbaaeSSubash Abhinov Kasiviswanathan kfree_skb(skb);
31f57bbaaeSSubash Abhinov Kasiviswanathan return RX_HANDLER_CONSUMED;
32f57bbaaeSSubash Abhinov Kasiviswanathan }
33f57bbaaeSSubash Abhinov Kasiviswanathan
34ceed73a2SSubash Abhinov Kasiviswanathan vnd = ep->egress_dev;
35ceed73a2SSubash Abhinov Kasiviswanathan
36ceed73a2SSubash Abhinov Kasiviswanathan /* Ignore the ip family and pass the sequence number for both v4 and v6
37ceed73a2SSubash Abhinov Kasiviswanathan * sequence. User space does not support creating dedicated flows for
38ceed73a2SSubash Abhinov Kasiviswanathan * the 2 protocols
39ceed73a2SSubash Abhinov Kasiviswanathan */
40ceed73a2SSubash Abhinov Kasiviswanathan r = rmnet_vnd_do_flow_control(vnd, enable);
41ceed73a2SSubash Abhinov Kasiviswanathan if (r) {
42ceed73a2SSubash Abhinov Kasiviswanathan kfree_skb(skb);
43ceed73a2SSubash Abhinov Kasiviswanathan return RMNET_MAP_COMMAND_UNSUPPORTED;
44ceed73a2SSubash Abhinov Kasiviswanathan } else {
45ceed73a2SSubash Abhinov Kasiviswanathan return RMNET_MAP_COMMAND_ACK;
46ceed73a2SSubash Abhinov Kasiviswanathan }
47ceed73a2SSubash Abhinov Kasiviswanathan }
48ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_map_send_ack(struct sk_buff * skb,unsigned char type,struct rmnet_port * port)49ceed73a2SSubash Abhinov Kasiviswanathan static void rmnet_map_send_ack(struct sk_buff *skb,
5023c76eb7SSubash Abhinov Kasiviswanathan unsigned char type,
5123c76eb7SSubash Abhinov Kasiviswanathan struct rmnet_port *port)
52ceed73a2SSubash Abhinov Kasiviswanathan {
53*9d131d04SAlex Elder struct rmnet_map_header *map_header = (void *)skb->data;
54ceed73a2SSubash Abhinov Kasiviswanathan struct rmnet_map_control_command *cmd;
553602207cSSubash Abhinov Kasiviswanathan struct net_device *dev = skb->dev;
56ceed73a2SSubash Abhinov Kasiviswanathan
57721ce0f6SSubash Abhinov Kasiviswanathan if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4)
58721ce0f6SSubash Abhinov Kasiviswanathan skb_trim(skb,
59721ce0f6SSubash Abhinov Kasiviswanathan skb->len - sizeof(struct rmnet_map_dl_csum_trailer));
6023c76eb7SSubash Abhinov Kasiviswanathan
61ceed73a2SSubash Abhinov Kasiviswanathan skb->protocol = htons(ETH_P_MAP);
62ceed73a2SSubash Abhinov Kasiviswanathan
63*9d131d04SAlex Elder /* Command data immediately follows the MAP header */
64*9d131d04SAlex Elder cmd = (struct rmnet_map_control_command *)(map_header + 1);
65ceed73a2SSubash Abhinov Kasiviswanathan cmd->cmd_type = type & 0x03;
66ceed73a2SSubash Abhinov Kasiviswanathan
673602207cSSubash Abhinov Kasiviswanathan netif_tx_lock(dev);
683602207cSSubash Abhinov Kasiviswanathan dev->netdev_ops->ndo_start_xmit(skb, dev);
693602207cSSubash Abhinov Kasiviswanathan netif_tx_unlock(dev);
70ceed73a2SSubash Abhinov Kasiviswanathan }
71ceed73a2SSubash Abhinov Kasiviswanathan
72ceed73a2SSubash Abhinov Kasiviswanathan /* Process MAP command frame and send N/ACK message as appropriate. Message cmd
73ceed73a2SSubash Abhinov Kasiviswanathan * name is decoded here and appropriate handler is called.
74ceed73a2SSubash Abhinov Kasiviswanathan */
rmnet_map_command(struct sk_buff * skb,struct rmnet_port * port)752ffbbf0fSSubash Abhinov Kasiviswanathan void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port)
76ceed73a2SSubash Abhinov Kasiviswanathan {
77*9d131d04SAlex Elder struct rmnet_map_header *map_header = (void *)skb->data;
78ceed73a2SSubash Abhinov Kasiviswanathan struct rmnet_map_control_command *cmd;
79ceed73a2SSubash Abhinov Kasiviswanathan unsigned char command_name;
80ceed73a2SSubash Abhinov Kasiviswanathan unsigned char rc = 0;
81ceed73a2SSubash Abhinov Kasiviswanathan
82*9d131d04SAlex Elder /* Command data immediately follows the MAP header */
83*9d131d04SAlex Elder cmd = (struct rmnet_map_control_command *)(map_header + 1);
84ceed73a2SSubash Abhinov Kasiviswanathan command_name = cmd->command_name;
85ceed73a2SSubash Abhinov Kasiviswanathan
86ceed73a2SSubash Abhinov Kasiviswanathan switch (command_name) {
87ceed73a2SSubash Abhinov Kasiviswanathan case RMNET_MAP_COMMAND_FLOW_ENABLE:
88b665f4f8SSubash Abhinov Kasiviswanathan rc = rmnet_map_do_flow_control(skb, port, 1);
89ceed73a2SSubash Abhinov Kasiviswanathan break;
90ceed73a2SSubash Abhinov Kasiviswanathan
91ceed73a2SSubash Abhinov Kasiviswanathan case RMNET_MAP_COMMAND_FLOW_DISABLE:
92b665f4f8SSubash Abhinov Kasiviswanathan rc = rmnet_map_do_flow_control(skb, port, 0);
93ceed73a2SSubash Abhinov Kasiviswanathan break;
94ceed73a2SSubash Abhinov Kasiviswanathan
95ceed73a2SSubash Abhinov Kasiviswanathan default:
96ceed73a2SSubash Abhinov Kasiviswanathan rc = RMNET_MAP_COMMAND_UNSUPPORTED;
97ceed73a2SSubash Abhinov Kasiviswanathan kfree_skb(skb);
98ceed73a2SSubash Abhinov Kasiviswanathan break;
99ceed73a2SSubash Abhinov Kasiviswanathan }
100ceed73a2SSubash Abhinov Kasiviswanathan if (rc == RMNET_MAP_COMMAND_ACK)
10123c76eb7SSubash Abhinov Kasiviswanathan rmnet_map_send_ack(skb, rc, port);
102ceed73a2SSubash Abhinov Kasiviswanathan }
103