Анализ драйвера сетевой карты драйвера Linux
Анализ драйвера сетевой карты драйвера Linux

Сетевые устройства отличаются от символьных и блочных устройств.,не соответствует/devв каталогеиздокумент,Заявка принята socket Завершение взаимодействия с сетевым устройством не отражает идею дизайна «все есть файл» на сетевом устройстве.

Архитектура драйвера сетевого устройства Linux

Архитектура драйвера разделена на 4 уровня сверху вниз:

  • Уровень интерфейса протокола
  • уровень интерфейса устройства
  • Функциональный уровень драйвера устройства
  • Сетевое оборудование и медиа-уровень

Уровень интерфейса протокола

Уровень интерфейса Основная функция протокола — предоставление интерфейсов приема и отправки для протоколов верхнего уровня. Когда стек протоколов ядра должен отправить данные, он вызывает dev_queue_xmit Функция отправки данных. Аналогично, стек протоколов ядра получает данные через Уровень интерфейса протоколаиз netif_rx функциявыполнятьиз。передачаиз Данные описываются как буфер сокета.,использоватьstruct sk_buffСтруктурное описание,Определение структуры находится вinclude/linux/skbuff.hсередина,Используется для передачи данных между различными уровнями сетевой подсистемы Linux.,Эта структура существует на протяжении всего процесса работы сетевого приемопередатчика середина.

Структуру буфера sk можно разделить на две части: одна часть хранит реальный пакет данных (на рисунке это Packetdata), а другая часть состоит из набора указателей.

  • head указывает на буфер ядра (Packetdata) и head (запас)
  • данные указывают на фактический заголовок пакета данных
  • хвост указывает на фактический хвост пакета данных
  • end Указывает на конец буфера ядра

уровень интерфейса устройства

сетьуровень интерфейса устройство используется для абстрагирования различных сетевых устройств, используя struct net_deviceПриходитьвыражатьсетьоборудование,Эта структура эквивалентна символьному устройству.изабстрактное описаниеstruct cdev

Функциональный уровень драйвера устройства

Похоже на символьное устройство,struct net_deviceСтруктура также обеспечивает операциюфункциянаборstruct net_device_opsописать сетевую картуизразличные операции。

Анализ исходного кода

Автор опирается на S5PV210 из DM9000 драйвер, это вообще будет правильно DM9000 изводить машину Исходный код провести анализ, анализировать Исходный кодродыDM9000 Исходный код

анализ структуры платформы

DM9000 драйвер основан на platform Реализация архитектуры, начиная с platform Начните с фреймворка.

Язык кода:javascript
копировать
static struct platform_driver dm9000_driver = {
    .driver    = {
        .name    = "dm9000",
        .owner     = THIS_MODULE,
        .pm     = &dm9000_drv_pm_ops,
    },
    .probe   = dm9000_probe,
    .remove  = __devexit_p(dm9000_drv_remove),
};

static int __init dm9000_init(void)
{
    /* disable buzzer */
    s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);
    s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1));
    gpio_set_value(S5PV210_GPD0(2), 0);

    dm9000_power_int();
    printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);

    return platform_driver_register(&dm9000_driver);
}

Эта функция вызывает platform_driver_register Функция зарегистрировала драйвер платформенной шины, соответствующий определению регистрации устройства платформы, расположенному по адресу xxx_machine_initсередина,существовать Автор опирается наизs5pv210 kernel начальствородыarch/arm/mach-s5pv210/mach-x210.cсерединаизsmdkc110_machine_initсередина,специфическийизанализироватьпроцессупущение,Автор прямо перечисляет соответствующие устройства платформенной шины.

Язык кода:javascript
копировать
/* DM9000 registrations */
#ifdef CONFIG_DM9000
static struct resource s5p_dm9000_resources[] = {
    [0] = {
        .start = S5P_PA_DM9000,
        .end   = S5P_PA_DM9000 + 3,
        .flags = IORESOURCE_MEM,    // ресурсы памяти (DM900 адрес порта)
    },
    [1] = {
        .start = S5P_PA_DM9000 + 4,
        .end   = S5P_PA_DM9000 + 7,
        .flags = IORESOURCE_MEM,      // ресурсы памяти  (DM900 порт данных)
    },
    [2] = {
        .start = IRQ_EINT10,
        .end   = IRQ_EINT10,
        .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, // серединабрейк ресурсы (триггер высокого уровня)
    }
};

static struct dm9000_plat_data s5p_dm9000_platdata = {
    .flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM,
    .dev_addr = {0x00,0x09,0xc0,0xff,0xec,0x48},
};

