Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
81d4e8e
Documentation
LICENSES
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
connector
counter
cpufreq
cpuidle
crypto
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fpga
fsi
gnss
gpio
gpu
greybus
hid
hsi
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
ide
idle
iio
infiniband
input
interconnect
iommu
ipack
irqchip
isdn
leds
lightnvm
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
most
mtd
mux
net
appletalk
arcnet
bonding
caif
can
dsa
b53
microchip
mv88e6xxx
ocelot
qca
sja1105
Kconfig
Makefile
bcm_sf2.c
bcm_sf2.h
bcm_sf2_cfp.c
bcm_sf2_regs.h
dsa_loop.c
dsa_loop.h
dsa_loop_bdinfo.c
lan9303-core.c
lan9303.h
lan9303_i2c.c
lan9303_mdio.c
lantiq_gswip.c
lantiq_pce.h
mt7530.c
mt7530.h
mv88e6060.c
mv88e6060.h
qca8k.c
qca8k.h
realtek-smi-core.c
realtek-smi-core.h
rtl8366.c
rtl8366rb.c
vitesse-vsc73xx-core.c
vitesse-vsc73xx-platform.c
vitesse-vsc73xx-spi.c
vitesse-vsc73xx.h
ethernet
fddi
fjes
hamradio
hippi
hyperv
ieee802154
ipa
ipvlan
netdevsim
phy
plip
ppp
slip
team
usb
vmxnet3
wan
wimax
wireguard
wireless
xen-netback
Kconfig
LICENSE.SRC
Makefile
Space.c
bareudp.c
dummy.c
eql.c
geneve.c
gtp.c
ifb.c
loopback.c
macsec.c
macvlan.c
macvtap.c
mdio.c
mii.c
net_failover.c
netconsole.c
nlmon.c
ntb_netdev.c
rionet.c
sb1000.c
sungem_phy.c
tap.c
thunderbolt.c
tun.c
veth.c
virtio_net.c
vrf.c
vsockmon.c
vxlan.c
xen-netfront.c
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
oprofile
parisc
parport
pci
pcmcia
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sfi
sh
siox
slimbus
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
uio
usb
vdpa
vfio
vhost
video
virt
virtio
visorbus
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
net
/
dsa
/
dsa_loop.c
Blame
Blame
Latest commit
History
History
369 lines (294 loc) · 8.8 KB
Breadcrumbs
linux
/
drivers
/
net
/
dsa
/
dsa_loop.c
Top
File metadata and controls
Code
Blame
369 lines (294 loc) · 8.8 KB
Raw
// SPDX-License-Identifier: GPL-2.0-or-later /* * Distributed Switch Architecture loopback driver * * Copyright (C) 2016, Florian Fainelli <f.fainelli@gmail.com> */ #include <linux/platform_device.h> #include <linux/netdevice.h> #include <linux/phy.h> #include <linux/phy_fixed.h> #include <linux/export.h> #include <linux/ethtool.h> #include <linux/workqueue.h> #include <linux/module.h> #include <linux/if_bridge.h> #include <net/dsa.h> #include "dsa_loop.h" struct dsa_loop_vlan { u16 members; u16 untagged; }; struct dsa_loop_mib_entry { char name[ETH_GSTRING_LEN]; unsigned long val; }; enum dsa_loop_mib_counters { DSA_LOOP_PHY_READ_OK, DSA_LOOP_PHY_READ_ERR, DSA_LOOP_PHY_WRITE_OK, DSA_LOOP_PHY_WRITE_ERR, __DSA_LOOP_CNT_MAX, }; static struct dsa_loop_mib_entry dsa_loop_mibs[] = { [DSA_LOOP_PHY_READ_OK] = { "phy_read_ok", }, [DSA_LOOP_PHY_READ_ERR] = { "phy_read_err", }, [DSA_LOOP_PHY_WRITE_OK] = { "phy_write_ok", }, [DSA_LOOP_PHY_WRITE_ERR] = { "phy_write_err", }, }; struct dsa_loop_port { struct dsa_loop_mib_entry mib[__DSA_LOOP_CNT_MAX]; u16 pvid; }; #define DSA_LOOP_VLANS 5 struct dsa_loop_priv { struct mii_bus *bus; unsigned int port_base; struct dsa_loop_vlan vlans[DSA_LOOP_VLANS]; struct net_device *netdev; struct dsa_loop_port ports[DSA_MAX_PORTS]; }; static struct phy_device *phydevs[PHY_MAX_ADDR]; static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp) { dev_dbg(ds->dev, "%s: port: %d\n", __func__, port); return DSA_TAG_PROTO_NONE; } static int dsa_loop_setup(struct dsa_switch *ds) { struct dsa_loop_priv *ps = ds->priv; unsigned int i; for (i = 0; i < ds->num_ports; i++) memcpy(ps->ports[i].mib, dsa_loop_mibs, sizeof(dsa_loop_mibs)); dev_dbg(ds->dev, "%s\n", __func__); return 0; } static int dsa_loop_get_sset_count(struct dsa_switch *ds, int port, int sset) { if (sset != ETH_SS_STATS && sset != ETH_SS_PHY_STATS) return 0; return __DSA_LOOP_CNT_MAX; } static void dsa_loop_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) { struct dsa_loop_priv *ps = ds->priv; unsigned int i; if (stringset != ETH_SS_STATS && stringset != ETH_SS_PHY_STATS) return; for (i = 0; i < __DSA_LOOP_CNT_MAX; i++) memcpy(data + i * ETH_GSTRING_LEN, ps->ports[port].mib[i].name, ETH_GSTRING_LEN); } static void dsa_loop_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) { struct dsa_loop_priv *ps = ds->priv; unsigned int i; for (i = 0; i < __DSA_LOOP_CNT_MAX; i++) data[i] = ps->ports[port].mib[i].val; } static int dsa_loop_phy_read(struct dsa_switch *ds, int port, int regnum) { struct dsa_loop_priv *ps = ds->priv; struct mii_bus *bus = ps->bus; int ret; ret = mdiobus_read_nested(bus, ps->port_base + port, regnum); if (ret < 0) ps->ports[port].mib[DSA_LOOP_PHY_READ_ERR].val++; else ps->ports[port].mib[DSA_LOOP_PHY_READ_OK].val++; return ret; } static int dsa_loop_phy_write(struct dsa_switch *ds, int port, int regnum, u16 value) { struct dsa_loop_priv *ps = ds->priv; struct mii_bus *bus = ps->bus; int ret; ret = mdiobus_write_nested(bus, ps->port_base + port, regnum, value); if (ret < 0) ps->ports[port].mib[DSA_LOOP_PHY_WRITE_ERR].val++; else ps->ports[port].mib[DSA_LOOP_PHY_WRITE_OK].val++; return ret; } static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *bridge) { dev_dbg(ds->dev, "%s: port: %d, bridge: %s\n", __func__, port, bridge->name); return 0; } static void dsa_loop_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *bridge) { dev_dbg(ds->dev, "%s: port: %d, bridge: %s\n", __func__, port, bridge->name); } static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { dev_dbg(ds->dev, "%s: port: %d, state: %d\n", __func__, port, state); } static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) { dev_dbg(ds->dev, "%s: port: %d, vlan_filtering: %d\n", __func__, port, vlan_filtering); return 0; } static int dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { struct dsa_loop_priv *ps = ds->priv; struct mii_bus *bus = ps->bus; dev_dbg(ds->dev, "%s: port: %d, vlan: %d-%d", __func__, port, vlan->vid_begin, vlan->vid_end); /* Just do a sleeping operation to make lockdep checks effective */ mdiobus_read(bus, ps->port_base + port, MII_BMSR); if (vlan->vid_end > DSA_LOOP_VLANS) return -ERANGE; return 0; } static void dsa_loop_port_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; struct dsa_loop_priv *ps = ds->priv; struct mii_bus *bus = ps->bus; struct dsa_loop_vlan *vl; u16 vid; /* Just do a sleeping operation to make lockdep checks effective */ mdiobus_read(bus, ps->port_base + port, MII_BMSR); for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { vl = &ps->vlans[vid]; vl->members |= BIT(port); if (untagged) vl->untagged |= BIT(port); else vl->untagged &= ~BIT(port); dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n", __func__, port, vid, untagged ? "un" : "", pvid); } if (pvid) ps->ports[port].pvid = vid; } static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; struct dsa_loop_priv *ps = ds->priv; struct mii_bus *bus = ps->bus; struct dsa_loop_vlan *vl; u16 vid, pvid = ps->ports[port].pvid; /* Just do a sleeping operation to make lockdep checks effective */ mdiobus_read(bus, ps->port_base + port, MII_BMSR); for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { vl = &ps->vlans[vid]; vl->members &= ~BIT(port); if (untagged) vl->untagged &= ~BIT(port); if (pvid == vid) pvid = 1; dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n", __func__, port, vid, untagged ? "un" : "", pvid); } ps->ports[port].pvid = pvid; return 0; } static const struct dsa_switch_ops dsa_loop_driver = { .get_tag_protocol = dsa_loop_get_protocol, .setup = dsa_loop_setup, .get_strings = dsa_loop_get_strings, .get_ethtool_stats = dsa_loop_get_ethtool_stats, .get_sset_count = dsa_loop_get_sset_count, .get_ethtool_phy_stats = dsa_loop_get_ethtool_stats, .phy_read = dsa_loop_phy_read, .phy_write = dsa_loop_phy_write, .port_bridge_join = dsa_loop_port_bridge_join, .port_bridge_leave = dsa_loop_port_bridge_leave, .port_stp_state_set = dsa_loop_port_stp_state_set, .port_vlan_filtering = dsa_loop_port_vlan_filtering, .port_vlan_prepare = dsa_loop_port_vlan_prepare, .port_vlan_add = dsa_loop_port_vlan_add, .port_vlan_del = dsa_loop_port_vlan_del, }; static int dsa_loop_drv_probe(struct mdio_device *mdiodev) { struct dsa_loop_pdata *pdata = mdiodev->dev.platform_data; struct dsa_loop_priv *ps; struct dsa_switch *ds; int ret; if (!pdata) return -ENODEV; ds = devm_kzalloc(&mdiodev->dev, sizeof(*ds), GFP_KERNEL); if (!ds) return -ENOMEM; ds->dev = &mdiodev->dev; ds->num_ports = DSA_MAX_PORTS; ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL); if (!ps) return -ENOMEM; ps->netdev = dev_get_by_name(&init_net, pdata->netdev); if (!ps->netdev) return -EPROBE_DEFER; pdata->cd.netdev[DSA_LOOP_CPU_PORT] = &ps->netdev->dev; ds->dev = &mdiodev->dev; ds->ops = &dsa_loop_driver; ds->priv = ps; ps->bus = mdiodev->bus; dev_set_drvdata(&mdiodev->dev, ds); ret = dsa_register_switch(ds); if (!ret) dev_info(&mdiodev->dev, "%s: 0x%0x\n", pdata->name, pdata->enabled_ports); return ret; } static void dsa_loop_drv_remove(struct mdio_device *mdiodev) { struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); struct dsa_loop_priv *ps = ds->priv; dsa_unregister_switch(ds); dev_put(ps->netdev); } static struct mdio_driver dsa_loop_drv = { .mdiodrv.driver = { .name = "dsa-loop", }, .probe = dsa_loop_drv_probe, .remove = dsa_loop_drv_remove, }; #define NUM_FIXED_PHYS (DSA_LOOP_NUM_PORTS - 2) static int __init dsa_loop_init(void) { struct fixed_phy_status status = { .link = 1, .speed = SPEED_100, .duplex = DUPLEX_FULL, }; unsigned int i; for (i = 0; i < NUM_FIXED_PHYS; i++) phydevs[i] = fixed_phy_register(PHY_POLL, &status, NULL); return mdio_driver_register(&dsa_loop_drv); } module_init(dsa_loop_init); static void __exit dsa_loop_exit(void) { unsigned int i; mdio_driver_unregister(&dsa_loop_drv); for (i = 0; i < NUM_FIXED_PHYS; i++) if (!IS_ERR(phydevs[i])) fixed_phy_unregister(phydevs[i]); } module_exit(dsa_loop_exit); MODULE_SOFTDEP("pre: dsa_loop_bdinfo"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Florian Fainelli"); MODULE_DESCRIPTION("DSA loopback driver");
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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
You can’t perform that action at this time.