• led设备驱动(s3c_led.c)


    s3c_led.c分析:http://blog.csdn.net/hurry_liu/article/details/8770206
     
    1,注册设备号
    int register_chrdev_region(dev_t from, unsigned count, const char *name)
    动态分配设备号
    int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
    // 无法再安装驱动前创建设备文件,因为安装前没有分配设备号;安装驱动后,从/proc/devices中查询设备号
    释放设备号
    void unregister_chrdev_region(dev_t from, unsigned count)
     
    2,重要的结构体
    <1>struct file       <linux/fs.h>
    //系统中每个打开的文件在内核空间都有一个关联的struct file。打开文件时创建,关闭时创建
    重要成员:
    loff_t f_pos //文件读写位置
    struct file_operations *f_op
    void *private_data;
     
    <2>struct file_operations            <linux/fs.h>
    //操作函数指针的集合,每个指针表示用户空间能对设备文件进行的操作(其中的各函数是最主要的工作)
    重要成员:
    int (*open)(struct inode *, struct file *)  //初始化设备和标明次设备号
    void (*release)(struct inode *,struct file *) //关闭设备
    ssize_t (*read)(sruct file *flip, char __user *buff, size_t count, loff_t *offp) 
    //从设备中读取数据到用户空间,offp文件当前的访问位置 
    //buff指向数据缓存, 是指向用户空间,不能被内核代码直接引用 count传输的数据量  (这两个参数由用户空间提供)
    ssize_t (*write)(sruct file *, const char __user *buff, size_t, loff_t *)  //将数据传递给驱动程序
    int (*ioctl)(struct inode *, srtuct file *, unsigned int, unsigned long)    //控制设备
    off_t (*llseek)(struct file *, loff_t, int)
     
    <3>struct inode结构   
    //记录文件的物理上的信息,一个文件只有一个inode结构
    重要成员:
    dev_t  i_rdev;    //设备号(对于表示设备文件的inode)
    struct  *cdev *i_cdev;    //当inode指向字符设备文件时

    可以从inode中获取主次设备号

    MAJOR(dev_t dev)

    MINOR(dev_t dev)

    MKDEV(int major,int minor)   //通过主次设备号来生成dev_t

     

    <4>struct cdev结构                 <linux/cdev.h>
    //内核使用cdev结构体描述字符设备
    重要成员:

    struct module *owner;    //所属模块

    const struct file_operations *ops;  

    dev_t dev;          //设备号

    3,一些函数

    <1>用来对cdev结构操作的函数:

    void cdev_init(struct cdev *, const struct file_operations *);

    //初始化,建立cdev和file_operation 之间的连接

    struct cdev *cdev_alloc(void); //动态申请一个cdev内存

    void cdev_put(struct cdev *p); //释放

    int cdev_add(struct cdev *, dev_t, unsigned); //注册设备,通常发生在驱动模块的加载函数中

    void cdev_del(struct cdev *);//注销设备,通常发生在驱动模块的卸载函数中

     

    <2>内核提供专门的函数用于访问用户空间的指针

    int copy_from_user(void *to, const void __user *from,int n)   write()
    int copt_to_user(void __user *to, const void *from, int n)    read()
     
    <3>宏定义request_mem_region和ioremap     include/linux/ioport.h
    request_mem_region(S3C_GPB_BASE, S3C_GPB_LEN,DEV_NAME))
     /* 申请内存。注意:这里的内存是FL2440中实际的物理内存,他对应了与LED的相关的寄存器
    占用起始物理地址S3C_GPB_BASE之后的连续S3C_GPB_LEN字节大小空间
    该函数并没有做实际性的映射工作,只是告诉内核要使用一块内存地址,声明占有,也方便内核管理这些资源。*/
    fl2440_gpb_membase=ioremap(S3C_GPB_BASE, S3C_GPB_LEN)      ioremap()在mm/ioremap.c中定义,返回void型指针
    //主要是检查传入地址的合法性,建立页表(包括访问权限),完成物理地址到虚拟地址的转换。
    release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN)
    void iounmap(fl2440_gpb_membase);
     
    //一些宏定义
    __raw_writel((val),(reg)+s3c_gpb_membase)
    __raw_readll((reg)+s3c_gpb_membase)   //读操作和写操作寄存器
     
     
    4,led字符设备
    //字符设备用struct cdev来描述;struct cdev *led_cdev;
    加载模块int __init s3c_led_init():
    <1>硬件初始化:s3c_hw_init():
    申请内存request_mem_region()--> 建立物理内存到虚拟内存的映射ioremap()-->初始化硬件设备
    <2>申请设备号:
    <3>为cdev分配内存(定义为指针式需要)
    led_cdev = cdev_alloc();
    //可以将cdev结构嵌入到自己的设备特定结构中
    <4>初始化cdev  
    led_cdev->owner = THIS_MODULE;
    cdev_init(led_cdev, &led_fops);    //将led_cdev和file_operations挂钩
    <5>,添加cdev    
    result = cdev_add(led_dev, devno, dev_count); 
    //将设备号和设备挂钩   devno是设备号
    //在驱动程序准备好处理设备上的操作时,调用该函数
    <6>出错处理[函数]
     
    卸载模块s3c_led_exit():
    <1>调用s3c_hw_term()  :
    关闭led-->释放内存release_mem_region()-->解除映射关系iounmap()
    <2>注销设备:cdev_del()
    <3>释放设备号:unregister_chrdev_region()
     
    设备操作的实现
    定义struct file_operations led_fops =
    {
        .owner = THIS_MODULE;
        .open = led_open;
        .release = led_release;
        .unlocked_ioctl = led_ioctl;
    };
     
    三,arm开发板上
    tftp -gr s3c_hello.ko 192.168.1.3
    insmod,rmmod      //加载和卸载模块命令(root权限)
    lsmod     //列举内核模块及引用计数  cat /proc/modules
     
    insmod s3c_led.ko
    cat /proc/drivers
    cd dev/ 
    mknod -m666 c led0  252 0
    mknod -m666 c led1  252 1
    mknod -m666 c led2  252 2
    mknod -m666 c led3  252 3
    ./test_led /dev/led0 1     //点亮第一个灯
  • 相关阅读:
    c基础_笔记_1
    python基础_类型_str
    MySQL的btree索引和hash索引的区别
    最大最小距离算法
    vim代码格式化插件clang-format
    Linux文件检索
    linux下视频转gif
    linux下直接复制文件内容到剪切板
    vim中的分屏操作
    为archlinux终端ls不同类型文件设置不同显示颜色
  • 原文地址:https://www.cnblogs.com/zhoutian220/p/3965100.html
Copyright © 2020-2023  润新知