Linux字符设备驱动——经典标准字符模型

本文介绍了Linux2.6之前的经典标准字符模型,该模型不自动创建设备节点,需手动用mknod。主设备号注册后占用0~255次设备号,而杂项设备只占1个。注册和注销函数分别为register_chrdev和unregister_chrdev。通过实例展示了设备节点创建及测试次设备号占用情况。
摘要由CSDN通过智能技术生成

经典标准字符模型,即为Linux2.6之前的早期经典标准字符模型。

特征

  1. 没有使用一个核心的结构体,把需要的信息进行封装
  2. 安装驱动后,不会在/dev/目录下创建设备节点,需要使用mknod创建。
  3. 一个主设备号只能被注册一次,一次注册0~255的次设备号就被占用了。
    杂项设备,每次注册,只占用了一个次设备号,(主设备号固定为10)

头文件

#include<linux/fs.h>

注册函数

注册一个标准字符设备
int register_chrdev(unsigned int major,const char *name, const struct file_operations *fops)
参数:

major:	主设备号
mane:	设备名,不需要和/dev 下对应节点名相同
fops:	文件操作集合

返回值
(1)、当major为0时,表示动态分配
成功:返回分配的主设备号
失败:返回负数

(2)、当major>0时,表示静态注册,使用用户定义的主设备号
成功:返回0
失败:返回负数

注销函数

void unregister_chrdev(unsigned int major, const char *name)
参数

major:主设备号
name:设备名,使用register_chrdev 注册的设备名。
返回值:无

设备号,设备驱动节点需要手动创建

主设备号:0~255,10为杂项设备号
次设备号:0~255

注意:
早期经典字符设备不会在dev目录下创建设备驱动节点,需要用户手动创建,并且注册后占用一个主设备号,因此次设备号被全部占用。

注册字符设备后,都会在/proc/decices目录下出现主设备号。

创建主设备号250,次设备号100的设备节点
mknod /dev/abc c 250 100

打印等级,数字越小,等级越高
查看开发板等级
cat /proc/sys/kernel/prink

早期模型,注册了主设备号,就会占用其下的次设备号。

经典标准字符驱动模型

驱动程序zx_chrdev_led.c源代码

#include <linux/module.h>       //驱动模块头文件 
#include <linux/init.h>            
#include <linux/fs.h>
#include <asm/io.h>             //开发板引脚支持头文件
#include <asm/uaccess.h>        //copy_from_user ,copy_to_user
#include<linux/gpio.h>          // GPIO头文件

#define DEV_NAME "zx_chrdev_led"
#define LED_NUM     (4)

static unsigned int LED_pin[LED_NUM] = {
	EXYNOS4X12_GPM4(0),
	EXYNOS4X12_GPM4(1),
	EXYNOS4X12_GPM4(2),
	EXYNOS4X12_GPM4(3),	
};

static unsigned int major = 0;  //动态配置设置0,否则设置正数1~255 (自己保证可用)
static  char * pchrdev_name = DEV_NAME;

static ssize_t chrdev_read (struct file *pfile, char __user *buff, size_t size, loff_t *off)
{
    printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
    return 0;
}

static ssize_t chrdev_write(struct file *filp,
                          const char __user *user_buf,
                          size_t count,
                          loff_t *off)
{
    int ret = 0;
    char buf[LED_NUM] = {0};
    int i = 0;
	
    printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
    
    if(count == 0){
        return 0;
	}
   
    if(count > LED_NUM){  //因为板子只有4盏灯
        count = LED_NUM;
	}

    ret = copy_from_user(buf, user_buf, count); //用户空间传数据到内核空间
    if(ret){
        return ret;
	}

    for(i = 0; i < count; i++) 
	{
        if(buf[i] == 1){
            gpio_set_value(LED_pin[i],0);/* 亮 */

        }else if(buf[i] == 0){
            gpio_set_value(LED_pin[i],1);/* 灭 */
		}
    }
	
    return count;
}

static int  chrdev_open(struct inode *pinode, struct file *pfile)
{
    printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);

    return 0;
}

static int chrdev_release(struct inode *pinode, struct file *pfile)
{
    printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);

    return 0;
}

static const  struct file_operations chrdev_fops = {
    .read     = chrdev_read,
    .write    = chrdev_write,
    .release  = chrdev_release,
    .open     = chrdev_open,
};

