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\n");

      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\n");

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









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

Linux 字符驱动程序(一)的更多相关文章

  1. linux字符驱动程序结构

    linux内核为字符设备的驱动程序设计,提供了一些数据结构,和函数,供开发人员调用,将设备驱动程序注册到内核去.现代操作系统几乎都不直接和硬件通信,而是通过定义的接口,是硬件厂商自己来开发符合标准某个 ...

  2. 简单linux字符设备驱动程序

    本文代码参考<LINUX设备驱动程序>第三章 字符设备驱动程序 本文中的“字符设备”是一段大小为PAGE_SIZE的内存空间 功能:向字符设备写入字符串:从字符设备读出字符串 代码: 1. ...

  3. ARM Linux字符设备驱动程序

    1.主设备号和次设备号(二者一起为设备号): 一个字符设备或块设备都有一个主设备号和一个次设备号.主设备号用来标识与设备文件相连的驱动程序,用来反  映设备类型.次设备号被驱动程序用来辨别操作的是哪个 ...

  4. 浅析Linux字符设备驱动程序内核机制

    前段时间在学习linux设备驱动的时候,看了陈学松著的<深入Linux设备驱动程序内核机制>一书. 说实话.这是一本非常好的书,作者不但给出了在设备驱动程序开发过程中的所须要的知识点(如对 ...

  5. Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】

    本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...

  6. Smart210学习记录----beep linux字符设备驱动

    今天搞定了beep linux字符设备驱动,心里还是很开心的,哈哈...但在完成的过程中却遇到了一个非常棘手的问题,花费了我大量的时间,,,, 还是把问题描述一下吧,好像这个问题很普遍的,网上许多解决 ...

  7. linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)

    原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...

  8. 【转】linux设备驱动程序中的阻塞机制

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...

  9. Linux 字符设备驱动模型

    一.使用字符设备驱动程序 1. 编译/安装驱动 在Linux系统中,驱动程序通常采用内核模块的程序结构来进行编码.因此,编译/安装一个驱动程序,其实质就是编译/安装一个内核模块 2. 创建设备文件 通 ...

随机推荐

  1. Balsamiq Mockups注册码

    Name: personalKey: eJzzzU/OLi0odswsqilILSrOz0vMqbFEAjXONYY1fu6ufgA/CA4X Name: helloWorldKey: eJzzzU/ ...

  2. 关于cocos2dx3.0 UITextField不能使用退格键删除字符的解决方式

    近日開始将项目移植到cocos2dx 3.0版本号,出现了一些问题,UI方面眼下就发现UITextField控件不能响应退格键或者删除键,在Windows以下调试如此,我開始以为是平台支持不好,后来公 ...

  3. HTML 5最终确定,八年后,我们再谈谈如何改变世界

    从原:http://www.36kr.com/p/216655.html 我们第一次谈论HTML5要改变世界大概是由于乔布斯,他坚持在iOS上不兼容Flash,在Adobe统治多媒体开发的那个年代.这 ...

  4. Java&Android反编工具打包

    Java&Android反编工具: 1.Eclipse反编插件:安装到Eclipse后,可以简要的查看jar包中的*.class; 2.DoAPK:反编*.apk为smali和一些资源文件,可 ...

  5. 状态压缩dp入门

    poj1321 http://poj.org/problem?id=1321 我们可以把棋盘的每一行看做是一个状态,如果某一列放置了棋子,那么就标记为1,否则就标记为0.然后把它看成是一个二进制数,然 ...

  6. [Django](1093, &quot;You can&#39;t specify target table &#39;fee_details_invoices&#39; for update in FROM clause&quot;) 错误

    dele_id = Fee_details_invoices.objects.filter(fee_detail_id__in=fee_id_list, return_type='2').values ...

  7. 查看文章strncpy()功能更好的文章

    strncpy()功能 原型:extern char *strncpy(char *dest, char *src, int n);    使用方法:#include <string.h> ...

  8. 数据市中心全省中国mysql脚本

    1.查尔斯省 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2h6aGFvY2hhbw==/font/5a6L5L2T/fontsize/400/fill ...

  9. 【Android进阶】在开发中Application到底用来做什么

    首先,在一个Android程序中,有且只有一个Application对象,在程序开始跑起来的时候,首先执行Application的onCreate方法,这是一个Android应用的入口,在开发中,我们 ...

  10. 故障排查:是什么 导致了服务器端口telnet失败?(转)

    telnet命令的主要作用是与目标端口进行TCP连接(即完成TCP三次握手).当服务端启动后,但是telnet其监听的端口,却失败了.或者,当服务端运行了一段时间后,突然其监听的端口telnet不通了 ...