good02xaut的笔记 https://bbs.21ic.com/?183157 [收藏] [复制] [RSS]

日志

linux无线网卡驱动解析

已有 5077 次阅读2007-8-9 23:46 |个人分类:netdriver|系统分类:嵌入式系统

linux无线网卡驱动解析

                                by good02xaut

linux无线网卡驱动在网卡驱动的基础上添加无线支持部分。

本文解析drivers/net/wireless/atmel.c中的部分代码。

具体的实现不解析,从框架上已经给出了做好的解析:)

 

 

Driver for Atmel at76c502 at76c504 and at76c506 wireless cards.

        Copyright 2000-2001 ATMEL Corporation.
        Copyright 2003-2004 Simon Kelley.

 

1。发送函数

发送过程如下:

start_tx=>IRQ=>tx_done_irq=>netif_wake_queue;

 

static int start_tx(struct sk_buff *skb, struct net_device *dev)
{
 static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
 struct atmel_private *priv = netdev_priv(dev);
 struct ieee80211_hdr_4addr header;
 unsigned long flags;
 u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;

 if (priv->card && priv->present_callback &&
     !(*priv->present_callback)(priv->card)) {
  priv->stats.tx_errors++;
  dev_kfree_skb(skb);
  return 0;
 }

 if (priv->station_state != STATION_STATE_READY) {
  priv->stats.tx_errors++;
  dev_kfree_skb(skb);
  return 0;
 }

 /* first ensure the timer func cannot run */
 spin_lock_bh(&priv->timerlock);
 /* then stop the hardware ISR */
 spin_lock_irqsave(&priv->irqlock, flags);
 /* nb doing the above in the opposite order will deadlock */

 /* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the
    12 first bytes (containing DA/SA) and put them in the appropriate
    fields of the Wireless Header. Thus the packet length is then the
    initial + 18 (+30-12) */

 if (!(buff = find_tx_buff(priv, len + 18))) {
  priv->stats.tx_dropped++;
  spin_unlock_irqrestore(&priv->irqlock, flags);
  spin_unlock_bh(&priv->timerlock);
  netif_stop_queue(dev);
  return 1;
 }

 frame_ctl = IEEE80211_FTYPE_DATA;
 header.duration_id = 0;
 header.seq_ctl = 0;
 if (priv->wep_is_on)
  frame_ctl |= IEEE80211_FCTL_PROTECTED;
 if (priv->operating_mode == IW_MODE_ADHOC) {
  skb_copy_from_linear_data(skb, &header.addr1, 6);
  memcpy(&header.addr2, dev->dev_addr, 6);
  memcpy(&header.addr3, priv->BSSID, 6);
 } else {
  frame_ctl |= IEEE80211_FCTL_TODS;
  memcpy(&header.addr1, priv->CurrentBSSID, 6);
  memcpy(&header.addr2, dev->dev_addr, 6);
  skb_copy_from_linear_data(skb, &header.addr3, 6);
 }

 if (priv->use_wpa)
  memcpy(&header.addr4, SNAP_RFC1024, 6);

 header.frame_ctl = cpu_to_le16(frame_ctl);
 /* Copy the wireless header into the card */
 atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE);
 /* Copy the packet sans its 802.3 header addresses which have been replaced */
 atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12);
 priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE;

 /* low bit of first byte of destination tells us if broadcast */
 tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA);
 dev->trans_start = jiffies;
 priv->stats.tx_bytes += len;

 spin_unlock_irqrestore(&priv->irqlock, flags);
 spin_unlock_bh(&priv->timerlock);
 dev_kfree_skb(skb);

 return 0;
}

 

static void tx_done_irq(struct atmel_private *priv)
{
 int i;

 for (i = 0;
      atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE &&
       i < priv->host_info.tx_desc_count;
      i++) {
  u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head));
  u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head));
  u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head));

  atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0);

  priv->tx_free_mem += msdu_size;
  priv->tx_desc_free++;

  if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size))
   priv->tx_buff_head = 0;
  else
   priv->tx_buff_head += msdu_size;

  if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1))
   priv->tx_desc_head++ ;
  else
   priv->tx_desc_head = 0;

  if (type == TX_PACKET_TYPE_DATA) {
   if (status == TX_STATUS_SUCCESS)
    priv->stats.tx_packets++;
   else
    priv->stats.tx_errors++;
   netif_wake_queue(priv->dev);
  }
 }
}

 

