【RK3568 驅動開發:實現一個最基礎的網絡設備】

RK3568 驅動開發:實現一個最基礎的網絡設備

  • 一、引言
  • 二、編寫網絡設備驅動代碼
    • 1. 核心數據結構與接口
    • 2. 核心功能實現
    • 3. 網絡命名空間管理
    • 4.源代碼
  • 三、編譯與驗證
    • 1.加載模塊
    • 2.驗證網絡
  • 四、注意事項

一、引言

RK3568 作為一款高性能 ARM 架構處理器,廣泛應用于嵌入式設備中。本文將帶領讀者從零開始開發一個簡單的 RK3568 網絡設備驅動,幫助理解 Linux 網絡子系統的工作原理和驅動開發流程。

二、編寫網絡設備驅動代碼

1. 核心數據結構與接口

設備操作集 (struct net_device_ops)

static const struct net_device_ops loopback_ops = {.ndo_start_xmit  = loopback_xmit,.ndo_get_stats64 = loopback_get_stats64,
};

ndo_start_xmit:處理數據包發送的回調函數
ndo_get_stats64:獲取 64 位統計信息的回調函數

網絡命名空間操作 (struct pernet_operations)

struct pernet_operations __net_initdata loopback_net_ops = {.init = loopback_net_init,.exit = loopback_net_exit,
};

init:每個網絡命名空間創建時調用的初始化函數
exit:每個網絡命名空間銷毀時調用的清理函數

2. 核心功能實現

數據包發送與回環 (loopback_xmit)

static netdev_tx_t loopback_xmit(struct sk_buff *skb, struct net_device *dev)
{netif_stop_queue(dev);skb->protocol = eth_type_trans(skb, dev);if (netif_rx(skb) == NET_RX_SUCCESS) {bytes += skb->len;packets++;}netif_wake_queue(dev);return NETDEV_TX_OK;
}

工作流程:
暫停設備發送隊列
確定數據包的協議類型
通過netif_rx()將數據包重新注入網絡棧(模擬回環)
更新統計信息
恢復發送隊列

統計信息收集 (loopback_get_stats64)

static void loopback_get_stats64(struct net_device *dev,struct rtnl_link_stats64 *stats)
{stats->rx_packets = packets;stats->tx_packets = packets;stats->rx_bytes   = bytes;stats->tx_bytes   = bytes;
}

由于是回環設備,收發數據包和字節數完全相同
直接使用全局變量packets和bytes作為統計數據源

3. 網絡命名空間管理

命名空間初始化 (loopback_net_init)

static __net_init int loopback_net_init(struct net *net)
{dev = alloc_netdev(0, "loopback%d", NET_NAME_UNKNOWN, loopback_setup);register_netdev(dev);net->loopback_dev = dev;return 0;
}

為每個網絡命名空間創建一個回環設備
設備命名規則:loopback0, loopback1等
將設備指針保存到網絡命名空間的loopback_dev字段
設備配置 (loopback_setup)

static void loopback_setup(struct net_device *dev)
{dev->mtu        = 64 * 1024;dev->type       = ARPHRD_LOOPBACK;dev->flags      = IFF_LOOPBACK;dev->features   = NETIF_F_LOOPBACK;dev->header_ops = &eth_header_ops;dev->netdev_ops = &loopback_ops;
}

MTU:設置為 64KB,遠大于標準以太網的 1500 字節
設備類型:設置為回環設備 (ARPHRD_LOOPBACK)
設備標志:設置IFF_LOOPBACK標志,表示這是一個回環設備
功能特性:支持NETIF_F_LOOPBACK特性
協議頭操作:使用以太網頭操作集

4.源代碼

