| /* |
| * Intel Wireless Multicomm 3200 WiFi driver |
| * |
| * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com> |
| * Samuel Ortiz <samuel.ortiz@intel.com> |
| * Zhu Yi <yi.zhu@intel.com> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License version |
| * 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| * 02110-1301, USA. |
| * |
| */ |
| |
| /* |
| * This is the netdev related hooks for iwm. |
| * |
| * Some interesting code paths: |
| * |
| * iwm_open() (Called at netdev interface bringup time) |
| * -> iwm_up() (main.c) |
| * -> iwm_bus_enable() |
| * -> if_sdio_enable() (In case of an SDIO bus) |
| * -> sdio_enable_func() |
| * -> iwm_notif_wait(BARKER_REBOOT) (wait for reboot barker) |
| * -> iwm_notif_wait(ACK_BARKER) (wait for ACK barker) |
| * -> iwm_load_fw() (fw.c) |
| * -> iwm_load_umac() |
| * -> iwm_load_lmac() (Calibration LMAC) |
| * -> iwm_load_lmac() (Operational LMAC) |
| * -> iwm_send_umac_config() |
| * |
| * iwm_stop() (Called at netdev interface bringdown time) |
| * -> iwm_down() |
| * -> iwm_bus_disable() |
| * -> if_sdio_disable() (In case of an SDIO bus) |
| * -> sdio_disable_func() |
| */ |
| #include <linux/netdevice.h> |
| #include <linux/slab.h> |
| |
| #include "iwm.h" |
| #include "commands.h" |
| #include "cfg80211.h" |
| #include "debug.h" |
| |
| static int iwm_open(struct net_device *ndev) |
| { |
| struct iwm_priv *iwm = ndev_to_iwm(ndev); |
| |
| return iwm_up(iwm); |
| } |
| |
| static int iwm_stop(struct net_device *ndev) |
| { |
| struct iwm_priv *iwm = ndev_to_iwm(ndev); |
| |
| return iwm_down(iwm); |
| } |
| |
| /* |
| * iwm AC to queue mapping |
| * |
| * AC_VO -> queue 3 |
| * AC_VI -> queue 2 |
| * AC_BE -> queue 1 |
| * AC_BK -> queue 0 |
| */ |
| static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; |
| |
| int iwm_tid_to_queue(u16 tid) |
| { |
| if (tid > IWM_UMAC_TID_NR - 2) |
| return -EINVAL; |
| |
| return iwm_1d_to_queue[tid]; |
| } |
| |
| static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb) |
| { |
| skb->priority = cfg80211_classify8021d(skb); |
| |
| return iwm_1d_to_queue[skb->priority]; |
| } |
| |
| static const struct net_device_ops iwm_netdev_ops = { |
| .ndo_open = iwm_open, |
| .ndo_stop = iwm_stop, |
| .ndo_start_xmit = iwm_xmit_frame, |
| .ndo_select_queue = iwm_select_queue, |
| }; |
| |
| void *iwm_if_alloc(int sizeof_bus, struct device *dev, |
| struct iwm_if_ops *if_ops) |
| { |
| struct net_device *ndev; |
| struct wireless_dev *wdev; |
| struct iwm_priv *iwm; |
| int ret = 0; |
| |
| wdev = iwm_wdev_alloc(sizeof_bus, dev); |
| if (IS_ERR(wdev)) |
| return wdev; |
| |
| iwm = wdev_to_iwm(wdev); |
| iwm->bus_ops = if_ops; |
| iwm->wdev = wdev; |
| |
| ret = iwm_priv_init(iwm); |
| if (ret) { |
| dev_err(dev, "failed to init iwm_priv\n"); |
| goto out_wdev; |
| } |
| |
| wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode); |
| |
| ndev = alloc_netdev_mq(0, "wlan%d", ether_setup, IWM_TX_QUEUES); |
| if (!ndev) { |
| dev_err(dev, "no memory for network device instance\n"); |
| ret = -ENOMEM; |
| goto out_priv; |
| } |
| |
| ndev->netdev_ops = &iwm_netdev_ops; |
| ndev->ieee80211_ptr = wdev; |
| SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); |
| wdev->netdev = ndev; |
| |
| iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile), |
| GFP_KERNEL); |
| if (!iwm->umac_profile) { |
| dev_err(dev, "Couldn't alloc memory for profile\n"); |
| ret = -ENOMEM; |
| goto out_profile; |
| } |
| |
| iwm_init_default_profile(iwm, iwm->umac_profile); |
| |
| return iwm; |
| |
| out_profile: |
| free_netdev(ndev); |
| |
| out_priv: |
| iwm_priv_deinit(iwm); |
| |
| out_wdev: |
| iwm_wdev_free(iwm); |
| return ERR_PTR(ret); |
| } |
| |
| void iwm_if_free(struct iwm_priv *iwm) |
| { |
| if (!iwm_to_ndev(iwm)) |
| return; |
| |
| cancel_delayed_work_sync(&iwm->ct_kill_delay); |
| free_netdev(iwm_to_ndev(iwm)); |
| iwm_priv_deinit(iwm); |
| kfree(iwm->umac_profile); |
| iwm->umac_profile = NULL; |
| iwm_wdev_free(iwm); |
| } |
| |
| int iwm_if_add(struct iwm_priv *iwm) |
| { |
| struct net_device *ndev = iwm_to_ndev(iwm); |
| int ret; |
| |
| ret = register_netdev(ndev); |
| if (ret < 0) { |
| dev_err(&ndev->dev, "Failed to register netdev: %d\n", ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| void iwm_if_remove(struct iwm_priv *iwm) |
| { |
| unregister_netdev(iwm_to_ndev(iwm)); |
| } |