Linux嵌入式驱动开发06——第一个相对完整的驱动实践编写

文章目录

  • 全系列传送门
  • 设计目标
    • 分析
  • 代码

全系列传送门

Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)

Linux嵌入式驱动开发02——驱动编译到内核

Linux嵌入式驱动开发03——杂项设备驱动(附源码)

Linux嵌入式驱动开发04——应用层和内核层数据传输

Linux嵌入式驱动开发05——物理地址到虚拟地址映射

Linux嵌入式驱动开发06——第一个相对完整的驱动实践编写

Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)

Linux嵌入式驱动开发08——字符设备(步步为营)

Linux嵌入式驱动开发09——平台总线详解及实战

Linux嵌入式驱动开发10——设备树开发详解

Linux嵌入式驱动开发11——平台总线模型修改为设备树实例

Linux嵌入式驱动开发12——pinctl和gpio子系统实践操作

Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)

Linux嵌入式驱动开发14——中断的原理以及按键中断的实现(tasklet中断下文)

Linux嵌入式驱动开发15——等待队列和工作队列

Linux嵌入式驱动开发16——按键消抖实验(内核定时器)

Linux嵌入式驱动开发17——输入子系统

Linux嵌入式驱动开发18——I2C通信

设计目标

  1. 使用杂项设备完成一个蜂鸣器的驱动

  2. 完成一个上层测试应用
    应用要求:在上层应用中传入参数1为打开蜂鸣器,传入参数0为关闭蜂鸣器

分析

想要操作蜂鸣器,就要完成read函数 open函数,等等,我们做驱动,大部分情况下也都是使用这几个函数。

要完成上层应用的测试,就需要应用层和内核层传输数据,copy_to_user和copy_from_user

代码

beep.c

#include 
#include 
#include 
#include 
#include 
#include 

#define GPIO5_DR 0x020AC000

unsigned int *vir_gpio5_dr;

int misc_open (struct inode *inode, struct file *file){
    
    printk("hello misc_open!!!\n");
    return 0;
}

int misc_release(struct inode *inode, struct file *file){

    printk("bye bye misc_release!!!\n");

    return 0;

}

ssize_t misc_read(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t){
    
    char kbuf[64] = "copy to user!!!\n";

    if( copy_to_user(ubuf, kbuf, size) != 0 ){
        printk("copy_to_user error!!!\n");
        return -1;
    }

    printk("hello misc_read!!!\n");

    return 0;
}

ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t){
    
    char kbuf[64] = "copy from user!!!\n";
    printk("hello misc_write!!!\n");
    if( copy_from_user(kbuf, ubuf, size) != 0 ){
        printk("copy_from_user error!!!\n");
        return -1;
    }
    printk("buf is:%s\n", kbuf);
    
    if(kbuf[0] == 1){                       //对蜂鸣器的控制,如果是1,控制gpio口
        *vir_gpio5_dr |= (1 << 1);          //因为是gpio5的01,所以左移一位就可以,给一个高电平,使蜂鸣器工作
    }else if(kbuf[0] == 0){
        *vir_gpio5_dr &= ~(1 << 1);                   
    }

    return 0;
}

struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_release,
    .read = misc_read,
    .write = misc_write,
};

struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "hello_misc",
    .fops = &misc_fops
};


static int misc_init(void)
{
    int ret;

    ret = misc_register(&misc_dev);

    if(ret < 0){
        printk("misc_register failed!!!\n");
        return -1;
    }

    printk("misc_register succeed!!!\n");           // 在内核中无法使用c语言库,所以不用printf
    
    vir_gpio5_dr = ioremap(GPIO5_DR, 4);            // 物理地址到虚拟地址的映射

    if(vir_gpio5_dr == NULL){                       //如果映射失败
        printk("GPIO5_DR ioremap error!!!\n");
        return -EBUSY;                              //EBUSY是Linux的预留参数,前面加个负号
    }

    return 0;
}

static void misc_exit(void)
{
    misc_deregister(&misc_dev);

    iounmap(vir_gpio5_dr);                          //取消虚拟地址的映射

    printk("misc exit!!!\n");
}

module_init(misc_init);
module_exit(misc_exit);


MODULE_LICENSE("GPL");              //声明模块拥有开源许可

app.c

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    int fd;

    char buff[64] = {0};

    fd = open("/dev/hello_misc", O_RDWR);

    if(fd < 0){

        perror("open error\n");                // perror在应用中打印
        return fd;
    }

    buff[0] = atoi(argv[1]);                    //字符串转化成整形                            

    // read(fd, buff, sizeof(buff));
    write(fd, buff,sizeof(buff));               //在write中就传数据到底层,这样可以调用驱动的mis_write的操作了,直接操作 ./app 1或者./app 0

    // printf("buf is:%s\n", buff);
    
    close(fd);

    return 0;
}

你可能感兴趣的:(i.MX6,内核,linux)