struct platform_device s5p_device_dm9000 = {
    .name      = "dm9000",
    .id        =  0,
    .num_resources    = ARRAY_SIZE(s5p_dm9000_resources),
    .resource   = s5p_dm9000_resources,
    .dev        = {
        .platform_data = &s5p_dm9000_platdata,
    }
};

По принципу платформенной шины,После сопоставления драйвера и устройства,Отрегулируюиспользоватьводить машинуиз probe функция dm9000_probe,Анализируйте по сегментам

Язык кода:javascript
копировать
struct dm9000_plat_data *pdata = pdev->dev.platform_data;
struct board_info *db;    /* Point a board information structure */
struct net_device *ndev;   /* struct net_device Абстракция для сетевых устройств из */
const unsigned char *mac_src;
int ret = 0;
int iosize;
int i;
u32 id_val;

/* Init network device */
ndev = alloc_etherdev(sizeof(struct board_info)); /* В то же время для ndev и db Подать заявку на память, db Память находится в ndev позже */
if (!ndev) {
    dev_err(&pdev->dev, "could not allocate device.\n");
    return -ENOMEM;
}

SET_NETDEV_DEV(ndev, &pdev->dev);

dev_dbg(&pdev->dev, "dm9000_probe()\n");dm9000_opendm9000_open

/* setup board info structure */
db = netdev_priv(ndev);

db->dev = &pdev->dev;
db->ndev = ndev;

spin_lock_init(&db->lock);
mutex_init(&db->addr_lock);

INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);

Эта часть struct net_device и struct board_info Структура применяется для памяти,struct board_infoопределениесуществовать DM9000 из файла драйвера середина, который представляет собой личные данные устройства, а затем подключил каждый указатель и инициализировал его часть. struct board_info серединаизчлен。

Язык кода:javascript
копировать
   db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* dm9000 адресный порт */
   db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); /* dm9000 порт данных */
   db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0); /* dm9000 irq Число */

   if (db->addr_res == NULL || db->data_res == NULL ||
       db->irq_res == NULL) {
       dev_err(db->dev, "insufficient resources\n");
       ret = -ENOENT;
       goto out;
   }

/*
 * Второй параметр 1 Означает получение из второго серединабрейка ресурсы。
 * Зависит от Определен только один разрыв середина, Все вернулось -ENXIO
 */
   db->irq_wake = platform_get_irq(pdev, 1);
   if (db->irq_wake >= 0) {
   	/* Этот фрагмент кода не будет выполнен, упущение */
       // ...
   }

   iosize = resource_size(db->addr_res); // res->end - res->start + 1 = 4
   /* Применятьадресный портпамять */
   db->addr_req = request_mem_region(db->addr_res->start, iosize,
                     pdev->name);

   if (db->addr_req == NULL) {
       dev_err(db->dev, "cannot claim address reg area\n");
       ret = -EIO;
       goto out;
   }

/* картографированиеадресный виртуальный адрес порта */
   db->io_addr = ioremap(db->addr_res->start, iosize);

   if (db->io_addr == NULL) {
       dev_err(db->dev, "failed to ioremap address reg\n");
       ret = -EINVAL;
       goto out;
   }


   iosize = resource_size(db->data_res);
   /* Применятьпорт данных Память */
   db->data_req = request_mem_region(db->data_res->start, iosize,
                     pdev->name);

   if (db->data_req == NULL) {
       dev_err(db->dev, "cannot claim data reg area\n");
       ret = -EIO;
       goto out;
   }

  /* картографированиепорт виртуальный адрес данных */
   db->io_data = ioremap(db->data_res->start, iosize);

   if (db->io_data == NULL) {
       dev_err(db->dev, "failed to ioremap data reg\n");
       ret = -EINVAL;
       goto out;
   }

   /* fill in parameters for net-dev structure */
   ndev->base_addr = (unsigned long)db->io_addr;
   ndev->irq       = db->irq_res->start;

к Приведенный выше код начинается сplatform_deviceсерединаполучать DM9000 ресурс: адресный порт、порт данныхадресисерединаперерыв Число, и это адрес порта ioremap

Язык кода:javascript
копировать
    /* ensure at least we have a default set of IO routines */
    dm9000_set_io(db, iosize); /* существоватьпод if Принято считать, что середина будет установлена ​​снова, Таким образом, настройка здесь недействительна. */

    /* check to see if anything is being over-ridden */
    if (pdata != NULL) {
        /* check to see if the driver wants to over-ride the
         * default IO width */

        if (pdata->flags & DM9000_PLATF_8BITONLY)
            dm9000_set_io(db, 1);

        if (pdata->flags & DM9000_PLATF_16BITONLY)  /* только это if учредил */
            dm9000_set_io(db, 2);  /* настраивать board_info из Читай и пишифункция */

        if (pdata->flags & DM9000_PLATF_32BITONLY)
            dm9000_set_io(db, 4);

        /* check to see if there are any IO routine
         * over-rides */

        if (pdata->inblk != NULL)
            db->inblk = pdata->inblk;

        if (pdata->outblk != NULL)
            db->outblk = pdata->outblk;

        if (pdata->dumpblk != NULL)
            db->dumpblk = pdata->dumpblk;

        db->flags = pdata->flags;
    }

#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL
    db->flags |= DM9000_PLATF_SIMPLE_PHY;
#endif

    dm9000_reset(db);    /* Перезапуск dm9000 */

По оборудованию платформы согласно данным платформы, DM9000 Настроен 16бит из режима,Местокэта частьнастраиватьтолькоdm9000_set_io(db, 2);это успехиз。 dm9000_set_io функцияиспользовать Внастраивать DM9000 из Читай и пишифункция。

Язык кода:javascript
копировать
static void dm9000_set_io(struct board_info *db, int byte_width)
{
	/* use the size of the data resource to work out what IO
	 * routines we want to use
	 */

	switch (byte_width) {
	case 1:
		db->dumpblk = dm9000_dumpblk_8bit;
		db->outblk  = dm9000_outblk_8bit;
		db->inblk   = dm9000_inblk_8bit;
		break;


	case 3:
		dev_dbg(db->dev, ": 3 byte IO, falling back to 16bit\n");
	case 2:
		db->dumpblk = dm9000_dumpblk_16bit;
		db->outblk  = dm9000_outblk_16bit;
		db->inblk   = dm9000_inblk_16bit;
		break;

	case 4:
	default:
		db->dumpblk = dm9000_dumpblk_32bit;
		db->outblk  = dm9000_outblk_32bit;
		db->inblk   = dm9000_inblk_32bit;
		break;
	}
}

настраиватьнад Читай и пишифункцияназад,программное обеспечение Перезапуск DM9000。

Язык кода:javascript
копировать
static void dm9000_reset(board_info_t * db)
{
	dev_dbg(db->dev, "resetting device\n");

	/* RESET device */
	writeb(DM9000_NCR, db->io_addr); //  DM9000_NCR: 0x00
	udelay(200);
	writeb(NCR_RST, db->io_data);    // NCR_RST: 1 << 0
	udelay(200);
}

DM9000 Управление регистрами через порты, Первый Волязарегистрироватьсяиззначение смещения или Заказнаписание кодаадресный порт, Затем запишите значение в порт данных. Перезапуск DM900 Просто зайдите по адресу 0 из портового письма 1。

Перезапуск завершен DM9000 После этого начинайте читать DM9000 иззарегистрироваться

Язык кода:javascript
копировать
/* try multiple times, DM9000 sometimes gets the read wrong */
for (i = 0; i < 8; i++) {
    id_val  = ior(db, DM9000_VIDL);             /* DM9000_VIDL:0x28, читать vendor id */
    id_val |= (u32)ior(db, DM9000_VIDH) << 8;   /* DM9000_VIDH: 0x29 */
    id_val |= (u32)ior(db, DM9000_PIDL) << 16;  /* DM9000_PIDL: 0x2A, читать product id */
    id_val |= (u32)ior(db, DM9000_PIDH) << 24;  /* DM9000_PIDH: 0x2B */

    if (id_val == DM9000_ID)   /* Проверьте, так ли это DM900 */
        break;
    dev_err(db->dev, "read wrong id 0x%08x\n", id_val);
}

if (id_val != DM9000_ID) {
    dev_err(db->dev, "wrong id: 0x%08x\n", id_val);
    ret = -ENODEV;
    goto out;
}

/* Identify what type of DM9000 we are working on */

/* I/O mode */
db->io_mode = ior(db, DM9000_ISR) >> 6;    /* ISR bit7:6 keeps I/O mode */ // читать I/O mode
id_val = ior(db, DM9000_CHIPR);  /* DM9000_CHIPR: 0x2C, читать chip revision */
dev_dbg(db->dev, "dm9000 revision 0x%02x  , io_mode %02x \n", id_val, db->io_mode);

switch (id_val) {
case CHIPR_DM9000A:
    db->type = TYPE_DM9000A;
    break;
case 0x1a:
    db->type = TYPE_DM9000C;
    break;
default:
    dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);
    db->type = TYPE_DM9000E;
}

