1/* 2 * Intel Wireless WiMAX Connection 2400m 3 * Implement backend for the WiMAX stack rfkill support 4 * 5 * 6 * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> 7 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License version 11 * 2 as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 * 02110-1301, USA. 22 * 23 * 24 * The WiMAX kernel stack integrates into RF-Kill and keeps the 25 * switches's status. We just need to: 26 * 27 * - report changes in the HW RF Kill switch [with 28 * wimax_rfkill_{sw,hw}_report(), which happens when we detect those 29 * indications coming through hardware reports]. We also do it on 30 * initialization to let the stack know the initial HW state. 31 * 32 * - implement indications from the stack to change the SW RF Kill 33 * switch (coming from sysfs, the wimax stack or user space). 34 */ 35#include "i2400m.h" 36#include <linux/wimax/i2400m.h> 37#include <linux/slab.h> 38 39 40 41#define D_SUBMODULE rfkill 42#include "debug-levels.h" 43 44/* 45 * Return true if the i2400m radio is in the requested wimax_rf_state state 46 * 47 */ 48static 49int i2400m_radio_is(struct i2400m *i2400m, enum wimax_rf_state state) 50{ 51 if (state == WIMAX_RF_OFF) 52 return i2400m->state == I2400M_SS_RF_OFF 53 || i2400m->state == I2400M_SS_RF_SHUTDOWN; 54 else if (state == WIMAX_RF_ON) 55 /* state == WIMAX_RF_ON */ 56 return i2400m->state != I2400M_SS_RF_OFF 57 && i2400m->state != I2400M_SS_RF_SHUTDOWN; 58 else { 59 BUG(); 60 return -EINVAL; /* shut gcc warnings on certain arches */ 61 } 62} 63 64 65/* 66 * WiMAX stack operation: implement SW RFKill toggling 67 * 68 * @wimax_dev: device descriptor 69 * @skb: skb where the message has been received; skb->data is 70 * expected to point to the message payload. 71 * @genl_info: passed by the generic netlink layer 72 * 73 * Generic Netlink will call this function when a message is sent from 74 * userspace to change the software RF-Kill switch status. 75 * 76 * This function will set the device's software RF-Kill switch state to 77 * match what is requested. 78 * 79 * NOTE: the i2400m has a strict state machine; we can only set the 80 * RF-Kill switch when it is on, the HW RF-Kill is on and the 81 * device is initialized. So we ignore errors steaming from not 82 * being in the right state (-EILSEQ). 83 */ 84int i2400m_op_rfkill_sw_toggle(struct wimax_dev *wimax_dev, 85 enum wimax_rf_state state) 86{ 87 int result; 88 struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev); 89 struct device *dev = i2400m_dev(i2400m); 90 struct sk_buff *ack_skb; 91 struct { 92 struct i2400m_l3l4_hdr hdr; 93 struct i2400m_tlv_rf_operation sw_rf; 94 } __packed *cmd; 95 char strerr[32]; 96 97 d_fnstart(4, dev, "(wimax_dev %p state %d)\n", wimax_dev, state); 98 99 result = -ENOMEM; 100 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 101 if (cmd == NULL) 102 goto error_alloc; 103 cmd->hdr.type = cpu_to_le16(I2400M_MT_CMD_RF_CONTROL); 104 cmd->hdr.length = sizeof(cmd->sw_rf); 105 cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION); 106 cmd->sw_rf.hdr.type = cpu_to_le16(I2400M_TLV_RF_OPERATION); 107 cmd->sw_rf.hdr.length = cpu_to_le16(sizeof(cmd->sw_rf.status)); 108 switch (state) { 109 case WIMAX_RF_OFF: /* RFKILL ON, radio OFF */ 110 cmd->sw_rf.status = cpu_to_le32(2); 111 break; 112 case WIMAX_RF_ON: /* RFKILL OFF, radio ON */ 113 cmd->sw_rf.status = cpu_to_le32(1); 114 break; 115 default: 116 BUG(); 117 } 118 119 ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); 120 result = PTR_ERR(ack_skb); 121 if (IS_ERR(ack_skb)) { 122 dev_err(dev, "Failed to issue 'RF Control' command: %d\n", 123 result); 124 goto error_msg_to_dev; 125 } 126 result = i2400m_msg_check_status(wimax_msg_data(ack_skb), 127 strerr, sizeof(strerr)); 128 if (result < 0) { 129 dev_err(dev, "'RF Control' (0x%04x) command failed: %d - %s\n", 130 I2400M_MT_CMD_RF_CONTROL, result, strerr); 131 goto error_cmd; 132 } 133 134 /* Now we wait for the state to change to RADIO_OFF or RADIO_ON */ 135 result = wait_event_timeout( 136 i2400m->state_wq, i2400m_radio_is(i2400m, state), 137 5 * HZ); 138 if (result == 0) 139 result = -ETIMEDOUT; 140 if (result < 0) 141 dev_err(dev, "Error waiting for device to toggle RF state: " 142 "%d\n", result); 143 result = 0; 144error_cmd: 145 kfree_skb(ack_skb); 146error_msg_to_dev: 147error_alloc: 148 d_fnend(4, dev, "(wimax_dev %p state %d) = %d\n", 149 wimax_dev, state, result); 150 return result; 151} 152 153 154/* 155 * Inform the WiMAX stack of changes in the RF Kill switches reported 156 * by the device 157 * 158 * @i2400m: device descriptor 159 * @rfss: TLV for RF Switches status; already validated 160 * 161 * NOTE: the reports on RF switch status cannot be trusted 162 * or used until the device is in a state of RADIO_OFF 163 * or greater. 164 */ 165void i2400m_report_tlv_rf_switches_status( 166 struct i2400m *i2400m, 167 const struct i2400m_tlv_rf_switches_status *rfss) 168{ 169 struct device *dev = i2400m_dev(i2400m); 170 enum i2400m_rf_switch_status hw, sw; 171 enum wimax_st wimax_state; 172 173 sw = le32_to_cpu(rfss->sw_rf_switch); 174 hw = le32_to_cpu(rfss->hw_rf_switch); 175 176 d_fnstart(3, dev, "(i2400m %p rfss %p [hw %u sw %u])\n", 177 i2400m, rfss, hw, sw); 178 /* We only process rw switch evens when the device has been 179 * fully initialized */ 180 wimax_state = wimax_state_get(&i2400m->wimax_dev); 181 if (wimax_state < WIMAX_ST_RADIO_OFF) { 182 d_printf(3, dev, "ignoring RF switches report, state %u\n", 183 wimax_state); 184 goto out; 185 } 186 switch (sw) { 187 case I2400M_RF_SWITCH_ON: /* RF Kill disabled (radio on) */ 188 wimax_report_rfkill_sw(&i2400m->wimax_dev, WIMAX_RF_ON); 189 break; 190 case I2400M_RF_SWITCH_OFF: /* RF Kill enabled (radio off) */ 191 wimax_report_rfkill_sw(&i2400m->wimax_dev, WIMAX_RF_OFF); 192 break; 193 default: 194 dev_err(dev, "HW BUG? Unknown RF SW state 0x%x\n", sw); 195 } 196 197 switch (hw) { 198 case I2400M_RF_SWITCH_ON: /* RF Kill disabled (radio on) */ 199 wimax_report_rfkill_hw(&i2400m->wimax_dev, WIMAX_RF_ON); 200 break; 201 case I2400M_RF_SWITCH_OFF: /* RF Kill enabled (radio off) */ 202 wimax_report_rfkill_hw(&i2400m->wimax_dev, WIMAX_RF_OFF); 203 break; 204 default: 205 dev_err(dev, "HW BUG? Unknown RF HW state 0x%x\n", hw); 206 } 207out: 208 d_fnend(3, dev, "(i2400m %p rfss %p [hw %u sw %u]) = void\n", 209 i2400m, rfss, hw, sw); 210} 211