Изображение выше взято из руководства по физическому чипу сетевой карты серии RTL8201F от RealTek. В соответствии с 7-уровневой сетевой моделью OSI чип PHY сетевой карты (RTL8201F на рисунке) расположен на физическом уровне, а соответствующий программный уровень — это уровень драйвера PHY, обсуждаемый в этой статье, а MAC расположен на уровне; Уровень канала передачи данных, который в программном обеспечении обычно называют драйвером сетевой карты, не является предметом данной статьи и не будет расширяться. Кроме того, чип PHY можно настроить через интерфейс MDIO (например, чтение и запись регистра чипа PHY), а PHY и MAC выполняют передачу данных через MII/RMII.
Чип PHY подключен к чипу MAC уровня канала передачи данных через различные независимые от среды интерфейсы (независимые от среды интерфейсы), такие как MII/GMII/RMII/SGMII/XGMII, и отслеживает, настраивает и управляет состоянием PHY через интерфейс MDIO.
Общая блок-схема подключения PHY и MAC:
Каждый физический чип создаст устройство типа struct phy_device, соответствующее драйверу типа struct phy_driver. Эти два фактически монтируются на шине mdio_bus_type, и Mac будет зарегистрирован как struct net_device.
struct phy_device {
struct phy_driver *drv; // PHYоборудованиеводить машину
struct mii_bus *bus; // Переписка изMIIавтобус
struct device dev; // оборудованиедокумент
u32 phy_id; // PHY ID
struct phy_c45_device_ids c45_ids;
bool is_c45;
bool is_internal;
bool has_fixups;
bool suspended;
enum phy_state state; // PHY-статус
u32 dev_flags;
phy_interface_t interface; // PHYинтерфейс
int addr; // PHY автобусадрес(0~31)
int speed; // скорость
int duplex; // дуплексный режим
int pause; // останавливаться
int asym_pause;
int link;
u32 interrupts; // Флаг разрешения прерывания
u32 supported;
u32 advertising;
u32 lp_advertising;
int autoneg;
int link_timeout;
int irq; // номер прерывания
void *priv; // личные данные
struct work_struct phy_queue; // Рабочая очередь PHY
struct delayed_work state_queue; // Очередь задержанной работы PHY
atomic_t irq_disable;
struct mutex lock;
struct net_device *attached_dev; // сетьоборудование
void (*adjust_link)(struct net_device *dev);
};
struct phy_driver {
struct mdio_driver_common mdiodrv;
u32 phy_id;
char *name;
u32 phy_id_mask;
u32 features;
u32 flags;
const void *driver_data;
int (*soft_reset)(struct phy_device *phydev);
int (*config_init)(struct phy_device *phydev);
int (*probe)(struct phy_device *phydev);
int (*suspend)(struct phy_device *phydev);
int (*resume)(struct phy_device *phydev);
int (*config_aneg)(struct phy_device *phydev);
int (*aneg_done)(struct phy_device *phydev);
int (*read_status)(struct phy_device *phydev);
int (*ack_interrupt)(struct phy_device *phydev);
int (*config_intr)(struct phy_device *phydev);
int (*did_interrupt)(struct phy_device *phydev);
void (*remove)(struct phy_device *phydev);
int (*match_phy_device)(struct phy_device *phydev);
int (*ts_info)(struct phy_device *phydev, struct ethtool_ts_info *ti);
int (*hwtstamp)(struct phy_device *phydev, struct ifreq *ifr);
bool (*rxtstamp)(struct phy_device *dev, struct sk_buff *skb, int type);
void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb, int type);
int (*set_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
void (*get_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
void (*link_change_notify)(struct phy_device *dev);
int (*read_mmd)(struct phy_device *dev, int devnum, u16 regnum);
int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum,
u16 val);
int (*read_page)(struct phy_device *dev);
int (*write_page)(struct phy_device *dev, int page)
int (*module_info)(struct phy_device *dev,
struct ethtool_modinfo *modinfo);
int (*module_eeprom)(struct phy_device *dev,
struct ethtool_eeprom *ee, u8 *data);
int (*get_sset_count)(struct phy_device *dev);
void (*get_strings)(struct phy_device *dev, u8 *data);
void (*get_stats)(struct phy_device *dev,
struct ethtool_stats *stats, u64 *data);
int (*get_tunable)(struct phy_device *dev,
struct ethtool_tunable *tuna, void *data);
int (*set_tunable)(struct phy_device *dev,
struct ethtool_tunable *tuna,
const void *data);
int (*set_loopback)(struct phy_device *dev, bool enable);
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
};
struct mii_bus {
const char *name; // автобусимя char id[MII_BUS_ID_SIZE]; // ID MII_BUS_ID_SIZE=61
void *priv; // личные данные
int (*read)(struct mii_bus *bus, int phy_id, int regnum); // Метод чтения
int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val); // метод письма
int (*reset)(struct mii_bus *bus); // перезагрузить
struct mutex mdio_lock;
struct device *parent; // отецоборудование enum {
MDIOBUS_ALLOCATED = 1,
MDIOBUS_REGISTERED,
MDIOBUS_UNREGISTERED,
MDIOBUS_RELEASED,
} state; // автобуссостояние
struct device dev; // оборудованиедокумент struct phy_device *phy_map[PHY_MAX_ADDR]; // PHYоборудование массива
u32 phy_mask;
int *irq; // прерывать
};
struct net_device {
char name[IFNAMSIZ]; /* Используется для хранения названий сетевыхоборудованиеизоборудований. */
char *ifalias; /* сетьоборудованиеизалиас */
int ifindex; /* значение индекса сетиоборудованиеизинтерфейса, уникальный идентификатор изсетиоборудования */
struct hlist_node name_hlist; /* Это поле используется для построения хеш-таблицы имя_оборудования сети, а структура struct изname_hlist в сети указывает на каждую хеш-таблицу из заголовка связанного списка */
struct hlist_node index_hlist; /* Используется для построения хеш-таблицы значений индекса сетиоборудованиеизинтерфейса, существующей структуры. изindex_hlist в сети используется для указания на хеш-таблицу значений индекса интерфейса из заголовка связанного списка. */
struct list_head dev_list; /* Используется для добавления каждого «одинсетьоборудование» в пространство имен «одинсеть» двусвязного списка «одинсетьоборудование». */
unsigned int flags; /* идентификатор сетевого оборудованияинтерфейсиз */
unsigned int priv_flags; /* идентификатор сетевого оборудованияинтерфейсиз,Но не виден в пользовательском пространстве;*/
unsigned short type; /* Тип оборудования интерфейса */
unsigned int mtu; /* сетьоборудованиеинтерфейсиз блок максимальной передачи */
unsigned short hard_header_len; /* Аппаратный интерфейс Длина заголовка */
unsigned char *dev_addr; /* сетьоборудованиеинтерфейсизMAC-адрес */
bool uc_promisc; /* Одноадресный режим */
unsigned int promiscuity; /* сетьоборудованиеинтерфейсиз беспорядочного режима */
unsigned int allmulti; /* сетьоборудованиеинтерфейсиз Полный многоадресный режим */
struct netdev_hw_addr_list uc; /* Список вторичных одноадресных MAC-адресов */
struct netdev_hw_addr_list mc; /* Основной список Mac-адресов */
struct netdev_hw_addr_list dev_addrs; /* hwоборудование список адресов */
unsigned char broadcast[MAX_ADDR_LEN]; /* широковещательный адрес */
struct netdev_rx_queue *_rx; /* сетьоборудованиеинтерфейсиз очередь приема пакетов */
struct netdev_queue *_tx /* сетьоборудованиеинтерфейсиз очередь отправки пакетов */
unsigned int num_tx_queues; /* Количество очередей передачи */
unsigned int real_num_tx_queues; /* текущийоборудование Активностьиз Количество очередей передачи */
unsigned long tx_queue_len; /* В каждой очереди разрешено максимальное количество кадров */
unsigned long state; /* сетьоборудованиеинтерфейсиз СТАТУС */
struct net_device_stats stats; /* сетьоборудованиеинтерфейсизстатистика */
possible_net_t nd_net; /* Используется для выполнения пространства имен сетиоборудование существуиз. */
};
На примере сетевой карты Fec драйвер сетевой карты проходит определение dts при инициализации fec_probe() и создает устройство соответствующего типа struct phy_device. Основные шаги:
fec_probe(struct platform_device *pdev)
-> struct device_node *np = pdev->dev.of_node, *phy_node; // Получите дескриптор узла дерева оборудования и создайте дескриптор узла дерева однофизоборудования.
-> fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); // Получить значения атрибутов fsl, num-tx-queuesifsl, num-rx-queuesиз из дерева оборудования.
-> ndev = alloc_etherdev_mqs // Применятьnet_device
-> netdev_priv(ndev) // получатьличные данныекосмос сначалаадрес
--------------------------------------------------------------------------------------------------------------------------
-> of_parse_phandle(np, "phy-handle", 0) // Получить дочерний узел phy из узла дерева Macizоборудование.
-> of_get_phy_mode(pdev->dev.of_node) // Получить режим phy из узла дерева оборудования, phy-mode = "rmii";
-> fec_reset_phy(pdev); // перезагрузитьphy
-> fec_enet_init(ndev) // Применить очередь и DMA, установить MAC-адрес
-> of_property_read_u32(np, "fsl,wakeup_irq", &irq) // будитьпрерывать
-> fec_enet_mii_init(pdev); // Зарегистрируйте MDIOавтобус, зарегистрируйте phy_device
-> fep->mii_bus = mdiobus_alloc() //ПрименятьMDIOавтобус
-> fep->mii_bus->name = "fec_enet_mii_bus"; // автобусимя -> fep->mii_bus->read = fec_enet_mdio_read; // функция чтения автобуса
-> fep->mii_bus->write = fec_enet_mdio_write; // функция записи автобусиз
-> snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, fep->dev_id + 1); // автобусid
-> of_get_child_by_name(pdev->dev.of_node, "mdio"); // Получить дескриптор узла phy
-> of_mdiobus_register // Зарегистрировать дочерние узлы дерева mii_busоборудование и провестиоборудование, создать PHYоборудование. drivers/of/of_mdio.c of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
-> mdio->phy_mask = ~0; // Экранируйте все PHY, чтобы предотвратить автоматическое обнаружение. Вместо этого оборудование, указанное в дереве, будет заполнено после регистрации.
-> mdio->dev.of_node = np;
-> mdio->reset_delay_us = DEFAULT_GPIO_RESET_DELAY;
-> mdiobus_register(mdio) // Зарегистрировать МДИОавтобусоборудование
-> bus->dev.parent = bus->parent;
-> bus->dev.class = &mdio_bus_class; // автобусоборудованиедобрый“/sys/bus/mdio_bus” /*-----------------------------------------
static struct class mdio_bus_class = {
.name = "mdio_bus",
.dev_release = mdiobus_release,
};
-------------------------------------------*/
-> bus->dev.groups = NULL;
-> dev_set_name(&bus->dev, "%s", bus->id); //Установить название автобусоборудованиеиз
-> device_register(&bus->dev); // зарегистрироватьсяавтобусоборудование -> if (bus->reset) bus->reset(bus); // автобусперезагрузить
---------------------------------------Другой анализ ветвей (можно игнорировать)--- -------------------------------------------------- ---
-> phydev = mdiobus_scan(bus, i); // Скан фиоборудование
-> phydev = get_phy_device(bus, addr); //Получаем созданное фиоборудование
->err = phy_device_register(phydev); //Регистрация фиоборудования
--------------------------------------------------------------------------------------------------------------------
-> for_each_available_child_of_node(np, child) { // Пройдитесь по этой платформе дочерних узлов и зарегистрируйте по одному_устройству для каждого.
-> addr = of_mdio_parse_addr(&mdio->dev, child) // Получите адрес PHYоборудованиеиз из атрибута «reg» дочернего узла.
-> of_property_read_u32(np, "reg", &addr)
-> if (addr < 0) scanphys = true; continue; // Если дочерний узел с атрибутом «reg» не получен, существование может быть сохранено, если сканирование будет включено позже, а затем зарегистрировано.
-> of_mdiobus_register_phy(mdio, child, addr) } // Создать и зарегистрироватьсяPHYоборудование
-> is_c45 = of_device_is_compatible(child,"ethernet-phy-ieee802.3-c45") //Определяем, соответствует ли атрибут изPHYиз в дереве оборудования разделу 45
-> if (!is_c45 && !of_get_phy_id(child, &phy_id)) //Если атрибут изPHYиз в дереве оборудования не указан, п.45 И PHYизID не указан в атрибуте "ethernet-phy-id%4x.%4x"
-> phy = phy_device_create(mdio, addr, phy_id, 0, NULL);
-> else phy = get_phy_device(mdio, addr, is_c45); //используем эту ветку
-> get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids);//проходитьmdioполучатьPHYизID
-> mdiobus_read(bus, addr, MII_PHYSID1)
-> __mdiobus_read(bus, addr, regnum);
-> bus->read(bus, addr, regnum)
-> mdiobus_read(bus, addr, MII_PHYSID2)
-> phy_device_create(bus, addr, phy_id, is_c45, &c45_ids) // Создать PHYоборудование
-> struct phy_device *dev;
-> dev = kzalloc(sizeof(*dev), GFP_KERNEL);
dev->dev.release = phy_device_release;
dev->speed = 0;
dev->duplex = -1;
dev->pause = 0;
dev->asym_pause = 0;
dev->link = 1;
dev->interface = PHY_INTERFACE_MODE_GMII;
dev->autoneg = AUTONEG_ENABLE; // По умолчанию поддерживается автосогласование (включается автоматически)
dev->is_c45 = is_c45;
dev->addr = addr;
dev->phy_id = phy_id;
if (c45_ids)
dev->c45_ids = *c45_ids;
dev->bus = bus;
dev->dev.parent = bus->parent;
dev->dev.bus = &mdio_bus_type; //PHYоборудованиеиводить Все машины будут подключены к существующему mdio_bus, и при сопоставлении будет вызываться соответствующая функция изmatch. --
/*----------------------------------------------------------------------------------------------------
struct bus_type mdio_bus_type = {
.name = "mdio_bus",//автобусимя .match = mdio_bus_match, //используется длясоответствоватьавтобусначальствооборудованиеиводить функция машиныиз
.pm = MDIO_BUS_PM_OPS,
.dev_groups = mdio_dev_groups,
};
----------------------------------------------------------------------------------------------------*/
dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL;
dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr);
dev->state = PHY_DOWN; //инструктироватьPHYоборудованиеиводить программа машины еще не готова,существоватьPHYпривод Функция машиныизпробе будет изменена на ГОТОВО
-> INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); //PHYиз конечного автомата (ядро РАБОТЫ) последующий анализ
-> INIT_WORK(&dev->phy_queue, phy_change); // Автор: phy_interrupt / расписание таймера для обработки изменений PHY-статуса
-> request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id)); // Загрузить модуль ядра
-> device_initialize(&dev->dev); //В модели присутствует некоторое оборудование, в основном настройки kset, kobject, ktypeиз
-> irq_of_parse_and_map(child, 0); //Разобрать и сопоставить прерывание с Linux виркспейс(
-> of_node_get(child); //Связываем узел OF со структурой оборудования
-> phy->dev.of_node = child;
-> phy_device_register(phy) // Зарегистрировать фиоборудование
-> if (phydev->bus->phy_map[phydev->addr]) //Определяем, зарегистрирован ли PHY
-> phydev->bus->phy_map[phydev->addr] = phydev; //Добавляем PHY в busizphy_map
-> phy_scan_fixups(phydev); //Выполняем сопоставление исправлений
-> device_add(&phydev->dev); // Зарегистрируйтесь в рамках модели linuxоборудование
-> if (!scanphys) return 0; // если Получите адрес PHYоборудованиеиз из атрибута «reg» дочернего узла.,scanphys=false,Вернитесь прямо сюда,Потому что больше нет необходимости сканировать
------------Вообще говоря, пока в дереве оборудования указан атрибут PHYоборудованиеиз"reg", последующий процесс может быть автоматически проигнорирован. ------------
-> register_netdev(ndev) // Зарегистрируйте net_device в ядре
Есть универсальный драйвер genphy_driver,а также Собственный драйвер (здесь начинается с NXP TJA драйвер в качестве примера), различия заключаются в следующем:
static struct phy_driver genphy_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
.get_features = genphy_read_abilities,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
};
static struct phy_driver tja11xx_driver[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_TJA1100),
.name = "NXP TJA1100",
.features = PHY_BASIC_T1_FEATURES,
.probe = tja11xx_probe,
.soft_reset = tja11xx_soft_reset,
.config_aneg = tja11xx_config_aneg,
.config_init = tja11xx_config_init,
.read_status = tja11xx_read_status,
.get_sqi = tja11xx_get_sqi,
.get_sqi_max = tja11xx_get_sqi_max,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
/* Statistics */
.get_sset_count = tja11xx_get_sset_count,
.get_strings = tja11xx_get_strings,
.get_stats = tja11xx_get_stats,
}
};
module_phy_driver(tja11xx_driver);
Процесс регистрации структуры phy_driver genphy_driver выглядит следующим образом:
phy_init
phy_driver_register()
driver_register(&new_driver->mdiodrv.driver)
bus_add_driver(drv)
driver_attach(drv)
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
while ((dev = next_device(&i)) && !error)
/* Переход к регистрации из PHY оборудованиечас */
fn(dev, data) = __driver_attach()
/* соответствоватьоборудованиеиводить машину */
driver_match_device(drv, dev)
mdio_bus_match(dev, drv)
phy_bus_match(dev, drv)
/* в соответствии с phy_id & phy_id_mask соответствовать */
return (phydrv->phy_id & phydrv->phy_id_mask) == (phydev->phy_id & phydrv->phy_id_mask);
/* соответствоватьприезжатьоборудованиеиводить машину,нагрузкаводить машину */
driver_probe_device(drv, dev)
really_probe(dev, drv)
dev->driver = drv; /* обязательностьоборудованиеизводить машину */
drv->probe(dev) = phy_probe
Одним из ключевых моментов является то, что функция проверки драйвера mdio является общей функцией phy_probe:
static int phy_probe(struct device *dev)
{
struct phy_device *phydev = to_phy_device(dev); // GetPHYоборудование
struct device_driver *drv = phydev->mdio.dev.driver;
struct phy_driver *phydrv = to_phy_driver(drv); // GetPHYпривод машину
int err = 0;
phydev->drv = phydrv; /* обязательность phy_device и phy_driver */
/* PHY прерыватьфинальный режим Конфигурация */
if (!phy_drv_supports_irq(phydrv) && phy_interrupt_is_valid(phydev)) // Настроить способ прерывания
phydev->irq = PHY_POLL;
if (phydrv->flags & PHY_IS_INTERNAL)
phydev->is_internal = true;
/* Deassert the reset signal */
phy_device_reset(phydev, 0);
if (phydev->drv->probe) { // суждениеводить у машины есть метод зондирования
err = phydev->drv->probe(phydev); /* PHY водить машинуиз probe */
if (err)
goto out;
}
......
if (phydrv->features) {
linkmode_copy(phydev->supported, phydrv->features);
}
else if (phydrv->get_features)
err = phydrv->get_features(phydev);
else if (phydev->is_c45)
err = genphy_c45_pma_read_abilities(phydev);
else
err = genphy_read_abilities(phydev); //Читаем регистр состояния, чтобы определить phy Чип по возможностям
if (err)
goto out;
if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
phydev->supported))
phydev->autoneg = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
phydev->supported))
phydev->is_gigabit_capable = 1;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
phydev->supported))
phydev->is_gigabit_capable = 1;
/* PHY Функции Конфигурация */
of_set_phy_supported(phydev);
phy_advertise_supported(phydev);
......
of_set_phy_eee_broken(phydev);
......
if (!test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported) &&
!test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported)) {
linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
phydev->supported);
}
/* Set the state to READY by default */
phydev->state = PHY_READY; /* отметка PHY оборудование готово */
out:
/* Re-assert the reset signal on error */
if (err)
phy_device_reset(phydev, 1);
return err;
}
Общий физический драйвер вызовет функцию genphy_read_abilities для чтения регистра состояния и определения возможностей физического чипа:
genphy_read_abilities()
`-| {
| val = phy_read(phydev, MII_BMSR); // читать mdio 0x01 зарегистрироваться, чтобы определить phy из 10/100M способность
| linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported, val & BMSR_ANEGCAPABLE);
| linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported, val & BMSR_100FULL);
| linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported, val & BMSR_100HALF);
| linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported, val & BMSR_10FULL);
| linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported, val & BMSR_10HALF);
| if (val & BMSR_ESTATEN) {
| val = phy_read(phydev, MII_ESTATUS); // читать mdio 0x0f зарегистрироваться, чтобы определить phy из 1000M способность
| linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev->supported, val & ESTATUS_1000_TFULL);
| linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, phydev->supported, val & ESTATUS_1000_THALF);
| linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, phydev->supported, val & ESTATUS_1000_XFULL);
| }
| }
Процесс регистрации структуры phy_driver драйвера NXP TJA выглядит следующим образом:
#define phy_module_driver(__phy_drivers, __count) \
static int __init phy_module_init(void) \
{ \
return phy_drivers_register(__phy_drivers, __count, THIS_MODULE); \
} \
module_init(phy_module_init); \
static void __exit phy_module_exit(void) \
{ \
phy_drivers_unregister(__phy_drivers, __count); \
} \
module_exit(phy_module_exit)
#define module_phy_driver(__phy_drivers) \
phy_module_driver(__phy_drivers, ARRAY_SIZE(__phy_drivers))
int phy_drivers_register(struct phy_driver *new_driver, int n,
struct module *owner)
{
int i, ret = 0;
for (i = 0; i < n; i++) {
ret = phy_driver_register(new_driver + i, owner); // Зарегистрируйте все изфиговоды в массиве машину
if (ret) {
while (i-- > 0)
phy_driver_unregister(new_driver + i);
break;
}
}
return ret;
}
EXPORT_SYMBOL(phy_drivers_register);
Согласно приведенному выше анализу, из-за существования phydev->drv->probe,Поэтому будет вызвана его зарегистрированная функция tja11xx_probe.
В ядре Linux Mac Ethernet будет зарегистрирован как struct net_device, а физический чип будет зарегистрирован как struct phy_device. Как передать статус phy_device в net_device, чтобы он мог вносить соответствующие изменения в конфигурацию при изменении статуса канала. Эта задача ложится на вышеупомянутый посредник struct phylink.
Ниже в качестве примера используется драйвер сетевого порта fec, демонстрирующий процесс взаимодействия сетевой карты fec и phy. Основной процесс вызова всего физического драйвера показан на рисунке ниже:
Принцип действия физического драйвера на самом деле очень прост. Общий процесс выглядит следующим образом:
Статус фи-чипа уже отображается при регистрации фи-устройства. Вот подробное описание, как правильно настроить статус mac при изменении статуса фи-линка.
void phy_state_machine(struct work_struct *work)
{
old_state = phydev->state;
/* (1) Орган государственной машины */
switch (phydev->state) {
/* (1.1) существовать PHY_DOWN/PHY_READY Никаких действий в штате */
case PHY_DOWN:
case PHY_READY:
break;
/* (1.2) существовать PHY_UP статус, указывающий, что сетевой порт up Чтобы встать, вам нужно запустить автосогласование и запросить процесс автосогласования. link состояние
Если результат самосогласования link вверх, войти PHY_RUNNING состояние
Если результат самосогласования link вниз, войти PHY_NOLINK состояние
*/
case PHY_UP:
needs_aneg = true;
break;
/* (1.3) существуют Регулярное голосование во время забега из link состояние
если link вверх, войти PHY_RUNNING состояние
если link вниз, войти PHY_NOLINK состояние
*/
case PHY_NOLINK:
case PHY_RUNNING:
err = phy_check_link_status(phydev);
break;
}
/* (2) еслинуждаться,запускать Конфигурация автосогласования */
if (needs_aneg)
err = phy_start_aneg(phydev);
/* (3) если phy link Есть изменения в состоянии,уведомить на соответствующий сетевой порт netdev */
if (old_state != phydev->state) {
phydev_dbg(phydev, "PHY state change %s -> %s\n",
phy_state_to_str(old_state),
phy_state_to_str(phydev->state));
if (phydev->drv && phydev->drv->link_change_notify)
phydev->drv->link_change_notify(phydev);
}
/* (4) Перезапуск работа, период 1s */
if (phy_polling_mode(phydev) && phy_is_started(phydev))
phy_queue_state_machine(phydev, PHY_STATE_TIME);
}
Конкретный процесс кода для запуска автосогласования phy выглядит следующим образом:
phy_state_machine()
`-| phy_start_aneg()
`-| phy_config_aneg()
`-| genphy_config_aneg()
`-| __genphy_config_aneg()
`-| genphy_setup_master_slave() // (1) если Это гигабитный сетевой порт,Конфигурация Что master/slave
`-| {
| phy_modify_changed(phydev, MII_CTRL1000, // Конфигурация mdio 0x09 зарегистрироваться
| (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER | CTL1000_PREFER_MASTER), ctl);
| }
| genphy_config_advert() // (2) Установите локальный терминал из advert способность
`-| {
| linkmode_and(phydev->advertising, phydev->advertising, phydev->supported);
| adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
| phy_modify_changed(phydev, MII_ADVERTISE, // 10M/100M способность Конфигурацияприезжать mdio 0x04 зарегистрироваться
| ADVERTISE_ALL | ADVERTISE_100BASE4 |
| ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, adv);
| if (!(bmsr & BMSR_ESTATEN)) return changed;
| adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
| phy_modify_changed(phydev, MII_CTRL1000, // 1000M способность Конфигурацияприезжать mdio 0x09 зарегистрироваться
| ADVERTISE_1000FULL | ADVERTISE_1000HALF, adv);
| }
| genphy_check_and_restart_aneg()
`-| genphy_restart_aneg() // (3) запускать phy самопереговоры
`-| {
| phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, // Конфигурация mdio 0x00 зарегистрироваться
| BMCR_ANENABLE | BMCR_ANRESTART);
| }
Физическое чтение последствий ссылок. Поток кода выглядит следующим образом:
phy_state_machine()
`-| phy_check_link_status()
`-| phy_read_status() // (1) читать link состояние
`-| genphy_read_status()
`-| {
| genphy_update_link(phydev); // (1.1) возобновлять link состояние
| if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) return 0;
| genphy_read_master_slave(phydev); // (1.2) если Это гигабитный сетевой порт,возобновлять Локальный конецивглядетьсяиз master/slave
| genphy_read_lpa(phydev); // (1.3) возобновлятьвглядеться(link partner) Заявление о способности
| if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
| phy_resolve_aneg_linkmode(phydev); // (1.4.1) самопереговорымодель,анализировать link результат
| } else if (phydev->autoneg == AUTONEG_DISABLE) {
| genphy_read_status_fixed(phydev); // (1.4.2) Фиксированный режим, парсинг link результат
| }
| }
| if (phydev->link && phydev->state != PHY_RUNNING) { // (2) link состояние change событие: стать link up
| phydev->state = PHY_RUNNING;
| phy_link_up(phydev); // link up событие, уведомление phylink
| } else if (!phydev->link && phydev->state != PHY_NOLINK) { // (3) link состояние change событие: стать link down
| phydev->state = PHY_NOLINK;
| phy_link_down(phydev); // link down событие, уведомление phylink
| }
Как уведомить netdev об изменении статуса ссылки phy и разрешить Mac внести соответствующие изменения в конфигурацию. Это достигается через промежуточную ссылку phylink.
phy_link_up()/phy_link_down()
`-| phydev->phy_link_change(phydev, true/false);
`-| phylink_phy_change()
`-| {
| pl->phy_state.speed = phydev->speed; // (1) Пучок `phy_device` состояниевозобновлять Давать `phylink`
| pl->phy_state.duplex = phydev->duplex;
| pl->phy_state.interface = phydev->interface;
| pl->phy_state.link = up;
| phylink_run_resolve(pl); // (2) уведомить `phylink` из задачи опросазапуск
| }