читать vendor id и product id Проверьте,так ли это DM9000。Сновачитать I/O mode и chip revision, и по разным revision верноdb->typeСделать задание。

Язык кода:javascript
копировать
/* driver system function */
ether_setup(ndev);

ndev->netdev_ops    = &dm9000_netdev_ops;     // net device из ops
ndev->watchdog_timeo    = msecs_to_jiffies(watchdog);
ndev->ethtool_ops    = &dm9000_ethtool_ops;   // ethtool из ops, Используется для поддержки прикладного уровня. ethtool Заказ

db->msg_enable       = NETIF_MSG_LINK;
db->mii.phy_id_mask  = 0x1f;
db->mii.reg_num_mask = 0x1f;
db->mii.force_media  = 0;
db->mii.full_duplex  = 0;
db->mii.dev         = ndev;
db->mii.mdio_read    = dm9000_phy_read;
db->mii.mdio_write   = dm9000_phy_write;

mac_src = "eeprom";

/* try reading the node address from the attached EEPROM */
/* platdata настраивать Понятно DM9000_PLATF_NO_EEPROM flag, Так что это читать недействительно */
for (i = 0; i < 6; i += 2)
    dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);

if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
    mac_src = "platform data";
    //memcpy(ndev->dev_addr, pdata->dev_addr, 6);
    /* mac from bootloader */
    memcpy(ndev->dev_addr, mac, 6);  /* Это правда изнастройки mac адрес, Другие настройки недействительны */
}

if (!is_valid_ether_addr(ndev->dev_addr)) {
    /* try reading from mac */

    mac_src = "chip";
    for (i = 0; i < 6; i++)
        ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
}

if (!is_valid_ether_addr(ndev->dev_addr))
    dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
         "set using ifconfig\n", ndev->name);

platform_set_drvdata(pdev, ndev);
ret = register_netdev(ndev);   // Зарегистрируйте сетевое устройство

if (ret == 0)
    printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",
           ndev->name, dm9000_type_to_char(db->type),
           db->io_addr, db->io_data, ndev->irq,
           ndev->dev_addr, mac_src);
return 0;

настраиватьиспользоватьether_setupфункцияверноndevчленруководитьинициализация。

Язык кода:javascript
копировать
void ether_setup(struct net_device *dev)
{
    dev->header_ops   = &eth_header_ops; /* Работа с аппаратным заголовком Набор функций, в основном завершает создание аппаратного заголовка и из sk_buf Анализ первоклассной работы оборудования */
    dev->type         = ARPHRD_ETHER;    // настройка протокола Ethernet
    dev->hard_header_len     = ETH_HLEN; // Размер заголовка Ethernet   14B
    dev->mtu          = ETH_DATA_LEN;    // настраиватькEthernet MTU  1500B
    dev->addr_len     = ETH_ALEN;        // mac Длина адреса    6B
    dev->tx_queue_len = 1000;    /* Ethernet wants good queues */
    dev->flags        = IFF_BROADCAST|IFF_MULTICAST;

    memset(dev->broadcast, 0xFF, ETH_ALEN);
}

инициализациянадndevназад,настраивать Понятноnetdev_ops и mac адрес,большинствоназаднастраиватьиспользоватьregister_netdevфункциязарегистрироваться Понятносетьоборудование。До сих пор,probe После завершения анализа функции сосредоточьтесь на на Нажмите и отпуститесуществоватьnetdev_opsначальство。

Язык кода:javascript
копировать
static const struct net_device_ops dm9000_netdev_ops = {
	.ndo_open		= dm9000_open,              /* ifconfig eth0 up */
	.ndo_stop		= dm9000_stop,              /* ifconfig eth0 down */
	.ndo_start_xmit		= dm9000_start_xmit,    /* Когда пакет отправлен от Вызов стека сетевых протоколов */
	.ndo_tx_timeout		= dm9000_timeout,       /* Будет вызван после истечения времени отправки пакета данных. */
	.ndo_set_multicast_list	= dm9000_hash_table,
	.ndo_do_ioctl		= dm9000_ioctl,
	.ndo_change_mtu		= eth_change_mtu,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_set_mac_address	= eth_mac_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
	.ndo_poll_controller	= dm9000_poll_controller,
#endif
};

dm9000 анализ открытого процесса

когдаиспользовать Исполнение счета Заказifconfig eth0 upназад Отрегулируюиспользоватьсетевая картаводить машинуиз open функция

Язык кода:javascript
копировать
/*
 *  Open the interface.
 *  The interface is opened whenever "ifconfig" actives it.
 */
static int dm9000_open(struct net_device *dev)
{
	board_info_t *db = netdev_priv(dev);
	unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;

	if (netif_msg_ifup(db))
		dev_dbg(db->dev, "enabling %s\n", dev->name);

	/* If there is no IRQ type specified, default to something that
	 * may work, and tell the user that this is a problem */

	if (irqflags == IRQF_TRIGGER_NONE)
		dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");

	irqflags |= IRQF_SHARED;

	/* Подайте заявку на отправку и получение середина */
	if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))
		return -EAGAIN;

	/* Initialize DM9000 board */
//	dm9000_reset(db);
	dm9000_init_dm9000(dev);   /* инициализация DM9000 */

	/* Init driver variable */
	db->dbug_cnt = 0;

	mii_check_media(&db->mii, netif_msg_link(db), 1);
	netif_start_queue(dev);   /* Активируйте очередь отправки устройства и разрешите вызовы верхнего уровня. xxx_xmit функция */

	dm9000_schedule_poll(db);

	return 0;
}

open функция В основном делаю Понятно Подайте заявку на отправку и получение середина、инициализация DM9000, активируйте очередь отправки устройства. в DM900 изинициализация Всеверноаппаратное обеспечениезарегистрироватьсяиздействовать,существоватьэтотупущение。

Анализ процесса отправки DM9000

отвечатьиспользовать程序настраиватьиспользоватьsendфункциядля отправки данных,стек протоколов ядра Воля Данные структурированы вstruct sk_buffназадпоставить в очередь ожидания,настраиватьиспользоватьstart_xmitУведомить сетевую карту об отправке данных。

Язык кода:javascript
копировать
static int dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	unsigned long flags;
	board_info_t *db = netdev_priv(dev);

	dm9000_dbg(db, 3, "%s:\n", __func__);

	if ((db->tx_pkt_cnt > 0) && !netif_carrier_ok(dev))
		return NETDEV_TX_BUSY;

	spin_lock_irqsave(&db->lock, flags);

	netif_stop_queue(dev);  /* Закрыть очередь отправки, уведомить Уровень интерфейса протокола Остановить передачу пакетов */

	db->tx_pkt_cnt++;
	dev->stats.tx_packets++;
	dev->stats.tx_bytes += skb->len;

	/* Set TX length to DM9000 */       // настройка общей длины пакета данных
	iow(db, DM9000_TXPLL, skb->len);    // DM9000_TXPLL: 0xFC
	iow(db, DM9000_TXPLH, skb->len >> 8); // DM9000_TXPLH: 0xFD

	/* Move data to DM9000 TX RAM */   /* Воля Пакет положил TX SRAM середина */
	writeb(DM9000_MWCMD, db->io_addr);   // DM9000_MWCMD: 0xF8
	(db->outblk)(db->io_data, skb->data, skb->len);

	/* Issue TX polling command */  /* Старт Воля TX SRAM серединаизданные рассылаются, Мы уведомим вас через середину, когда доставка будет завершена. */
	iow(db, DM9000_TCR, TCR_TXREQ);	/* Cleared after TX complete */ // DM9000_TCR: 0x02, TCR_TXREQ: 1 << 0
	dev->trans_start = jiffies;

	spin_unlock_irqrestore(&db->lock, flags);

	/* free this SKB */
	dev_kfree_skb(skb);

	return NETDEV_TX_OK;
}

Код можно найти на сайте Зависит отк.,Первый Закрыть очередь отправки, уведомить Уровень интерфейса протокола Остановить передачу пакетов, Однаконазаднастраивать Общая длина пакета данныхназад Воля Копирование пакета в DM9000 из TX SRAM в, затем установите TCR После регистрации сетевая карта начнет отправлять данные. Этот флаг будет автоматически сброшен оборудованием после завершения отправки. 0, Наконец уведомлено прерыванием CPU Отправка данных завершена

существовать open приложение из функционального центра DM9000 из аппаратного прерывания середина, прерывание середина существования будет срабатывать после отправки и получения, существование - это только фокус насередина прерывает обработкуфункциюизотправки процесса завершения