2。接收函数


接收过程如下:

IRQ=>rx_done_irq=>fast_rx_path=>netif_rx;

 

 

static void rx_done_irq(struct atmel_private *priv)
{
 int i;
 struct ieee80211_hdr_4addr header;

 for (i = 0;
      atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID &&
       i < priv->host_info.rx_desc_count;
      i++) {

  u16 msdu_size, rx_packet_loc, frame_ctl, seq_control;
  u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head));
  u32 crc = 0xffffffff;

  if (status != RX_STATUS_SUCCESS) {
   if (status == 0xc1) /* determined by experiment */
    priv->wstats.discard.nwid++;
   else
    priv->stats.rx_errors++;
   goto next;
  }

  msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head));
  rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head));

  if (msdu_size < 30) {
   priv->stats.rx_errors++;
   goto next;
  }

  /* Get header as far as end of seq_ctl */
  atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24);
  frame_ctl = le16_to_cpu(header.frame_ctl);
  seq_control = le16_to_cpu(header.seq_ctl);

  /* probe for CRC use here if needed  once five packets have
     arrived with the same crc status, we assume we know what's
     happening and stop probing */
  if (priv->probe_crc) {
   if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) {
    priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size);
   } else {
    priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24);
   }
   if (priv->do_rx_crc) {
    if (priv->crc_ok_cnt++ > 5)
     priv->probe_crc = 0;
   } else {
    if (priv->crc_ko_cnt++ > 5)
     priv->probe_crc = 0;
   }
  }

  /* don't CRC header when WEP in use */
  if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) {
   crc = crc32_le(0xffffffff, (unsigned char *)&header, 24);
  }
  msdu_size -= 24; /* header */

  if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
   int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS;
   u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG;
   u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4;

   if (!more_fragments && packet_fragment_no == 0) {
    fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc);
   } else {
    frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc,
          packet_sequence_no, packet_fragment_no, more_fragments);
   }
  }

  if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
   /* copy rest of packet into buffer */
   atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size);

   /* we use the same buffer for frag reassembly and control packets */
   memset(priv->frag_source, 0xff, 6);

   if (priv->do_rx_crc) {
    /* last 4 octets is crc */
    msdu_size -= 4;
    crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size);
    if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) {
     priv->stats.rx_crc_errors++;
     goto next;
    }
   }

   atmel_management_frame(priv, &header, msdu_size,
            atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head)));
  }

next:
  /* release descriptor */
  atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED);

  if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1))
   priv->rx_desc_head++;
  else
   priv->rx_desc_head = 0;
 }
}

 

static void fast_rx_path(struct atmel_private *priv,
    struct ieee80211_hdr_4addr *header,
    u16 msdu_size, u16 rx_packet_loc, u32 crc)
{
 /* fast path: unfragmented packet copy directly into skbuf */
 u8 mac4[6];
 struct sk_buff *skb;
 unsigned char *skbp;

 /* get the final, mac 4 header field, this tells us encapsulation */
 atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6);
 msdu_size -= 6;

 if (priv->do_rx_crc) {
  crc = crc32_le(crc, mac4, 6);
  msdu_size -= 4;
 }

 if (!(skb = dev_alloc_skb(msdu_size + 14))) {
  priv->stats.rx_dropped++;
  return;
 }

 skb_reserve(skb, 2);
 skbp = skb_put(skb, msdu_size + 12);
 atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size);

 if (priv->do_rx_crc) {
  u32 netcrc;
  crc = crc32_le(crc, skbp + 12, msdu_size);
  atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4);
  if ((crc ^ 0xffffffff) != netcrc) {
   priv->stats.rx_crc_errors++;
   dev_kfree_skb(skb);
   return;
  }
 }

 memcpy(skbp, header->addr1, 6); /* destination address */
 if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
  memcpy(&skbp[6], header->addr3, 6);
 else
  memcpy(&skbp[6], header->addr2, 6); /* source address */

 priv->dev->last_rx = jiffies;
 skb->protocol = eth_type_trans(skb, priv->dev);
 skb->ip_summed = CHECKSUM_NONE;
 netif_rx(skb);
 priv->stats.rx_bytes += 12 + msdu_size;
 priv->stats.rx_packets++;
}

 

3。中断函数