#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/in.h>#include <linux/uaccess.h>
#include <linux/io.h>#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <net/sock.h>
#include <net/checksum.h>
#include <linux/if_ether.h>	/* For the statistics structure. */
#include <linux/if_arp.h>	/* For ARPHRD_ETHER */
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/percpu.h>
#include <linux/net_tstamp.h>
#include <net/net_namespace.h>
#include <linux/u64_stats_sync.h>extern int eth_header(struct sk_buff *skb, struct net_device *dev,unsigned short type,const void *daddr, const void *saddr, unsigned int len);extern int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr);extern int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type);extern void eth_header_cache_update(struct hh_cache *hh,const struct net_device *dev,const unsigned char *haddr);const struct header_ops eth_header_ops ____cacheline_aligned = {.create		= eth_header,.parse		= eth_header_parse,.cache		= eth_header_cache,.cache_update	= eth_header_cache_update,
};u64 packets;
u64 bytes;/* The higher levels take care of making this non-reentrant (it's* called with bh's disabled).*/
static netdev_tx_t loopback_xmit(struct sk_buff *skb,struct net_device *dev)
{netif_stop_queue(dev);skb->protocol = eth_type_trans(skb, dev);if (netif_rx(skb) == NET_RX_SUCCESS) {printk("loopback_xmit NET_RX_SUCCESS");bytes += skb->len;packets++;} else {printk("loopback_xmit NET_RX_FAILURE");}netif_wake_queue(dev);return NETDEV_TX_OK;
}static void loopback_get_stats64(struct net_device *dev,struct rtnl_link_stats64 *stats)
{stats->rx_packets = packets;stats->tx_packets = packets;stats->rx_bytes   = bytes;stats->tx_bytes   = bytes;printk("loopback_get_stats64");
}static const struct net_device_ops loopback_ops = {.ndo_start_xmit  = loopback_xmit,.ndo_get_stats64 = loopback_get_stats64,
};/* The loopback device is special. There is only one instance* per network namespace.*/
static void loopback_setup(struct net_device *dev)
{dev->mtu		= 64 * 1024;dev->type		= ARPHRD_LOOPBACK;	/* 0x0001*/dev->flags		= IFF_LOOPBACK;dev->priv_flags		&= ~(IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM);dev->features		= NETIF_F_LOOPBACK;dev->header_ops		= &eth_header_ops;dev->netdev_ops		= &loopback_ops;printk("loopback_setup");
}/* Setup and register the loopback device. */
static __net_init int loopback_net_init(struct net *net)
{struct net_device *dev;int err;err = -ENOMEM;dev = alloc_netdev(0, "loopback%d", NET_NAME_UNKNOWN, loopback_setup);if (!dev)goto out;err = register_netdev(dev);if (err)goto out_free_netdev;net->loopback_dev = dev;printk("loopback_net_init");return 0;out_free_netdev:free_netdev(dev);
out:if (net_eq(net, &init_net))panic("loopback: Failed to register netdevice: %d\n", err);return err;
}static void __net_exit loopback_net_exit(struct net *net)
{struct net_device *dev = net->loopback_dev;unregister_netdev(dev);printk("netdev_exit");
}/* Registered in net/core/dev.c */
struct pernet_operations __net_initdata loopback_net_ops = {.init = loopback_net_init,.exit = loopback_net_exit,
};/* 模塊初始化 */
static int __init veth_init(void)
{return register_pernet_subsys(&loopback_net_ops);
}/* 模塊退出 */
static void __exit veth_exit(void)
{unregister_pernet_subsys(&loopback_net_ops);
}module_init(veth_init);
module_exit(veth_exit);MODULE_DESCRIPTION("RK3568 Virtual Network Device Driver");
MODULE_AUTHOR("cmy");
MODULE_LICENSE("GPL");

三、編譯與驗證

1.加載模塊

將編譯好的ko文件拷貝到開發板并加載模塊
執行ifconfig -a 查看所有網絡設備
在這里插入圖片描述
可以看到我們寫的網絡設備已經加載進來了,使能loopback0網絡設備

ifconfig loopback0 up

在這里插入圖片描述

2.驗證網絡

使用socket驗證網絡是否正常