Язык кода:javascript
копировать
static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
{
	struct net_device *dev = dev_id;
	board_info_t *db = netdev_priv(dev);
	int int_status;
	unsigned long flags;
	u8 reg_save;

	dm9000_dbg(db, 3, "entering %s\n", __func__);

	/* A real interrupt coming */

	/* holders of db->lock must always block IRQs */
	spin_lock_irqsave(&db->lock, flags);

	/* Save previous register address */
	reg_save = readb(db->io_addr);

	/* Disable all interrupts */
	iow(db, DM9000_IMR, IMR_PAR);   // Первый disable Отбросьте все середина

	/* Got DM9000 interrupt status */
	int_status = ior(db, DM9000_ISR);	/* Got ISR */  /* Получите статус перерыва середина, Получать середину или отправлять середину */
	iow(db, DM9000_ISR, int_status);	/* Clear ISR status */  /* прозрачныйсерединаперерыв */

	if (netif_msg_intr(db))
		dev_dbg(db->dev, "interrupt status %02x\n", int_status);

	/* Received the coming packet */
	if (int_status & ISR_PRS)   /* ISR_PRS: 1 << 0, получить скидку середина */
		dm9000_rx(dev);

	/* Got DM9000 interrupt status */
	int_status |= ior(db, DM9000_ISR);	/* Got ISR */

	/* Trnasmit Interrupt check */
	if (int_status & ISR_PTS)   /* ISR_PTS: 1 << 1, Отправить середина прочь */
	{
		iow(db, DM9000_ISR, ISR_PTS);	/* Clear ISR status */
		dm9000_tx_done(dev, db);
	}

	if (db->type != TYPE_DM9000E) {
		if (int_status & ISR_LNKCHNG) {
			/* fire a link-change request */
			schedule_delayed_work(&db->phy_poll, 1);
		}
	}

	/* Re-enable interrupt mask */
	iow(db, DM9000_IMR, db->imr_all);

	/* Restore previous register address */
	writeb(reg_save, db->io_addr);

	spin_unlock_irqrestore(&db->lock, flags);

	return IRQ_HANDLED;
}

Первыйзапретитьиспользовать Местоиметьсерединаперерыв,Однаконазадпроходитьчитать ISR Регистр получает статус прерывания

Зависит от bit 0 и 1 Он может определить, является ли это прерыванием приема или прерыванием отправки. в случае Отправить середина прочь,Цзэцинсерединаперерывназаднастраиватьиспользоватьdm9000_tx_doneфункция

Язык кода:javascript
копировать
static void dm9000_tx_done(struct net_device *dev, board_info_t *db)
{
	int tx_status = ior(db, DM9000_TCR);	/* Got TX status */

	if (tx_status & TCR_TXREQ) {
		dev->stats.tx_fifo_errors++;
	} else {
		if (db->tx_pkt_cnt && !db->wait_reset) {
			/* One packet sent complete */
			db->tx_pkt_cnt = 0;
			dev->trans_start = 0;
			netif_wake_queue(dev);  /* Пробуждение очереди отправки, Уровень интерфейса Протокола может продолжать отправлять данные. */
		}
	}
}

Еще раз прочитать статус регистрации,если Отправить середина прочьне установлено,затем разбудите очередь отправки,выражать Уровень интерфейса Протокола может продолжать отправлять данные.。Зависит от Всуществоватьdm9000_start_xmitфункциясередина Воляотправлять Очередь закрыта Понятно并且настраиватьиспользоватьdm9000_tx_done前прозрачный Понятносерединаперерыв,В это время, если прерывание середина все еще установлено,Указывает, что что-то пошло не так,Местокdev->stats.tx_fifo_errors++;

к UDP Например, следующий рисунок иллюстрирует DM9000 Отправить пакетизпроцесс

Анализ процесса приема DM9000

Зависит ототправлятьанализ По процессу видно, что прием тоже Зависит. отсерединаперерывуведомитьиз。И сотправлятьпроцесс共использоватьтот же самыйсерединаперерывиметь дело сфункция,когдасерединаперерывдаполучить скидку середина时Отрегулируюиспользоватьdm9000_rxфункцияуправлять процессом получения。

RX SRAM Полный пакет содержит 4 Байт заголовка, первый байт которого фиксирован 0x01, Второй байт — статус пакета.,Последние два байта представляют допустимую длину данных. Код драйвера середина использует такую ​​структуру для представления заголовка.,Данные после заголовка являются действительно действительными данными.

Язык кода:javascript
копировать
struct dm9000_rxhdr {
	u8	RxPktReady;    // фиксированный к 0x01
	u8	RxStatus;
	__le16	RxLen;
} __attribute__((__packed__));

dm9000_rxфункцияотносительно длинный,Ключевыми частями являются комментарии существования кода середина.

