第二次写字符设备驱动程序,对字符设备驱动有了进一步的了解。程序的思考参考于:http://bbs.witech.com.cn/forum.php?mod=viewthread&tid=6408&extra=page%3D1
基于友善之臂micro2440,linux内核版本为2.6.29.4。
首先给出源代码(lightwater.c):
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/version.h>
#include<linux/errno.h>
#include<linux/cdev.h>
#include<linux/moduleparam.h>
#include<linux/ioctl.h>
/*GPIO端口的映射口,在arch/arm/mach-s3c2410/include/mach/regs-gpio.h下定义*/
#include<mach/regs-gpio.h>
/*声明对GPIO接口进行操作的外部接口函数,extern声明,函数原型在linux/arch/arm/plat-s3c24xx/下*/
#include<mach/hardware.h>
#include"lightwater.h"
static int major_dev = LIGHT_MAJOR;
module_param(major_dev ,int,S_IRUGO);
dev_t light_dev = MKDEV(LIGHT_MAJOR,0);
int ret=0;
struct cdev cdev;
unsigned long s3c2410_led_table[]={
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8
};
int light_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg){
switch(cmd){
case 0: s3c2410_gpio_setpin(s3c2410_led_table[0],0);
s3c2410_gpio_setpin(s3c2410_led_table[1],1);
s3c2410_gpio_setpin(s3c2410_led_table[2],1);
s3c2410_gpio_setpin(s3c2410_led_table[3],1);
break;
case 1: s3c2410_gpio_setpin(s3c2410_led_table[0],1);
s3c2410_gpio_setpin(s3c2410_led_table[1],0);
s3c2410_gpio_setpin(s3c2410_led_table[2],1);
s3c2410_gpio_setpin(s3c2410_led_table[3],1);
break;
case 2: s3c2410_gpio_setpin(s3c2410_led_table[0],1);
s3c2410_gpio_setpin(s3c2410_led_table[1],1);
s3c2410_gpio_setpin(s3c2410_led_table[2],0);
s3c2410_gpio_setpin(s3c2410_led_table[3],1);
break;
case 3: s3c2410_gpio_setpin(s3c2410_led_table[0],1);
s3c2410_gpio_setpin(s3c2410_led_table[1],1);
s3c2410_gpio_setpin(s3c2410_led_table[2],1);
s3c2410_gpio_setpin(s3c2410_led_table[3],0);
break;
default:break;
}
return 0;
}
struct file_operations light_ops={
.owner = THIS_MODULE,
.ioctl = light_ioctl,
};
static int __init lightwater_init(void){
unsigned int i;
if(major_dev)
/*静态注册函数的第二个参数是注册设备的个数,最开始不小心设为0了,所以若采用静态注册,成功运行模块后不会在/proc/devices中生成相应设备号和设备名。*/
ret = register_chrdev_region(light_dev,1,DEV_NAME);
else
ret = alloc_chrdev_region(&light_dev,0,1,DEV_NAME);
/*return -1的返回值以及下面的返回值都很重要,最开始大括号忘写了,后面载入模块的时候一直出现 "operation not permitted".这里的返回值,对载入模块之后的错误判断很重要*/
if(ret<0){
printk("Char device register failed\n ");
return -1;
}
/*加入字符设置,包括初始化字符设备和添加字符设备*/
cdev_init(&cdev,&light_ops);
cdev.owner = THIS_MODULE;
cdev.ops = &light_ops;
ret = cdev_add(&cdev,light_dev,1);
if(ret<0){
printk("cdev adding goes wrong!\n");
return -1;
}
printk("cdev is added\n");
/*初始化led灯,s3c2410_gpio_cfgpin的第二个参数设定端口功能,很多网上的例子是通过数组来对应设定每个io口的功能,这里因为是都作为输出,所以直接写一个s3c2410_GPIO_OUTPUT即可*/
for(i=0;i<4;i++){
s3c2410_gpio_cfgpin(s3c2410_led_table[i],S3C2410_GPIO_OUTPUT);
s3c2410_gpio_setpin(s3c2410_led_table[i],1);
}
printk("led driver installed!\n");
return ret;
}
static void __exit lightwater_exit(void){
unregister_chrdev_region(light_dev,1);
cdev_del(&cdev);
}
module_init(lightwater_init);
module_exit(lightwater_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
头文件(lightwater.h)中已对字符名和主设备号进行简单的定义
在编译及插入内核模块过程中出现几个问题:
1,找不到reg-gpio.h文件,但在相应的文件夹内确实可以找到该文件,却可以找到hardware.h文件,这两个文件都存在于同样的文件夹中。修改源代码的.config文件后再编译就可以找到了。
2,将编译后的内核模块传送到开发板去运行时,出现truncated的错误,表示文件传输过程中被截断了,再传一次就可以了
3,插入模块时出现“operation not permitted”的错误,但操作权限都使用root权限。原因解释已经在源代码中说过,返回值会解释插入模块时出现的错误。
4,编写程序时,应注意函数原型,如果函数原型有返回值,则写的程序代码也必须有返回值。
5,在开发板中使用insmod *.ko 插入模块后,用rmmod时出现“no such file or directory”的错误,修改 /lib/modules/下的文件夹的名称,我是将2.6.29-FriendlyARM修改成2.6.29.4-FriendlyARM。然后rmmod就会出现module '*' not found,但用lsmod时却没有了插入的模块,表示模块已经卸载了。但是为什么会出现not found的错误呢?按网上说的方法,将编写的模块拷入/lib/modules/2.6.29.4-FriendlyARM中则可以成功卸载并且没有提示错误。why?怎么解决?