Skip to content

Instantly share code, notes, and snippets.

@Jamlee
Last active April 27, 2022 18:08
Show Gist options
  • Save Jamlee/c34af3b7874f5a947407c5bb4f54d922 to your computer and use it in GitHub Desktop.
Save Jamlee/c34af3b7874f5a947407c5bb4f54d922 to your computer and use it in GitHub Desktop.
Linux 系统内核例子
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/platform_device.h>
//////////////////////////////////////////////////////////////////////////////////
//
// 这里为了感知新PlatformDevice时,创建 1 个网络设备的代码。和Platform总线代码无关
//
//////////////////////////////////////////////////////////////////////////////////
struct eth_struct {
int bar;
int foo;
struct net_device *dummy_ndev;
};
static int fake_eth_open(struct net_device *dev) {
printk("%s\n", __FUNCTION__);
/* We are now ready to accept transmit request from
* the queueing layer of the networking. */
netif_start_queue(dev);
return 0;
}
static int fake_eth_release(struct net_device *dev) {
pr_info("%s\n", __FUNCTION__);
netif_stop_queue(dev);
return 0;
}
static int fake_eth_xmit(struct sk_buff *skb, struct net_device *dev) {
pr_info("%s\n", __FUNCTION__);
dev->stats.tx_bytes +=skb->len;
dev->stats.tx_packets++;
skb_tx_timestamp(skb);
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
static int fake_eth_init(struct net_device *dev) {
pr_info("%s is inited \n", __FUNCTION__);
return 0;
}
// 网卡设备操作函数
static const struct net_device_ops my_netdev_ops = {
.ndo_init = fake_eth_init,
.ndo_open = fake_eth_open,
.ndo_stop = fake_eth_release,
.ndo_start_xmit = fake_eth_xmit,
.ndo_validate_addr = eth_validate_addr,
};
//////////////////////////////////////////////////////////////////////////////////
//
// 这里为了注册 1 个网络设备。和平台代码无关
//
//////////////////////////////////////////////////////////////////////////////////
static const struct of_device_id fake_eth_dt_ids[] = {
{ .compatible = "packt,fake-eth", },
{ /* */ }
};
static int fake_eth_probe(struct platform_device *pdev) {
int ret;
struct eth_struct *priv;
struct net_device *dummy_ndev;
priv = devm_kzalloc(&pdev->dev,sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
// 分配 1 个网络设备
dummy_ndev = alloc_etherdev(sizeof(struct eth_struct));
dummy_ndev->if_port = IF_PORT_10BASET;
dummy_ndev->netdev_ops = &my_netdev_ops;
// 注册网络设备
ret = register_netdev(dummy_ndev);
if(ret) {
pr_info("dummy net dev: Error %d initializaing card...", ret);
return ret;
}
priv -> dummy_ndev = dummy_ndev;
platform_set_drvdata(pdev,priv);
return 0;
}
static int fake_eth_remove(struct platform_device *pdev) {
struct eth_struct *priv;
priv = platform_get_drvdata(pdev);
pr_info("Cleaning UP the Module\n");
unregister_netdev(priv->dummy_ndev);
free_netdev(priv->dummy_ndev);
return 0;
}
// https://blog.csdn.net/weixin_43790707/article/details/104900370
// 定义1个 platform 驱动,注册在 platform 总线上。简单来说,我们在编写驱动时指定的 struct platform_device_id *id_table
// const char *name、const struct of_device_id *of_match_table 将是判断我们的驱动和设备是否能匹配的依据
// 我们可以看到匹配的顺序:
// 1、先用设备树中的 compatible 属性和 platform_drive r中的 driver 中的 of_match_table 来匹配
// 2、再用 platform_driver 中的 id_table 中的 name 和 platform_device 中的 nam e来匹配
// 3、最后用 platform_devic e中的 name 和 platform_driver 中的 driver 中的 nam e来匹配
static struct platform_driver mypdrv = {
.probe = fake_eth_probe, // 驱动和设备匹配成功之后调用
.remove = fake_eth_remove, // 卸载驱动时调用
.driver = {
.name = "fake-eth", // 与设备匹配时会用到的驱动名,一样就匹配成
.of_match_table = of_match_ptr(fake_eth_dt_ids), // 与设备树匹配,名称一样就匹配成功
.owner = THIS_MODULE,
},
};
// 注册驱动到 platfrom 总线上
module_platform_driver(mypdrv);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Madieu <john.madieu@gmail.com");
MODULE_DESCRIPTION("Fake Ethernet driver");
CONFIG_MODULE_SIG=n
CONFIG_MODULE_SIG_ALL=n
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
obj-m := fake_driver.o
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
rm -f fake_driver.mod* .fake_driver* fake_driver.o Module.symvers modules.order
endif
// https://tldp.org/LDP/lkmpg/2.6/html/hello2.html
#include <linux/module.h> // Needed by all modules
#include <linux/kernel.h> // Needed for KERN_ALERT
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, world\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
CONFIG_MODULE_SIG=n
CONFIG_MODULE_SIG_ALL=n
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
// 1 个假的 platform device
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h> // Needed for KERN_ALERT
#include <linux/platform_device.h>
#define MY_DUMMY_PLATFORM_DEV_NAME "fake-eth"
static struct platform_device MY_DUMMY_device = {
.name = MY_DUMMY_PLATFORM_DEV_NAME,
.id = 100, //when .name is same, using .id to distgunish
};
// -- Module INIT
static int __init MY_DUMMY_init(void)
{
return platform_device_register(&MY_DUMMY_device);
}
static void __exit MY_DUMMY_exit(void)
{
platform_device_del(&MY_DUMMY_device);
}
module_init(MY_DUMMY_init);
module_exit(MY_DUMMY_exit);
//MODULE_DESCRIPTION("Platform device and driver example");
//MODULE_AUTHOR("ITTraining");
MODULE_LICENSE("GPL v2");
//MODULE_ALIAS("platform:" MY_DUMMY_PLATFORM_DEV_NAME);
CONFIG_MODULE_SIG=n
CONFIG_MODULE_SIG_ALL=n
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
obj-m := platform_device.o
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
rm -f platform_device.mod* .platform_device* platform_device.o Module.symvers modules.order
endif
$ sudo insmod eth-skel.ko # rmmod
$ ls /sys/bus/platform/drivers/fake-eth/
bind module uevent unbind
$ make && sudo rmmod fake_driver.ko; sudo insmod fake_driver.ko; dmesg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment