xref: /openbmc/linux/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
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