Понимание сетевого Phy-драйвера Linux в одной статье
Понимание сетевого Phy-драйвера Linux в одной статье

Обзор

Изображение выше взято из руководства по физическому чипу сетевой карты серии 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.

phy_device

Язык кода:javascript
копировать
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);
};

phy_driver

Язык кода:javascript
копировать
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);
};

mii_bus

Язык кода:javascript
копировать
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;                   // прерывать
};

net_device

Язык кода:javascript
копировать
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. Основные шаги:

  1. Зарегистрировать сетевое оборудование net_device
  2. Применятьочередьи DMA
  3. Подать заявку на шину MDIO
  4. Создать и зарегистрировать устройство Phy
Язык кода:javascript
копировать
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

Есть универсальный драйвер genphy_driver,а также Собственный драйвер (здесь начинается с NXP TJA драйвер в качестве примера), различия заключаются в следующем:

Язык кода:javascript
копировать
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,
};
Язык кода:javascript
копировать
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 выглядит следующим образом:

Язык кода:javascript
копировать
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:

Язык кода:javascript
копировать
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 для чтения регистра состояния и определения возможностей физического чипа:

Язык кода:javascript
копировать
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);
     |  }
     | }

Собственный драйвер NXP TJA

Процесс регистрации структуры phy_driver драйвера NXP TJA выглядит следующим образом:

Язык кода:javascript
копировать
#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))
Язык кода:javascript
копировать
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.

Сотрудничество между сетевой картой fec и Phy

В ядре Linux Mac Ethernet будет зарегистрирован как struct net_device, а физический чип будет зарегистрирован как struct phy_device. Как передать статус phy_device в net_device, чтобы он мог вносить соответствующие изменения в конфигурацию при изменении статуса канала. Эта задача ложится на вышеупомянутый посредник struct phylink.

Ниже в качестве примера используется драйвер сетевого порта fec, демонстрирующий процесс взаимодействия сетевой карты fec и phy. Основной процесс вызова всего физического драйвера показан на рисунке ниже:

Принцип действия физического драйвера на самом деле очень прост. Общий процесс выглядит следующим образом:

  1. Используйте опрос/прерывание, чтобы прочитать состояние физического чипа через шину mdio.
  2. существовать phy link Изменение статуса в зависимости от случая, правильная Конфигурация mac статус. (Например: на основе phy скорость автосогласования 10/100/1000M Пучок mac Конфигурация в соответствующий тариф)

Статус фи-чипа уже отображается при регистрации фи-устройства. Вот подробное описание, как правильно настроить статус mac при изменении статуса фи-линка.

Язык кода:javascript
копировать
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 выглядит следующим образом:

Язык кода:javascript
копировать
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);
                    | }

чтение статуса ссылки

Физическое чтение последствий ссылок. Поток кода выглядит следующим образом:

Язык кода:javascript
копировать
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.

Язык кода:javascript
копировать
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` из задачи опросазапуск
        | }
boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose