1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
4  * All rights reserved.
5  *
6  * Purpose: Handles the management command interface functions
7  *
8  * Author: Lyndon Chen
9  *
10  * Date: May 8, 2003
11  *
12  * Functions:
13  *	vnt_cmd_complete - Command Complete function
14  *	vnt_schedule_command - Push Command and wait Command Scheduler to do
15  *	vnt_cmd_timer_wait- Call back timer
16  *
17  * Revision History:
18  *
19  */
20 
21 #include "device.h"
22 #include "mac.h"
23 #include "wcmd.h"
24 #include "power.h"
25 #include "usbpipe.h"
26 #include "rxtx.h"
27 #include "rf.h"
28 
vnt_cmd_timer_wait(struct vnt_private * priv,unsigned long msecs)29 static void vnt_cmd_timer_wait(struct vnt_private *priv, unsigned long msecs)
30 {
31 	schedule_delayed_work(&priv->run_command_work, msecs_to_jiffies(msecs));
32 }
33 
add_one_with_wrap_around(u32 var,u8 modulo)34 static u32 add_one_with_wrap_around(u32 var, u8 modulo)
35 {
36 	if (var >= (modulo - 1))
37 		var = 0;
38 	else
39 		var++;
40 	return var;
41 }
42 
vnt_cmd_complete(struct vnt_private * priv)43 static int vnt_cmd_complete(struct vnt_private *priv)
44 {
45 	priv->command_state = WLAN_CMD_IDLE;
46 	if (priv->free_cmd_queue == CMD_Q_SIZE) {
47 		/* Command Queue Empty */
48 		priv->cmd_running = false;
49 		return true;
50 	}
51 
52 	priv->command = priv->cmd_queue[priv->cmd_dequeue_idx];
53 
54 	priv->cmd_dequeue_idx = add_one_with_wrap_around(priv->cmd_dequeue_idx, CMD_Q_SIZE);
55 	priv->free_cmd_queue++;
56 	priv->cmd_running = true;
57 
58 	switch (priv->command) {
59 	case WLAN_CMD_INIT_MAC80211:
60 		priv->command_state = WLAN_CMD_INIT_MAC80211_START;
61 		break;
62 
63 	case WLAN_CMD_TBTT_WAKEUP:
64 		priv->command_state = WLAN_CMD_TBTT_WAKEUP_START;
65 		break;
66 
67 	case WLAN_CMD_BECON_SEND:
68 		priv->command_state = WLAN_CMD_BECON_SEND_START;
69 		break;
70 
71 	case WLAN_CMD_SETPOWER:
72 		priv->command_state = WLAN_CMD_SETPOWER_START;
73 		break;
74 
75 	case WLAN_CMD_CHANGE_ANTENNA:
76 		priv->command_state = WLAN_CMD_CHANGE_ANTENNA_START;
77 		break;
78 
79 	default:
80 		break;
81 	}
82 
83 	vnt_cmd_timer_wait(priv, 0);
84 
85 	return true;
86 }
87 
vnt_run_command(struct work_struct * work)88 void vnt_run_command(struct work_struct *work)
89 {
90 	struct vnt_private *priv =
91 		container_of(work, struct vnt_private, run_command_work.work);
92 
93 	if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags))
94 		return;
95 
96 	if (!priv->cmd_running)
97 		return;
98 
99 	switch (priv->command_state) {
100 	case WLAN_CMD_INIT_MAC80211_START:
101 		if (priv->mac_hw)
102 			break;
103 
104 		dev_info(&priv->usb->dev, "Starting mac80211\n");
105 
106 		if (vnt_init(priv)) {
107 			/* If fail all ends TODO retry */
108 			dev_err(&priv->usb->dev, "failed to start\n");
109 			usb_set_intfdata(priv->intf, NULL);
110 			ieee80211_free_hw(priv->hw);
111 			return;
112 		}
113 
114 		break;
115 
116 	case WLAN_CMD_TBTT_WAKEUP_START:
117 		vnt_next_tbtt_wakeup(priv);
118 		break;
119 
120 	case WLAN_CMD_BECON_SEND_START:
121 		if (!priv->vif)
122 			break;
123 
124 		vnt_beacon_make(priv, priv->vif);
125 
126 		vnt_mac_reg_bits_on(priv, MAC_REG_TCR, TCR_AUTOBCNTX);
127 
128 		break;
129 
130 	case WLAN_CMD_SETPOWER_START:
131 
132 		vnt_rf_setpower(priv, priv->hw->conf.chandef.chan);
133 
134 		break;
135 
136 	case WLAN_CMD_CHANGE_ANTENNA_START:
137 		dev_dbg(&priv->usb->dev, "Change from Antenna%d to",
138 			priv->rx_antenna_sel);
139 
140 		if (priv->rx_antenna_sel == 0) {
141 			priv->rx_antenna_sel = 1;
142 			if (priv->tx_rx_ant_inv)
143 				vnt_set_antenna_mode(priv, ANT_RXA);
144 			else
145 				vnt_set_antenna_mode(priv, ANT_RXB);
146 		} else {
147 			priv->rx_antenna_sel = 0;
148 			if (priv->tx_rx_ant_inv)
149 				vnt_set_antenna_mode(priv, ANT_RXB);
150 			else
151 				vnt_set_antenna_mode(priv, ANT_RXA);
152 		}
153 		break;
154 
155 	default:
156 		break;
157 	}
158 
159 	vnt_cmd_complete(priv);
160 }
161 
vnt_schedule_command(struct vnt_private * priv,enum vnt_cmd command)162 int vnt_schedule_command(struct vnt_private *priv, enum vnt_cmd command)
163 {
164 	if (priv->free_cmd_queue == 0)
165 		return false;
166 
167 	priv->cmd_queue[priv->cmd_enqueue_idx] = command;
168 
169 	priv->cmd_enqueue_idx = add_one_with_wrap_around(priv->cmd_enqueue_idx, CMD_Q_SIZE);
170 	priv->free_cmd_queue--;
171 
172 	if (!priv->cmd_running)
173 		vnt_cmd_complete(priv);
174 
175 	return true;
176 }
177 
vnt_reset_command_timer(struct vnt_private * priv)178 void vnt_reset_command_timer(struct vnt_private *priv)
179 {
180 	priv->free_cmd_queue = CMD_Q_SIZE;
181 	priv->cmd_dequeue_idx = 0;
182 	priv->cmd_enqueue_idx = 0;
183 	priv->command_state = WLAN_CMD_IDLE;
184 	priv->cmd_running = false;
185 }
186