Язык кода:javascript
копировать
static void dm9000_rx(struct net_device *dev)
{
	board_info_t *db = netdev_priv(dev);
	struct dm9000_rxhdr rxhdr;   /* RX SRAM Хранит данные и четырехбайтовый заголовок, После удаления заголовка пакет данных */
	struct sk_buff *skb;
	u8 rxbyte, *rdptr;
	bool GoodPacket;
	int RxLen;
	int save_mrr, calc_mrr, check_mrr;

	/* Check packet ready or not */
	do {
		ior(db, DM9000_MRCMDX);	/* Dummy read */
		save_mrr = (ior(db, 0xf5) << 8) | ior(db, 0xf4);
		/* Get most updated data */
		rxbyte = ior(db, DM9000_MRCMDX); /* читать RX SRAM изданные, Адрес не будет увеличиваться автоматически */

		if(rxbyte != DM9000_PKT_RDY)  /* DM9000_PKT_RDY: 0x01, RX sram хранит данные из первого байта четырехбайтового заголовка фиксированного к 0x01 */
		{
			/* Status check: this byte must be 0 or 1 */
			if (rxbyte > DM9000_PKT_RDY) {
				dev_warn(db->dev, "status check fail: %d\n", rxbyte);
				iow(db, DM9000_RCR, 0x00);	/* Stop Device */
				iow(db, DM9000_IMR, IMR_PAR);	/* Stop INT request */

				db->wait_reset = 1;
				dev->trans_start = 1;
			}

			return;
		}

		/* A packet ready now  & Get status/length */
		GoodPacket = true;
		writeb(DM9000_MRCMD, db->io_addr);  /* читать RX SRAM изданные, И адрес увеличивается автоматически */
		(db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));

		RxLen = le16_to_cpu(rxhdr.RxLen);  // Общая длина пакета данных

		calc_mrr = save_mrr + 4 + RxLen;
		if(0x00 == db->io_mode)  //16 bit only
		{
			if(RxLen & 0x01) calc_mrr++;
		}
		if(calc_mrr > 0x3fff) calc_mrr -= 0x3400;

		if (netif_msg_rx_status(db))
			dev_dbg(db->dev, "RX: status %02x, length %04x\n",
				rxhdr.RxStatus, RxLen);

		/* Packet Status check */
		/* 64 < Длина кадра Ethernet <= 1536 */
		if (RxLen < 0x40) {
			GoodPacket = false;
			if (netif_msg_rx_err(db))
				dev_dbg(db->dev, "RX: Bad Packet (runt)\n");
		}

		if (RxLen > DM9000_PKT_MAX) {
			dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen);
		}

        // Проверьте значение состояния заголовка, чтобы определить, является ли это обычным пакетом данных.
		/* rxhdr.RxStatus is identical to RSR register. */
		if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |
				      RSR_PLE | RSR_RWTO |
				      RSR_LCS | RSR_RF)) {
			if (rxhdr.RxStatus & RSR_FOE) {
				if (netif_msg_rx_err(db))
					dev_dbg(db->dev, "fifo error\n");
				dev->stats.rx_fifo_errors++;
			}
			if (rxhdr.RxStatus & RSR_CE) {
				if (netif_msg_rx_err(db))
					dev_dbg(db->dev, "crc error\n");
				dev->stats.rx_crc_errors++;
				GoodPacket = false;
			}
			if (rxhdr.RxStatus & RSR_RF) {
				if (netif_msg_rx_err(db))
					dev_dbg(db->dev, "length error\n");
				dev->stats.rx_length_errors++;
				GoodPacket = false;
			}
		}

		/* Move data from DM9000 */
		if (GoodPacket &&
		    ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {   // Если это обычный пакет данных, примените sk buffer
			skb_reserve(skb, 2);
			rdptr = (u8 *) skb_put(skb, RxLen - 4);

			/* Read received packet from RX SRAM */

			(db->inblk)(db->io_data, rdptr, RxLen);  // Воля RX SRAM серединаиз Действительные данные копируются в sk buffer середина
			dev->stats.rx_bytes += RxLen;

			/* Pass to upper layer */
			skb->protocol = eth_type_trans(skb, dev);

			netif_rx(skb);           /* Воля skb uffer Отправить вверх на Уровень интерфейса протокола */
			dev->stats.rx_packets++;

			check_mrr = (ior(db, 0xf5) << 8) | ior(db, 0xf4);
			if(calc_mrr != check_mrr)
			{

				if (netif_msg_rx_err(db))
					dev_dbg(db->dev, "rx point error %04x %04x %04x %04x\n",
						save_mrr, RxLen, calc_mrr, check_mrr);

				iow(db, 0xf5, (calc_mrr >> 8) & 0xff);
				iow(db, 0xf4, calc_mrr & 0xff);
			}

		} else {
			/* need to dump the packet's data */
			iow(db, 0xf5, (calc_mrr >> 8) & 0xff);
			iow(db, 0xf4, calc_mrr & 0xff);
		}

	} while (rxbyte & DM9000_PKT_RDY);
}