#define PORT 8888extern "C"
JNIEXPORT jint JNICALL
Java_com_example_led9_MainActivity_startTcpServer(JNIEnv *env, jobject thiz) {int server_fd, new_socket;struct sockaddr_in address;int opt = 1;int addrlen = sizeof(address);char buffer[1024] = {0};const char *hello = "Hello from server";// 創建 socket 文件描述符if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {LOGD("socket failed");exit(EXIT_FAILURE);}// 設置 socket 選項if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {LOGD("setsockopt");exit(EXIT_FAILURE);}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;  // 監聽所有可用地址address.sin_port = htons(PORT);// 綁定 socket 到指定地址和端口if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {LOGD("bind failed");exit(EXIT_FAILURE);}// 監聽連接if (listen(server_fd, 3) < 0) {LOGD("listen");exit(EXIT_FAILURE);}// 接受連接if ((new_socket = accept(server_fd, (struct sockaddr *)&address,(socklen_t*)&addrlen)) < 0) {LOGD("accept");exit(EXIT_FAILURE);}// 讀取客戶端消息int valread = read(new_socket, buffer, 1024);LOGD("Client: %s\n", buffer);// 發送響應send(new_socket, hello, strlen(hello), 0);LOGD("Hello message sent\n");// 關閉連接close(new_socket);close(server_fd);return 0;
}extern "C"
JNIEXPORT jint JNICALL
Java_com_example_led9_MainActivity_startTcpClient(JNIEnv *env, jobject thiz) {const char *server_ip = "127.0.0.1";int sock = 0;struct sockaddr_in serv_addr;char *hello = "Hello from client";char buffer[1024] = {0};// 創建 socket 文件描述符if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {LOGD("\nSocket creation error\n");return -1;}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);// 將 IPv4 地址從點分十進制轉換為二進制形式if(inet_pton(AF_INET, server_ip, &serv_addr.sin_addr) <= 0) {LOGD("\nInvalid address/ Address not supported\n");return -1;}// 連接到服務器if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {LOGD("\nConnection Failed\n");return -1;}// 發送消息send(sock, hello, strlen(hello), 0);LOGD("Hello message sent\n");// 讀取服務器響應int valread = read(sock, buffer, 1024);LOGD("Server: %s\n", buffer);// 關閉連接close(sock);return 0;}

可以看到是有數據收發打印,以及數據包數量統計。
在這里插入圖片描述

四、注意事項

剛開始編譯源碼文件時出現Unknown symbol eth_header_ops (err -2)
是因為我們沒有將源碼編譯進內核,是以ko模塊的形式進行測試的,所以需要自己實現eth_header_ops 里面的函數。
在這里插入圖片描述
具體操作流程如下:
通過命令查找關鍵結構體:grep -rw "eth_header_ops"
在這里插入圖片描述
最終在net/ethernet/eth.c文件中找到,具體定義如下:
在這里插入圖片描述
由于這些函數已導出,所以我們的文件中可以直接使用:
在這里插入圖片描述

extern int eth_header(struct sk_buff *skb, struct net_device *dev,unsigned short type,const void *daddr, const void *saddr, unsigned int len);extern int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr);extern int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type);extern void eth_header_cache_update(struct hh_cache *hh,const struct net_device *dev,const unsigned char *haddr);const struct header_ops eth_header_ops ____cacheline_aligned = {.create		= eth_header,.parse		= eth_header_parse,.cache		= eth_header_cache,.cache_update	= eth_header_cache_update,
};

到這里就可以不用編譯內核,解決報錯問題。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87946.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87946.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87946.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CAIDCP系列對話:AI 驅動安全

數字時代&#xff0c;AI浪潮翻涌&#xff0c;網絡安全攻防戰已悄然升級&#xff1a; 某工業控制系統遭AI驅動勒索攻擊&#xff1a;攻擊者借 AI 精準捕捉異常網絡掃描、遠程 PowerShell 痕跡&#xff0c;瞬間加密文件索要贖金&#xff1b; 另一邊&#xff0c;某大型科技公司用AI…

ARMv8 沒開mmu執行memset引起的非對齊訪問異常

