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\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 字符驱动程序(一)

时间: 2024-07-29 01:22:50

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

小白的linux字符驱动程序

关于如何编译一个测试型的字符驱动程序,网上一搜还是很多的在此给出一个不错的教程http://blog.chinaunix.net/uid-11829250-id-337300.html 我主要是在搜索ioctl的时候才有自己编写一个字符驱动的想法,因为刚工作的时候就看到有同事在用ioctl,当时在网上搜了下ioctl也没怎么明白.现在才发现原来ioctl就是对应的设备驱动程序里的ioctl函数.好了,废话就不多说了.写这篇文档的主要意义在于给后来编写驱动程序的新手们提示几个可能会遇到的问题,希望

20150216简单的Linux字符设备驱动程序

20150216简单的Linux字符设备驱动程序 2015-02-16 李海沿 关于字符设备驱动程序详细的知识点,本文就不再介绍了,很多同志,看了知识点,还是一头雾水,写不出来,所以,本文从实战出发,带领各位同胞们来实现一个字符设备驱动程序,改程序可作为字符设备的通用模板. 好了废话不多说,先上驱动程序,在驱动程序中加入详细注释: 1 /****************************** 2 linux 字符设备驱动程序 3 *****************************/

LINUX设备驱动程序笔记(三)字符设备驱动程序

      <一>.主设备号和次设备号        对字符设备的访问时通过文件系统内的设备名称进行的.那些设备名称简单称之为文件系统树的节点,它们通常位于/dev目录.字符设备驱动程序的设备文件可通过ls -l命令输出的第一列中的'c'来识别.块设备同样位于/dev下,由字符'b'标识 crw-rw----  1 root root    253,   0 2013-09-11 20:33 usbmon0 crw-rw----  1 root root    253,   1 2013-09

Linux 设备驱动程序 字符设备

已经无法再精简,适合入门. 1 #include<linux/module.h> 2 #include<linux/init.h> 3 4 #include<asm/uaccess.h> 5 #include <linux/types.h> 6 #include<linux/fs.h> 7 #include<linux/cdev.h> 8 struct mengc_dev{ 9 char data[64]; 10 struct cde

linux字符设备驱动程序框架

#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/a

linux字符设备驱动程序框架(老方法)

#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/a

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

前段时间在学习linux设备驱动的时候,看了陈学松著的<深入Linux设备驱动程序内核机制>一书. 说实话.这是一本非常好的书,作者不但给出了在设备驱动程序开发过程中的所须要的知识点(如对应的函数和数据结构),还深入到linux内核里去分析了这些函数或数据结构的原理.对设备驱动开发的整个过程和原理都分析的非常到位.但可能是因为知识点太多.原理也比較深的原因,这本书在知识点的排版上跨度有些大.所以读起来显得有点吃力,可是假设第一遍看的比較认真的话,再回头看第二次就真的可以非常好地理解作者的写作思

linux字符设备驱动程序源码分析

本文主要分析linux-2.6.28内核版本的字符设备抽象层源码文件char_dev.c.该文件代码量不大,但其为linux应用程序访问实际字符型硬件设备搭建了桥梁,进一步限定了linux字符设备驱动的设计框架. void __init chrdev_init(void) {  cdev_map = kobj_map_init(base_probe, &chrdevs_lock);  bdi_init(&directly_mappable_cdev_bdi); } 该函数在系统初始化启动时

linux字符设备驱动

一.字符设备.字符设备驱动与用户空间访问该设备的程序三者之间的关系. 如图,在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主.次设备号)以确定字符设备的唯一性.通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open().read().write()等. 在Linux字符设备驱动中,模块加载函数通过register_chrdev_region( ) 或alloc_chrdev_region( )来静态或者动态获