Общую логику можно разделить на следующий процесс:

1.Первыйчитать RX SRAM середина 4 Байтовый заголовок дляstruct dm9000_rxhdr rxhdrсередина

2. Определить, то есть первый байт 0x01, определить, соответствует ли общая длина пакета данных спецификации Ethernet, и, наконец, определить, является ли заголовок серединаиз значения статуса обычным пакетом.

3. пройти мимо 2 После того, как будет установлено, что это обычный пакет, прочитайте действительные данные.

4. Создать раздачу sk буфер и скопируйте действительные данные в sk buffer середина

5.настраиватьиспользоватьnetif_rx, Воля sk buffer Отправить вверх на Уровень интерфейса протокола

к UDP Например, следующий рисунок иллюстрирует DM9000 получить пакетизпроцесс

Введение в прием метода NAPI

Обычно,Сетевой драйвер ксередина получает данные в режиме прерывания,Однако, когда объем данных велик, перебои будут возникать часто.,CPU Частая обработка прерываний приводит к низкой эффективности и не так хороша, как режим чистого опроса. существовать kernel 2.5 Позже был введен новый метод обработки, названный NAPI сочетает в себе метод прерывания и метод опроса. НАПИ Имя неизвестно, говорят, что Зависит. от Поскольку я не смог найти на тот момент подходящего имени, я позвонил ему NAPI (New API) теперь признано именем собственным.

NAPI получать данныеизпроцесс:получить скидку серединапоявление -> закрытиеполучить скидку середина -> Получать все пакеты данных в режиме опроса, пока они не станут пустыми. -> включатьполучить скидку середина -> получить скидку серединапоявление -> …

авторсуществовать DM9000 середина присоединилась NAPI из поддержки git commit

Основные изменения заключаются в следующем:

1.существоватьdriver/net/Kconfigсередина Добавить конфигурацию

Язык кода:javascript
копировать
config DM9000_NAPI
    bool "DM9000 NAPI"
    depends on DM9000
    default n
    help
        Support DM9000 driver run NAPI mode

2.существоватьstruct board_infoдобавить вчлен

Язык кода:javascript
копировать
#ifdef CONFIG_DM9000_NAPI
    struct napi_struct napi;
#endif

3.существовать probe функциясерединанастраиватьиспользоватьnetif_napi_addзарегистрироваться NAPI Для Планирования выполните функцию опроса

Язык кода:javascript
копировать
#define DM9000_NAPI_WEIGHT 64

#ifdef CONFIG_DM9000_NAPI
    netif_napi_add(ndev, &db->napi, dm9000_napi_poll, DM9000_NAPI_WEIGHT);
#endif

dm9000_napi_pollфункцияследующее

Язык кода:javascript
копировать
#ifdef CONFIG_DM9000_NAPI
static int dm9000_napi_poll(struct napi_struct *napi, int budget)
{
    board_info_t *db = container_of(napi, board_info_t, napi);
    unsigned long flags;
    u8 reg_save;

    spin_lock_irqsave(&db->lock, flags);

    reg_save = readb(db->io_addr);

    dm9000_rx(db->ndev, budget);    // Обработка опроса для получения пакетов

    napi_complete(napi);

    iow(db, DM9000_IMR, db->imr_all);

    writeb(reg_save, db->io_addr);

    spin_unlock_irqrestore(&db->lock, flags);

    return 0;
}
#endif

dm9000_rxопросиметь дело снад Получить посылкуназад,需要настраиватьиспользоватьnapi_completeвыражатьопроснадполный。

4.существовать open функциясерединанастраиватьиспользоватьnapi_enableдавать возможность NAPI Планирование

Язык кода:javascript
копировать
#ifdef CONFIG_DM9000_NAPI
    napi_enable(&db->napi);
#endif

такой жесуществовать stop функциясерединазапретить NAPI Планирование

Язык кода:javascript
копировать
#ifdef CONFIG_DM9000_NAPI
    napi_disable(&db->napi);
#endif

Автор этой статьи: Ifan Tsai  (кай-кай)

Ссылка на эту статью: https://cloud.tencent.com/developer/article/2164607

Заявление об авторских правах: Эта статья принимает Creative Commons Attribution-NonCommercial-ShareAlike 4.0 Международное лицензионное соглашение Дайте разрешение. При перепечатке просьба указывать источник!

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