static irqreturn_t service_interrupt(int irq, void *dev_id)
{
 struct net_device *dev = (struct net_device *) dev_id;
 struct atmel_private *priv = netdev_priv(dev);
 u8 isr;
 int i = -1;
 static u8 irq_order[] = {
  ISR_OUT_OF_RANGE,
  ISR_RxCOMPLETE,
  ISR_TxCOMPLETE,
  ISR_RxFRAMELOST,
  ISR_FATAL_ERROR,
  ISR_COMMAND_COMPLETE,
  ISR_IBSS_MERGE,
  ISR_GENERIC_IRQ
 };

 if (priv->card && priv->present_callback &&
     !(*priv->present_callback)(priv->card))
  return IRQ_HANDLED;

 /* In this state upper-level code assumes it can mess with
    the card unhampered by interrupts which may change register state.
    Note that even though the card shouldn't generate interrupts
    the inturrupt line may be shared. This allows card setup
    to go on without disabling interrupts for a long time. */
 if (priv->station_state == STATION_STATE_DOWN)
  return IRQ_NONE;

 atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */

 while (1) {
  if (!atmel_lock_mac(priv)) {
   /* failed to contact card */
   printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name);
   return IRQ_HANDLED;
  }

  isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET));
  atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);

  if (!isr) {
   atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */
   return i == -1 ? IRQ_NONE : IRQ_HANDLED;
  }

  atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */

  for (i = 0; i < ARRAY_SIZE(irq_order); i++)
   if (isr & irq_order)
    break;

  if (!atmel_lock_mac(priv)) {
   /* failed to contact card */
   printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name);
   return IRQ_HANDLED;
  }

  isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET));
  isr ^= irq_order;
  atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr);
  atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);

  switch (irq_order) {

  case ISR_OUT_OF_RANGE:
   if (priv->operating_mode == IW_MODE_INFRA &&
       priv->station_state == STATION_STATE_READY) {
    priv->station_is_associated = 0;
    atmel_scan(priv, 1);
   }
   break;

  case ISR_RxFRAMELOST:
   priv->wstats.discard.misc++;
   /* fall through */
  case ISR_RxCOMPLETE:
   rx_done_irq(priv);
   break;

  case ISR_TxCOMPLETE:
   tx_done_irq(priv);
   break;

  case ISR_FATAL_ERROR:
   printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name);
   atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
   break;

  case ISR_COMMAND_COMPLETE:
   atmel_command_irq(priv);
   break;

  case ISR_IBSS_MERGE:
   atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS,
          priv->CurrentBSSID, 6);
   /* The WPA stuff cares about the current AP address */
   if (priv->use_wpa)
    build_wpa_mib(priv);
   break;
  case ISR_GENERIC_IRQ:
   printk(KERN_INFO "%s: Generic_irq received.\n", dev->name);
   break;
  }
 }
}

 

4。无线网卡扩展部分

struct net_device *init_atmel_card(unsigned short irq, unsigned long port,
       const AtmelFWType fw_type,
       struct device *sys_dev,
       int (*card_present)(void *), void *card)
{


 dev->open = atmel_open;
 dev->stop = atmel_close;
 dev->change_mtu = atmel_change_mtu;
 dev->set_mac_address = atmel_set_mac_address;
 dev->hard_start_xmit = start_tx;
 dev->get_stats = atmel_get_stats;
 dev->wireless_handlers = (struct iw_handler_def *)&atmel_handler_def;
 dev->do_ioctl = atmel_ioctl;
 dev->irq = irq;
 dev->base_addr = port;

}

 

static const struct iw_handler_def  atmel_handler_def =
{
 .num_standard = ARRAY_SIZE(atmel_handler),
 .num_private = ARRAY_SIZE(atmel_private_handler),
 .num_private_args = ARRAY_SIZE(atmel_private_args),
 .standard = (iw_handler *) atmel_handler,
 .private = (iw_handler *) atmel_private_handler,
 .private_args = (struct iw_priv_args *) atmel_private_args,
 .get_wireless_stats = atmel_get_wireless_stats
};

 

 

