zoukankan      html  css  js  c++  java
  • Linux 字符驱动程序(一)

    Linux 字符驱动程序(一)

    于linux有三个主要的内核设备:
    1 字符设备:   
         •字符设备的读写以字节为单位,存取时没有缓存。
         •对字符设备发出读写请求时。实际的硬件I/O紧接着就发生了。

    一般来说。字符设备不支持随机訪问。

         •典型的字符设备包含鼠标、键盘及串行口等。
    2 块设备:
         •块设备读写以块为单位,典型的块大小为512或1024字节。

         •利用一块系统内存作为缓冲区,当用户进程对设备发出读写请求时,驱动程序先察看缓冲区中的内容。若缓冲区中的数据能满足用户的要求就返回对应的数据,否则就调用对应的请求函数来进行实际的I/O操作,以提高效率。
         •块设备主要包含硬盘、软盘、CD-ROM等。
    3 网络设备:
         •Linux的网络系统主要基于BSD Unix的Socket机制。

    在系统和驱动程序之间定义有专门的数据结构进行数据的传递。系统里支持对发送数据和接收数据的缓存,提高流量控制机制。提供对多协议的支持。


    4 每一个设备相应一个文件。放在/dev文件夹下
    5 每一个设备文件都相应有两个设备号。存放在inode节点中
       •主设备号标示设备的种类,也标识了该设备所使用的驱动程序;
       •次设备号标识了使用同一设备驱动程序的不同硬件设备。
    6 能够通过/proc/devices 来查看对应的设备号。通过mknod  /dev/xxx c major minor 来产生设备节点。从而将设备挂接到/dev文件夹下。

    或者在编写驱动程序时动态的获取主设备号以及动态产生设备节点。


    7 以下详细分析一个led的驱动程序。(该程序沿用了2.6曾经版本号的驱动程序的书写方法,后面会介绍新的书写方法,可是本质是一样的)。
    声明为static是为了避免数据对内和造成污染,仅对该模块有效。
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <asm/uaccess.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    //相关的头文件,我们仿照其它模块载入就可以。

    #define DEVICE_NAME     "leds"  /* 载入模式后。运行”cat /proc/devices”命令看到的设备名称 */

    //用于自己主动产生设备节点
    static struct class *leds_class;
    static struct class_device * leds_class_devs[4];

    // LED的控制地址
    volatile unsigned long *gpfcon = NULL;
    volatile unsigned long *gpfdat = NULL;



    //应用程序运行open时调用该函数。
    static int first_drv_open(struct inode *inode, struct file *file)
    {
          int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev);

    switch(minor)
    {
           
      case 0: /* /dev/leds */
           
      {
                *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
    *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
                break;
            }


            case 1: /* /dev/led1 */
            {
               
                 *gpfcon &= ~((0x3<<(4*2));
    *gpfcon |= ((0x1<<(4*2));

                break;
            }


            case 2: /* /dev/led2 */
            {
                *gpfcon &= ~ (0x3<<(5*2));
    *gpfcon |= (0x1<<(5*2));

                break;
            }


            case 3: /* /dev/led3 */
            {
                *gpfcon &= ~(0x3<<(6*2));
    *gpfcon |= (0x1<<(6*2));

                
                break;
            }
            
    }
         

    return 0;
    }

    //应用程序运行write时调用该函数;
    static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
    {
        int minor = MINOR(file->f_dentry->d_inode->i_rdev); // 获取次设备号
        int val;
        copy_from_user(&val, buf, count);

        switch (minor)
        {
            case 0: /* /dev/leds */
            {            
                if (val == 1)
       {
    // 点灯
    *gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
       }
       else
       {
    // 灭灯
    *gpfdat |= (1<<4) | (1<<5) | (1<<6);
        }
                break;
            }


            case 1: /* /dev/led1 */
            {
                if (val == 1)
       {
    // 点灯
    *gpfdat &= ~(1<<4);
        }
        else
        {
    // 灭灯
    *gpfdat |= (1<<4);
        }
                break;
            }


            case 2: /* /dev/led2 */
            {
                if (val == 1)
        {
    // 点灯
    *gpfdat &= ~(1<<5);
        }
        else
        {
    // 灭灯
    *gpfdat |= (1<<5);
        }
                break;
            }


            case 3: /* /dev/led3 */
            {
                if (val == 1)
        {
    // 点灯
    *gpfdat &= ~(1<<6);
        }
        else
        {
    // 灭灯
    *gpfdat |= (1<<6);
         }
                break;
            }
            
        }

    return 0;
    }

    //驱动程序与内核的接口;
    static struct file_operations first_drv_fops = {
        .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自己主动创建的__this_module变量 */
        .open   =   first_drv_open,     
        .write =   first_drv_write,   
    };


    int major; //记录动态获取的设备号
    /*
     * 运行insmod命令时就会调用这个函数 
     */
    static int first_drv_init(void)
    {
          int minor = 0; //次设备号
          major = register_chrdev(LED_MAJOR, DEVICE_NAME, &first_drv_fops); //注冊设备

          if (major < 0) {
          printk(DEVICE_NAME " can't register major number ");
          return major;
         }

    leds_class = class_create(THIS_MODULE, "leds"); // 产生节点类,以leds_class声明的均为同一种设备
    if (IS_ERR(leds_class))
    return PTR_ERR(leds_class);
     
    leds_class_devs[0] = class_device_create(leds_class, NULL, MKDEV(major, 0), NULL, "leds");
    //产生不同的从设备,并以不同的名字挂接在/dev文件夹下。
    for (minor = 1; minor < 4; minor++)
    {
    leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(major, minor), NULL, "led%d", minor);
    if (unlikely(IS_ERR(leds_class_devs[minor])))
    return PTR_ERR(leds_class_devs[minor]);
    }
            
    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);//控制寄存器地址
    gpfdat = gpfcon + 1;  // 0x56000054  //数据寄存器地址。

            printk(DEVICE_NAME " initialized ");
    return 0;
    }

    /*
     * 运行rmmod命令时就会调用这个函数 
     */
    static void first_drv_exit(void)
    {
    int minor;
        /* 卸载驱动程序 */
           unregister_chrdev(major, DEVICE_NAME);


    for (minor = 0; minor < 4; minor++)
    {
    class_device_unregister(leds_class_devs[minor]);
    }
    class_destroy(leds_class);
    iounmap(gpfcon);
    }

    module_init(first_drv_init);
    module_exit(first_drv_exit);
    /* 描写叙述驱动程序的一些信息。不是必须的 */
    MODULE_AUTHOR("http://www.100ask.net");
    MODULE_VERSION("0.1.0");
    MODULE_DESCRIPTION("LED Driver");
    MODULE_LICENSE("GPL");

    我们写应用程序时。打开对应的设备/dev/leds , /dev/led1, /dev/led2, /dev/led3 向当中写1,0 就能够控制所有led或者某个led的亮灭.






    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    hihoCoder #1062 : 最近公共祖先·一
    hihoCoder #1050 : 树中的最长路
    hihoCoder #1049 : 后序遍历
    108 Convert Sorted Array to Binary Search Tree 将有序数组转换为二叉搜索树
    107 Binary Tree Level Order Traversal II 二叉树的层次遍历 II
    106 Construct Binary Tree from Inorder and Postorder Traversal 从中序与后序遍历序列构造二叉树
    105 Construct Binary Tree from Preorder and Inorder Traversal 从前序与中序遍历序列构造二叉树
    104 Maximum Depth of Binary Tree 二叉树的最大深度
    102 Binary Tree Level Order Traversal 二叉树的层次遍历
    101 Symmetric Tree 判断一颗二叉树是否是镜像二叉树
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/4887607.html
Copyright © 2011-2022 走看看