DSA
DSA
来源 https://www.right.com.cn/forum/thread-5807504-1-1.html
教程 https://openwrt.org/docs/guide-user/network/dsa/dsa-mini-tutorial#multiple_networks_using_vlan_tagging
参考 https://openwrt.org/docs/guide-user/network/vlan/switch_configuration
参考 https://www.cisco.com/c/en/us/td/docs/switches/datacenter/sw/5_x/nx-os/layer2/configuration/guide/Cisco_Nexus_7000_Series_NX-OS_Layer_2_Switching_Configuration_Guide_Release_5-x_chapter4.html
原链接:https://forum.openwrt.org/t/mini-tutorial-for-dsa-network-config/96998
Syntax | Member Port Is |
---|---|
lanx | untagged ~PVID |
lanx:u | untagged |
lanx:t | tagged |
lanx:* | PVID untagged |
lanx:u* | PVID untagged |
lanx:t* | PVID tagged |
=================
mini-tutorial-for-dsa-network-config
see https://forum.openwrt.org/t/mini-tutorial-for-dsa-network-config/96998
Update from 2021-05-31:
- Option
ifname
was renamed todevice
- This tutorial is valid for 21.02-rc2 (or newer) and recent snapshots
Introduction
DSA stands for Distributed Switch Architecture and is Linux kernel subsystem for network switches. It's an upstream replacement for OpenWrt's swconfig framework and many new routers use DSA drivers instead of swconfig drivers.
In DSA each switch port is a separated Linux interface. It means ip
/ ifconfig
command will show interfaces like lan1
, lan2
, wan
, etc.
DSA switch ports can be used as standalone interfaces (common solution for WAN) or can be bridged using Linux bridge interface. In the later case switch will still be able to route traffic on the hardware level so it won't affect performance.
Each port can be part of maximum of one bridge only.
Simple ports bridging
In the simplest scenario switch ports are simply bridged using Linux bridge interface and OpenWrt configures that interface with an IP protocol.
In such case all devices connected to bridged ports can communicate each other and router itself.
Example:
config device
option name 'br-lan'
option type 'bridge'
list ports 'lan1'
list ports 'lan2'
list ports 'lan3'
list ports 'lan4'
config interface 'lan'
option device 'br-lan'
option proto 'static'
option ipaddr '192.168.1.1'
option netmask '255.255.255.0'
Multiple networks (using bridges)
A switch can be setup to group selected ports into separated networks by using multiple bridge interfaces. With separated firewall zones devices connected to different port groups won't be able to communicate each other.
Example:
config device
option name 'br-home'
option type 'bridge'
list ports 'lan1'
list ports 'lan2'
config device
option name 'office'
option type 'bridge'
list ports 'lan3'
list ports 'lan4'
config interface 'home'
option device 'br-home'
option proto 'static'
option ipaddr '192.168.1.1'
option netmask '255.255.255.0'
config interface 'office'
option device 'office'
option proto 'static'
option ipaddr '192.168.13.1'
option netmask '255.255.255.0'
Multiple networks (using VLANs)
Ports can also be separated (grouped) using single bridge with multiple VLANs. That requires assigning interfaces to correct software VLANs.
Example:
config device
option name 'br-lan'
option type 'bridge'
list ports 'lan1'
list ports 'lan2'
list ports 'lan3'
list ports 'lan4'
config bridge-vlan
option device 'br-lan'
option vlan '1'
list ports 'lan1'
list ports 'lan2'
config bridge-vlan
option device 'br-lan'
option vlan '2'
list ports 'lan3'
list ports 'lan4'
config interface 'home'
option device 'br-lan.1'
option proto 'static'
option ipaddr '192.168.1.1'
option netmask '255.255.255.0'
config interface 'office'
option device 'br-lan.2'
option proto 'static'
option ipaddr '192.168.13.1'
option netmask '255.255.255.0'
VLAN tagged traffic
With proper bridge VLAN configuration it's also possible for selected port to use VLAN tagged traffic. It also requires assigning OpenWrt interface to the correct software VLAN.
Example:
Port lan4
uses tagged packets for VLAN 1 and has PVID 2.
config device
option name 'br-lan'
option type 'bridge'
list ports 'lan1'
list ports 'lan2'
list ports 'lan3'
list ports 'lan4'
config bridge-vlan
option device 'br-lan'
option vlan '1'
list ports 'lan1'
list ports 'lan2'
list ports 'lan3'
list ports 'lan4:t'
config bridge-vlan
option device 'br-lan'
option vlan '2'
list ports 'lan4:u*'
config interface 'lan'
option device 'br-lan.1'
option proto 'static'
option ipaddr '192.168.1.1'
option netmask '255.255.255.0'
=================
DSA with multiple WAN VLAN priorities
see https://forum.openwrt.org/t/dsa-with-multiple-wan-vlan-priorities/126544
I've recently upgraded my Linksys WRT1900ACS from OpenWrt 19.07 to 21.02.3. I've had to rewrite my /etc/config/network to work with the new DSA configuration and I've got most of what I need working, except for IPTV.
My ISP requires that internet traffic be sent over 802.1q VLAN 10 priority 0, and that IPTV is over VLAN 20, priority 4. The IPTV set-top box is plugged into lan1.
In OpenWrt 19.07, I've accomplished this by:
- Defining eth1.21, connected to swconfig port 3 (DSA = lan1)
- Defining eth0.20, connected to swconfig port 4 (DSA = wan)
- Defining a bridge for eth0.20 and 1.21
- Using
ip link set link eth0.20 dev eth0 type vlan egress-qos-map 0:4 1:4 2:4 3:4 4:4 5:4 6:4 7:4
as a hotplug.d/iface script that runs onifup
.
The idea is to take the traffic from the set-top box, add the 802.1q header (VLAN 20, priority 4), and send the packet onwards to the ISP. This set up works well. Internet works correctly, and IPTV works correctly as well.
In OpenWrt 21.02, after porting the equivalent configuration (internet configuration omitted for brevity):
config device
option name 'br-lan'
option type 'bridge'
list ports 'lan1'
list ports 'lan2'
list ports 'lan3'
list ports 'lan4'
config bridge-vlan
option device 'br-lan'
option vlan '21'
list ports 'lan1'
config device
option name 'br-iptv'
option type 'bridge'
list ports 'wan.20'
list ports 'br-lan.21'
and using the same ip link set link wan.20 dev wan type vlan egress-qos-map 0:4 1:4 2:4 3:4 4:4 5:4 6:4 7:4
command, I notice several things:
- When I try to perform a DHCP configuration of br-iptv, the configuration as described does not receive any DHCP responses. According to tcpdump, capturing
wan
and filtering byvlan 20
shows the correct 802.1q field: priority 4 and VLAN 20. However, the priority field doesn't seem to be properly set when the ethernet frame is sent over the wire (which can explain why I don't get any DHCP response). I get a DHCP response immediately when I configure:
config device
option name 'wan.20'
option type '8021q'
option vid '20'
option ifname 'wan'
list egress_qos_mapping '0:4'
in place of using ip link
. However, this now causes my normal internet access to break (presumably because it is now set to VLAN 10, priority 4 for the internet-facing VLAN 10). No other internet traffic is then possible, all packets are dropped on my ISP side.
Regardless which way I set the egress_qos_mapping, the VLAN configuration seems correct:
# cat /proc/net/vlan/wan.10 /proc/net/vlan/wan.20
wan.10 VID: 10 REORDER_HDR: 1 dev->priv_flags: 1121
total frames received 7368
total bytes received 7803993
Broadcast/Multicast Rcvd 0
total frames transmitted 3856
total bytes transmitted 628469
Device: wan
INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0
EGRESS priority mappings:
wan.20 VID: 20 REORDER_HDR: 1 dev->priv_flags: 1221
total frames received 3
total bytes received 706
Broadcast/Multicast Rcvd 1
total frames transmitted 10
total bytes transmitted 1520
Device: wan
INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0
EGRESS priority mappings: 0:4
Does setting the egress_qos_mapping
apply to all VLANs in the hardware switch? This doesn't seem to be the behaviour from 19.07.
tcpdump
-ing bothbr-iptv
andwan.20
at the same time when powering on the IPTV set-top box shows the correct DHCP configuration packets being sent by the set-top box, and it shows up in the br-iptv dump. However wan.20 never sees the packet. I can confirm this by temporary breaking my internet access (in point 1) and rebooting the set-top box, but it still does not obtain any IP address from my ISP.
Am I misconfiguring something, or has DSA fundamentally changed what is possible in terms of the network setup? Any guidance would be much appreciated.
=================
netifd调试总结(OpenWrt 22.03)
https://blog.csdn.net/u010687717/article/details/124570805
1.调试
1.1 gdb调试
如下所示,修改配置(问题:有些东西打印不出来,比如ifname,提示 ifname = <error reading variable>)
1.2 D打印
在/etc/init.d/network中增加如下内容就能使用“logread -e netifd”看到D打印的内容,“-d 15"表示打开以下所有模块的打印,如果”-d 1",那就只打印DEBUG_SYSTEM:
enum {
DEBUG_SYSTEM = 0,
DEBUG_DEVICE = 1,
DEBUG_INTERFACE = 2,
DEBUG_WIRELESS = 3,
};
注意:D打印的内容是"daemon.err netifd[10824]"开头的,而netifd使用的syslog级别为L_CRIT,L_WARNING,L_NOTICE,L_INFO,L_DEBUG,感觉是特意避开err级别,避免跟D混淆。
1.3 syslog打印
如果将上面的启动参数修改为“procd_set_param command /sbin/netifd -l 5 -d 15”,则代码及对应的打印内容如下所示:
DPRINTF("connected as %08x\n", ubus_ctx->local_id);
netifd_log_message(L_DEBUG, "connected as %08x\n", ubus_ctx->local_id);
netifd_log_message(L_INFO, "connected as %08x\n", ubus_ctx->local_id);
netifd_log_message(L_NOTICE, "connected as %08x\n", ubus_ctx->local_id);
netifd_log_message(L_WARNING, "connected as %08x\n", ubus_ctx->local_id);
netifd_log_message(L_CRIT, "connected as %08x\n", ubus_ctx->local_id);
daemon.err netifd[17096]: netifd_ubus_init(1364): connected as 0d6fc15a
daemon.debug netifd: connected as 0d6fc15a
daemon.info netifd: connected as 0d6fc15a
daemon.notice netifd: connected as 0d6fc15a
daemon.warn netifd: connected as 0d6fc15a
daemon.crit netifd: connected as 0d6fc15a
1.4 命令行获取IP
ifstatus WAN1 | jsonfilter -e '@["ipv4-address"][0].address'
2 初始化过程分析(BPI-R64)
2.1 创建CPU口和mdio驱动初始化
kernel_init->kernel_init_freeable->do_basic_setup->do_initcalls->do_one_initcall
->mtk_driver_init(mtk_eth_soc.c中通过module_platform_driver注册)
->platform_driver_register->__platform_driver_register->driver_register->bus_add_driver->driver_attach->bus_for_each_dev
->__driver_attach->device_driver_attach->driver_probe_device->really_probe
->platform_drv_probe(paltform.c中通过__platform_driver_register注册)->mtk_probe
2.1.1 创建CPU口
- 创建CPU口
mtk_probe->mtk_add_mac->alloc_etherdev->alloc_etherdev_mq->alloc_etherdev_mqs->alloc_netdev_mqs(此时接口名称为'eth%d')
- 将netdev与DTS关联起来
mtk_probe->mtk_add_mac(其中eth->netdev[id]->dev.of_node = np;关联起来,在设备初始化时match)
2.2.2 mdio驱动初始化
mtk_probe->mtk_mdio_init(读DTS)->of_mdiobus_register->of_mdiobus_register_device->mdio_device_register->device_add
->bus_probe_device->device_initial_probe->__device_attach->bus_for_each_drv->__device_attach_driver
->driver_probe_device->really_probe->mdio_probe(mdio.h中通过mdio_module_init注册)
->mt7530_probe(drivers\net\dsa\mt7530.c中通过mdio_module_driver(mt7530_mdio_driver)注册)
->dsa_register_switch->dsa_switch_probe->dsa_switch_parse_of->dsa_switch_parse_ports_of->dsa_port_parse_of
->of_find_net_device_by_node(DTS中port@6调用,因为此时接口没有注册,所以class_find_device返回NULL,进而dsa_port_parse_of返回-EPROBE_DEFER,驱动初始化完成)
2.2.3 注册CPU口
注册时,会自动给CPU口命名为命名为eth0和eth1
mtk_probe->register_netdev->register_netdevice->dev_get_valid_name->dev_alloc_name_ns->__dev_alloc_name
2.2 创建用户口(mdio设备初始化)
获取接口名称
deferred_probe_work_func(具体何时调用不清楚)->bus_probe_device->device_initial_probe->__device_attach->bus_for_each_drv
->__device_attach_driver->driver_probe_device->really_probe->mdio_probe(mdio.h中通过mdio_module_init注册)
->mt7530_probe(drivers\net\dsa\mt7530.c中通过mdio_module_driver(mt7530_mdio_driver)注册)
->dsa_register_switch->dsa_switch_probe
其中->dsa_switch_parse_of->dsa_switch_parse_ports_of->dsa_port_parse_of->dsa_port_parse_user(从DTS中读取接口名称)
另外->dsa_tree_setup->dsa_tree_setup_switches->dsa_port_setup->dsa_slave_create(创建lan1~lan4及wan口。)
2.3 生成/etc/config/network
config_generate根据target\linux\mediatek\mt7622\base-files\etc\board.d\02_network生成/etc/config/network,具体实现的参考链接如下:OpenWrt file/bin/config_generate network初始化分析
3 支持的device类型
LuCI类型 | UCI类型 | 对应结构体 | 使用场景 |
---|---|---|---|
Network device | N/A | simple_device_type | 对应真实物理端口,好像不创建也不影响功能。。。 |
Bridge device | bridge | bridge_device_type | 基于桥设备创建桥接口,比如br-lan |
VLAN(802.1q) | 8021q | vlan8021q_device_type | 基于802.1q设备创建接口,比如创建802.1q的路由连接 |
VLAN(802.1ad) | 8021ad | vlan8021ad_device_type | 基于802.1ad设备创建接口,比如创建802.1ad的路由连接 |
MAC VLAN | macvlan | macvlan_device_type | |
Virtual Ethernet | veth | veth_device_type |
所有类型都通过device_type_add来添加。
3.1 802.1q
-
创建带VLAN的device
-
创建interface,这样在WAN侧抓到的报文就是带VLAN 100的
-
对应的UCI配置如下:
config device
option type '8021q'
option ifname 'wan'
option vid '100'
option name 'wan.100'
config interface 'WAN'
option proto 'dhcp'
option device 'wan.100'
3.2 MAC VLAN
- 创建带MAC VLAN的device,wanmac1也用同样方法创建
- 创建interface,WAN1使用相同方法创建
- 对应的UCI配置如下:
config device
option type 'macvlan'
option ifname 'wan'
option mode 'bridge'
option name 'wanmac0'
config device
option type 'macvlan'
option ifname 'wan'
option mode 'bridge'
option name 'wanmac1'
config interface 'WAN0'
option proto 'dhcp'
option device 'wanmac0'
config interface 'WAN1'
option proto 'dhcp'
option device 'wanmac1'
- 两个MAC VLAN桥模式的WAN连接通信
执行如下命令,参考链接:https://unix.stackexchange.com/questions/398671/communication-problem-between-macvlan-interfaces-when-sockets-are-bound-to-devic
device0=`ifstatus WAN0 | jsonfilter -e '@["device"]'`
device1=`ifstatus WAN1 | jsonfilter -e '@["device"]'`
ip0=`ifstatus WAN0 | jsonfilter -e '@["ipv4-address"][0].address'`
ip1=`ifstatus WAN1 | jsonfilter -e '@["ipv4-address"][0].address'`
ip route del default
ip route del `ip route|grep "$device0"`
ip route del `ip route|grep "$device1"`
ip rule del pref 0
ip rule add pref 200 to "$ip1" lookup 100
ip rule add pref 200 to "$ip0" lookup 101
ip route add default dev "$device0" table 100
ip route add default dev "$device1" table 101
ip rule add pref 1000 lookup local
ip rule add pref 100 to "$ip0" iif "$device0" lookup local
ip rule add pref 100 to "$ip1" iif "$device1" lookup local
ping -I $device0 $ip1 #此时能ping通
4 监听内核接口状态的变化
1、system_init中调用create_event_socket创建监听netlink的socket
2、当接口link up或者link down时,在内核中调用netif_carrier_on,最终会走到netdev_state_change,
再通过rtmsg_ifinfo发出netlink消息。
3、在system_init中注册的cb_rtnl_event监听到消息之后,做相应的处理。
=================
Debian for MT7621 折腾记 (2) - 实现 MT7530 DSA
https://blog.hackpascal.net/2019/10/debian-for-mt7621-%e6%8a%98%e8%85%be%e8%ae%b0-2-%e5%ae%9e%e7%8e%b0-mt7530-dsa/
OpenWrt 自带的 swconfig 功能对于 Debian 来说,就不是那么好用了。除此之外,Linux 对交换机的支持就只剩 DSA 了。幸好 MT7530 有 DSA 驱动。
但是 MT7530 的 DSA 驱动是给 MT7623 用的,里面硬编码了很多 MT7623 的设置,还用到了很多 MT7621 中不存在的东西,例如 Power domain。
这些都需要全部去掉,并为 MT7621 添加所需的代码。
此外,用于 MT7621 的以太网驱动 mtk_soc_eth 也一团糟,里面杂揉了各个芯片的代码,不纯粹,不适合拿给 DSA 用。因此也需要进行修改。
1. 修改 mtk_soc_eth
我选择对 ARM 版的 mtk_soc_eth 下手。1.1 加入 MT7621 支持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
--- a/drivers/net/ethernet/mediatek/Kconfig +++ b/drivers/net/ethernet/mediatek/Kconfig @@ -1,6 +1,6 @@ config NET_VENDOR_MEDIATEK bool "MediaTek ethernet driver" - depends on ARCH_MEDIATEK + depends on ARCH_MEDIATEK || SOC_MT7621 ---help--- If you have a Mediatek SoC with ethernet, say Y. --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -76,7 +76,6 @@ static int mtk_mdio_busy_wait(struct mtk return 0; if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT)) break; - usleep_range(10, 20); } dev_err(eth->dev, "mdio: MDIO timeout\n"); @@ -1748,6 +1747,34 @@ static void mtk_tx_timeout(struct net_de schedule_work(ð->pending_work); } +static irqreturn_t mtk_handle_irq_tx_rx(int irq, void *_eth) +{ + struct mtk_eth *eth = _eth; + u32 tx_status, rx_status; + + tx_status = mtk_r32(eth, MTK_QMTK_INT_STATUS); + + if (tx_status & MTK_TX_DONE_INT) { + if (likely(napi_schedule_prep(ð->tx_napi))) { + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); + __napi_schedule(ð->tx_napi); + } + mtk_w32(eth, tx_status, MTK_QMTK_INT_STATUS); + } + + rx_status = mtk_r32(eth, MTK_PDMA_INT_STATUS); + + if (rx_status & MTK_RX_DONE_INT) { + if (likely(napi_schedule_prep(ð->rx_napi))) { + mtk_rx_irq_disable(eth, MTK_RX_DONE_INT); + __napi_schedule(ð->rx_napi); + } + mtk_w32(eth, rx_status, MTK_PDMA_INT_STATUS); + } + + return IRQ_HANDLED; +} + static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth) { struct mtk_eth *eth = _eth; @@ -1950,14 +1977,16 @@ static int mtk_hw_init(struct mtk_eth *e } regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val); - /* Set GE2 driving and slew rate */ - regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00); + if (eth->pctl) { + /* Set GE2 driving and slew rate */ + regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00); - /* set GE2 TDSEL */ - regmap_write(eth->pctl, GPIO_OD33_CTRL8, 0x5); + /* set GE2 TDSEL */ + regmap_write(eth->pctl, GPIO_OD33_CTRL8, 0x5); - /* set GE2 TUNE */ - regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0); + /* set GE2 TUNE */ + regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0); + } /* Set linkdown as the default for each GMAC. Its own MCR would be set * up with the more appropriate value when mtk_phy_link_adjust call is @@ -2536,14 +2565,16 @@ static int mtk_probe(struct platform_dev } } - eth->pctl = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "mediatek,pctl"); - if (IS_ERR(eth->pctl)) { - dev_err(&pdev->dev, "no pctl regmap found\n"); - return PTR_ERR(eth->pctl); + if (eth->soc->require_pctl) { + eth->pctl = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "mediatek,pctl"); + if (IS_ERR(eth->pctl)) { + dev_info(&pdev->dev, "no pctl regmap found\n"); + return PTR_ERR(eth->pctl); + } } - for (i = 0; i < 3; i++) { + for (i = 0; i < eth->soc->irq_num; i++) { eth->irq[i] = platform_get_irq(pdev, i); if (eth->irq[i] < 0) { dev_err(&pdev->dev, "no IRQ%d resource found\n", i); @@ -2591,15 +2622,22 @@ static int mtk_probe(struct platform_dev goto err_deinit_hw; } - err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0, - dev_name(eth->dev), eth); - if (err) - goto err_free_dev; + if (eth->soc->irq_num > 1) { + err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0, + dev_name(eth->dev), eth); + if (err) + goto err_free_dev; - err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0, - dev_name(eth->dev), eth); - if (err) - goto err_free_dev; + err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0, + dev_name(eth->dev), eth); + if (err) + goto err_free_dev; + } else { + err = devm_request_irq(eth->dev, eth->irq[0], mtk_handle_irq_tx_rx, 0, + dev_name(eth->dev), eth); + if (err) + goto err_free_dev; + } err = mtk_mdio_init(eth); if (err) @@ -2666,23 +2704,37 @@ static int mtk_remove(struct platform_de static const struct mtk_soc_data mt2701_data = { .caps = MTK_GMAC1_TRGMII, - .required_clks = MT7623_CLKS_BITMAP + .required_clks = MT7623_CLKS_BITMAP, + .require_pctl = true, + .irq_num = 3, }; static const struct mtk_soc_data mt7622_data = { .caps = MTK_DUAL_GMAC_SHARED_SGMII | MTK_GMAC1_ESW, - .required_clks = MT7622_CLKS_BITMAP + .required_clks = MT7622_CLKS_BITMAP, + .require_pctl = false, + .irq_num = 3, }; static const struct mtk_soc_data mt7623_data = { .caps = MTK_GMAC1_TRGMII, - .required_clks = MT7623_CLKS_BITMAP + .required_clks = MT7623_CLKS_BITMAP, + .require_pctl = true, + .irq_num = 3, +}; + +static const struct mtk_soc_data mt7621_data = { + .caps = MTK_GMAC1_TRGMII, + .required_clks = MT7621_CLKS_BITMAP, + .require_pctl = false, + .irq_num = 1, }; const struct of_device_id of_mtk_match[] = { { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data}, { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data}, { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data}, + { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data}, {}, }; MODULE_DEVICE_TABLE(of, of_mtk_match); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -486,6 +486,8 @@ enum mtk_clks_map { BIT(MTK_CLK_SGMII_CDR_FB) | \ BIT(MTK_CLK_SGMII_CK) | \ BIT(MTK_CLK_ETH2PLL)) +#define MT7621_CLKS_BITMAP 0 + enum mtk_dev_state { MTK_HW_INIT, MTK_RESETTING @@ -575,6 +577,8 @@ struct mtk_rx_ring { struct mtk_soc_data { u32 caps; u32 required_clks; + bool require_pctl; + u32 irq_num; }; /* currently no SoC has more than 2 macs */ }; /* currently no SoC has more than 2 macs */ |
1.2 从 mediatek 这个 target 中提取 mtk_soc_eth 相关的改动
0027-net-next-mediatek-fix-DQL-support.patch0035-net-mediatek-disable-RX-VLan-offloading.patch
0042-net-next-mediatek-honour-special-tag-bit-inside-RX-D.patch
0043-net-next-mediatek-enable-special-tag-indication-for-.patch
0051-net-mediatek-increase-tx_timeout.patch
0063-atomic-sleep.patch
2. 修改 DSA 驱动
2.1 从 mediatek 这个 target 中提取 DSA 相关的改动
0032-net-dsa-mediatek-add-support-for-GMAC2-wired-to-ext-.patch0033-dsa-multi-cpu.patch
0044-net-next-dsa-mediatek-tell-GDMA-when-we-are-turning-.patch
0045-net-dsa-mediatek-turn-into-platform-driver.patch
2.2 针对 MT7621 进行修改
2.2.1 去除 MT7623 TRGMII 相关的设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
--- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -76,58 +76,6 @@ static const struct mt7530_mib_desc mt75 }; static int -mt7623_trgmii_write(struct mt7530_priv *priv, u32 reg, u32 val) -{ - int ret; - - ret = regmap_write(priv->ethernet, TRGMII_BASE(reg), val); - if (ret < 0) - dev_err(priv->dev, - "failed to priv write register\n"); - return ret; -} - -static u32 -mt7623_trgmii_read(struct mt7530_priv *priv, u32 reg) -{ - int ret; - u32 val; - - ret = regmap_read(priv->ethernet, TRGMII_BASE(reg), &val); - if (ret < 0) { - dev_err(priv->dev, - "failed to priv read register\n"); - return ret; - } - - return val; -} - -static void -mt7623_trgmii_rmw(struct mt7530_priv *priv, u32 reg, - u32 mask, u32 set) -{ - u32 val; - - val = mt7623_trgmii_read(priv, reg); - val &= ~mask; - val |= set; - mt7623_trgmii_write(priv, reg, val); -} - -static void -mt7623_trgmii_set(struct mt7530_priv *priv, u32 reg, u32 val) -{ - mt7623_trgmii_rmw(priv, reg, 0, val); -} - -static void -mt7623_trgmii_clear(struct mt7530_priv *priv, u32 reg, u32 val) -{ - mt7623_trgmii_rmw(priv, reg, val, 0); -} - -static int core_read_mmd_indirect(struct mt7530_priv *priv, int prtad, int devad) { struct mii_bus *bus = priv->bus; @@ -515,24 +463,6 @@ mt7530_pad_clk_setup(struct dsa_switch * for (i = 0 ; i < NUM_TRGMII_CTRL; i++) mt7530_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_MASK, RD_TAP(16)); - else - mt7623_trgmii_set(priv, GSW_INTF_MODE, INTF_MODE_TRGMII); - - return 0; -} - -static int -mt7623_pad_clk_setup(struct dsa_switch *ds) -{ - struct mt7530_priv *priv = ds->priv; - int i; - - for (i = 0 ; i < NUM_TRGMII_CTRL; i++) - mt7623_trgmii_write(priv, GSW_TRGMII_TD_ODT(i), - TD_DM_DRVP(8) | TD_DM_DRVN(8)); - - mt7623_trgmii_set(priv, GSW_TRGMII_RCK_CTRL, RX_RST | RXC_DQSISEL); - mt7623_trgmii_clear(priv, GSW_TRGMII_RCK_CTRL, RX_RST); return 0; } @@ -619,12 +549,6 @@ static void mt7530_adjust_link(struct ds /* Setup TX circuit incluing relevant PAD and driving */ mt7530_pad_clk_setup(ds, phydev->interface); - - /* Setup RX circuit, relevant PAD and driving on the host - * which must be placed after the setup on the device side is - * all finished. - */ - mt7623_pad_clk_setup(ds); } else { u16 lcl_adv = 0, rmt_adv = 0; u8 flowctrl; |
2.2.2 添加 MT7621 TRGMII 相关的设置
目前 Upstream 并没有完善的 TRGMII 支持,因此暂时先用 RGMII。毕竟 MT7621 的 TRGMII 只有 1.2Gbps,只比 RGMII 的 1Gbps 快了一点,影响不大。这个先留着,等以后再补充。
3. 修改 DTS 文件
3.1 修改 mt7621.dtsi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
--- a/target/linux/ramips/dts/mt7621.dtsi +++ b/target/linux/ramips/dts/mt7621.dtsi @@ -406,22 +406,38 @@ 0x1e003800 0x800>; }; + ethsys: ethsys@1e000000 { + compatible = "mediatek,mt7621-ethsys", "syscon"; + reg = <0x1e000000 0x8000>; + }; + ethernet: ethernet@1e100000 { - compatible = "mediatek,mt7621-eth"; + compatible = "mediatek,mt7621-eth", "syscon"; reg = <0x1e100000 0x10000>; #address-cells = <1>; #size-cells = <1>; + mediatek,ethsys = <ðsys>; + resets = <&rstctrl 6 &rstctrl 23>; reset-names = "fe", "eth"; interrupt-parent = <&gic>; interrupts = <GIC_SHARED 3 IRQ_TYPE_LEVEL_HIGH>; - mediatek,switch = <&gsw>; + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "rgmii"; + fixed-link { + speed = <1000>; + full-duplex; + pause; + }; + }; - mdio-bus { + mdio: mdio-bus { #address-cells = <1>; #size-cells = <0>; @@ -430,16 +446,6 @@ phy-mode = "rgmii"; }; }; - - hnat: hnat@0 { - compatible = "mediatek,mt7623-hnat"; - reg = <0 0x10000>; - mtketh-ppd = "eth0"; - mtketh-lan = "eth0"; - mtketh-wan = "eth0"; - resets = <&rstctrl 0>; - reset-names = "mtketh"; - }; }; gsw: gsw@1e110000 { |
3.2 修改对应板子的 dts
这里用的是 CreativeBox 这个板子。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
--- a/target/linux/ramips/dts/CreativeBox-v1.dts +++ b/target/linux/ramips/dts/CreativeBox-v1.dts @@ -86,6 +86,10 @@ gpios = <&gpio0 27 GPIO_ACTIVE_HIGH>; }; }; + + mt7530: switch@0 { + compatible = "mediatek,mt7530"; + }; }; &sdhci { @@ -115,6 +119,67 @@ mtd-mac-address = <&factory 0xe000>; }; +&mt7530 { + compatible = "mediatek,mt7530"; + + #address-cells = <1>; + #size-cells = <0>; + + mediatek,mcm; + resets = <&rstctrl 2>; + reset-names = "mcm"; + + dsa,mii-bus = <&mdio>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + cpu = <&cpu_port0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + cpu = <&cpu_port0>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + cpu = <&cpu_port0>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + cpu = <&cpu_port0>; + }; + + port@4 { + reg = <4>; + label = "wan"; + cpu = <&cpu_port0>; + }; + + cpu_port0: port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "rgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; + &pinctrl { state_default: pinctrl0 { gpio { |
4. 修改 OpenWrt 中的网络配置
这里仅仅是为了在 OpenWrt 中测试 DSA 是否正常工作修改 target/linux/ramips/base-files/etc/board.d/02_network,将对应板子的脚本语句修改为:
1
|
ucidef_set_interfaces_lan_wan "lan0 lan1 lan2 lan3" "wan" |
5. 开启 kernel 网络子系统的 DSA 功能
Networking support ---> Networking options ---> Distributed Switch ArchitectureDevice Drivers ---> Network device support ---> Distributed Switch Architecture drivers ---> Mediatek MT7530 Ethernet switch support
6. 编译测试
略
=================
Linksys wrt1200ac + openwrt在新DSA驱动架构下的vlan设置
https://zhuanlan.zhihu.com/p/24884136
编辑于 2017-01-14 23:58
关于openwrt上vlan的设置,我在大约三年前曾经分享过两篇心得:
通过使用swconfig命令配置vlan,解决internet和iptv流量共用一根网线的问题,互不干扰。后来家里设备有所变化,3700v4刷上了openwrt,老旧的TPLink WR1041N也换成了Netgear r6300v2,运行Tomato Shibby,但vlan的配置并没有什么不同。顺便提一句,在web UI易用性上,Tomato仍然是开源固件中的王者。
今天要分享一些有趣的后续。2016年上半年我一时手痒,入手了Linksys wrt1200ac,打算换掉年迈的主路由3700v4,但因为种种原因一直懒得动手。这几天终于决定挤出点时间把wrt1200ac配置起来。
首先是选择合适的openwrt版本。入手wrt1200ac后不久,我就自己从openwrt git代码编译了一版Chaos Calmer,刷上运行也没什么问题。不过从论坛讨论历史来看,有不少有用的补丁都因为种种原因并没有进入openwrt主干,而是各路大神在维护自己的版本。我最初想使用openwrt论坛用户davidc502自行编译的版本(需要FQ),但他从来不放出自己的补丁和config文件源码,我也就一直比较犹豫。最近发现论坛用户sera一直在发布和更新自己的补丁包,并且也很热心的回答其他用户的提问,就决定利用他的补丁自己编译。从目前运行情况来看一切正常。
然后首要解决的问题是vlan配置。sera在论坛帖子中提到过,他发布的补丁已经默认使用新的DSA(Distributed Switch Architecture)交换机驱动架构,之前openwrt一直使用的swconfig不再支持。DSA的细节我不打算深入解释,对我这样的普通用户而言,直观可见的变化在于:
- 传统的openwrt上,不同的lan口并不是可见的网卡,用户通过swconfig命令来管理交换机设备
- 在DSA驱动架构下,lan口变成了独立可见的lan1, lan2, lan3, lan4网卡。交换机设备不再对用户可见
- openwrt上vlan的配置方式已经和普通桌面/server Linux统一,用标准的iproute2包来管理,不再需要专用的swconfig
听起来是不是更容易了?不一定。先回顾一下我家里二级路由用的vlan设置:
这个配置对于swconfig而言非常简单,定义两个vlan,把不同的port标记为tagged或untagged即可。
但使用iproute2包中的ip命令,就无法完成这样的配置。因为ip并不直接支持把trunk port和untagged port划到同一个vlan!
具体的说,ip命令通过在普通网络设备上创建"别名"设备来创建vlan,例如假设有块网卡eth0:
ip link add name eth0.5 link eth0 type vlan id 5
这样会创建一个新的eth0.5的网卡,被划分到vlan 5下。所有在eth0物理端口上收到以太网包,如果带有vlan 5的tag,会被送给eth0.5这块网卡,而所有untagged的包还是会送给eth0。
也就是说,使用ip命令,只能建立进出流量都带tag的虚拟网卡,而无法直接把某块网卡直接加到某个vlan,也无法让这块网卡上收发的untagged数据在送到vlan中其他设备上时自动添加vlan tag。
以我非常有限的网络知识,觉得这个问题可以用网桥来解决。仍然使用ip命令来建立一个新的网桥设备,在trunk port对应的设备上建立vlan设备,并把vlan设备和untagged port对应的设备加入到同一个网桥:
# 新建网桥
ip link add name br0 type bridge
# 在lan1设备上新建vlan设备lan1.3并启用
ip link add name lan1.3 link lan1 type vlan id 3
ip link set lan1.3 up
# 将lan1.3加入到网桥
ip link set dev lan1.3 master br0
# 将lan3设备加入到网桥
ip link set dev lan3 master br0
然而这样还不行。虽然网桥会在设备间转发数据,但从lan3来的以太网包仍然没有vlan tag,lan1.3设备不会理睬;同样来自lan1.3的以太网包因为有vlan tag,也不会被lan3所连接的普通设备(IPTV机顶盒)所接受。
奇妙的是,似乎很少有人有这种需求。我只能Google到几条看起来有用的讨论,例如[Bridge] using bridges to connect vlans and untagged data。基本上都是需要使用ebtables规则来处理。我觉得使用ebtables一来麻烦,二来可能因为数据包要经过CPU处理而影响性能。
还好,我还搜到了这个短短的讨论串:Untagged vlan sub-interface。里面给出了重要的线索:iproute2包里其实还有一个叫做bridge的命令,看起来正是我需要的。
# 指定lan3在网桥中收发的数据都自动增加/去除vlan 3的tag
bridge vlan add dev lan3 vid 3 pvid untagged self
# 指定lan1.3在网桥中收发的数据都携带vlan 3的tag
bridge vlan add dev lan1.3 vid 3 self
# 查看网桥各端口设备的vlan设置情况
bridge vlan
# 以下为bridge vlan命令的输出,已略去无关内容
port vlan ids
...
lan3 None
lan3 3 PVID Egress Untagged
...
lan1 None
lan1 3
...
lan1.3 None
lan1.3 3
...
br0 None
解释一下,关键的命令
bridge vlan add dev lan3 vid3 pvid untagged self
pvid表示来自lan3网口(也就是IPTV)的以太网包会被自动加上vlan tag,而untagged表示来自网桥并经过lan3网口送出(到IPTV)的以太网包会被自动去除vlan tag。
接上网线,打开电视和IPTV,还是显示连接失败。用tcpdump -i lan1.3上有没有数据进出,发现有提示lan1.3设备没有up,手动up之;还是不行,用ifconfig给网桥br0加上IP地址,终于在IPTV上看到正常的用户界面了。(本人不确定这一步是不是必需,还望专家指正)
但无论选什么台,都没有画面,只有各级菜单界面是正常的。这个比较好解决,IPTV的视频数据是多播(multicast)来的,既然用户界面正常,只是没有画面,多半是网桥上还需要额外的多播设置。查了一下,果然linux网桥默认的multicast snooping(即IGMP snooping)是打开的,运行
echo 0 > /sys/class/net/br0/bridge/multicast_snooping
关闭之,节目画面正常啦。因为对Linux switch驱动并不了解,不清楚这样的设置下有多少是经由wrt1200ac的硬件完成,有多少部分需要CPU参与,我特意开着top看了一会儿IPTV节目,在观看大约10Mbps码率的视频流的情况下,wrt1200ac的CPU空闲率大致在92%到97%之间波动,与不开启IPTV时并无区别。
虽然需要的命令看起来不少,不过vlan设备和网桥的建立仍然可以在openwrt中使用uci命令或直接在/etc/config/network中配置,openwrt会正确处理。只有bridge vlan和设置多播开关一共三条命令需要写到rc.local里。重要的是,使用ip+bridge命令的配置方式对于运行openwrt或其他任何Linux发行版本的x86软路由也是适用的。
对了,提醒一下,openwrt下iproute2的bridge命令并未与ip命令一起打包,而是以ip-bridge的名字单独打包。openwrt中名为bridge的包也不是bridge命令,而是传统的brctl命令。
================== End