1. 概述
介绍在linux环境下编写内核模块Makefile和应用程序Makefile的方法
2. 环境linux-2.4内核
Makefile文件内容:
########################################################################
KDIR = /usr/src/linux
LIB = -I$(KDIR)/include
CFLAGS = -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer $(LIB)
MOD = -D__KERNEL__ -DMODULE
all: netlink_service.c
gcc $(CFLAGS) $(MOD) -o netlink_service.o -c netlink_service.c
gcc $(CFLAGS) -o netlink_clinet.o netlink_client.c
clean:
$(RM) -f *.o
########################################################################
KDIR = /usr/src/linux
定义宏,指定linux源码目录
LIB = -I$(KDIR)/include
-I表示依赖的宏定义文件,$(KDIR)调用了KDIR这个宏
CFLAGS = -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer $(LIB)
定义了编译变量
MOD = -D__KERNEL__ -DMODULE
编译内核模块时需要加入的编译变量
all: netlink_service.c
表示如果所依赖的文件netlink_service.c有更新的话,再次编译时就重编,如果没有更新,不重新编译
gcc $(CFLAGS) $(MOD) -o netlink_service.o -c netlink_service.c
用以上设置的条件编译内核模块netlink_service.c,生成内核模块netlink_service.o
gcc $(CFLAGS) -o netlink_clinet.o netlink_client.c
用以上设置的条件编译应用程序netlink_client.c,生成应用程序netlink_client.o
clean:
$(RM) -f *.o
表示运行命令make clean时,删除所有的.o文件
3. 环境linux-2.6内核
linux-2.6内核下编译内核模块,需要内核源码树的支持,而且是编译过的。
先下载linux-2.6.26内核源码,解压缩后编译,目录在/usr/local/x86/linux/
Makefile文件内容:
########################################################################
ARCH ?= i386
ifeq ("$(ARCH)", "i386")
KDIR := /usr/local/x86/linux/
else
KDIR := ../../linux
endif
ifneq ($(KERNELRELEASE),)
obj-m := sys_reset.o
sys_reset-objs := sys_reset26.o
else
PWD:= $(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.o *.ko .*.mod.c .*.cmd *.mod.c *.o.p
rm -rf .tmp_versions
install:
cp *.ko /tftpboot/
endif
########################################################################
有些部分与linux-2.4内核相同
ARCH ?= i386
ifeq ("$(ARCH)", "i386")
KDIR := /usr/local/x86/linux/
else
KDIR := ../../linux
endif
指定宏KDIR,指向内核源码树
ifneq ($(KERNELRELEASE),)
else
endif
两次编译,首先进行的else部分
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
这时make命令会再次使用这个Makefile文件进行编译。
再次编译时,就会进入if部分
obj-m := sys_reset.o
指定要编译的内核模块,sys_reset.o
sys_reset-objs := sys_reset26.o
指定内核模块sys_reset.o所依赖的文件sys_reset26.o
过程就是sys_reset26.c -> sys_reset26.o -> sys_reset.o
这里可以依赖多个文件
install:
cp *.ko /tftpboot/
运行命令make install时所做的操作
2.6内核的应用程序编译和2.4基本一样,不再赘述。
4. 试验文件源代码
4.1 netlink_service.c
#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/delay.h> #include <linux/mii.h> #include <linux/socket.h> #include <linux/cache.h> #include <asm/io.h> #include <asm/errno.h> #include <linux/netlink.h> #include <linux/sched.h> #include <net/sock.h> #define NETLINK_TEST 22 struct sock *nl_sk = NULL; static void nl_data_ready (struct sock *sk, int len) { wake_up_interruptible(sk->sleep); } static void netlink_test(void) { struct sk_buff *skb = NULL; struct nlmsghdr *nlh = NULL; int err; u32 pid; nl_sk = netlink_kernel_create(NETLINK_TEST, nl_data_ready); skb = skb_recv_datagram(nl_sk, 0, 0, &err); nlh = (struct nlmsghdr *)skb->data; printk("%s: received netlink message payload:%s ", __FUNCTION__, (char *)NLMSG_DATA(nlh)); pid = nlh->nlmsg_pid; NETLINK_CB(skb).groups = 0; NETLINK_CB(skb).pid = 0; NETLINK_CB(skb).dst_pid = pid; NETLINK_CB(skb).dst_groups = 0; netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT); sock_release(nl_sk->socket); } int __init reset_module_init(void) { netlink_test(); } void __exit reset_module_clearnup(void) { if (nl_sk != NULL) { sock_release(nl_sk->socket); } printk("net_link: remove ok.\n"); } MODULE_LICENSE("GPL"); module_init(reset_module_init); module_exit(reset_module_clearnup);
4.2 netlink_client.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <asm/types.h> #include <linux/netlink.h> #define MAX_PAYLOAD 1024 #define NETLINK_TEST 22 struct sockaddr_nl src_addr, dest_addr; struct msghdr msg; struct nlmsghdr *nlh = NULL; struct iovec iov; int sock_fd; int main(void) { sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_TEST); memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); src_addr.nl_groups = 0; bind(sock_fd, (struct sockaddr*)&src_addr,sizeof(src_addr)); memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; dest_addr.nl_groups = 0; nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = 0; strcpy(NLMSG_DATA(nlh), "Hello you!"); iov.iov_base = (void *)nlh; iov.iov_len = nlh->nlmsg_len; msg.msg_name = (void *)&dest_addr; msg.msg_namelen = sizeof(dest_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; printf("sendmsg ...\n"); sendmsg(sock_fd, &msg, 0); memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD)); printf("recvmsg ...\n"); recvmsg(sock_fd, &msg, 0); printf(" Received message payload: %s ", (char *)NLMSG_DATA(nlh)); close(sock_fd); return 0; }
4.3 sys_reset26.c
#include <linux/kernel.h> #include <linux/module.h> #include <linux/types.h> #include <linux/sched.h> #include <net/sock.h> #include <net/netlink.h> #include <linux/pci.h> #define GPIO_PORT 0x480//ICH GPIO base address #define INDEX_PORT 0x2e #define DATA_PORT 0x2f #define BANK_REG 0x07 #define LDN8 0x08 #define LDN9 0x09 #define NETLINK_TEST 21 static unsigned long timestamp1, timestamp2; static struct timer_list timer; static DECLARE_WAIT_QUEUE_HEAD (reset_wait); static int board_num = 0; struct sock *nl_sk = NULL; EXPORT_SYMBOL_GPL(nl_sk); static void nl_data_ready (struct sk_buff *__skb) { struct sk_buff *skb; struct nlmsghdr *nlh; u32 pid; int rc; if (__skb->len < sizeof(*nlh)) return; skb = skb_get(__skb); nlh = nlmsg_hdr(skb); pid = nlh->nlmsg_pid; NETLINK_CB(skb).pid = 0; NETLINK_CB(skb).dst_group = 0; while(1) { interruptible_sleep_on(&reset_wait); if((timestamp2 - timestamp1) < 2*HZ) { strcpy(NLMSG_DATA(nlh), "reset0"); } else if((timestamp2 - timestamp1) < 5*HZ) { strcpy(NLMSG_DATA(nlh), "reset2"); } else { strcpy(NLMSG_DATA(nlh), "reset5"); } rc = netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT); if (rc < 0) { printk(KERN_ERR "net_link: can not unicast skb (%d)\n", rc); } } return; } static void entry_config(void) { outb(0x87, INDEX_PORT); mdelay(1); outb(0x87, INDEX_PORT); //Entering W83627HF Configuration } static void exit_config(void) { outb(0xAA, INDEX_PORT); //exit config } //;Write SuperIO register static int read_w83627_reg(int LDN, int reg) { entry_config(); outb(0x07, INDEX_PORT); //LDN register mdelay(1); outb(LDN, DATA_PORT); mdelay(1); outb(reg, INDEX_PORT); mdelay(1); return inb(DATA_PORT); exit_config(); } static void write_w83627_reg(int LDN, int reg, int value) { entry_config(); outb(0x07, INDEX_PORT); //LDN register mdelay(1); outb(LDN, DATA_PORT); mdelay(1); outb(reg, INDEX_PORT); mdelay(1); outb(value, DATA_PORT); exit_config(); } static void init_gpio25(void) { write_w83627_reg(LDN9,0x30, 0x02); //enable function write_w83627_reg(LDN9,0xE4,(read_w83627_reg(LDN9,0xE4) | (0x20))); // set GPOIO 25 = input // //enable function } static void initial_gpio(void) { int aaa; aaa = inb(GPIO_PORT + 0x30); aaa = aaa | 0x2; //;enable GPIO33 outb(aaa, GPIO_PORT + 0x30); aaa = inb(GPIO_PORT + 0x34); //; GPIO33 set as GPI33 aaa = aaa | 0x2; outb(aaa, GPIO_PORT + 0x34); } static int get_gpio_input_value(void)//Read GPI33 value { int temp; if (board_num == 1) { temp = inb(GPIO_PORT + 0x38); temp &= 0x2; } else if (board_num == 2) { temp = read_w83627_reg(LDN9,0xE5); //;read 0xf1[6] for GPI25 temp &= 0x20; } else { printk("can not support this board!\n"); return -1; } return temp; } static int get_board_num(void) { if( pci_find_device(0x8086, 0x2590, NULL) && pci_find_device(0x8086, 0x2591, NULL) && pci_find_device(0x8086, 0x2592, NULL) && pci_find_device(0x8086, 0x10D3, NULL)) return 1; if( pci_find_device(0x8086, 0x27ac, NULL) && pci_find_device(0x8086, 0x27b9, NULL) && pci_find_device(0x8086, 0x27c9, NULL) && pci_find_device(0x8086, 0x27da, NULL)) return 2; return -1; } static void timer_get_gpio_state(unsigned long p) { int input_value; if (board_num == 1) { initial_gpio(); } else if (board_num == 2) { init_gpio25(); } timestamp1 = jiffies; while ((input_value = get_gpio_input_value()) == 0x0); timestamp2 = jiffies; if((timestamp2 - timestamp1) < 2*HZ); else if((timestamp2 - timestamp1) < 5*HZ) { printk("reset = 2\n"); } else { printk("reset = 5\n"); } mod_timer(&timer, jiffies + HZ/10); wake_up_interruptible(&reset_wait); } static int reset_netlink_startup(void) { board_num = get_board_num(); if (board_num == 1) { printk("this is board A\n"); } else if (board_num == 2) { printk("this is board B\n"); } else { printk("can not support this board!\n"); return -1; } init_timer(&timer); timer.data = 1; timer.expires = jiffies + HZ/10; timer.function = (void (*)(unsigned long)) timer_get_gpio_state; add_timer(&timer); nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, nl_data_ready, NULL, THIS_MODULE); if (!nl_sk) { printk(KERN_ERR "net_link: Cannot create netlink socket.\n"); goto err; } printk("net_link: create socket ok.\n"); return 0; err: del_timer(&timer); if(nl_sk) sock_release(nl_sk->sk_socket); return -1; } int __init reset_module_init(void) { int res; res = reset_netlink_startup(); return res; } void __exit reset_module_clearnup(void) { if (nl_sk != NULL) { sock_release(nl_sk->sk_socket); } printk("net_link: remove ok.\n"); } MODULE_LICENSE("GPL"); module_init(reset_module_init); module_exit(reset_module_clearnup);
PS: Makefile中可以使用正则表达式来简化和提高通用性,本文中的代码都是关于netlink方面的,均经过调试。