应用netlink的内核模块和应用程序Makefile编写

时间:2021-11-18 22:26:07

 

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方面的,均经过调试。

相关文章