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的亮灭.






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

  • 相关阅读:
    DNS服务器配置
    动态网站技术CGI
    SED单行脚本快速参考(Unix 流编辑器)
    xen 安静的角落
    IP命令
    oracle 监听文件 说明
    LRU ,LRUW,CKPT-Q
    重建控制文件ORA-12720
    历史备份控制文件恢复数据库
    增量检查点和完全检查点
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/4887607.html
Copyright © 2011-2022 走看看