驱动开发3——简单的led驱动

一、字符设备驱动框架
字符设备驱动的编写主要就是驱动对应的open、close、read。。。其实就是file_operations结构体的成员变量的实现。

二、驱动模块的加载与卸载
1、Linux驱动程序有两种存在形式:
编译到kernel里面,也就是zImage
编译为模块,.ko。

TIP:
1、编译驱动的时候需要用到linux内核源码!因此要解压缩linux内核源码,编译linux内核源码。得到zImage和.dtb。需要使用编译后的到的zImage和dtb启动系统
2、从SD卡启动,SD卡烧写了uboot。uboot通过tftp从ubuntu里面获取zimage和dtb,rootfs也是通过nfs挂在。
3、设置bootcmd和bootargs
setenv bootargs root=/dev/nfs nfsroot=192.168.3.112:/rootfs rw console=ttySAC2,115200 init=/linuxrc
setenv bootcmd tftp 41000000 uImage ; tftp 42000000 exynos4412-fs4412.dtb ; bootm 41000000 - 42000000
4、将编译出来的.ko文件放到根文件系统里面。
加载驱动会用到加载命令:insmod,modprobe。
移除驱动使用命令rmmod。对于一个新的模块使用modprobe加载的时候需要先调用一下depmod命令。
5,驱动模块加载成功以后可以使用lsmod查看一下。
6,卸载模块使用rmmod命令

三、字符设备的注册与注销
1、我们需要向系统注册一个字符设备,使用函数register_chrdev。
2、卸载驱动的时候需要注销掉前面注册的字符设备,使用函数unregister_chrdev,注销字符设备。

四、设备号
1,Linux内核使用dev_t。(用来保存设备号)
typedef __kernel_dev_t dev_t;
typedef __u32 __kernel_dev_t;
typedef unsigned int __u32;
2、Linux内核将设备号分为两部分:
主设备号和次设备号。
主设备号占用前12位,次设备号占用低20位。(根据不同内核占用位数不同)
3、设备号的操作函数,
a 、用宏从dev_t获取主设备号和次设备号
MAJOR(dev_t),
MINOR(dev_t)。
b、用函数把主设备号和次设备号构成dev_t,
MKDEV(major,minor)

五、file_operations的具体实现
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*mremap)(struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long,unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif};

主要注意ioctl,read,write,open,release函数实现使用

六、字符设备驱动框架的搭建

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
static int major = 200;
static int minor=0;
static dev_t devno;
static struct class *cls;
static struct device *test_device;
/* 寄存器物理地址 */
#define  GPX2CON    0x11000c40
#define  GPX2DAT    0x11000c44
#define  GPX1CON    0x11000c20
#define  GPX1DAT    0x11000c24
#define  GPF3CON    0x114001e0
#define  GPF3DAT    0x114001e4

/* 地址映射后的虚拟地址指针 */
static int *pgpx2con  ;
static int *pgpx2dat; 
static int *pgpx1con  ;
static int *pgpx1dat; 
static int *pgpf3con  ;
static int *pgpf3dat;

void fs4412_led_on(int num)
{ 
	switch(num)
	{  
		case 1:   writel(readl(pgpx2dat) |(0x1<<7), pgpx2dat);   break;  
		case 2:   writel(readl(pgpx1dat) |(0x1<<0), pgpx1dat);   break;     
	        case 3:   writel(readl(pgpf3dat) |(0x1<<4), pgpf3dat);    break;  
	        case 4:   writel(readl(pgpf3dat) |(0x1<<5), pgpf3dat);      break;   
	        default:   
	        fs4412_led_off(1);   
	        fs4412_led_off(2);   
	        fs4412_led_off(3);   
	        fs4412_led_off(4);  
	    	break;    
	  }
}
 void fs4412_led_off(int num)
 { 
 	switch(num) 
 	{  case 1:   writel(readl(pgpx2dat) &(~(0x1<<7)), pgpx2dat);   break;  
 	   case 2:   writel(readl(pgpx1dat)&(~(0x1<<0)), pgpx1dat);      break;     
 	   case 3:   writel(readl(pgpf3dat) &(~(0x1<<4)), pgpf3dat);    break;  
 	   case 4:   writel(readl(pgpf3dat) &(~(0x1<<5)), pgpf3dat);     break;   
 	}
} 
 static int led_open (struct inode *inode, struct file *filep)
 {
 	fs4412_led_off(1);
 	 fs4412_led_off(2); 
 	 fs4412_led_off(3); 
 	 fs4412_led_off(4); 
 	  return 0;
} 
static int led_release(struct inode *inode, struct file *filep)
{	
	fs4412_led_off(1); 
	fs4412_led_off(2); 
	fs4412_led_off(3); 
	fs4412_led_off(4);  
	return 0;
} 
static ssize_t led_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)
{	
 	return 0;
 } 
static ssize_t led_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos)
{ 
	int led_num; 
	if(len !=4) 
	{  
	return -EINVAL; } 
	if(copy_from_user(&led_num,buf,len)) 
	return -EFAULT;  
	fs4412_led_on(led_num); 
	printk("led_num =%d \n",led_num);  
	return 0;
}
//编写fop结构体
static struct file_operations hello_ops={ 
	.open     = led_open, 
	.release = le/d_release, 
	.read     = led_read, 
	.write    = led_write,
};
//编写加载函数
static void fs4412_led_init(void)
{ 
	pgpx2con = ioremap(GPX2CON,4);
	pgpx2dat = ioremap(GPX2DAT,4);  
	pgpx1con = ioremap(GPX1CON,4);
 	pgpx1dat =ioremap(GPX1DAT,4);  
 	pgpf3con  = ioremap(GPF3CON,4);
 	pgpf3dat =ioremap(GPF3DAT,4);  
	writel((readl(pgpx2con)& ~(0xf<<28)) |(0x1<<28),pgpx2con) ; 
	writel((readl(pgpx1con)& ~(0xf<<0)) |(0x1<<0),pgpx1con) ;  
	writel((readl(pgpf3con)& ~(0xff<<16)) |(0x11<<16),pgpf3con) ; 
} 
//编写卸载函数
static int led_init(void)
{ 
	int ret;  
	devno = MKDEV(major,minor); //存储设备号
	ret = register_chrdev(major,"led",&hello_ops);  
	cls = class_create(THIS_MODULE, "myclass"); 
	if(IS_ERR(cls)) 
	{ 
		unregister_chrdev(major,"led");  
		return -EBUSY; 
	} 
	test_device = device_create(cls,NULL,devno,NULL,"led");//自动创建设备节点
	if(IS_ERR(test_device)) 
	{  
		class_destroy(cls);  
		unregister_chrdev(major,"led"); 
		 return -EBUSY; 
	}  
	fs4412_led_init(); 
	return 0;
} 
void fs4412_led_unmap(void)
{ 
	iounmap(pgpx2con); 
	iounmap(pgpx2dat );  
	iounmap(pgpx1con); 
	iounmap(pgpx1dat );  
	iounmap(pgpf3con ); 
	iounmap(pgpf3dat );
} 
static void led_exit(void)
{ 
	fs4412_led_unmap(); 
	device_destroy(cls,devno); class_destroy(cls);  
	unregister_chrdev(major,"led"); 
printk("led_exit \n");
}
 MODULE_LICENSE("GPL");//指定许可
 module_init(led_init);//指定加载函数
 module_exit(led_exit);//指定卸载函数

七、测 试
1、加载驱动。
modprobe led.ko
2,进入/dev查看设备文件,
lsmod。/dev/chrdevbase。
3、测试

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值