Skip to main content

12.4 Power Save Mode

Beyond forwarding packets, an AP has another critical function: acting as a caretaker for "sleeping" clients by caching their data.

Most wireless clients run on battery power—phones, laptops, and IoT devices. To conserve energy, the wireless NIC can't keep its receiver on all the time; it needs to take naps periodically. But this raises a problem: what happens to the messages sent to you while you're asleep?

In wired networks, this isn't an issue because the other end of the cable is always powered. In the wireless world, however, it's a major challenge. In this section, we'll look at how APs and clients in Infrastructure mode solve this problem using an elegant "push-pull" mechanism.


Entering Power Save Mode

When a client decides to enter power save mode, it can't just quietly doze off—it needs to notify the AP first.

How? Typically by sending a Null Data Packet. The name sounds convoluted, but it's simply a data frame with only an 802.11 header and no payload. The real purpose of this packet isn't to carry data, but to flip a flag in the Frame Control field of the header: the PM (Power Management) bit.

Once this PM bit is set to 1, the AP understands: this client is going to sleep.

You can think of this as hanging a "Do Not Disturb" sign on your door. You put up the sign (send the Null Packet), and when the waiter (AP) passes by and sees it, they stop knocking on your door to answer calls.

But the "hanging a sign" analogy falls short in one respect: a real waiter might just not knock, but the caller might hang up. In 802.11, however, the AP not only avoids disturbing you, but also records and stores all incoming calls (packets) for you (caching).

Upon receiving a packet with PM=1, the AP immediately activates a caching mechanism. Inside the kernel (specifically in the mac80211 subsystem), it maintains a queue for each associated station called ps_tx_buf.

This ps_tx_buf is a linked list structure dedicated to storing unicast packets destined for that station.

However, memory is finite. mac80211 sets a hard limit on the ps_tx_buf for each station: 128 packets (STA_MAX_TX_BUFFER).

⚠️ Warning

This limit is strict. If a client sleeps for too long while the sender is aggressive, these 128 slots will fill up quickly.

What happens when it's full? The AP won't crash; it starts "throwing things out." It follows the FIFO (First In, First Out) principle—dropping the oldest packets to make room for new ones. This means if your phone goes into a deep sleep in power save mode, you might find that the earliest messages are missing when you wake up, and they are gone for good.

Beyond unicast packets, there's an even trickier customer: multicast/broadcast packets.

Why are they tricky? Because by definition in 802.11, a broadcast packet must be delivered to everyone in the BSS. As long as even one person is sleeping, the broadcast packet cannot be transmitted—because the sleeping client wouldn't receive it anyway.

To solve this, the AP maintains a global, shared buffer: bc_buf.

As long as at least one station in the BSS is in sleep mode, the AP will stuff all multicast and broadcast packets into this bc_buf. This buffer also has a limit, which is likewise 128 packets (AP_MAX_BC_BUFFER).

Now we can refine our analogy: the AP is like a front desk. When you're asleep (PM=1), the front desk stuffs your personal mail into your private mailbox, and locks the mass-mailed flyers in a drawer—as long as even one person in the room is sleeping, the flyers aren't distributed; they wait until everyone is awake.


Exiting Power Save Mode

Clients sleep to save power, not to enter a coma. They need to wake up and do work. How do they know when to wake up? Usually via a timer.

What's the first thing to do upon waking up? Listen to the broadcast.

The AP periodically sends a special management frame called a Beacon. This frequency is quite high, typically 10 per second (100ms interval). You can think of it as the AP's heartbeat, or a clock chiming on the hour.

Beacon frames don't just carry timing synchronization; they also carry a critical Information Element called the TIM (Traffic Indication Map).

This is a very clever bitmap design.

Remember the AID we mentioned earlier? Every associated station has an ID from 1 to 2007. The TIM is an array containing 2008 bits (corresponding to AID 0-2007). If the AP finds packets sitting in your ps_tx_buf, it sets your corresponding bit in the TIM to 1.

After waking up, the client catches a Beacon and starts checking the TIM.

This check is implemented in the kernel via the ieee80211_check_tim() method (defined in include/linux/ieee80211.h). It doesn't need to read the entire TIM; it only needs to calculate whether the bit corresponding to its AID is 1.

  • If it's 0: Great, nobody is looking for me, go back to sleep.
  • If it's 1: Wake up, there's work to do.

Once a client sees its bit set to 1, it knows the AP is holding packages for it. At this point, it needs to actively "pick up the goods."

How? By sending a PS-Poll (Power Save Poll) control frame.

