null_func的作用

本文深入探讨了802.11标准中的NULL帧及其在无线网络中的作用,特别是它如何帮助客户端进行节能管理和无缝漫游。

I was speaking to a friend last evening on the topic of client troubleshooting. The discussion came up about roaming and roaming aggressiveness. We talked about the different aspects of client behavior and the discussion turned into an 802.11 frame discussion. More specifically the NULL frame.

The Null Data Frame is a very interesting frame. In fact, most folks overlook these frames, perhaps because they don’t know their importance. Just a few months ago I was troubleshooting a client issue and the NULL frame confirmed by idea and backed my findings as it pertained to a wireless issue I was troubleshooting

Lets look at the NULL frame and it's importance. 

The Null Data Frame is a control frame. It is only transmitted by a STA (wireless client). Access points do not transmit these frames. It carry’s no data payload. In fact, the only purpose of this frame (by standard) is to carry the power management bit in the frame controlled field. The power management bit will be either "0" zero or "1" one. 

When the STA sends a power management bit of "0" to the access point in which it is associated to, it is the STAs way of informing the access point that the STA is in an active power state (awake) and transmission of frames from access point to STA should be normal.  

When the STA sends a power management bit of "1" to the access point in which it is associated to. This is informing the access point that the STA is going offline and any frames that come into the access point for this STA should be buffered at the access point till the STA returns and sends a NULL frame of "0", active state. 

A text example of the exchange: 

STA ---NULL FRAME "0"----->  AP "Client says to the AP: Hey AP I’m online send me data"

 

STA ---NULL FRAME "1"----->  AP "Client says to the AP: Hey AP buffer any transmissions coming in for me. Ill be back in a bit (no pun intended)"

 

So why would a client go offline and what is the importance !?!?! Its very important. Lets talk through a few examples. 

There are two main reasons why a STA will go offline, or send a power man bit of "1" to an access point.

Power Save Mode: PSM allows a STA to go into "sleep or doze" mode. PSM essentially turns off the NIC radio for short burst to conserve battery power for a device. You will notice significant power conservation and longer battery life when PSM is enabled. VoIP phones, PDAs and other small battery form factor devices benefits from PSM. A word of *caution*, be aware that some applications can suffer from aggressive power save mode options. 

Active/Passive Scanning: The other reason why a STA will inform an access point to buffer its frames by sending a power man bit of "1" is when it’s ready to roam. Suppose a client has hit its roaming threshold and is seeking out another access point to associate to. In order to seek out other access points in the area it has to go off channel. By doing so, the STA tells the AP, buffer the frames man, ill be back for them in a bit!

Example:

The STA is on AP TEST, AP test is on channel 1

The client will send a NULL FRAME to the access point with the man bit of 1. The STA goes offline and floods channels 2,3,4,5,6,7,8,9,10,11 (depending on configuration of the client of course) with probe request looking for other APs.

Lets look at a packet capture:

Note frame 75 - This is my STA sending the AP a Data Null Frame. If you open the packet and drill down into the frame control you will see the power management bit is set 1.

 

Note frame 78 - This is my STA sending the AP a Data Null Frame. Note that the power bit is set to 0. Indicating to the STA is back on channel and any data that was buffered and future date should be sent to the client until its next doze state. 

Note frame 82 - STA is going back to bed!

 

What is also interesting to note is the TIME stamp. Look at the time delta between frames 75 - 78. This is the period of time the STA was off line, generally speaking.

You might ask when does the client come back online to the AP. Well that is dependent on how the STA is configured. For example, Intel has what I call the "slide bar" for PSM. The more aggressive the mode the longer the STA will be in sleep or doze mode. 

Now that you know what a NULL frame is and its purpose. If you are troubleshooting a STA issue pay attention to what the STA is telling the access point! If a client is sending NULL frames there is a reason why!

/* * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2022-2022. All rights reserved. * Description: hsan pinctrl driver. * Author : hsan * Create : 2022-10-18 * History: Init. */ #include <linux/io.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinconf-generic.h> #include "hi_types.h" #include "hi_pinctrl.h" #define IOSEL0 0x00 #define IOSEL1 0x04 #define JTAG_SEL 0x08 #define LVL1_SEL_L 0x0c #define LVL1_SEL_H 0x10 #define NULL_BIT 0x01 union hi_pin_cfg_reg_luofu { uint32_t value; struct { uint32_t pu : 1; /* pull up */ uint32_t pd : 1; /* pull down */ uint32_t ds : 3; /* drive strength */ uint32_t sten : 1; /* slow rate enable */ uint32_t pocfgb : 1; /* power on configuration bit */ uint32_t ien : 1; /* reserved */ uint32_t in : 1; /* input value */ uint32_t oen : 1; /* output enable */ uint32_t out : 1; /* output value */ uint32_t rsv0 : 21; } bits; }; hi_peri_pindata(0, 0x000, IOSEL0, 0, hi_func_name(gpio), LVL1_SEL_H, 7, hi_func_name(led0), /* LED1 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(1, 0x004, IOSEL0, 1, hi_func_name(gpio), LVL1_SEL_H, 6, hi_func_name(led0), /* LED0 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(2, 0x008, IOSEL0, 2, hi_func_name(jtag_gpio2), LVL1_SEL_H, 8, hi_func_name(eth_act_led0), /* ETH_ACK0 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(3, 0x00c, IOSEL0, 3, hi_func_name(jtag_gpio3), LVL1_SEL_H, 9, hi_func_name(eth_act_led1), /* ETH_ACK1 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(4, 0x010, IOSEL0, 4, hi_func_name(jtag_gpio4), LVL1_SEL_H, 10, hi_func_name(eth_act_led2), /* ETH_ACK2 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(5, 0x014, IOSEL0, 5, hi_func_name(jtag_gpio5), LVL1_SEL_H, 11, hi_func_name(eth_act_led3), /* ETH_ACK3 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(6, 0x018, LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(7, 0x01c, IOSEL0, 7, hi_func_name(gpio), LVL1_SEL_H, 12, hi_func_name(eth_act_led4), /* ETH_ACK4 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(8, 0x020, IOSEL0, 8, hi_func_name(gpio), LVL1_SEL_L, 0, hi_func_name(avs), /* CPU_VG */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(9, 0x024, IOSEL0, 9, hi_func_name(gpio), LVL1_SEL_L, 24, hi_func_name(phy_clk), /* PHY_REF_CLK */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(10, 0x028, IOSEL0, 10, hi_func_name(gpio), LVL1_SEL_H, 21, hi_func_name(mdio1), /* MDC1 MDIO CLK */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(11, 0x02c, IOSEL0, 11, hi_func_name(gpio), LVL1_SEL_H, 21, hi_func_name(mdio1), /* MDIO DATA */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(12, 0x030, IOSEL0, 12, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_TDATA3 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(13, 0x034, IOSEL0, 13, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_TDATA2 */ LVL1_SEL_L, 23, hi_func_name(spi), /* SPI_RXD */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(14, 0x038, IOSEL0, 14, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_TDATA1 */ LVL1_SEL_L, 23, hi_func_name(spi), /* SPI_TXD */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(15, 0x03c, IOSEL0, 15, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_TDATA0 */ LVL1_SEL_L, 23, hi_func_name(spi), /* SPI_CLK */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(16, 0x040, IOSEL0, 16, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_TXCLK */ LVL1_SEL_L, 9, hi_func_name(uart0), /* UART0_SIN */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(17, 0x044, IOSEL0, 17, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_TXEN */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(18, 0x048, IOSEL0, 18, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_RXCLK */ LVL1_SEL_L, 10, hi_func_name(uart0), /* UART0_CTS_N */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(19, 0x04c, IOSEL0, 19, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_RXDV */ LVL1_SEL_L, 10, hi_func_name(uart0), /* UART0_RTS_N */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(20, 0x050, IOSEL0, 20, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_RDATA0 */ LVL1_SEL_H, 14, hi_func_name(led1), /* LED1_1 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(21, 0x054, IOSEL0, 21, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_RDATA1 */ LVL1_SEL_H, 13, hi_func_name(led1), /* LED0_0 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(22, 0x058, IOSEL0, 22, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_RDATA2 */ LVL1_SEL_L, 9, hi_func_name(uart0), /* UART0_SOUT */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(23, 0x05c, IOSEL0, 23, hi_func_name(gpio), LVL1_SEL_L, 11, hi_func_name(rgmii0), /* RGMII_RDATA3 */ LVL1_SEL_L, 22, hi_func_name(spi), /* SPI_CS0 */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(24, 0x060, IOSEL0, 24, hi_func_name(gpio), LVL1_SEL_L, 15, hi_func_name(i2c), /* I2C_M_SCL */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(25, 0x064, IOSEL0, 25, hi_func_name(gpio), LVL1_SEL_L, 15, hi_func_name(i2c), /* I2C2_M_SDA */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(26, 0x068, IOSEL0, 26, hi_func_name(gpio), LVL1_SEL_L, 14, hi_func_name(clk_test), /* TEST_CLK */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(27, 0x06c, IOSEL0, 27, hi_func_name(gpio), LVL1_SEL_H, 17, hi_func_name(usb), /* USB_PWREN */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(28, 0x070, IOSEL0, 28, hi_func_name(gpio), LVL1_SEL_H, 16, hi_func_name(usb), /* USB_OVRCUR */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(29, 0x074, LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(30, 0x078, IOSEL0, 30, hi_func_name(gpio), LVL1_SEL_L, 2, hi_func_name(sfc), /* SFC_WP */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(31, 0x07c, LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(32, 0x080, LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(33, 0x084, IOSEL0, 1, hi_func_name(gpio), LVL1_SEL_L, 3, hi_func_name(sfc), /* DFC_HOLD */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(34, 0x088, LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(35, 0x08c, IOSEL0, 3, hi_func_name(gpio), LVL1_SEL_L, 7, hi_func_name(uart1), /* UART1_SIN */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); hi_peri_pindata(36, 0x090, IOSEL1, 3, hi_func_name(gpio), LVL1_SEL_L, 7, hi_func_name(uart1), /* UART1_SIN */ LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null), LVL1_SEL_L, NULL_BIT, hi_func_name(null)); /* group */ hi_pinctrl_groups(sfc); hi_pinctrl_groups(uart1); hi_pinctrl_groups(uart0); hi_pinctrl_groups(mdio1); hi_pinctrl_groups(spi); hi_pinctrl_groups(i2c); hi_pinctrl_groups(rgmii0); hi_pinctrl_groups(eth_act_led0); hi_pinctrl_groups(eth_act_led1); hi_pinctrl_groups(eth_act_led2); hi_pinctrl_groups(eth_act_led3); hi_pinctrl_groups(eth_act_led4); hi_pinctrl_groups(led0); hi_pinctrl_groups(led1); hi_pinctrl_groups(clk_test); hi_pinctrl_groups(avs); hi_pinctrl_groups(phy_clk); hi_pinctrl_groups(usb); hi_pinctrl_groups(jtag_gpio2); hi_pinctrl_groups(jtag_gpio3); hi_pinctrl_groups(jtag_gpio4); hi_pinctrl_groups(jtag_gpio5); /* sfc */ static const uint32_t sfc_pins[] = {30, 33}; /* uart1 */ static const uint32_t uart1_pins[] = {35, 36}; /* uart0 */ static const uint32_t uart0_pins[] = {16, 18, 19, 22}; /* mdio */ static const uint32_t mdio1_pins[] = {10, 11}; /* spi */ static const uint32_t spi_pins[] = {13, 14, 15, 23}; /* i2c */ static const uint32_t i2c_pins[] = {24, 25}; /* rgmii */ static const uint32_t rgmii0_pins[] = {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; /* eth led */ static const uint32_t eth_act_led0_pins[] = {2}; static const uint32_t eth_act_led1_pins[] = {3}; static const uint32_t eth_act_led2_pins[] = {4}; static const uint32_t eth_act_led3_pins[] = {5}; static const uint32_t eth_act_led4_pins[] = {7}; /* led */ static const uint32_t led0_pins[] = {0, 1}; static const uint32_t led1_pins[] = {20, 21}; /* clk_test */ static const uint32_t clk_test_pins[] = {26}; /* avs */ static const uint32_t avs_pins[] = {8}; /* phy_clk */ static const uint32_t phy_clk_pins[] = {9}; /* usb */ static const uint32_t usb_pins[] = {27, 28}; /* jtag_gpio */ static const uint32_t jtag_gpio2_pins[] = {2}; static const uint32_t jtag_gpio3_pins[] = {3}; static const uint32_t jtag_gpio4_pins[] = {4}; static const uint32_t jtag_gpio5_pins[] = {5}; static struct pinctrl_pin_desc g_peri_pins[] = { hi_pin(0), hi_pin(1), hi_pin(2), hi_pin(3), hi_pin(4), hi_pin(5), hi_pin(6), hi_pin(7), hi_pin(8), hi_pin(9), hi_pin(10), hi_pin(11), hi_pin(12), hi_pin(13), hi_pin(14), hi_pin(15), hi_pin(16), hi_pin(17), hi_pin(18), hi_pin(19), hi_pin(20), hi_pin(21), hi_pin(22), hi_pin(23), hi_pin(24), hi_pin(25), hi_pin(26), hi_pin(27), hi_pin(28), hi_pin(29), hi_pin(30), hi_pin(31), hi_pin(32), hi_pin(33), hi_pin(34), hi_pin(35), hi_pin(36), }; static const struct hi_pmux_func g_peri_functions[] = { hi_pinctrl_pmux_func(sfc), hi_pinctrl_pmux_func(uart1), hi_pinctrl_pmux_func(uart0), hi_pinctrl_pmux_func(mdio1), hi_pinctrl_pmux_func(spi), hi_pinctrl_pmux_func(i2c), hi_pinctrl_pmux_func(rgmii0), hi_pinctrl_pmux_func(eth_act_led0), hi_pinctrl_pmux_func(eth_act_led1), hi_pinctrl_pmux_func(eth_act_led2), hi_pinctrl_pmux_func(eth_act_led3), hi_pinctrl_pmux_func(eth_act_led4), hi_pinctrl_pmux_func(led0), hi_pinctrl_pmux_func(led1), hi_pinctrl_pmux_func(clk_test), hi_pinctrl_pmux_func(avs), hi_pinctrl_pmux_func(phy_clk), hi_pinctrl_pmux_func(usb), hi_pinctrl_pmux_func(jtag_gpio2), hi_pinctrl_pmux_func(jtag_gpio3), hi_pinctrl_pmux_func(jtag_gpio4), hi_pinctrl_pmux_func(jtag_gpio5), }; static struct hi_pin_group g_peri_groups[] = { hi_pinctrl_pin_group(sfc), hi_pinctrl_pin_group(uart1), hi_pinctrl_pin_group(uart0), hi_pinctrl_pin_group(mdio1), hi_pinctrl_pin_group(spi), hi_pinctrl_pin_group(i2c), hi_pinctrl_pin_group(rgmii0), hi_pinctrl_pin_group(eth_act_led0), hi_pinctrl_pin_group(eth_act_led1), hi_pinctrl_pin_group(eth_act_led2), hi_pinctrl_pin_group(eth_act_led3), hi_pinctrl_pin_group(eth_act_led4), hi_pinctrl_pin_group(led0), hi_pinctrl_pin_group(led1), hi_pinctrl_pin_group(clk_test), hi_pinctrl_pin_group(avs), hi_pinctrl_pin_group(phy_clk), hi_pinctrl_pin_group(usb), hi_pinctrl_pin_group_with_sig_sel(jtag_gpio2, 0x08, 1, 1), hi_pinctrl_pin_group_with_sig_sel(jtag_gpio3, 0x08, 1, 1), hi_pinctrl_pin_group_with_sig_sel(jtag_gpio4, 0x08, 1, 1), hi_pinctrl_pin_group_with_sig_sel(jtag_gpio5, 0x08, 1, 1), }; static int32_t luofu_pin_config_get(struct hi_pinctrl_drvdata *data, struct hi_pin_drvdata *pin_data, uintptr_t *config) { enum pin_config_param param = pinconf_to_config_param(*config); union hi_pin_cfg_reg_luofu pin_cfg; uint16_t arg; pin_cfg.value = readl(data->virt_conf_base + pin_data->cfg_offset); switch (param) { case PIN_CONFIG_BIAS_PULL_DOWN: arg = pin_cfg.bits.pd; break; case PIN_CONFIG_BIAS_PULL_UP: arg = pin_cfg.bits.pu; break; case PIN_CONFIG_DRIVE_STRENGTH: arg = pin_cfg.bits.ds; break; case PIN_CONFIG_INPUT_ENABLE: arg = pin_cfg.bits.ien; break; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: arg = pin_cfg.bits.sten; break; case PIN_CONFIG_OUTPUT: arg = pin_cfg.bits.out; break; default: return -EINVAL; } *config = pinconf_to_config_packed(param, arg); return 0; } static int luofu_pin_config_set(struct hi_pinctrl_drvdata *data, struct hi_pin_drvdata *pin_data, uintptr_t *configs, uint32_t num_configs) { union hi_pin_cfg_reg_luofu pin_cfg; enum pin_config_param param; uint32_t idx; uint16_t arg; for (idx = 0; idx < num_configs; idx++) { param = pinconf_to_config_param(configs[idx]); arg = pinconf_to_config_argument(configs[idx]); pin_cfg.value = readl(data->virt_conf_base + pin_data->cfg_offset); switch (param) { case PIN_CONFIG_BIAS_PULL_DOWN: pin_cfg.bits.pd = arg; break; case PIN_CONFIG_BIAS_PULL_UP: pin_cfg.bits.pu = arg; break; case PIN_CONFIG_DRIVE_STRENGTH: pin_cfg.bits.ds = arg; break; case PIN_CONFIG_INPUT_ENABLE: pin_cfg.bits.ien = arg; break; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: pin_cfg.bits.sten = arg; break; case PIN_CONFIG_OUTPUT: pin_cfg.bits.out = arg; break; default: return -EINVAL; } writel(pin_cfg.value, data->virt_conf_base + pin_data->cfg_offset); } return 0; } const struct hi_pinctrl_soc_data g_luofu_peri_pinctrl_soc_data = { .name = "luofu-peripinctrl", .pins = g_peri_pins, .npins = ARRAY_SIZE(g_peri_pins), .groups = g_peri_groups, .ngroup = ARRAY_SIZE(g_peri_groups), .funcs = g_peri_functions, .nfunc = ARRAY_SIZE(g_peri_functions), .pin_config_set = luofu_pin_config_set, .pin_config_get = luofu_pin_config_get, }; static const struct of_device_id g_luofu_pinctrl_match[] = { { .compatible = "hsan,luofu-peri-pinctrl", .data = &g_luofu_peri_pinctrl_soc_data, }, {} }; MODULE_DEVICE_TABLE(of, g_luofu_pinctrl_match); static int32_t luofu_pinctrl_probe(struct platform_device *pdev) { const struct of_device_id *match = of_match_device(g_luofu_pinctrl_match, &pdev->dev); const struct hi_pinctrl_soc_data *soc_data = NULL; if (match != NULL && match->data != NULL) soc_data = match->data; return hi_pinctrl_probe(pdev, soc_data); } static SIMPLE_DEV_PM_OPS(pinctrl_hi_pm_ops, hi_pinctrl_suspend, hi_pinctrl_resume); static struct platform_driver g_luofu_pinctrl_driver = { .probe = luofu_pinctrl_probe, .remove = hi_pinctrl_remove, .driver = { .name = "luofu-pinctrl", .of_match_table = g_luofu_pinctrl_match, .pm = &pinctrl_hi_pm_ops, }, }; module_platform_driver(g_luofu_pinctrl_driver); MODULE_AUTHOR("hsan"); MODULE_DESCRIPTION("hsan pinctrl driver"); MODULE_LICENSE("GPL"); 告诉我代码的功能和实现的什么?
最新发布
07-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值