As we described in the hello world post, the madwifi driver sits between the kernel’s network layer and the physical hardware abstraction layer (HAL). When a user sends packets from his computer, using a socket connection, for example, the network layer invokes certain predefined functions of the MAC layer. The driver implements these entry points and processes the packets, adding MAC headers, deciding transmit rate and so on, and then calls the HAL code to physically transmit the packet. It is the HAL’s responsibility to enable the required registers on the hardware for sending the packet at the appropriate rate and timing.
When a packet is received by the hardware, it causes an interrupt at the driver level. The driver processes the interrupt and effectively receives the packet in software. The MAC header is stripped off the packet, and the packet is sent over to the network layer for further processing.
The Linux network layer calls the function pointed to by the function pointer hard_start_xmit in the net_device structure. One of the first things the madwifi driver does (in ath_attach()) is to initialize this function pointer to point to ath_hardstart(). The parameters to ath_hardstart() are the sk_buff structure and the net_device structure. This simplicity is an artifact of the layering structure that networking follows. The layer above passes to the lower layer only the cooked packet. The lower layer further cooks the packet and passes it to the next layer. The contents of the packet handed over by the network layer are accessible from the skb->data pointer. From this point forward, the packet is prepared for transmission on the wifi interface. It must be given a valid 802.11 header and the driver must decide the various values in this header. All this is done if the wifi device is in ap, sta, adhoc or ahdemo mode. If the device is in the monitor mode, no 802.11 header is attached to the packet and the packet is sent raw on air. Therefore, when in the monitor mode, ath_hardstart() quickly calls the ath_tx_startraw() function, whereas in other modes, the ath_tx_start() function is called. The monitor mode in madwifi thus actually allows one to transmit raw packets. A very important step performed by both the ath_start and ath_startraw functions is the call to ath_hal_setuptxdesc() HAL function. This call hands over the buffer to the hardware and sets up the parameters essential to send the packet on air. It sets the transmit power, the transmit rate, the antenna to use (most Atheros cards have more than one antenna), the type of packet and a host of other parameters through the flags. A lot of variations in the standard packet transmission style can be achieved by varying some of these parameters. For example, if you write some new auto-rate algorithm, you may just change the value of the txrate parameter to force the hardware to send packets at that rate. However, note that not all packets go through this function call. Some packets such as acknowledgments are produced by the hardware and are sent only at predefined rates. But all packets that are generated by the network layer go through this call, and therefore, it can be a good place to hack in. Another feature that we found useful, and had a very hard time figuring out, is to get all packets timestamped by the hardware while transmission. We were operating in the monitor mode and had our own MAC header attached to the network layer packet instead of the standard 802.11 header. Now, we wanted all our packets to be timestamped by the hardware to facilitate synchronization between nodes. The hardware always timestamps beacons, hence, if we can fool the hardware into believing that the packet being sent is a beacon, we can achieve timestamping. This fooling can be done by changing the packet type (atype) parameter to HAL_PKT_TYPE_BEACON in the call to ath_hal_setuptxdesc().
Both the ath_start() and the ath_startraw() functions call the ath_tx_txqaddbuf() function that actually inserts a buffer on a specific transmit queue (there are multiple hardware queues) and then calls the ath_hal_txstart() function. This function opens up a gate in the hardware that releases all the buffered packets buffered on the particular hardware queue.
When the hardware receives a packet, it causes the HAL_INT_RX interrupt. Interrupts are handled by the ath_intr() function. Since a lot of processing might be required on a received packet, this processing is deferred to a tasklet known as ath_rx_tasklet(). The tasklet is scheduled soon after the interrupt is fired, whenever the OS deems fit. Hence, it is inappropriate to assume the time of call to the rx_tasklet as being equivalent to the time of packet reception. Instead, a variable inside the ath_softc structure can give us the exact time of complete packet reception. The tasklet is a good place to check for physical errors, the reception rate, the per packet reception signal level and noise floor level. We have observed some odd packets that have length=0, frame type and subtype=0 in the ath_rx_tasklet() function. We believe that these frames are an artifact of the devices we used, but nonetheless, such frames must be ignored (all valid packets are received just fine even after ignoring these phantom entries). In ap, sta, adhoc and ahdemo mode, the tasklet goes on to process the packet further. In the monitor mode, the packet must be sent to the higher layer almost raw. A radiotap/prism2 header may be optionally attached to the raw received frame to preserve the PHY information regarding the rate of transmission etc. before passing it to the network layer. Applications such as tcpdump, wireshark and kismet benefit from the radiotap headers. Packets are passed to the ath_rx_capture() function which then calls the ieee80211_input_monitor() function. The ieee80211_input_monitor() function then calls the network layer function netif_rx() to complete its handling of the packet.
As a side note, the MAC layer does not free the skb it receives from the network layer or the hardware (in either direction). The skb is shared between all layers. Also, in the monitor mode the hardware sends all packets to the driver. In other modes, the driver receives only packets destined to it and broadcast packets. This fundamental difference between monitor mode and other modes, may cause a lot more interrupt handling at the driver for nodes in monitor mode than for other nodes. This may affect the performance of other applications if the monitor mode node is also expected to do other (time sensitive) tasks.