1/* 2 * This is a reverse-engineered driver for mobile WiMAX (802.16e) devices 3 * based on Samsung CMC-730 chip. 4 * This file contains binary protocol realization. 5 * Copyright (C) 2008-2009 Alexander Gordeev <lasaine@lvk.cs.msu.su> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22#include <string.h> 23 24#include "logging.h" 25#include "protocol.h" 26 27static int process_normal_config_response(struct wimax_dev_status *dev, const unsigned char *buf, int len) 28{ 29 short type_a = (buf[0x14] << 8) + buf[0x15]; 30 short type_b = (buf[0x16] << 8) + buf[0x17]; 31 short param_len = (buf[0x18] << 8) + buf[0x19]; 32 33 if (type_a == 0x8 && type_b == 0x2) { 34 if (param_len != 0x80) { 35 wmlog_msg(1, "bad param_len"); 36 return -1; 37 } 38 memcpy(dev->chip, buf + 0x1a, 0x40); 39 memcpy(dev->firmware, buf + 0x5a, 0x40); 40 dev->info_updated |= WDS_CHIP | WDS_FIRMWARE; 41 return 0; 42 } 43 if (type_a == 0x3 && type_b == 0x2) { 44 if (param_len != 0x6) { 45 wmlog_msg(1, "bad param_len"); 46 return -1; 47 } 48 memcpy(dev->mac, buf + 0x1a, 0x6); 49 dev->info_updated |= WDS_MAC; 50 return 0; 51 } 52 if (type_a == 0x1 && type_b == 0x2) { 53 if (param_len != 0x2) { 54 wmlog_msg(1, "bad param_len"); 55 return -1; 56 } 57 set_link_status((buf[0x1a] << 8) + buf[0x1b]); 58 return 0; 59 } 60 if (type_a == 0x1 && type_b == 0x3) { 61 if (param_len != 0x4) { 62 wmlog_msg(1, "bad param_len"); 63 return -1; 64 } 65 set_link_status(0); 66 return 0; 67 } 68 if (type_a == 0x1 && type_b == 0xa) { 69 if (param_len != 0x16) { 70 wmlog_msg(1, "bad param_len"); 71 return -1; 72 } 73 dev->rssi = (buf[0x1a] << 8) + buf[0x1b]; 74 dev->cinr = (float)((short)((buf[0x1c] << 8) + buf[0x1d])) / 8; 75 memcpy(dev->bsid, buf + 0x1e, 0x6); 76 dev->txpwr = (buf[0x26] << 8) + buf[0x27]; 77 dev->freq = (buf[0x28] << 24) + (buf[0x29] << 16) + (buf[0x2a] << 8) + buf[0x2b]; 78 dev->info_updated |= WDS_RSSI | WDS_CINR | WDS_BSID | WDS_TXPWR | WDS_FREQ; 79 return 0; 80 } 81 if (type_a == 0x1 && type_b == 0xc) { 82 if (param_len != 0x2) { 83 wmlog_msg(1, "bad param_len"); 84 return -1; 85 } 86 set_state((buf[0x1a] << 8) + buf[0x1b]); 87 return 0; 88 } 89 90 dev->info_updated |= WDS_OTHER; 91 92 return 0; 93} 94 95static int process_debug_config_response(struct wimax_dev_status *dev, const unsigned char *buf, int len) 96{ 97 dev->info_updated |= WDS_OTHER; 98 return 0; 99} 100 101static int process_config_response(struct wimax_dev_status *dev, const unsigned char *buf, int len) 102{ 103 if (buf[0x12] != 0x15) { 104 wmlog_msg(1, "bad format"); 105 return -1; 106 } 107 108 switch (buf[0x13]) { 109 case 0x00: 110 return process_normal_config_response(dev, buf, len); 111 case 0x02: 112 return process_debug_config_response(dev, buf, len); 113 case 0x04: 114 break; 115 default: 116 wmlog_msg(1, "bad format"); 117 return -1; 118 } 119 120 dev->info_updated |= WDS_OTHER; 121 122 return 0; 123} 124 125static int process_data_response(struct wimax_dev_status *dev, const unsigned char *buf, int len) 126{ 127 write_netif(buf + 0x06, len - 0x06); 128 return 0; 129} 130 131static int process_E_response(struct wimax_dev_status *dev, const unsigned char *buf, int len) 132{ 133 if (buf[0x5] == 0x3) { 134 dev->proto_flags = buf[0x7]; 135 dev->info_updated |= WDS_PROTO_FLAGS; 136 return 0; 137 } 138 139 dev->info_updated |= WDS_OTHER; 140 141 return 0; 142} 143 144static int process_P_response(struct wimax_dev_status *dev, const unsigned char *buf, int len) 145{ 146 dev->info_updated |= WDS_OTHER; 147 return 0; 148} 149 150int process_response(struct wimax_dev_status *dev, const unsigned char *buf, int len) 151{ 152 int check_len; 153 154 if(len < 4) { 155 wmlog_msg(1, "short read"); 156 return -1; 157 } 158 159 if(buf[0] != 0x57) { 160 wmlog_msg(1, "bad header"); 161 return -1; 162 } 163 164 check_len = 4 + buf[2] + (buf[3] << 8); 165 if(buf[1] == 0x43 || buf[1] == 0x44) { 166 check_len += 2; 167 } 168 169 if(check_len != len) { 170 wmlog_msg(1, "bad length: %02x instead of %02x", check_len, len); 171 return -1; 172 } 173 174 switch (buf[1]) { 175 case 0x43: 176 return process_config_response(dev, buf, len); 177 case 0x44: 178 return process_data_response(dev, buf, len); 179 case 0x45: 180 return process_E_response(dev, buf, len); 181 case 0x50: 182 return process_P_response(dev, buf, len); 183 default: 184 wmlog_msg(1, "bad response type: %02x", buf[1]); 185 return -1; 186 } 187} 188 189 190static inline void fill_outgoing_packet_header(unsigned char *buf, unsigned char type, int body_len) 191{ 192 buf[0x00] = 0x57; 193 buf[0x01] = type; 194 buf[0x02] = body_len & 0xff; 195 buf[0x03] = (body_len >> 8) & 0xff; 196} 197 198int fill_protocol_info_req(unsigned char *buf, unsigned char flags) 199{ 200 fill_outgoing_packet_header(buf, 0x45, 4); 201 buf += HEADER_LENGTH_LOWLEVEL; 202 buf[0x00] = 0x00; 203 buf[0x01] = 0x02; 204 buf[0x02] = 0x00; 205 buf[0x03] = flags; 206 return HEADER_LENGTH_LOWLEVEL + 4; 207} 208 209int fill_mac_lowlevel_req(unsigned char *buf) 210{ 211 fill_outgoing_packet_header(buf, 0x50, 0x14); 212 buf += HEADER_LENGTH_LOWLEVEL; 213 memset(buf, 0, 0x14); 214 buf[0x0c] = 0x15; 215 buf[0x0d] = 0x0a; 216 return HEADER_LENGTH_LOWLEVEL + 0x14; 217} 218 219static inline void fill_config_req(unsigned char *buf, int body_len) 220{ 221 fill_outgoing_packet_header(buf, 0x43, body_len); 222 memset(buf + HEADER_LENGTH, 0, 12); 223} 224 225static int fill_normal_config_req(unsigned char *buf, unsigned short type_a, unsigned short type_b, unsigned short param_len, unsigned char *param) 226{ 227 int body_len = 0x14 + param_len; 228 fill_config_req(buf, body_len); 229 buf[0x04] = 0x15; 230 buf[0x05] = 0x00; 231 buf += HEADER_LENGTH; 232 buf[0x0c] = 0x15; 233 buf[0x0d] = 0x00; 234 buf[0x0e] = type_a >> 8; 235 buf[0x0f] = type_a & 0xff; 236 buf[0x10] = type_b >> 8; 237 buf[0x11] = type_b & 0xff; 238 buf[0x12] = param_len >> 8; 239 buf[0x13] = param_len & 0xff; 240 memcpy(buf + 0x14, param, param_len); 241 return HEADER_LENGTH + body_len; 242} 243 244int fill_init_cmd(unsigned char *buf) 245{ 246 fill_config_req(buf, 0x12); 247 buf[0x04] = 0x15; 248 buf[0x05] = 0x04; 249 buf += HEADER_LENGTH; 250 buf[0x0c] = 0x15; 251 buf[0x0d] = 0x04; 252 buf[0x0e] = 0x50; 253 buf[0x0f] = 0x04; 254 return HEADER_LENGTH + 0x12; 255} 256 257int fill_string_info_req(unsigned char *buf) 258{ 259 return fill_normal_config_req(buf, 0x8, 0x1, 0x0, NULL); 260} 261 262int fill_diode_control_cmd(unsigned char *buf, int turn_on) 263{ 264 unsigned char param[0x2] = {0x0, turn_on ? 0x1 : 0x0}; 265 return fill_normal_config_req(buf, 0x30, 0x1, sizeof(param), param); 266} 267 268int fill_mac_req(unsigned char *buf) 269{ 270 return fill_normal_config_req(buf, 0x3, 0x1, 0x0, NULL); 271} 272 273int fill_auth_policy_req(unsigned char *buf) 274{ 275 return fill_normal_config_req(buf, 0x20, 0x8, 0x0, NULL); 276} 277 278int fill_auth_method_req(unsigned char *buf) 279{ 280 return fill_normal_config_req(buf, 0x20, 0xc, 0x0, NULL); 281} 282 283int fill_auth_set_cmd(unsigned char *buf, char *netid) 284{ 285 short netid_len = strlen(netid) + 1; 286 unsigned char param[netid_len + 4]; 287 param[0] = 0x0; 288 param[1] = 0x10; 289 param[2] = netid_len >> 8; 290 param[3] = netid_len & 0xff; 291 memcpy(param + 4, netid, netid_len); 292 return fill_normal_config_req(buf, 0x20, 0x20, sizeof(param), param); 293} 294 295int fill_find_network_req(unsigned char *buf, unsigned short level) 296{ 297 unsigned char param[0x2] = {level >> 8, level & 0xff}; 298 return fill_normal_config_req(buf, 0x1, 0x1, sizeof(param), param); 299} 300 301int fill_connection_params_req(unsigned char *buf) 302{ 303 unsigned char param[0x2] = {0x00, 0x00}; 304 return fill_normal_config_req(buf, 0x1, 0x9, sizeof(param), param); 305} 306 307int fill_state_req(unsigned char *buf) 308{ 309 return fill_normal_config_req(buf, 0x1, 0xb, 0x0, NULL); 310} 311 312int fill_network_list_req(unsigned char *buf) 313{ 314 unsigned char param[0x2] = {0x00, 0x00}; 315 return fill_normal_config_req(buf, 0x24, 0x1, sizeof(param), param); 316} 317 318 319int get_header_len() 320{ 321 return HEADER_LENGTH; 322} 323 324int fill_data_packet_header(unsigned char *buf, int body_len) 325{ 326 fill_outgoing_packet_header(buf, 0x44, body_len); 327 return HEADER_LENGTH + body_len; 328} 329 330