Сетевые устройства отличаются от символьных и блочных устройств.,не соответствует/dev
в каталогеиздокумент,Заявка принята socket Завершение взаимодействия с сетевым устройством не отражает идею дизайна «все есть файл» на сетевом устройстве.
Архитектура драйвера разделена на 4 уровня сверху вниз:
Уровень интерфейса Основная функция протокола — предоставление интерфейсов приема и отправки для протоколов верхнего уровня. Когда стек протоколов ядра должен отправить данные, он вызывает dev_queue_xmit
Функция отправки данных. Аналогично, стек протоколов ядра получает данные через Уровень интерфейса протоколаиз netif_rx
функциявыполнятьиз。передачаиз Данные описываются как буфер сокета.,использоватьstruct sk_buff
Структурное описание,Определение структуры находится вinclude/linux/skbuff.h
середина,Используется для передачи данных между различными уровнями сетевой подсистемы Linux.,Эта структура существует на протяжении всего процесса работы сетевого приемопередатчика середина.
Структуру буфера sk можно разделить на две части: одна часть хранит реальный пакет данных (на рисунке это Packetdata), а другая часть состоит из набора указателей.
сетьуровень интерфейса устройство используется для абстрагирования различных сетевых устройств, используя struct net_device
Приходитьвыражатьсетьоборудование,Эта структура эквивалентна символьному устройству.изабстрактное описаниеstruct cdev
。
Похоже на символьное устройство,struct net_device
Структура также обеспечивает операциюфункциянаборstruct net_device_ops
описать сетевую картуизразличные операции。
Автор опирается на S5PV210 из DM9000 драйвер, это вообще будет правильно DM9000 изводить машину Исходный код провести анализ, анализировать Исходный кодродыDM9000 Исходный код
DM9000 драйвер основан на platform Реализация архитектуры, начиная с platform Начните с фреймворка.
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
середина,специфическийизанализироватьпроцессупущение,Автор прямо перечисляет соответствующие устройства платформенной шины.
/* 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
,Анализируйте по сегментам
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
серединаизчлен。
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
。
/* 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 из Читай и пишифункция。
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。
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 иззарегистрироваться
/* 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
Сделать задание。
/* 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
членруководитьинициализация。
void ether_setup(struct net_device *dev)
{
dev->header_ops = ð_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
начальство。
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
};
когдаиспользовать Исполнение счета Заказifconfig eth0 up
назад Отрегулируюиспользоватьсетевая картаводить машинуиз open функция
/*
* 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 изинициализация Всеверноаппаратное обеспечениезарегистрироватьсяиздействовать,существоватьэтотупущение。
отвечатьиспользовать程序настраиватьиспользоватьsend
функциядля отправки данных,стек протоколов ядра Воля Данные структурированы вstruct sk_buff
назадпоставить в очередь ожидания,настраиватьиспользоватьstart_xmit
Уведомить сетевую карту об отправке данных。
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 из аппаратного прерывания середина, прерывание середина существования будет срабатывать после отправки и получения, существование - это только фокус насередина прерывает обработкуфункциюизотправки процесса завершения
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
функция
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_rx
функцияуправлять процессом получения。
RX SRAM Полный пакет содержит 4 Байт заголовка, первый байт которого фиксирован 0x01, Второй байт — статус пакета.,Последние два байта представляют допустимую длину данных. Код драйвера середина использует такую структуру для представления заголовка.,Данные после заголовка являются действительно действительными данными.
struct dm9000_rxhdr {
u8 RxPktReady; // фиксированный к 0x01
u8 RxStatus;
__le16 RxLen;
} __attribute__((__packed__));
dm9000_rx
функцияотносительно длинный,Ключевыми частями являются комментарии существования кода середина.
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 получить пакетизпроцесс
Обычно,Сетевой драйвер ксередина получает данные в режиме прерывания,Однако, когда объем данных велик, перебои будут возникать часто.,CPU Частая обработка прерываний приводит к низкой эффективности и не так хороша, как режим чистого опроса. существовать kernel 2.5 Позже был введен новый метод обработки, названный NAPI сочетает в себе метод прерывания и метод опроса. НАПИ Имя неизвестно, говорят, что Зависит. от Поскольку я не смог найти на тот момент подходящего имени, я позвонил ему NAPI (New API) теперь признано именем собственным.
NAPI получать данныеизпроцесс:получить скидку серединапоявление -> закрытиеполучить скидку середина -> Получать все пакеты данных в режиме опроса, пока они не станут пустыми. -> включатьполучить скидку середина -> получить скидку серединапоявление -> …
авторсуществовать DM9000 середина присоединилась NAPI из поддержки git commit。
Основные изменения заключаются в следующем:
1.существоватьdriver/net/Kconfig
середина Добавить конфигурацию
config DM9000_NAPI
bool "DM9000 NAPI"
depends on DM9000
default n
help
Support DM9000 driver run NAPI mode
2.существоватьstruct board_info
добавить вчлен
#ifdef CONFIG_DM9000_NAPI
struct napi_struct napi;
#endif
3.существовать probe функциясерединанастраиватьиспользоватьnetif_napi_add
зарегистрироваться NAPI Для Планирования выполните функцию опроса
#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
функцияследующее
#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 Планирование
#ifdef CONFIG_DM9000_NAPI
napi_enable(&db->napi);
#endif
такой жесуществовать stop функциясерединазапретить NAPI Планирование
#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 Международное лицензионное соглашение Дайте разрешение. При перепечатке просьба указывать источник!