static const iw_handler   atmel_handler[] =
{
 (iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */
 (iw_handler) atmel_get_name,  /* SIOCGIWNAME */
 (iw_handler) NULL,   /* SIOCSIWNWID */
 (iw_handler) NULL,   /* SIOCGIWNWID */
 (iw_handler) atmel_set_freq,  /* SIOCSIWFREQ */
 (iw_handler) atmel_get_freq,  /* SIOCGIWFREQ */
 (iw_handler) atmel_set_mode,  /* SIOCSIWMODE */
 (iw_handler) atmel_get_mode,  /* SIOCGIWMODE */
 (iw_handler) NULL,   /* SIOCSIWSENS */
 (iw_handler) NULL,   /* SIOCGIWSENS */
 (iw_handler) NULL,   /* SIOCSIWRANGE */
 (iw_handler) atmel_get_range,           /* SIOCGIWRANGE */
 (iw_handler) NULL,   /* SIOCSIWPRIV */
 (iw_handler) NULL,   /* SIOCGIWPRIV */
 (iw_handler) NULL,   /* SIOCSIWSTATS */
 (iw_handler) NULL,   /* SIOCGIWSTATS */
 (iw_handler) NULL,   /* SIOCSIWSPY */
 (iw_handler) NULL,   /* SIOCGIWSPY */
 (iw_handler) NULL,   /* -- hole -- */
 (iw_handler) NULL,   /* -- hole -- */
 (iw_handler) atmel_set_wap,  /* SIOCSIWAP */
 (iw_handler) atmel_get_wap,  /* SIOCGIWAP */
 (iw_handler) NULL,   /* -- hole -- */
 (iw_handler) NULL,   /* SIOCGIWAPLIST */
 (iw_handler) atmel_set_scan,  /* SIOCSIWSCAN */
 (iw_handler) atmel_get_scan,  /* SIOCGIWSCAN */
 (iw_handler) atmel_set_essid,  /* SIOCSIWESSID */
 (iw_handler) atmel_get_essid,  /* SIOCGIWESSID */
 (iw_handler) NULL,   /* SIOCSIWNICKN */
 (iw_handler) NULL,   /* SIOCGIWNICKN */
 (iw_handler) NULL,   /* -- hole -- */
 (iw_handler) NULL,   /* -- hole -- */
 (iw_handler) atmel_set_rate,  /* SIOCSIWRATE */
 (iw_handler) atmel_get_rate,  /* SIOCGIWRATE */
 (iw_handler) atmel_set_rts,  /* SIOCSIWRTS */
 (iw_handler) atmel_get_rts,  /* SIOCGIWRTS */
 (iw_handler) atmel_set_frag,  /* SIOCSIWFRAG */
 (iw_handler) atmel_get_frag,  /* SIOCGIWFRAG */
 (iw_handler) NULL,   /* SIOCSIWTXPOW */
 (iw_handler) NULL,   /* SIOCGIWTXPOW */
 (iw_handler) atmel_set_retry,  /* SIOCSIWRETRY */
 (iw_handler) atmel_get_retry,  /* SIOCGIWRETRY */
 (iw_handler) atmel_set_encode,  /* SIOCSIWENCODE */
 (iw_handler) atmel_get_encode,  /* SIOCGIWENCODE */
 (iw_handler) atmel_set_power,  /* SIOCSIWPOWER */
 (iw_handler) atmel_get_power,  /* SIOCGIWPOWER */
 (iw_handler) NULL,   /* -- hole -- */
 (iw_handler) NULL,   /* -- hole -- */
 (iw_handler) NULL,   /* SIOCSIWGENIE */
 (iw_handler) NULL,   /* SIOCGIWGENIE */
 (iw_handler) atmel_set_auth,  /* SIOCSIWAUTH */
 (iw_handler) atmel_get_auth,  /* SIOCGIWAUTH */
 (iw_handler) atmel_set_encodeext, /* SIOCSIWENCODEEXT */
 (iw_handler) atmel_get_encodeext, /* SIOCGIWENCODEEXT */
 (iw_handler) NULL,   /* SIOCSIWPMKSA */
};

easy from hardworking~~

路过

鸡蛋

鲜花

握手

雷人

发表评论 评论 (2 个评论)

sweec 2008-1-10 06:03
看过你几篇关于驱动的文章,感觉都是介绍的好几年前的资料。现如今的linux还是用的这些吗?你的关于framebuffer驱动介绍的是vesa驱动吧?
访客I41Pgs 2008-4-17 16:42
能不能详细解释下 为什么发送时要进IRQ?