static int __init zx_chrdev_led_init(void) 
{
    int ret;
	int i;

	for(i = 0;i < LED_NUM; i++)
	{	
		ret = gpio_request(LED_pin[i],"leds");
		if(ret < 0){
			printk(KERN_EMERG "gpio_request is error\n");
			goto gpio_request_err;
		}
		
		gpio_direction_output(LED_pin[i],1); //四盏灯设置为输出功能,并且输出高电平
	}

	if(major){
		ret = register_chrdev(major,pchrdev_name,&chrdev_fops);  //静态分设备号配方式
		if ( ret < 0 ){
			printk(KERN_EMERG "register_chrdev  error\n");
			
			goto register_chrdev_err;
		}else{		   
			major = register_chrdev(0, pchrdev_name, &chrdev_fops); //动态分配设备号方式
			if ( major < 0 ){
				ret = major;
				printk(KERN_EMERG "register_chrdev  error\n");
				
				goto register_chrdev_err;
			}
	}	

	printk(KERN_EMERG "major:%d\n",major);
	printk(KERN_EMERG "%s is OK!!!\n",__FUNCTION__);
		
    return 0;
	
register_chrdev_err:

gpio_request_err:
	for(--i; i >= 0; i--)
	{
		gpio_free(LED_pin[i]);
	}
	
	return ret ;
}

static void  __exit zx_chrdev_led_exit(void)
{
	int i = 0;
	
	for(i=0;i<4;i++)
	{
		 gpio_set_value(LED_pin[i],1);  //灭灯
		 gpio_free(LED_pin[i]);    		//释放GPIO
	}
	
    unregister_chrdev(major, pchrdev_name);
	
    printk(KERN_EMERG "Goodbye,zx_chrdev_led\n");
}

module_init(zx_chrdev_led_init);
module_exit(zx_chrdev_led_exit);
MODULE_LICENSE("GPL");

应用程序app.c源代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define DEV_NAME  "/dev/zx_chrdev_led"
#define LED_NUM   (4)

int main(void)
{
    char buf[] = {1, 0, 0, 0}; //1£¬ÁÁ£¬0±íʾÃð£¬
    int i = 0;
    int fd;

    fd = open(DEV_NAME, O_RDWR);
    if(fd < 0){
        printf("open :%s failt!\r\n", DEV_NAME);
	}

    while(1) 
	{
        write(fd, buf, LED_NUM);
        memset(buf, 0, LED_NUM);
        buf[i++ % LED_NUM] = 1;
		
        sleep(1);
    }

    return 0;
}

对应的Makefile只需修改杂项设备对应的Makefile,修改驱动的名字就可以。

实例代码测试

[root@ZX20150811 /home]# ls
app               zx_chrdev_led.ko
[root@ZX20150811 /home]# insmod zx_chrdev_led.ko 
[ 3643.345000] major:249
[ 3643.345000] zx_chrdev_led_init is OK!!!
[root@ZX20150811 /home]# mknod /dev/zx_chrdev_led c 249 1
[root@ZX20150811 /home]# ./app 
[ 3740.410000] line:61,chrdev_open is call
[ 3740.410000] line:36,chrdev_write is call
[ 3741.410000] line:36,chrdev_write is call
[ 3742.410000] line:36,chrdev_write is call
[ 3743.410000] line:36,chrdev_write is call
[ 3744.410000] line:36,chrdev_write is call
[ 3745.410000] line:36,chrdev_write is call
[ 3746.410000] line:36,chrdev_write is call
^C[ 3746.990000] line:68,chrdev_release is call

[root@ZX20150811 /home]#

这里的次设备号为1,因为经典标准字符模型一旦注册了主设备号,这个主设备号下的次设备号都被占用。下面测试其它的次设备号。

测试次设备号是否是全部占用

[root@ZX20150811 /home]# rmmod zx_chrdev_led.ko 
[ 3879.105000] Goodbye,zx_chrdev_led
[root@ZX20150811 /home]# rm -f /dev/zx_chrdev_led
[root@ZX20150811 /home]# ls
app               zx_chrdev_led.ko
[root@ZX20150811 /home]# insmod zx_chrdev_led.ko 
[ 3913.425000] major:249
[ 3913.425000] zx_chrdev_led_init is OK!!!
[root@ZX20150811 /home]# mknod /dev/zx_chrdev_led c 249 23
[root@ZX20150811 /home]# ./app 
[ 3950.420000] line:61,chrdev_open is call
[ 3950.420000] line:36,chrdev_write is call
[ 3951.420000] line:36,chrdev_write is call
[ 3952.420000] line:36,chrdev_write is call
[ 3953.420000] line:36,chrdev_write is call
[ 3954.420000] line:36,chrdev_write is call
^C[ 3954.925000] line:68,chrdev_release is call

[root@ZX20150811 /home]#

结果可以知道,换个次设备号应用程序依然能够识别并使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值