18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux WiMAX 48c2ecf20Sopenharmony_ci * Initialization, addition and removal of wimax devices 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com> 78c2ecf20Sopenharmony_ci * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This implements: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * - basic life cycle of 'struct wimax_dev' [wimax_dev_*()]; on 128c2ecf20Sopenharmony_ci * addition/registration initialize all subfields and allocate 138c2ecf20Sopenharmony_ci * generic netlink resources for user space communication. On 148c2ecf20Sopenharmony_ci * removal/unregistration, undo all that. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * - device state machine [wimax_state_change()] and support to send 178c2ecf20Sopenharmony_ci * reports to user space when the state changes 188c2ecf20Sopenharmony_ci * [wimax_gnl_re_state_change*()]. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * See include/net/wimax.h for rationales and design. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * ROADMAP 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * [__]wimax_state_change() Called by drivers to update device's state 258c2ecf20Sopenharmony_ci * wimax_gnl_re_state_change_alloc() 268c2ecf20Sopenharmony_ci * wimax_gnl_re_state_change_send() 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * wimax_dev_init() Init a device 298c2ecf20Sopenharmony_ci * wimax_dev_add() Register 308c2ecf20Sopenharmony_ci * wimax_rfkill_add() 318c2ecf20Sopenharmony_ci * wimax_gnl_add() Register all the generic netlink resources. 328c2ecf20Sopenharmony_ci * wimax_id_table_add() 338c2ecf20Sopenharmony_ci * wimax_dev_rm() Unregister 348c2ecf20Sopenharmony_ci * wimax_id_table_rm() 358c2ecf20Sopenharmony_ci * wimax_gnl_rm() 368c2ecf20Sopenharmony_ci * wimax_rfkill_rm() 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci#include <linux/device.h> 398c2ecf20Sopenharmony_ci#include <linux/gfp.h> 408c2ecf20Sopenharmony_ci#include <net/genetlink.h> 418c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 428c2ecf20Sopenharmony_ci#include <linux/wimax.h> 438c2ecf20Sopenharmony_ci#include <linux/module.h> 448c2ecf20Sopenharmony_ci#include "wimax-internal.h" 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define D_SUBMODULE stack 488c2ecf20Sopenharmony_ci#include "debug-levels.h" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic char wimax_debug_params[128]; 518c2ecf20Sopenharmony_cimodule_param_string(debug, wimax_debug_params, sizeof(wimax_debug_params), 528c2ecf20Sopenharmony_ci 0644); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, 548c2ecf20Sopenharmony_ci "String of space-separated NAME:VALUE pairs, where NAMEs " 558c2ecf20Sopenharmony_ci "are the different debug submodules and VALUE are the " 568c2ecf20Sopenharmony_ci "initial debug value to set."); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Authoritative source for the RE_STATE_CHANGE attribute policy 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * We don't really use it here, but /me likes to keep the definition 628c2ecf20Sopenharmony_ci * close to where the data is generated. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_cistatic const struct nla_policy wimax_gnl_re_status_change[WIMAX_GNL_ATTR_MAX + 1] = { 668c2ecf20Sopenharmony_ci [WIMAX_GNL_STCH_STATE_OLD] = { .type = NLA_U8 }, 678c2ecf20Sopenharmony_ci [WIMAX_GNL_STCH_STATE_NEW] = { .type = NLA_U8 }, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci*/ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * Allocate a Report State Change message 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * @header: save it, you need it for _send() 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * Creates and fills a basic state change message; different code 788c2ecf20Sopenharmony_ci * paths can then add more attributes to the message as needed. 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * Use wimax_gnl_re_state_change_send() to send the returned skb. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Returns: skb with the genl message if ok, IS_ERR() ptr on error 838c2ecf20Sopenharmony_ci * with an errno code. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic 868c2ecf20Sopenharmony_cistruct sk_buff *wimax_gnl_re_state_change_alloc( 878c2ecf20Sopenharmony_ci struct wimax_dev *wimax_dev, 888c2ecf20Sopenharmony_ci enum wimax_st new_state, enum wimax_st old_state, 898c2ecf20Sopenharmony_ci void **header) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int result; 928c2ecf20Sopenharmony_ci struct device *dev = wimax_dev_to_dev(wimax_dev); 938c2ecf20Sopenharmony_ci void *data; 948c2ecf20Sopenharmony_ci struct sk_buff *report_skb; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(wimax_dev %p new_state %u old_state %u)\n", 978c2ecf20Sopenharmony_ci wimax_dev, new_state, old_state); 988c2ecf20Sopenharmony_ci result = -ENOMEM; 998c2ecf20Sopenharmony_ci report_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 1008c2ecf20Sopenharmony_ci if (report_skb == NULL) { 1018c2ecf20Sopenharmony_ci dev_err(dev, "RE_STCH: can't create message\n"); 1028c2ecf20Sopenharmony_ci goto error_new; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci /* FIXME: sending a group ID as the seq is wrong */ 1058c2ecf20Sopenharmony_ci data = genlmsg_put(report_skb, 0, wimax_gnl_family.mcgrp_offset, 1068c2ecf20Sopenharmony_ci &wimax_gnl_family, 0, WIMAX_GNL_RE_STATE_CHANGE); 1078c2ecf20Sopenharmony_ci if (data == NULL) { 1088c2ecf20Sopenharmony_ci dev_err(dev, "RE_STCH: can't put data into message\n"); 1098c2ecf20Sopenharmony_ci goto error_put; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci *header = data; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci result = nla_put_u8(report_skb, WIMAX_GNL_STCH_STATE_OLD, old_state); 1148c2ecf20Sopenharmony_ci if (result < 0) { 1158c2ecf20Sopenharmony_ci dev_err(dev, "RE_STCH: Error adding OLD attr: %d\n", result); 1168c2ecf20Sopenharmony_ci goto error_put; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci result = nla_put_u8(report_skb, WIMAX_GNL_STCH_STATE_NEW, new_state); 1198c2ecf20Sopenharmony_ci if (result < 0) { 1208c2ecf20Sopenharmony_ci dev_err(dev, "RE_STCH: Error adding NEW attr: %d\n", result); 1218c2ecf20Sopenharmony_ci goto error_put; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci result = nla_put_u32(report_skb, WIMAX_GNL_STCH_IFIDX, 1248c2ecf20Sopenharmony_ci wimax_dev->net_dev->ifindex); 1258c2ecf20Sopenharmony_ci if (result < 0) { 1268c2ecf20Sopenharmony_ci dev_err(dev, "RE_STCH: Error adding IFINDEX attribute\n"); 1278c2ecf20Sopenharmony_ci goto error_put; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci d_fnend(3, dev, "(wimax_dev %p new_state %u old_state %u) = %p\n", 1308c2ecf20Sopenharmony_ci wimax_dev, new_state, old_state, report_skb); 1318c2ecf20Sopenharmony_ci return report_skb; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cierror_put: 1348c2ecf20Sopenharmony_ci nlmsg_free(report_skb); 1358c2ecf20Sopenharmony_cierror_new: 1368c2ecf20Sopenharmony_ci d_fnend(3, dev, "(wimax_dev %p new_state %u old_state %u) = %d\n", 1378c2ecf20Sopenharmony_ci wimax_dev, new_state, old_state, result); 1388c2ecf20Sopenharmony_ci return ERR_PTR(result); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* 1438c2ecf20Sopenharmony_ci * Send a Report State Change message (as created with _alloc). 1448c2ecf20Sopenharmony_ci * 1458c2ecf20Sopenharmony_ci * @report_skb: as returned by wimax_gnl_re_state_change_alloc() 1468c2ecf20Sopenharmony_ci * @header: as returned by wimax_gnl_re_state_change_alloc() 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * Returns: 0 if ok, < 0 errno code on error. 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * If the message is NULL, pretend it didn't happen. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_cistatic 1538c2ecf20Sopenharmony_ciint wimax_gnl_re_state_change_send( 1548c2ecf20Sopenharmony_ci struct wimax_dev *wimax_dev, struct sk_buff *report_skb, 1558c2ecf20Sopenharmony_ci void *header) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int result = 0; 1588c2ecf20Sopenharmony_ci struct device *dev = wimax_dev_to_dev(wimax_dev); 1598c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(wimax_dev %p report_skb %p)\n", 1608c2ecf20Sopenharmony_ci wimax_dev, report_skb); 1618c2ecf20Sopenharmony_ci if (report_skb == NULL) { 1628c2ecf20Sopenharmony_ci result = -ENOMEM; 1638c2ecf20Sopenharmony_ci goto out; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci genlmsg_end(report_skb, header); 1668c2ecf20Sopenharmony_ci genlmsg_multicast(&wimax_gnl_family, report_skb, 0, 0, GFP_KERNEL); 1678c2ecf20Sopenharmony_ciout: 1688c2ecf20Sopenharmony_ci d_fnend(3, dev, "(wimax_dev %p report_skb %p) = %d\n", 1698c2ecf20Sopenharmony_ci wimax_dev, report_skb, result); 1708c2ecf20Sopenharmony_ci return result; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic 1758c2ecf20Sopenharmony_civoid __check_new_state(enum wimax_st old_state, enum wimax_st new_state, 1768c2ecf20Sopenharmony_ci unsigned int allowed_states_bm) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci if (WARN_ON(((1 << new_state) & allowed_states_bm) == 0)) { 1798c2ecf20Sopenharmony_ci pr_err("SW BUG! Forbidden state change %u -> %u\n", 1808c2ecf20Sopenharmony_ci old_state, new_state); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* 1868c2ecf20Sopenharmony_ci * Set the current state of a WiMAX device [unlocking version of 1878c2ecf20Sopenharmony_ci * wimax_state_change(). 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_civoid __wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct device *dev = wimax_dev_to_dev(wimax_dev); 1928c2ecf20Sopenharmony_ci enum wimax_st old_state = wimax_dev->state; 1938c2ecf20Sopenharmony_ci struct sk_buff *stch_skb; 1948c2ecf20Sopenharmony_ci void *header; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(wimax_dev %p new_state %u [old %u])\n", 1978c2ecf20Sopenharmony_ci wimax_dev, new_state, old_state); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (WARN_ON(new_state >= __WIMAX_ST_INVALID)) { 2008c2ecf20Sopenharmony_ci dev_err(dev, "SW BUG: requesting invalid state %u\n", 2018c2ecf20Sopenharmony_ci new_state); 2028c2ecf20Sopenharmony_ci goto out; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci if (old_state == new_state) 2058c2ecf20Sopenharmony_ci goto out; 2068c2ecf20Sopenharmony_ci header = NULL; /* gcc complains? can't grok why */ 2078c2ecf20Sopenharmony_ci stch_skb = wimax_gnl_re_state_change_alloc( 2088c2ecf20Sopenharmony_ci wimax_dev, new_state, old_state, &header); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Verify the state transition and do exit-from-state actions */ 2118c2ecf20Sopenharmony_ci switch (old_state) { 2128c2ecf20Sopenharmony_ci case __WIMAX_ST_NULL: 2138c2ecf20Sopenharmony_ci __check_new_state(old_state, new_state, 2148c2ecf20Sopenharmony_ci 1 << WIMAX_ST_DOWN); 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case WIMAX_ST_DOWN: 2178c2ecf20Sopenharmony_ci __check_new_state(old_state, new_state, 2188c2ecf20Sopenharmony_ci 1 << __WIMAX_ST_QUIESCING 2198c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_UNINITIALIZED 2208c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_RADIO_OFF); 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci case __WIMAX_ST_QUIESCING: 2238c2ecf20Sopenharmony_ci __check_new_state(old_state, new_state, 1 << WIMAX_ST_DOWN); 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci case WIMAX_ST_UNINITIALIZED: 2268c2ecf20Sopenharmony_ci __check_new_state(old_state, new_state, 2278c2ecf20Sopenharmony_ci 1 << __WIMAX_ST_QUIESCING 2288c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_RADIO_OFF); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci case WIMAX_ST_RADIO_OFF: 2318c2ecf20Sopenharmony_ci __check_new_state(old_state, new_state, 2328c2ecf20Sopenharmony_ci 1 << __WIMAX_ST_QUIESCING 2338c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_READY); 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci case WIMAX_ST_READY: 2368c2ecf20Sopenharmony_ci __check_new_state(old_state, new_state, 2378c2ecf20Sopenharmony_ci 1 << __WIMAX_ST_QUIESCING 2388c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_RADIO_OFF 2398c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_SCANNING 2408c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_CONNECTING 2418c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_CONNECTED); 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case WIMAX_ST_SCANNING: 2448c2ecf20Sopenharmony_ci __check_new_state(old_state, new_state, 2458c2ecf20Sopenharmony_ci 1 << __WIMAX_ST_QUIESCING 2468c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_RADIO_OFF 2478c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_READY 2488c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_CONNECTING 2498c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_CONNECTED); 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci case WIMAX_ST_CONNECTING: 2528c2ecf20Sopenharmony_ci __check_new_state(old_state, new_state, 2538c2ecf20Sopenharmony_ci 1 << __WIMAX_ST_QUIESCING 2548c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_RADIO_OFF 2558c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_READY 2568c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_SCANNING 2578c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_CONNECTED); 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case WIMAX_ST_CONNECTED: 2608c2ecf20Sopenharmony_ci __check_new_state(old_state, new_state, 2618c2ecf20Sopenharmony_ci 1 << __WIMAX_ST_QUIESCING 2628c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_RADIO_OFF 2638c2ecf20Sopenharmony_ci | 1 << WIMAX_ST_READY); 2648c2ecf20Sopenharmony_ci netif_tx_disable(wimax_dev->net_dev); 2658c2ecf20Sopenharmony_ci netif_carrier_off(wimax_dev->net_dev); 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci case __WIMAX_ST_INVALID: 2688c2ecf20Sopenharmony_ci default: 2698c2ecf20Sopenharmony_ci dev_err(dev, "SW BUG: wimax_dev %p is in unknown state %u\n", 2708c2ecf20Sopenharmony_ci wimax_dev, wimax_dev->state); 2718c2ecf20Sopenharmony_ci WARN_ON(1); 2728c2ecf20Sopenharmony_ci goto out; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Execute the actions of entry to the new state */ 2768c2ecf20Sopenharmony_ci switch (new_state) { 2778c2ecf20Sopenharmony_ci case __WIMAX_ST_NULL: 2788c2ecf20Sopenharmony_ci dev_err(dev, "SW BUG: wimax_dev %p entering NULL state " 2798c2ecf20Sopenharmony_ci "from %u\n", wimax_dev, wimax_dev->state); 2808c2ecf20Sopenharmony_ci WARN_ON(1); /* Nobody can enter this state */ 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci case WIMAX_ST_DOWN: 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case __WIMAX_ST_QUIESCING: 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci case WIMAX_ST_UNINITIALIZED: 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci case WIMAX_ST_RADIO_OFF: 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case WIMAX_ST_READY: 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci case WIMAX_ST_SCANNING: 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci case WIMAX_ST_CONNECTING: 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci case WIMAX_ST_CONNECTED: 2978c2ecf20Sopenharmony_ci netif_carrier_on(wimax_dev->net_dev); 2988c2ecf20Sopenharmony_ci netif_wake_queue(wimax_dev->net_dev); 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci case __WIMAX_ST_INVALID: 3018c2ecf20Sopenharmony_ci default: 3028c2ecf20Sopenharmony_ci BUG(); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci __wimax_state_set(wimax_dev, new_state); 3058c2ecf20Sopenharmony_ci if (!IS_ERR(stch_skb)) 3068c2ecf20Sopenharmony_ci wimax_gnl_re_state_change_send(wimax_dev, stch_skb, header); 3078c2ecf20Sopenharmony_ciout: 3088c2ecf20Sopenharmony_ci d_fnend(3, dev, "(wimax_dev %p new_state %u [old %u]) = void\n", 3098c2ecf20Sopenharmony_ci wimax_dev, new_state, old_state); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/** 3148c2ecf20Sopenharmony_ci * wimax_state_change - Set the current state of a WiMAX device 3158c2ecf20Sopenharmony_ci * 3168c2ecf20Sopenharmony_ci * @wimax_dev: WiMAX device descriptor (properly referenced) 3178c2ecf20Sopenharmony_ci * @new_state: New state to switch to 3188c2ecf20Sopenharmony_ci * 3198c2ecf20Sopenharmony_ci * This implements the state changes for the wimax devices. It will 3208c2ecf20Sopenharmony_ci * 3218c2ecf20Sopenharmony_ci * - verify that the state transition is legal (for now it'll just 3228c2ecf20Sopenharmony_ci * print a warning if not) according to the table in 3238c2ecf20Sopenharmony_ci * linux/wimax.h's documentation for 'enum wimax_st'. 3248c2ecf20Sopenharmony_ci * 3258c2ecf20Sopenharmony_ci * - perform the actions needed for leaving the current state and 3268c2ecf20Sopenharmony_ci * whichever are needed for entering the new state. 3278c2ecf20Sopenharmony_ci * 3288c2ecf20Sopenharmony_ci * - issue a report to user space indicating the new state (and an 3298c2ecf20Sopenharmony_ci * optional payload with information about the new state). 3308c2ecf20Sopenharmony_ci * 3318c2ecf20Sopenharmony_ci * NOTE: @wimax_dev must be locked 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_civoid wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci /* 3368c2ecf20Sopenharmony_ci * A driver cannot take the wimax_dev out of the 3378c2ecf20Sopenharmony_ci * __WIMAX_ST_NULL state unless by calling wimax_dev_add(). If 3388c2ecf20Sopenharmony_ci * the wimax_dev's state is still NULL, we ignore any request 3398c2ecf20Sopenharmony_ci * to change its state because it means it hasn't been yet 3408c2ecf20Sopenharmony_ci * registered. 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci * There is no need to complain about it, as routines that 3438c2ecf20Sopenharmony_ci * call this might be shared from different code paths that 3448c2ecf20Sopenharmony_ci * are called before or after wimax_dev_add() has done its 3458c2ecf20Sopenharmony_ci * job. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ci mutex_lock(&wimax_dev->mutex); 3488c2ecf20Sopenharmony_ci if (wimax_dev->state > __WIMAX_ST_NULL) 3498c2ecf20Sopenharmony_ci __wimax_state_change(wimax_dev, new_state); 3508c2ecf20Sopenharmony_ci mutex_unlock(&wimax_dev->mutex); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_state_change); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/** 3568c2ecf20Sopenharmony_ci * wimax_state_get() - Return the current state of a WiMAX device 3578c2ecf20Sopenharmony_ci * 3588c2ecf20Sopenharmony_ci * @wimax_dev: WiMAX device descriptor 3598c2ecf20Sopenharmony_ci * 3608c2ecf20Sopenharmony_ci * Returns: Current state of the device according to its driver. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_cienum wimax_st wimax_state_get(struct wimax_dev *wimax_dev) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci enum wimax_st state; 3658c2ecf20Sopenharmony_ci mutex_lock(&wimax_dev->mutex); 3668c2ecf20Sopenharmony_ci state = wimax_dev->state; 3678c2ecf20Sopenharmony_ci mutex_unlock(&wimax_dev->mutex); 3688c2ecf20Sopenharmony_ci return state; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_state_get); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/** 3748c2ecf20Sopenharmony_ci * wimax_dev_init - initialize a newly allocated instance 3758c2ecf20Sopenharmony_ci * 3768c2ecf20Sopenharmony_ci * @wimax_dev: WiMAX device descriptor to initialize. 3778c2ecf20Sopenharmony_ci * 3788c2ecf20Sopenharmony_ci * Initializes fields of a freshly allocated @wimax_dev instance. This 3798c2ecf20Sopenharmony_ci * function assumes that after allocation, the memory occupied by 3808c2ecf20Sopenharmony_ci * @wimax_dev was zeroed. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_civoid wimax_dev_init(struct wimax_dev *wimax_dev) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&wimax_dev->id_table_node); 3858c2ecf20Sopenharmony_ci __wimax_state_set(wimax_dev, __WIMAX_ST_NULL); 3868c2ecf20Sopenharmony_ci mutex_init(&wimax_dev->mutex); 3878c2ecf20Sopenharmony_ci mutex_init(&wimax_dev->mutex_reset); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_dev_init); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic const struct nla_policy wimax_gnl_policy[WIMAX_GNL_ATTR_MAX + 1] = { 3928c2ecf20Sopenharmony_ci [WIMAX_GNL_RESET_IFIDX] = { .type = NLA_U32, }, 3938c2ecf20Sopenharmony_ci [WIMAX_GNL_RFKILL_IFIDX] = { .type = NLA_U32, }, 3948c2ecf20Sopenharmony_ci [WIMAX_GNL_RFKILL_STATE] = { 3958c2ecf20Sopenharmony_ci .type = NLA_U32 /* enum wimax_rf_state */ 3968c2ecf20Sopenharmony_ci }, 3978c2ecf20Sopenharmony_ci [WIMAX_GNL_STGET_IFIDX] = { .type = NLA_U32, }, 3988c2ecf20Sopenharmony_ci [WIMAX_GNL_MSG_IFIDX] = { .type = NLA_U32, }, 3998c2ecf20Sopenharmony_ci [WIMAX_GNL_MSG_DATA] = { 4008c2ecf20Sopenharmony_ci .type = NLA_UNSPEC, /* libnl doesn't grok BINARY yet */ 4018c2ecf20Sopenharmony_ci }, 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic const struct genl_small_ops wimax_gnl_ops[] = { 4058c2ecf20Sopenharmony_ci { 4068c2ecf20Sopenharmony_ci .cmd = WIMAX_GNL_OP_MSG_FROM_USER, 4078c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 4088c2ecf20Sopenharmony_ci .flags = GENL_ADMIN_PERM, 4098c2ecf20Sopenharmony_ci .doit = wimax_gnl_doit_msg_from_user, 4108c2ecf20Sopenharmony_ci }, 4118c2ecf20Sopenharmony_ci { 4128c2ecf20Sopenharmony_ci .cmd = WIMAX_GNL_OP_RESET, 4138c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 4148c2ecf20Sopenharmony_ci .flags = GENL_ADMIN_PERM, 4158c2ecf20Sopenharmony_ci .doit = wimax_gnl_doit_reset, 4168c2ecf20Sopenharmony_ci }, 4178c2ecf20Sopenharmony_ci { 4188c2ecf20Sopenharmony_ci .cmd = WIMAX_GNL_OP_RFKILL, 4198c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 4208c2ecf20Sopenharmony_ci .flags = GENL_ADMIN_PERM, 4218c2ecf20Sopenharmony_ci .doit = wimax_gnl_doit_rfkill, 4228c2ecf20Sopenharmony_ci }, 4238c2ecf20Sopenharmony_ci { 4248c2ecf20Sopenharmony_ci .cmd = WIMAX_GNL_OP_STATE_GET, 4258c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 4268c2ecf20Sopenharmony_ci .flags = GENL_ADMIN_PERM, 4278c2ecf20Sopenharmony_ci .doit = wimax_gnl_doit_state_get, 4288c2ecf20Sopenharmony_ci }, 4298c2ecf20Sopenharmony_ci}; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic 4338c2ecf20Sopenharmony_cisize_t wimax_addr_scnprint(char *addr_str, size_t addr_str_size, 4348c2ecf20Sopenharmony_ci unsigned char *addr, size_t addr_len) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci unsigned int cnt, total; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci for (total = cnt = 0; cnt < addr_len; cnt++) 4398c2ecf20Sopenharmony_ci total += scnprintf(addr_str + total, addr_str_size - total, 4408c2ecf20Sopenharmony_ci "%02x%c", addr[cnt], 4418c2ecf20Sopenharmony_ci cnt == addr_len - 1 ? '\0' : ':'); 4428c2ecf20Sopenharmony_ci return total; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/** 4478c2ecf20Sopenharmony_ci * wimax_dev_add - Register a new WiMAX device 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * @wimax_dev: WiMAX device descriptor (as embedded in your @net_dev's 4508c2ecf20Sopenharmony_ci * priv data). You must have called wimax_dev_init() on it before. 4518c2ecf20Sopenharmony_ci * 4528c2ecf20Sopenharmony_ci * @net_dev: net device the @wimax_dev is associated with. The 4538c2ecf20Sopenharmony_ci * function expects SET_NETDEV_DEV() and register_netdev() were 4548c2ecf20Sopenharmony_ci * already called on it. 4558c2ecf20Sopenharmony_ci * 4568c2ecf20Sopenharmony_ci * Registers the new WiMAX device, sets up the user-kernel control 4578c2ecf20Sopenharmony_ci * interface (generic netlink) and common WiMAX infrastructure. 4588c2ecf20Sopenharmony_ci * 4598c2ecf20Sopenharmony_ci * Note that the parts that will allow interaction with user space are 4608c2ecf20Sopenharmony_ci * setup at the very end, when the rest is in place, as once that 4618c2ecf20Sopenharmony_ci * happens, the driver might get user space control requests via 4628c2ecf20Sopenharmony_ci * netlink or from debugfs that might translate into calls into 4638c2ecf20Sopenharmony_ci * wimax_dev->op_*(). 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ciint wimax_dev_add(struct wimax_dev *wimax_dev, struct net_device *net_dev) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci int result; 4688c2ecf20Sopenharmony_ci struct device *dev = net_dev->dev.parent; 4698c2ecf20Sopenharmony_ci char addr_str[32]; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(wimax_dev %p net_dev %p)\n", wimax_dev, net_dev); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* Do the RFKILL setup before locking, as RFKILL will call 4748c2ecf20Sopenharmony_ci * into our functions. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci wimax_dev->net_dev = net_dev; 4778c2ecf20Sopenharmony_ci result = wimax_rfkill_add(wimax_dev); 4788c2ecf20Sopenharmony_ci if (result < 0) 4798c2ecf20Sopenharmony_ci goto error_rfkill_add; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Set up user-space interaction */ 4828c2ecf20Sopenharmony_ci mutex_lock(&wimax_dev->mutex); 4838c2ecf20Sopenharmony_ci wimax_id_table_add(wimax_dev); 4848c2ecf20Sopenharmony_ci wimax_debugfs_add(wimax_dev); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci __wimax_state_set(wimax_dev, WIMAX_ST_DOWN); 4878c2ecf20Sopenharmony_ci mutex_unlock(&wimax_dev->mutex); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci wimax_addr_scnprint(addr_str, sizeof(addr_str), 4908c2ecf20Sopenharmony_ci net_dev->dev_addr, net_dev->addr_len); 4918c2ecf20Sopenharmony_ci dev_err(dev, "WiMAX interface %s (%s) ready\n", 4928c2ecf20Sopenharmony_ci net_dev->name, addr_str); 4938c2ecf20Sopenharmony_ci d_fnend(3, dev, "(wimax_dev %p net_dev %p) = 0\n", wimax_dev, net_dev); 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cierror_rfkill_add: 4978c2ecf20Sopenharmony_ci d_fnend(3, dev, "(wimax_dev %p net_dev %p) = %d\n", 4988c2ecf20Sopenharmony_ci wimax_dev, net_dev, result); 4998c2ecf20Sopenharmony_ci return result; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_dev_add); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci/** 5058c2ecf20Sopenharmony_ci * wimax_dev_rm - Unregister an existing WiMAX device 5068c2ecf20Sopenharmony_ci * 5078c2ecf20Sopenharmony_ci * @wimax_dev: WiMAX device descriptor 5088c2ecf20Sopenharmony_ci * 5098c2ecf20Sopenharmony_ci * Unregisters a WiMAX device previously registered for use with 5108c2ecf20Sopenharmony_ci * wimax_add_rm(). 5118c2ecf20Sopenharmony_ci * 5128c2ecf20Sopenharmony_ci * IMPORTANT! Must call before calling unregister_netdev(). 5138c2ecf20Sopenharmony_ci * 5148c2ecf20Sopenharmony_ci * After this function returns, you will not get any more user space 5158c2ecf20Sopenharmony_ci * control requests (via netlink or debugfs) and thus to wimax_dev->ops. 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * Reentrancy control is ensured by setting the state to 5188c2ecf20Sopenharmony_ci * %__WIMAX_ST_QUIESCING. rfkill operations coming through 5198c2ecf20Sopenharmony_ci * wimax_*rfkill*() will be stopped by the quiescing state; ops coming 5208c2ecf20Sopenharmony_ci * from the rfkill subsystem will be stopped by the support being 5218c2ecf20Sopenharmony_ci * removed by wimax_rfkill_rm(). 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_civoid wimax_dev_rm(struct wimax_dev *wimax_dev) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci d_fnstart(3, NULL, "(wimax_dev %p)\n", wimax_dev); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci mutex_lock(&wimax_dev->mutex); 5288c2ecf20Sopenharmony_ci __wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING); 5298c2ecf20Sopenharmony_ci wimax_debugfs_rm(wimax_dev); 5308c2ecf20Sopenharmony_ci wimax_id_table_rm(wimax_dev); 5318c2ecf20Sopenharmony_ci __wimax_state_change(wimax_dev, WIMAX_ST_DOWN); 5328c2ecf20Sopenharmony_ci mutex_unlock(&wimax_dev->mutex); 5338c2ecf20Sopenharmony_ci wimax_rfkill_rm(wimax_dev); 5348c2ecf20Sopenharmony_ci d_fnend(3, NULL, "(wimax_dev %p) = void\n", wimax_dev); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_dev_rm); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci/* Debug framework control of debug levels */ 5408c2ecf20Sopenharmony_cistruct d_level D_LEVEL[] = { 5418c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(debugfs), 5428c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(id_table), 5438c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(op_msg), 5448c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(op_reset), 5458c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(op_rfkill), 5468c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(op_state_get), 5478c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(stack), 5488c2ecf20Sopenharmony_ci}; 5498c2ecf20Sopenharmony_cisize_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic const struct genl_multicast_group wimax_gnl_mcgrps[] = { 5538c2ecf20Sopenharmony_ci { .name = "msg", }, 5548c2ecf20Sopenharmony_ci}; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistruct genl_family wimax_gnl_family __ro_after_init = { 5578c2ecf20Sopenharmony_ci .name = "WiMAX", 5588c2ecf20Sopenharmony_ci .version = WIMAX_GNL_VERSION, 5598c2ecf20Sopenharmony_ci .hdrsize = 0, 5608c2ecf20Sopenharmony_ci .maxattr = WIMAX_GNL_ATTR_MAX, 5618c2ecf20Sopenharmony_ci .policy = wimax_gnl_policy, 5628c2ecf20Sopenharmony_ci .module = THIS_MODULE, 5638c2ecf20Sopenharmony_ci .small_ops = wimax_gnl_ops, 5648c2ecf20Sopenharmony_ci .n_small_ops = ARRAY_SIZE(wimax_gnl_ops), 5658c2ecf20Sopenharmony_ci .mcgrps = wimax_gnl_mcgrps, 5668c2ecf20Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(wimax_gnl_mcgrps), 5678c2ecf20Sopenharmony_ci}; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci/* Shutdown the wimax stack */ 5728c2ecf20Sopenharmony_cistatic 5738c2ecf20Sopenharmony_ciint __init wimax_subsys_init(void) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci int result; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci d_fnstart(4, NULL, "()\n"); 5788c2ecf20Sopenharmony_ci d_parse_params(D_LEVEL, D_LEVEL_SIZE, wimax_debug_params, 5798c2ecf20Sopenharmony_ci "wimax.debug"); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci result = genl_register_family(&wimax_gnl_family); 5828c2ecf20Sopenharmony_ci if (unlikely(result < 0)) { 5838c2ecf20Sopenharmony_ci pr_err("cannot register generic netlink family: %d\n", result); 5848c2ecf20Sopenharmony_ci goto error_register_family; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci d_fnend(4, NULL, "() = 0\n"); 5888c2ecf20Sopenharmony_ci return 0; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cierror_register_family: 5918c2ecf20Sopenharmony_ci d_fnend(4, NULL, "() = %d\n", result); 5928c2ecf20Sopenharmony_ci return result; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_cimodule_init(wimax_subsys_init); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci/* Shutdown the wimax stack */ 5998c2ecf20Sopenharmony_cistatic 6008c2ecf20Sopenharmony_civoid __exit wimax_subsys_exit(void) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci wimax_id_table_release(); 6038c2ecf20Sopenharmony_ci genl_unregister_family(&wimax_gnl_family); 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_cimodule_exit(wimax_subsys_exit); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); 6088c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Linux WiMAX stack"); 6098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 610