最近在haps上驗證一個新的芯片&#xff0c;記錄一下memset訪問出錯的問題。在沒開mmu和cache的情況下&#xff0c;對全局變量指針進行memset清零操作&#xff0c;發現每次都會出現異常。最后發現是沒開mmu導致出現了數據非對齊訪問導致報錯。排查EC區域發現是0x25&#xff0c;產…

基于LiveKit Go 實現騰訊云實時音視頻功能

詳細的生產部署建議&#xff0c;適用于 LiveKit Go 服務器 Web 客戶端 TURN/HTTPS。 1. 服務器準備 推薦使用云服務器&#xff08;如阿里云、騰訊云、AWS、Azure等&#xff09;&#xff0c;公網IP&#xff0c;帶寬建議≥10Mbps。系統推薦 Ubuntu 20.04/22.04 或 CentOS 7/8&…

三位一體:Ovis-U1如何以30億參數重構多模態AI格局?

1. 時代命題&#xff1a;多模態統一模型的破局之戰當GPT-4o以萬億級參數構建多模態帝國時&#xff0c;中國AI軍團正在書寫另一種答案。Ovis-U1用30億參數證明&#xff1a;參數量并非決定性因素&#xff0c;架構創新與訓練策略的化學反應&#xff0c;同樣能催生出改變游戲規則的…

圖像處理基礎:鏡像、縮放與矯正

在圖像處理中&#xff0c;鏡像、縮放和矯正操作是常見的圖像變換手段。這些操作可以幫助我們對圖像進行調整&#xff0c;以滿足不同的需求。本文將詳細介紹這三種操作的原理和實現方法&#xff0c;并通過代碼示例展示它們的實際應用。一、圖片鏡像旋轉1.1 什么是鏡像旋轉&#…

「Java案例」猜數游戲

案例實現 猜數字游戲 設計一個三位數的猜數游戲,三位數隨機生成。程序提示用戶輸入一個三位的數字,依照以下的規則決定贏取多少獎金:1) 如果用戶輸入的數字和隨機數字完全一致,輸出:“恭喜恭喜!完全猜對了!獲得三個贊!”2) 如果用戶輸入的數字覆蓋了隨機生成的所有數…

創客匠人解析創始人 IP 內卷:知識變現時代的生存邏輯與破局路徑

當知識付費行業進入 “存量競爭” 階段&#xff0c;創始人 IP 的 “內卷” 已非選擇而是必然。創客匠人在服務數萬知識創業者的實踐中發現&#xff0c;那些實現逆勢增長的案例&#xff0c;其核心差異往往在于創始人是否具備 “從幕后走到臺前” 的決心與能力 —— 這種內卷并非…

250705-Debian12-sudo apt update加速+配置RDP遠程桌面環境+設置FRP服務為開機啟動項

A. 實現sudo apt update加速 在 Debian 12 上運行 sudo apt update 很慢的常見原因包括&#xff1a; &#x1f50d; 一、常見原因分析 使用了國外的軟件源 默認 Debian 安裝源多數是國際服務器&#xff0c;國內訪問會非常慢。 DNS 解析慢或失敗 軟件源地址解析時間長&#xf…

數學視頻動畫引擎Python庫 -- Manim Voiceover 語音服務 Speech Services

文中內容僅限技術學習與代碼實踐參考&#xff0c;市場存在不確定性&#xff0c;技術分析需謹慎驗證&#xff0c;不構成任何投資建議。 Manim Voiceover 是一個為 Manim 打造的專注于語音旁白的插件&#xff1a; 直接在 Python 中添加語音旁白&#xff1a; 無需使用視頻編輯器&…

C++11 forward_list 從基礎到精通:原理、實踐與性能優化

文章目錄一、為什么需要 forward_list&#xff1f;二、基礎篇&#xff1a;forward_list 的核心特性與接口2.1 數據結構與迭代器2.2 常用接口速覽2.3 基礎操作示例&#xff1a;從初始化到遍歷2.3.1 初始化與遍歷2.3.2 插入與刪除&#xff1a;before_begin 的關鍵作用三、進階篇&…

物聯網技術的核心組件與發展趨勢(截至2025年)

