* arch/arm/src/rtl8720c/amebaz_netdev.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <string.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/kmalloc.h>
#include "amebaz_netdev.h"
* Public Functions
****************************************************************************/
static void amebaz_netdev_notify_tx_done(struct amebaz_dev_s *priv)
{
work_queue(LPWORK, &priv->pollwork, amebaz_txavail_work, priv, 0);
}
static int amebaz_txpoll(struct net_driver_s *dev)
{
struct amebaz_dev_s *priv = (struct amebaz_dev_s *)dev->d_private;
net_lock();
if (!priv->curr)
{
net_unlock();
amebaz_netdev_notify_tx_done(priv);
return false;
}
DEBUGASSERT(priv->curr->tail == priv->dev.d_buf);
skb_put(priv->curr, priv->dev.d_len);
rltk_wlan_send_skb(priv->devnum, priv->curr);
priv->dev.d_buf = NULL;
priv->curr = NULL;
net_unlock();
NETDEV_TXPACKETS(&priv->dev);
amebaz_netdev_notify_tx_done(priv);
return true;
}
static int amebaz_transmit(struct amebaz_dev_s *priv)
{
struct sk_buff *skb;
skb = rltk_wlan_alloc_skb(priv->dev.d_len);
if (!skb)
{
NETDEV_TXERRORS(&priv->dev);
return -ENOMEM;
}
NETDEV_TXPACKETS(&priv->dev);
memcpy(skb->tail, priv->dev.d_buf, priv->dev.d_len);
skb_put(skb, priv->dev.d_len);
rltk_wlan_send_skb(priv->devnum, skb);
NETDEV_TXDONE(&priv->dev);
return OK;
}
static void amebaz_reply(struct amebaz_dev_s *priv)
{
if (priv->dev.d_len > 0)
{
amebaz_transmit(priv);
}
}
void amebaz_netdev_notify_receive(struct amebaz_dev_s *priv,
int index, unsigned int len)
{
struct net_driver_s *dev = &priv->dev;
struct eth_hdr_s *hdr;
struct sk_buff *skb;
void *oldbuf;
skb = rltk_wlan_get_recv_skb(index);
if (skb == NULL)
{
return;
}
if (!IFF_IS_UP(dev->d_flags))
{
skb_pull(skb, len);
return;
}
net_lock();
oldbuf = priv->dev.d_buf;
hdr = (struct eth_hdr_s *)skb->data;
priv->dev.d_buf = (void *)skb->data;
priv->dev.d_len = len;
NETDEV_RXPACKETS(&priv->dev);
#ifdef CONFIG_NET_PKT
pkt_input(&priv->dev);
#endif
if (hdr->type == HTONS(TPID_8021QVLAN))
{
uint8_t temp_buffer[12];
memcpy(temp_buffer, skb->data, 12);
memcpy(skb->data + 4, temp_buffer, 12);
priv->dev.d_buf = skb->data = skb->data + 4;
priv->dev.d_len -= 4;
}
#ifdef CONFIG_NET_IPv4
if (hdr->type == HTONS(ETHTYPE_IP))
{
NETDEV_RXIPV4(&priv->dev);
ipv4_input(&priv->dev);
amebaz_reply(priv);
}
else
{
#endif
#ifdef CONFIG_NET_IPv6
if (hdr->type == HTONS(ETHTYPE_IP6))
{
NETDEV_RXIPV6(&priv->dev);
ipv6_input(&priv->dev);
amebaz_reply(priv);
}
else
{
#endif
#ifdef CONFIG_NET_ARP
if (hdr->type == HTONS(ETHTYPE_ARP))
{
arp_input(&priv->dev);
NETDEV_RXARP(&priv->dev);
if (priv->dev.d_len > 0)
{
amebaz_transmit(priv);
}
}
else
#endif
{
NETDEV_RXDROPPED(&priv->dev);
}
#ifdef CONFIG_NET_IPv6
}
#endif
#ifdef CONFIG_NET_IPv4
}
#endif
skb_pull(skb, len);
priv->dev.d_buf = oldbuf;
net_unlock();
}
static void amebaz_txavail_work(void *arg)
{
struct amebaz_dev_s *priv = (struct amebaz_dev_s *)arg;
struct net_driver_s *dev = &priv->dev;
net_lock();
if (IFF_IS_UP(dev->d_flags))
{
if (!priv->curr && rltk_wlan_check_isup(priv->devnum))
{
priv->curr = rltk_wlan_alloc_skb(MAX_NETDEV_PKTSIZE);
if (priv->curr)
{
priv->dev.d_buf = priv->curr->tail;
priv->dev.d_len = 0;
}
}
if (priv->dev.d_buf)
{
devif_poll(&priv->dev, amebaz_txpoll);
}
}
net_unlock();
}
static int amebaz_txavail(struct net_driver_s *dev)
{
struct amebaz_dev_s *priv = (struct amebaz_dev_s *)dev->d_private;
if (work_available(&priv->pollwork))
{
work_queue(LPWORK, &priv->pollwork, amebaz_txavail_work, priv, 0);
}
return OK;
}
int amebaz_ioctl(struct net_driver_s *dev, int cmd,
unsigned long arg)
{
struct amebaz_dev_s *priv = (struct amebaz_dev_s *)dev->d_private;
int ret;
if (!IFF_IS_UP(dev->d_flags) ||
(!rltk_wlan_running(priv->devnum) && cmd != SIOCSIWMODE))
{
return -EINVAL;
}
switch (cmd)
{
case SIOCSIWSCAN:
ret = amebaz_wl_start_scan(priv, (void *)arg);
break;
case SIOCGIWSCAN:
ret = amebaz_wl_get_scan_results(priv, (void *)arg);
break;
case SIOCSIWENCODEEXT:
ret = amebaz_wl_set_encode_ext(priv, (void *)arg);
break;
case SIOCGIWENCODEEXT:
ret = amebaz_wl_get_encode_ext(priv, (void *)arg);
break;
case SIOCSIWESSID:
ret = amebaz_wl_set_ssid(priv, (void *)arg);
break;
case SIOCSIWAP:
ret = amebaz_wl_set_bssid(priv, (void *)arg);
break;
case SIOCSIWMODE:
ret = amebaz_wl_set_mode(priv, (void *)arg);
break;
case SIOCSIWCOUNTRY:
ret = amebaz_wl_set_country(priv, (void *)arg);
break;
case SIOCGIWFREQ:
ret = amebaz_wl_get_freq(priv, (void *)arg);
break;
case SIOCSIWFREQ:
ret = amebaz_wl_set_freq(priv, (void *)arg);
break;
case SIOCGIWAP:
case SIOCGIWMODE:
case SIOCGIWESSID:
case SIOCGIWSENS:
case SIOCSIWAUTH:
case SIOCGIWAUTH:
case SIOCSIFHWADDR:
case SIOCGIFHWADDR:
case SIOCSIWRATE:
case SIOCGIWRATE:
case SIOCSIWTXPOW:
case SIOCGIWTXPOW:
ret = amebaz_wl_process_command(priv, cmd, (void *)arg);
break;
default:
wlwarn("ERROR: Unrecognized IOCTL command: %d\n", cmd);
ret = -ENOTTY;
break;
}
return ret;
}
static int amebaz_ifup(struct net_driver_s *dev)
{
struct amebaz_dev_s *priv = (struct amebaz_dev_s *)dev->d_private;
if (!IFF_IS_UP(dev->d_flags))
{
priv->mode = RTW_MODE_NONE;
priv->conn.status = AMEBAZ_STATUS_DISABLED;
}
return OK;
}
static int amebaz_ifdown(struct net_driver_s *dev)
{
int ret = 0;
struct amebaz_dev_s *priv = (struct amebaz_dev_s *)dev->d_private;
if (priv->devnum == 0 && rltk_wlan_running(1))
{
printf("must ifdown wlan 1 first\r\n");
return ERROR;
}
nxmutex_lock(&priv->lock);
if (IFF_IS_UP(dev->d_flags))
{
if (priv->curr)
{
skb_put(priv->curr, 0);
rltk_wlan_send_skb(priv->devnum, priv->curr);
priv->curr = NULL;
}
if (priv->devnum == 0)
{
rltk_wlan_deinit();
}
else if (priv->mode == RTW_MODE_STA_AP)
{
ret = rltk_set_mode_prehandle(RTW_MODE_STA_AP,
RTW_MODE_STA, "wlan0");
rtw_msleep_os(50);
ret = rltk_set_mode_posthandle(RTW_MODE_STA_AP,
RTW_MODE_STA, "wlan0");
while (rltk_wlan_running(1))
{
rtw_msleep_os(50);
}
}
}
nxmutex_unlock(&priv->lock);
return ret;
}
int amebaz_netdev_register(struct amebaz_dev_s *priv)
{
struct net_driver_s *dev = &priv->dev;
dev->d_ifup = amebaz_ifup;
dev->d_ifdown = amebaz_ifdown;
dev->d_txavail = amebaz_txavail;
nxmutex_init(priv->lock);
#ifdef CONFIG_NETDEV_IOCTL
dev->d_ioctl = amebaz_ioctl;
#endif
dev->d_private = priv;
return netdev_register(dev, NET_LL_IEEE80211);
}