一,难点
1,ioremap 和 iounmap
要操作硬件,首先要操作相关IO寄存器,知道寄存器的物理地址后,要经过Io映射到虚拟地址里才可以使用,所以要用到ioremap 和 iounmap两个函数。
(1)void __iomem * ioremap (unsigned long phys_addr, unsigned long size),
第一个参数为物理地址,第二个参数为要映射的空间的大小,返回值为映射到的虚拟地址的指针,还要转换成相应的类型指针,如:
pGpbcon = (volatile unsigned long *)ioremap(0x56000010,16);
(2)void iounmap(void *addr),
释放映射关系,参数为申请映射的虚拟地址,如
iounmap(pGpbcon);
2,用户空间传递数据到内核空间
copy_from_user(&val,userbufer,count);//如果失败,返回有多少个Bytes未完成copy
第一个参数为内核空间变量的地址,第二个参数为用户空间,第三个参数为传递内容的大小。第二第三两个参数是file_operations指定的write函数的相应的2个参数
注意返回值是有多少个Bytes未完成copy,用法:
static int led_write(struct file * file, const char __user * userbuf,
size_t count, loff_t * off)
{
int val;
copy_from_user(&val, userbuf, count);//注意不要写成val = copy_from_user(&val, userbuf, count);
/*..............*/
return 0;
}
3,有关次设备号
一个设备文件拥有主设备号和次设备号,驱动程序只对应主设备号,次设备号给用户自定义使用,可以创建几个主设备号相同而次设备号不同的设备文件(应用程序打开的是设备文件),他们都对应相同的驱动程序,然后根据次设备号不同在驱动程序里执行不同的操作。
(1)创建几个主设备号相同而次设备号不同的设备文件的方法:
static struct class *led_class;
static struct class_device *led_class_device[4];
在模块初始化函数里:
led_class = class_create(THIS_MODULE, "ledClass");
for(i=0;i<4;i++)
{
led_class_device[i] = class_device_create(led_class,NULL,MKDEV(major,i),NULL,"led%d",i);
}
记得在模块退出函数里:
led_class = class_create(THIS_MODULE, "ledClass");
for(i=0;i<4;i++)
{
led_class_device[i] = class_device_create(led_class,NULL,MKDEV(major,i),NULL,"led%d",i);
}
(2)从打开的设备文件里获取次设备号方法
int minor=MINOR(inode->i_rdev);//在open函数里知道inode可以使用这个方法
int minor=MINOR(file->f_dentry->d_inode->r_dev);//在write、read等函数里知道file,可以用这个方法
二,方法
要操作LED只要初始化时候进行ioremap获得GPBCON和GPBDAT寄存器的地址,然后和裸机一样对寄存器进行相关操作即可,在函数退出函数时候要用iounmap解除映射。还有就是write和read等函数的使用
三,代码
1,驱动代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#define GPBCONADDR 0x56000010
static struct class *led_class;
static struct class_device *led_class_device[4];
volatile unsigned long *pGpbcon;
volatile unsigned long *pGpbdat;
int major;
int minor;
static int led_open(struct inode *inode, struct file *file)
{
printk("Hello, Driver of led\n");
minor=MINOR(inode->i_rdev);
*pGpbcon &= ~(3<<(minor+5)*2);
*pGpbcon |= (1<<(minor+5)*2);
return 0;
}
static int led_write(struct file * file, const char __user * userbuf,
size_t count, loff_t * off)
{
int val;
printk("Write led Done!\n");
MINOR(file->f_dentry->d_inode->r_dev);
copy_from_user(&val, userbuf, count);
if(val==1)
{
printk("led on!!!\n");
printk("minor:%d\n",minor);
*pGpbdat &= ~(1<<(minor+5));
}
else
{
*pGpbdat |= (1<<(minor+5));
}
return 0;
}
static struct file_operations led_ops =
{
.owner = THIS_MODULE,
.open = led_open,
.write = led_write
};
static int led_init(void)
{
int i=0;
major = register_chrdev(0,"leddriver",&led_ops);
led_class = class_create(THIS_MODULE, "ledClass");
for(i=0;i<4;i++)
{
led_class_device[i] = class_device_create(led_class,NULL,MKDEV(major,i),NULL,"led%d",i);
}
pGpbcon = (volatile unsigned long *)ioremap(GPBCONADDR,16);
pGpbdat = pGpbcon + 1;
return 0;
}
static void led_exit(void)
{
int i=0;
iounmap(pGpbcon);
unregister_chrdev(major,"leddriver");
for(i=0;i<4;i++)
{
class_device_unregister(led_class_device[i]);
}
class_destroy(led_class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
2,测试代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fid;
int val;
if(argc !=3 )
{
printf("input error\n");
return -1;
}
if(strcmp(argv[2],"on")==0)
val = 1;
else
val = 0;
char filename[40] = "/dev/";
strcat(filename,argv[1]);
fid = open(filename,O_RDWR);
if(fid < 0)
{
printf("%s Open error!\n",argv[1]);
return -1;
}
write(fid,&val,4);
close(fid);
return 0;
}
输入./ledTest led1 on 点亮第一站led,./ledTest led1 off 熄灭第一盏led
./ledTest led2 on 点亮第2站led,./ledTest led2 off 熄灭第2盏led 以此类推