Linux驱动程序学习笔记(3)——点亮LED

来源:互联网 发布:2016数控编程工资待遇 编辑:程序博客网 时间:2024/04/29 19:26

一,难点

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 0x56000010static 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;elseval = 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 以此类推

原创粉丝点击