(一)面向对象的编程思路--实例化对象-----封装----结构体封装
// 0,给hello对象申请空间----实例化对象
//将设备看成一个对象,封装成一个结构体
struct S5pv210_hello{
unsigned int major; //主设备号
struct class * cls; //类的指针
struct device *devi;//设备文件
};
struct S5pv210_hello *drv_hello;
drv_hello=kzalloc(sizeof(struct S5pv210_hello),GFP_KERNEL);
//参数1:申请空间的大小
//参数2:GFP_KERNEL到内核中请求空间
//返回值:返回申请到的空间地址
if(IS_ERR(drv_hello)){
printk("drv_hello kzalloc is error\n ");
ret=PTR_ERR(drv_hello);
return ret;
}
3》实现设备接口
实现设备接口:
int drv_hello_open(struct inode *inode, struct file *filp)
{
printk("------------%s--------------\n",__FUNCTION__);
return 0;
}
int drv_hello_close(struct inode *inode, struct file *filp)
{
printk("------------%s--------------\n",__FUNCTION__);
return 0;
}
const struct file_operations fops={
.open=drv_hello_open,
.release=drv_hello_close,
};
4》硬件初始化 --------------//地址映射和中断申请
1>地址映射:将物理空间的地址映射到虚拟空间
//实现地址映射的函数
void __iomem *ioremap (unsigned long phys_addr, unsigned long size)
//参数1:要映射的物理地址
//参数2:要映射的空间时多大----以字节为单位
//返回值:成功返回虚拟空间的地址,失败返回NULL
// 4,硬件初始化 ------地址映射
drv_led->gpc0_3con=ioremap (0xe0200060,8);
if(IS_ERR(drv_led->gpc0_3con)){
printk("led ioremap is error\n");
ret=PTR_ERR(drv_led->gpc0_3con);//获取NULL指针和野指针的错误码
goto device_err;
}
drv_led->gpc0_3dat=drv_led->gpc0_3con+1;
1>直接解引用
*drv_led->gpc0_3con &=~(0xf<<12);//实现清0
*drv_led->gpc0_3con |=0x1<<12;//实现置一
*drv_led->gpc0_3dat |=0x01<<3;
2>使用内核提供的函数
1,申请指定IO口的使用权
static inline int gpio_request(unsigned gpio, const char *label)
2,设置gpio为输入模式
static inline int gpio_direction_input(int gpio)
3,设置gpio为输出模式
static inline int gpio_direction_output(int gpio, int v)
4,获取gpio口的值
static inline int gpio_get_value(int gpio)
5,设置gpio的值
static inline void gpio_set_value(int gpio, int v)
6,释放gpio
static inline void gpio_free(unsigned gpio)
7,设备gpio为内部上拉或者下拉
int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull);
8,设置gpio为特殊功能
int s3c_gpio_cfgpin(unsigned int pin, unsigned int to);
3>内核中提供的标准函数接口
//实现将b写入addr
#define writeb(b,addr) (void)((*(volatile unsigned char *) (addr)) = (b))
#define writew(b,addr) (void)((*(volatile unsigned short *) (addr)) = (b))
#define writel(b,addr) (void)((*(volatile unsigned int *) (addr)) = (b))
//将addr数据读出
#define readb __raw_readb
#define readw(addr) __le16_to_cpu(__raw_readw(addr))
#define readl(addr) __le32_to_cpu(__raw_readl(addr))
reg_dat = readl(drv_led->gpc0_3dat);
reg_dat |=0x01<<3;
writel(reg_dat,drv_led->gpc0_3dat);
注意:Ubuntu中word替换命令
:%s/你要替换的单词/你要用什么单词替换/g 文中出现的全部替换
%s/hello/led/g 将文中出现的hello全部用led替换
:%s/你要替换的单词/你要用什么单词替换 文中每行出现的第一个替换
(二)应用空间和内核空间的数据传递
驱动开发和应用开发:
应用程序:实现策略--->去做什么
驱动程序:实现机制--->怎么做
1>应用空间的数据传递给内核空间--------实现write接口
应用空间:
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
内核空间 ---/dev/drv_hello
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);驱动的程序
//应用空间数据到内核空间不能直接使用-----需要转化
//下面此函数实现,将应用空间数据转化成内核空间的数据
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
//参数1:内核空间的地址----转换后放在哪里
//参数2:应用空间数据的起始地址
//参数3:转换多少个数据
//返回值:成功,返回0;失败:未转换成功的数据个数
ssize_t drv_led_write (struct file *filp, const char __user *buf, size_t size, loff_t *flags)
{
unsigned int on;
int ret;
printk("------------%s--------------\n",__FUNCTION__);
//将应用空间数据转化成内核空间的数据
ret=copy_from_user(&on,buf, size);
if(ret!=0){
printk("copy_from_user is error\n");
return ret;
}
if(on){
//开灯
*drv_led->gpc0_3dat |=0x01<<3;
}else{
//关灯
*drv_led->gpc0_3dat &=~(0x01<<3);
}
return size;
}
2>内核空间的数据传递给应用空间--------实现read接口
//内核空间数据到应用空间不能直接使用-----需要转化
//下面此函数实现,将内核空间数据转化成应用空间的数据
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
//参数1:应用空间数据的起始地址----转换后放在哪里
//参数2:内核空间的地址
//参数3:转换多少个数据
//返回值:成功,返回0;失败:未转换成功的数据个数
(三)ioctl在内核空间的实现
应用空间:
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
参数1:文件描述符
参数2:命令
参数3:变参,根据参数2决定是否有变参,该变参只能有一个参数
返回值:成功,0;失败,-1
如何定义ioctl命令:
//方法一:
#define LED_ALL_ON 0x111
#define LED_ALL_OFF 0x112
//方法二:
利用内核源码中提供的算法得到唯一的整数,来表示不同的命令
//ioctl只传递命令,不传递参数传递参数
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
//ioctl从内核空间向应用空间传递参数
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
//ioctl从应用空间向内核空间传递参数
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
参数1:整数,一般用设备的首字母来表示
参数2:整数,一般不要和当前模块的整数冲突
参数3:----ioctl中有没有第三个参数,表示第三参数的类型
(命令的定义方式已经决定了,ioctl传输数据的方向,如果定义命令时用的是_IOR(type,nr,size),代表内核空间向应用空间传递参数;如果你定义命令时用的是_IOW(type,nr,size),代表应用空间向内核空间传递参数。如果你定义命令时用的是_IO(type,nr),那就是不传递参数传递参数。)
int ioctl(fd,LED_ALL_ON);
#define LED_ALL_ON _IO(‘L’,0x1111)
#define LED_ALL_OFF _IO(‘L’,0x1131)
#define LED_NUM_ON _IOW(‘L’,0x1132,int)
#define LED_NUM_OFF _IOW(‘L’,0x1135,int)
内核空间:
long xxxx_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
{
switch(cmd){
case LED_ALL_ON:
break;
case LED_ALL_OFF:
break;
case LED_NUM_ON:
break;
}
}
驱动的实现程序
long drv_led_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
{
printk("------------%s--------------\n",__FUNCTION__);
switch(cmd){
case LED_ALL_ON:
*drv_led->gpc0_3dat |=0x03<<3;
break;
case LED_ALL_OFF:
*drv_led->gpc0_3dat &=~(0x03<<3);
break;
case LED_NUM_ON:
*drv_led->gpc0_3dat |=0x01<<args;
break;
case LED_NUM_OFF:
*drv_led->gpc0_3dat &=~(0x01<<args);
break;
default:
break;
}
return 0;
}