You can think of PS-Poll as you slapping the front desk: "Hey, where's my mail?".

Upon receiving the PS-Poll, the AP pulls a packet from the ps_tx_buf and sends it to you. Here's the subtle part: the AP sets a flag called IEEE80211_FCTL_MOREDATA in the Frame Control field of every packet it sends out.

  • If this bit is 1: It means "there's more, keep polling."
  • If this bit is 0: It means "this is the last one, take it and go back to sleep."

The client relies on this bit to decide whether to send another PS-Poll. Once it receives a packet with MOREDATA=0, it knows the inventory is empty and will typically re-enter sleep mode (of course, the standard doesn't force you to sleep; you can stay awake, but your battery will drain fast).

Back to the "front desk" analogy: The TIM is like a scrolling screen in the lobby displaying "Package for Room 101." You wake up, glance at the screen, see your name, and go tap your card at the counter. The front desk hands you the first package and casually says, "There's one more." You take it and tap your card again, until the front desk hands you the last one without saying anything—then you know you're done and can go back to your room to sleep.


Handling the Multicast/Broadcast Buffer

The logic for unicast packets is straightforward: cache for whoever is sleeping. But the logic for multicast/broadcast packets is slightly more complex.

Per the standard, the AID for multicast/broadcast is defined as 0.

So, when the AP has a broadcast packet to send and someone in the room is sleeping, it sets bit 0 of the TIM (TIM[0]) to 1. But that alone isn't enough.

Broadcast packets are noisy and power-hungry. If a client had to process broadcast packets every time it woke up, it would be miserable. Moreover, many devices aren't very interested in broadcast packets. Therefore, 802.11 introduced a concept called DTIM (Delivery TIM).

DTIM is a special type of TIM that doesn't appear in every Beacon, but rather every few Beacons. This interval is called the DTIM Period, typically configured to 3 or 5 (i.e., once every 300ms - 500ms).

Only after a Beacon carrying a DTIM does the AP dump all the backlogged broadcast/multicast packets from the bc_buf. This means clients can ignore the TIM[0] in normal Beacons and only wake up during the DTIM cycle to handle broadcast packets, drastically saving power.

In kernel code, we need to call the ieee80211_get_buffered_bc() method to "extract" these packets from the bc_buf.

Let's look at a diagram to clarify the positions of these buffers.

(Figure 12-3 here: Diagram of buffered packets in an AP, showing the sta_info linked list, their respective ps_tx_buf, and the shared bc_buf)

In the mac80211 implementation, the AP is abstracted as a ieee80211_if_ap object. This object has a member called ps (an instance of the ps_data structure). The shared bc_buf mentioned earlier lives right inside the ps structure.

The next diagram shows the process of a client frantically "picking up goods" using PS-Poll.

(Figure 12-4 here: Flowchart of a client sending PSPOLL packets to retrieve data packets from the AP's ps_tx_buf)

Notice the data flow in the diagram: every packet sent by the AP, except the last one, carries the MOREDATA flag. This is the signal that keeps the client hungry for more. Once this signal disappears, the interaction ends.

(For simplicity, the ACK flow isn't drawn in the diagram, but in reality every data packet requires an ACK acknowledgment. Don't forget this when implementing the driver.)


⚠️ Power Save vs. Power Management: Don't Mix Them Up

There's a terminology trap here that trips up many beginners (and even veterans).

Power Management and Power Save Mode are two entirely different things.

  • Power Save Mode: This is what we discussed above. The client sleeps for a few milliseconds, and the AP helps cache packets. This is a protocol-level task within 802.11 that happens during normal operation.
  • Power Management: This is a system-level task. For example, when you close your laptop lid (Suspend to RAM) or click hibernate (Hibernate). At this point, not only is the NIC down, but the entire CPU has nearly stopped. This is handled by the kernel's PM framework, in net/mac80211/pm.c, where it corresponds to the resume/suspend callbacks in the driver.

Don't confuse the two. If you find yourself trying to handle the TIM bitmap inside the suspend callback when writing a driver, you've walked into the wrong room.


In this section, we really only talked about one thing: trust.

The client trusts that the AP will safeguard its data packets while it sleeps; the AP trusts that the client will proactively pick them up when it wakes. To maintain this trust, they need seemingly tedious handshake signals like TIM, PS-Poll, and Null Data.

In the next section, we'll zoom out and look at the behind-the-scenes hero that supports all connections, scanning, and authentication—MLME (MAC Layer Management Entity). Without it, wireless networks would just be a disorganized mess.