一、物聯網技術的核心組件物聯網&#xff08;IoT&#xff09;技術體系由感知層、網絡層、平臺層、應用層和安全層構成&#xff0c;各層技術協同工作&#xff0c;實現物理世界與數字世界的深度融合。1. 感知層&#xff1a;數據采集與交互傳感器技術&#xff1a;類型&#xff1a;…

面試中常見的問題:JavaScript 宏任務與微任務,包教包會

事件循環Event Loop 我們都知道&#xff0c;JavaScript 是一種單線程的編程語言&#xff0c;簡單的說就是&#xff1a;js只有一條通道&#xff0c;那么在任務多的情況下&#xff0c;就會出現擁擠的情況&#xff0c;這種情況下就產生了 ‘多線程’ &#xff0c;但是這種“多線程…

【LeetCode102.二叉樹的層序遍歷】vs.【LeetCode103.二叉樹的鋸齒形層序遍歷】

題目鏈接 LeetCode102.二叉樹的層序遍歷&#xff1a;102. 二叉樹的層序遍歷 - 力扣&#xff08;LeetCode&#xff09;LeetCode103.二叉樹的鋸齒形層序遍歷&#xff1a;103. 二叉樹的鋸齒形層序遍歷 - 力扣&#xff08;LeetCode&#xff09; 實現思路 定義一個隊列&#xff0…

Redis On-CPU Profiling定位瓶頸到可視化火焰圖

1 . 前置檢查&#xff1a;確認 CPU 真的是瓶頸 在正式打性能“補丁”前&#xff0c;務必跑一遍系統級健康核對表&#xff08;推薦 Brendan Greg 的 USE Method&#xff09;&#xff1a;資源關注指標常用工具CPUUtil/Idle、RunQueuetop、vmstat、sar內存Fault、Swap、Cache Miss…

未來趨勢:AI與量子計算對服務器安全的影響

隨著技術的飛速發展&#xff0c;人工智能&#xff08;AI&#xff09;和量子計算正在深刻改變信息技術的各個領域。特別是在服務器安全領域&#xff0c;這兩項技術既帶來了新的可能性&#xff0c;也帶來了前所未有的挑戰。本文將探討AI和量子計算技術對服務器安全的影響&#xf…

markdown學習筆記(個人向) Part.1

markdown學習筆記&#xff08;個人向&#xff09; Part.1 1. 推薦插件 markdown&#xff1a; 安裝支持markdown的插件&#xff1b; markdown-preview-github-styles&#xff1a; 可以將VS Code上默認的markdown預覽樣式修改成github上常用的形式&#xff0c;很大程度上提高文件…

ZooKeeper 實現分布式鎖

1. 分布式鎖概述 在分布式系統中&#xff0c;為了保證共享資源在并發訪問下的數據一致性&#xff0c;需要引入分布式鎖。分布式鎖是一種在分布式環境下控制多個進程對共享資源進行互斥訪問的機制。它與單機環境下的鎖&#xff08;如Java中的synchronized或Lock&#xff09;不同…

Linux線程——基礎全解

一、什么是線程&#xff08;Thread&#xff09;&#xff1f;? 定義&#xff1a;線程是程序執行的最小單位。即線程&#xff08;Thread&#xff09;是操作系統能夠進行運算調度的最小單位&#xff0c;它被包含在進程之中&#xff0c;是進程中的實際運作單位。一個進程可以并發多…

Java基礎--封裝+static

目錄 什么是封裝&#xff1f; 什么是訪問限定符&#xff1f; static靜態修飾符 用static修飾的類變量或類方法的注意事項&#xff1a; 什么是封裝&#xff1f; 封裝是面向對象的三大特性之一&#xff0c;指的是將一個類中的實現細節進行隱藏&#xff0c;對外只提供一些開放…

DAY 51 復習日

作業&#xff1a;day43的時候我們安排大家對自己找的數據集用簡單cnn訓練&#xff0c;現在可以嘗試下借助這幾天的知識來實現精度的進一步提高import torch import torch.nn as nn import torch.nn.functional as F import torchvision import torchvision.transforms as trans…