Linux 驅動程式開發步驟(X86平台)
本文轉自:http://blog.sina.com.cn/s/blog_75f3979401015cwr.html
編寫好驅動,通過掛載的方法將驅動程式掛載到核心裡面,大致步驟如下:
一: 1>建立以.c為尾碼的c語言程式檔案(裡麵包含了裝置名稱及裝置號等)
2>建立Makefile檔案(作用是通過make來產生裝置檔案*.ko檔案,裡面可以建立自己的平台所需的裝置檔案如:arm等).make產生相應的裝置檔案
二: 要在/dev下建立相應的裝置結點(裝置名稱),用insomd *.ko命令將相應的驅動裝置檔案掛載到核心中.
三:編寫測試檔案(.c檔案)用來測試核心是否已近成功掛載到核心.(編寫好相應的測試檔案後,用gcc –o Filename Filename.c(測試檔案名)來產生相應的可執行檔).
四:如果裝置驅動掛載成功,當執行測試檔案(./Filename)時會產生相應的結果.
五:可能用到的相關命令:
1.lsmod:列出核心已經載入模組的專題.
輸出:
Module(模組名)size(大小)used by (被..使用)
2.demop:分析可載入模組的依賴性,產生modules.dep檔案和對應檔
3.uname –r 顯示核心版本(在編寫Makefile時使用到)
4.modprobe : linux核心添加和刪除模組(相關參數請查看man協助文檔)
5.modinfo:顯示核心模組的資訊.
6.insmod: 向linux核心中載入一個模組,用法:insmod[filename] [module options…]
7.rmmod: 刪除核心中的模組, 用法: rmmod [-f,w,s,v][modulename]
8.dmesg: 顯示核心緩衝區,核心的各種資訊,核心啟動時的資訊會寫入到/var/log/下.
六.例子1:
第一步:增加標頭檔和宏定義
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/module>
#include <linux/kernel>
第二步:添加與字元裝置定義及註冊有關的資料成員
//定義裝置名稱
#define DEVICE_NAME "test" //裝置名稱
#define BUF_SIZE 1024
static char tmpbuf[BUF_SIZE];
//定義主次裝置號
static unsigned int TestMajor=0; //主
static unsigned int TestMinor=0; //次
static struct cdev *test_cdev;
static dev_t dev;
第三步:增加open/release函數
static int test_chardev_open(struct inode *inode,struct file *file)
{
printk("open major=%d, minor=%d\n", imajor(inode),
iminor(inode));
return 0;
}
static int test_chardev_release(struct inode *inode,struct file *file)
{
printk("close major=%d,minor=%d\n",imajor(inode),
iminor(inode));
return 0;
}
第四步:增加read函數
static ssize_t test_chardev_read(struct file *file,char __user *buf,
size_t const count,loff_t *offset)
{
if(count < BUF_SIZE)
{
if(copy_to_user(buf,tmpbuf,count))
{
printk("copy to user fail \n");
return -EFAULT;
}
}else{
printk("read size must be less than %d\n", BUF_SIZE);
return -EINVAL;
}
*offset += count;
return count;
}
第五步:增加write函數
static ssize_t test_chardev_write(struct file *file, const char __user*buf,size_t const count,loff_t *offset)
{
if(count < BUF_SIZE)
{
if(copy_from_user(tmpbuf,buf,count))
{
printk("copy from user fail \n");
return -EFAULT;
}
}else{
printk("size must be less than %d\n", BUF_SIZE);
return -EINVAL;
}
*offset += count;
return count;
}
第六步:添加增加file_operations成員
static struct file_operations chardev_fops={
.owner = THIS_MODULE,
.read = test_chardev_read,
.write = test_chardev_write,
.open = test_chardev_open,
.release = test_chardev_release,
};
第七步:在模組的入口添加裝置的裝置號擷取及裝置註冊
static int __init chrdev_init(void)
{
int result;
if(TestMajor)
{
dev=MKDEV(TestMajor,TestMinor);//建立裝置編號
result=register_chrdev_region(dev,1,DEVICE_NAME);
} else {
result=alloc_chrdev_region(&dev,TestMinor,1,DEVICE_NAME);
TestMajor=MAJOR(dev);
}
if(result<0)
{
printk(KERN_WARNING"LED: cannot get major %d \n",TestMajor);
return result;
}
test_cdev=cdev_alloc();
cdev_init(test_cdev,&chardev_fops);
//test_cdev->ops=&chardev_fops;
test_cdev->owner=THIS_MODULE;
result=cdev_add(test_cdev,dev,1);
if(result)
printk("<1>Error %d while register led device!\n",result);
return 0;
}
第八步:在模組的出口函數增加裝置裝置號釋放及裝置登出函數
unregister_chrdev_region(MKDEV(TestMajor,TestMinor),1);
cdev_del(test_cdev);
第九步:編譯並載入該模組
第十步:根據裝置號的設定,在檔案系統中建立對應的裝置節點
#mknod /dev/test c XXX XX
例子2:
驅動檔案:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#define DEVICENAME "ccccc"
unsigned int major=221;
unsigned int minor=0;
struct cdev *abc;
dev_t dev;
static char bufrh[1024]="read success!";
static int aaaaa_open(struct inode *inodep, struct file *filep)
{
printk("read success!\n");
return 0;
}
int aaaaa_release(struct inode *inodep, struct file *filep)
{
return 0;
}
static ssize_t aaaaa_read (struct file *filep, char __user *buf, size_tcount, loff_t *offset)
{
if(copy_to_user(buf, bufrh, 1))
{
printk("copy_to_user fail!\n");
}
return 0;
}
ssize_t aaaaa_write (struct file *filep, const char __user *buf,size_t count, loff_t *offse)
{
printk("write!\n");
return 0;
}
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = aaaaa_open,
.release = aaaaa_release,
.read = aaaaa_read,
.write = aaaaa_write,
};
static int __init aaaaa_init(void)
{
int a;
dev=MKDEV(major, minor);
a=register_chrdev_region(dev, 1, DEVICENAME);
abc=cdev_alloc();
abc->owner=THIS_MODULE;
cdev_init(abc, &fops);
cdev_add(abc, dev, 1);
return 0;
}
static void __exit aaaaa_cleanup(void)
{
cdev_del(abc);
unregister_chrdev_region(dev, 1);
}
module_init(aaaaa_init);
module_exit(aaaaa_cleanup);
MODULE_LICENSE("GPL ");
Makefile檔案:
obj-m += firstqd.o(相應裝置檔案名稱)
KERDIR = /usr/src/linux-headers-2.6.32-24-generic
#KERDIR=/home/linux2.6/linux #arm騫沖彴
PWD=$(shell pwd)
modules:
$(MAKE) -C $(KERDIR) M=$(PWD)modules
pc:
gcc -o fristqd firstqd.c
arm:
arm-linux-gcc -o fristqd firstqd.c
clean:
rm -rf *.o *~core *.depend *.cmd *.ko *.mod.c *.tmp_versions
測試檔案(test.c):
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
char buf[1024];
char bufw[1024]="write success";
int main()
{
int fd,m,n;
fd=open("/dev/aaa",O_RDWR);
if (fd)
{
m=read(fd,buf,100);
printf("read kernel:%s\n",buf);
n=write(fd,bufw,10);
}
//printf("ni hao");
return 0;
}