初级驱动DAY3

(一)面向对象的编程思路--实例化对象-----封装----结构体封装

    // 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;
        }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Lewis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值