全部博文(298)
分类: LINUX
2011-03-24 12:32:12
(1) 初识linux字符设备驱动程序
注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方
1.本文所介绍的程序平台
开发板:arm9-mini2440
虚拟机为:Red Hat Enterprise Linux 5
开发板上系统内核版本:linux-2.6.32.2
2.程序清单
本次实验程序为国嵌培训代码,本人作了改动和较为详细的注释,如有错误请指出。
memdev.h
#ifndef _MEMDEV_H_
#define _MEMDEV_H_
#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 260
//memdev采用静态分配设备号,不要和其它设备重复
#endif
#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2
#endif
#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif
struct mem_dev
{
char *data;
unsigned long size;
};
#endif
memdev.c
/*********************************************
*memdev.c
*********************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "memdev.h"
static int mem_major = MEMDEV_MAJOR;
module_param(mem_major,int,S_IRUGO);
struct mem_dev *mem_devp;/*设备结构体指针*/
struct cdev cdev;
/*文件打开函数*/
int mem_open(struct inode *inode,struct file *filp)
{
struct mem_dev *dev;
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
/*大于最大的次设备号就错误了 因为注册的时候要指定次设备的数目*/
if(num >=MEMDEV_NR_DEVS)
return -ENODEV;
dev = &mem_devp[num];
/*将设备描述结构指针赋值给文件私有数据指针*/
filp->private_data = dev;
return 0;
}
/*文件释放函数*/
int mem_release(struct inode *inode,struct file *filp)
{
return 0;
}
/*读函数*/
static ssize_t mem_read(struct file *filp,char __user *buf,size_t size,loff_t *poss)
{
unsigned long p = *poss;
unsigned int count = size;
int ret = 0;
/*获得设备结构体指针由于read没有struct inode *inode结构 而且函数的
参数不能改 只能改函数名所以只能在open函数里面设置*/
struct mem_dev *dev = filp->private_data;
/*判断读位置是否有效*/
if(p >= MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE-p)
count = MEMDEV_SIZE-p;
/*读数据到用户空间*/
if(copy_to_user(buf,(void*)(dev->data+p),count))
{
ret = -EFAULT;
}
else
{
*poss +=count;
ret = count;
printk(KERN_INFO "read %d bytes from %lu\n",count,p);
}
return ret;
}
/*写函数*/
static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *poss)
{
unsigned long p = *poss;
unsigned int count = size;
int ret=0;
struct mem_dev *dev = filp->private_data;
if(p>=MEMDEV_SIZE)
return 0;
if(count>MEMDEV_SIZE-p)
count = MEMDEV_SIZE-p;
if(copy_from_user(dev->data+p,buf,count))
{
ret = -EFAULT;
}
else
{
*poss += count;
ret = count;
printk(KERN_INFO "write %d bytes from %lu\n",count,p);
}
return ret;
}
static loff_t mem_llseek(struct file *filp,loff_t offset,int whence)
{
loff_t newpos;
switch(whence)
{
case 0:
newpos = offset;
break;
case 1:
newpos = filp->f_pos + offset;
break;
case 2:
newpos = MEMDEV_SIZE - 1 + offset;
break;
default:
return -EINVAL;
}
if((newpos<0) || (newpos>MEMDEV_SIZE))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
static const struct file_operations mem_fops =
{
.owner = THIS_MODULE,
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
};
/*设备驱动模块加载函数*/
static int memdev_init(void)
{
int result;
int i;
dev_t devno = MKDEV(mem_major,0);
/*静态申请设备号*/
if(mem_major)
result = register_chrdev_region(devno,2,"memdev");
else/*动态申请设备号*/
{
result = alloc_chrdev_region(&devno,0,2,"memdev");
mem_major = MAJOR(devno);
}
if(result < 0)
return result;
/*初始化cdev结构*/
cdev_init(&cdev,&mem_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &mem_fops;
/*注册字符设备*/
cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);
/*为设备描述结构分配内存*/
mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev),GFP_KERNEL);
if(!mem_devp)/*申请失败*/
{
result = -ENOMEM;
goto fail_malloc;
}
memset(mem_devp,0,MEMDEV_NR_DEVS * sizeof(struct mem_dev));
/*为设备分配内存*/
for(i=0;i
{
mem_devp[i].size = MEMDEV_SIZE;
mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
memset(mem_devp[i].data,0,MEMDEV_SIZE);
}
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result;
}
static void memdev_exit(void)
{
cdev_del(&cdev);
kfree(mem_devp);
unregister_chrdev_region(MKDEV(mem_major,0),2);
}
MODULE_AUTHOR("Zechin Liao");
MODULE_LICENSE("GPL");
module_init(memdev_init);
module_exit(memdev_exit);
Makefile
ifneq ($(KERNELRELEASE),)
obj-m:=memdev.o
else
KERNELDIR:=/root/mini2440/linux/linux-2.6.32.2
PWD:=$(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -rf *.ko *.o *.mod.c *.mod.o *.symvers
endif
//KERNELDIR:=/root/mini2440/linux/linux-2.6.32.2为自己开发板当前运行的内核编译后的源码树位置
// ARCH=arm CROSS_COMPILE=arm-linux- 指明开发板平台和编译工具
//注意;Makefile的书写格式 该用tab键的时候一定要用, 若是安装驱动的时候显示的是
//[root@FriendlyARM /udisk]# insmod myadd.ko
//insmod: cannot insert 'myadd.ko': invalid module format
//表明linux内核版本不一致或者是平台不一致
memdevapp.c
/*********************************************
*memdevapp.c
*********************************************/
#include
#include
#include
#include
int main()
{
int fd;
char buf[4096];
//FILE *fp0 = NULL;
/*初始化buf*/
strcpy(buf,"This is a example of charactar devices driver");
printf("buf:%s\n",buf);
/*打开设备文件*/
fd=open("/dev/memdev0",O_RDWR);
if(fd == -1)
{
printf("open memdev failed!\n");
return -1;
}
/*写入设备*/
write(fd,buf,sizeof(buf));
/*重定位文件设备(思考没有该指令 会有什么后果??)*/
/*如没有重新定位,读数据时就会从刚刚写入的末尾去读*/
lseek(fd,0,SEEK_SET);
/*清除buf*/
strcpy(buf,"nothing");
/*读出设备*/
read(fd,buf,sizeof(buf));
/*检测结果*/
printf("buf:%s\n",buf);
return 0;
}
//注意;编译程序的工具-arm-linux-gcc 注意安装交叉编译链
//若是用gcc直接编译的 在我的平台上显示的是
//[root@FriendlyARM /udisk]# ./memdevapp1
//./memdevapp1: line 1: syntax error: "(" unexpected
Arm平台实验:
[root@FriendlyARM /mini_driver]# ls
memdev.ko memdevapp pwm_test test
[root@FriendlyARM /mini_driver]# insmod memdev.ko
[root@FriendlyARM /mini_driver]# cat /proc/devices | more //查看当前运行的设备
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
260 memdev
7 vcs
10 misc
13 input
14 sound
21 sg
29 fb
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 s3c2410_serial
254 rtc
Block devices:
259 blkext
7 loop
7 loop
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
//创建设备在/dev下名字为memdev0 ‘c’代表为字符设备 ‘260 0’分别为主次设备号
[root@FriendlyARM /mini_driver]# mknod /dev/memdev0 c 260 0
[root@FriendlyARM /mini_driver]# ./memdevapp
buf:This is a example of charactar devices driver
write 4096 bytes from 0
read 4096 bytes from 0
buf:This is a example of charactar devices driver
[root@FriendlyARM /mini_driver]#
以上方法采用的动态编译驱动,也可以静态方式将驱动编译进内核
1.将memdev.h和memdev.c两个驱动源文件拷贝至内核linux-2.6.24/drivers/char目录
2.修改该目录下Kconfig文件,添加如下内容
config MEMDEV_DRIVER
tristate "memdev driver"
depends on MACH_SMDK2440
default y if MACH_SMDK2440
help
this option enables support for memdev experiment
3.修改该目录下Makefile,添加如下内容
obj-$(CONFIG_MEMDEV_DRIVER) +=memdev.o
4.在make menuconfig时在字符设备中找到菜单项“memdev drinver”,选择为Y或M,编译进内核还是模块。