Linux设备驱动

1.驱动在Linux中有着比较重要地位,Linux中一切皆文件的思想,将所有的设备(而不仅是磁盘上的文件)全部看成文件,纳入文件系统的范畴。
每一项都至少由文件系统中的一个文件(更确切地说是节点)代表,因而都有一个“文件名”。每个这样的设备文件都惟一地确定了系统中的一项设备。应用程序通过设备的文件名寻访具体的设备,而设备则像普通文件一样受到文件系统访问权限控制机制的保护。
应用程序通常可以通过系统调用open()“打开”设备文件,建立起与目标设备的连接。代表着该设备的文件节点中记载着建立这种连接所需的信息,对于执行该应用程序的进程而言,建立起的连接就表现为一个已打开文件。
打开了代表目标设备的文件,即建立起与设备的连接以后,就可以通过read()、write()、ioctl()等常规文件操作对目标设备进行操作。从应用程序的角度看。设备文件逻辑上的空间是个线性空间。从这个逻辑空间到具体设备的物理空间的映射则由内核提供,并划分成文件操作与设备驱动两个层次。
这样,对于一个具体的设备来说,文件操作和设备驱动就成为同一事物的不同层次,而不是互相独立或平行的两个概念。

2.Linux将设备分成两大类。一类是块设备,一类是字符设备。
块设备:以磁盘那样以记录块或“扇区”为单位,成块进行输入/输出的设备。
字符设备:像键盘那样以字符(字节)为单位,逐个进行输入/输出的设备,称为“字符设备”。

3.驱动的连接
使用系统调用mknod(),在文件系统中创建一个代表此项设备的文件节点,使设备在系统中成为可见,使应用程序可以访问。
设备驱动程序的连接
设备驱动程序连接到系统有静态连接和动态连接两种方式。
静态连接:将驱动程序和系统一起编译生成。
动态连接:用户挑选一些事先已经编译好的模块,同时由系统生成工具对以高级语言编写的“设备表”加以修改,在表中加入相应驱动程序的函数指针,然后加以编译并将所挑选的模块与内核连接在一起。
/sbin/insmod程序,不但负责模块与内核的连接,也负责把模块的目标文件(.o文件)“装入到内核空间”。与/sbin/insmod相对应,还有一个应用程序/sbin/rmmod,其作用是将一个已安装的模块从内核中拆除。当然,像insmod一样,只能特权用户才能执行rmmod。

4.驱动实验
编写hello.c的模块代码

/* hello.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>


#include <linux/kernel.h>	/* printk() */
#include <linux/fs.h>	   
#include <linux/errno.h>	/* error codes */
#include <linux/types.h>	/* size_t */
#include <linux/cdev.h>
#include <asm/uaccess.h>	/* copy_to_user() */
#include <linux/slab.h>
#include <linux/uaccess.h>
/* function prototypes */
static int hello_open( struct inode *inode, struct file *filp );
static int hello_release( struct inode *inode, struct file *filp );
ssize_t hello_read( struct file *flip, char __user *buf, size_t count,loff_t
					*f_pos);
ssize_t hello_write( struct file *filp, const char __user *buf, size_t count,
					 loff_t *f_pos );
static int hello_major = 0;		/* major device number */
//模块的作者
MODULE_AUTHOR( "xiaobenzhu" );
//模块的版权  代码遵从两种协议BSD 和 GPL
MODULE_LICENSE( "Dual BSD/GPL" );


static struct cdev helloDev;	/* hello device structure */

/* file operations for hello device */
static struct file_operations hello_ops = {
	.owner = THIS_MODULE,
	.open = hello_open,
	.read = hello_read,
	.write = hello_write,
	.release = hello_release,
};
/* Open the device */
static int hello_open( struct inode *inode, struct file *filp ){
	printk( KERN_NOTICE"Hello device open!\n" );
	return 0;
}

/* Close hello_device */
static int hello_release( struct inode *inode, struct file *filp ){
	printk( KERN_NOTICE"Hello device close!\n" );
	return 0;
}

/* set up the cdev stucture for a device */
static void hello_setup_cdev( struct cdev *dev, int minor, struct
file_operations *fops ){
	int err;
	int devno = MKDEV( hello_major, minor );
	/* initialize the cdev struct */
	cdev_init( dev,fops );
	dev->owner = THIS_MODULE;
	dev->ops = fops;				 /* why not do it in cdev_init ? */
	err = cdev_add( dev, devno, 1 ); /* register the cdev in the kernel */
	if( err )
		printk( KERN_NOTICE"Error %d adding hello%d\n",err ,minor );
}

/* Module housekeeping */
static int hello_init(void){
	int result;
	dev_t dev = MKDEV( hello_major, 0 );

	/* alloc the major	device number dynamicly */
	result = alloc_chrdev_region(&dev, 0 ,1, "hello" );
	if( result < 0 ){
		printk( KERN_NOTICE"Hello: unable to get major %d\n",hello_major );
		return result;
	}
	hello_major = MAJOR(dev);
	/* set up devices, in this case, there is only one device */
	printk( KERN_NOTICE"hello init: %d, %d\n",hello_major,0 );
	//printk( KERN_ALERT"hello init: %d, %d\n",hello_major,0 );
	hello_setup_cdev(&helloDev, 0 , &hello_ops );
	
	return 0;
	
}

/* Exit routine */
static void hello_exit(void){
    /* remove the cdev from kernel */
	cdev_del(&helloDev );
	/* release the device numble alloced earlier */
	unregister_chrdev_region( MKDEV( hello_major, 0 ), 1 );
	printk( KERN_NOTICE"hello exit. major:%d,minor %d\n",hello_major,0 );
}

/* user read from hello device*/
ssize_t hello_read( struct file *flip, char __user *buf, size_t count,loff_t
					*f_pos){
	ssize_t retval = 0;
	char *bank;
	bank = kmalloc(count+1, GFP_KERNEL );
	if( bank == NULL )
		return -1;
	memset( bank, 'A',count );
	if( copy_to_user( buf, bank, count ) ){
		retval = -EFAULT;
		goto out;
	}
	retval += count;
	*(bank+count)=0;
	printk( KERN_NOTICE"hello: user read %d bytes from me. %s\n",count,bank );
  out:
	kfree(bank);
	return retval;
}

/* write to hello device */
ssize_t hello_write( struct file *filp, const char __user *buf, size_t count,
					 loff_t *f_pos ){
	ssize_t retval = 0;
	char *bank = kmalloc( count ,GFP_KERNEL );
	if( bank == NULL )
		return retval;
	if( copy_from_user(bank, buf, count ) ){
		retval = -EFAULT;
		printk( KERN_NOTICE"hello: write error\n" );
		goto out;
	}
	retval += count;
	printk( KERN_NOTICE"hello: user has written %d bytes to me: %s\n",count,
			bank );
  out:
	kfree(bank );
	return retval;
}

/* register the init and exit routine of the module */
//加载模块时 所执行的函数
module_init( hello_init );
//模块被卸载时 所执行的函数
module_exit( hello_exit );

MODULE_AUTHOR( “xiaobenzhu” ); 模块的作者
MODULE_LICENSE( “Dual BSD/GPL” ); 模块的版权
module_init( hello_init ); 加载模块时所执行的函数
module_exit( hello_exit ); 模块被卸载时所执行的函数

编写Makefile文件

#将hello.c编译成模块
obj-m:= hello.o
CURRENT_PATH:= $(shell pwd)
LINUX_KERNEL:= $(shell uname -r)
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)

all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

使用 make命令编译
在这里插入图片描述

使用insmod命令加载模块
在这里插入图片描述
使用 cat /dev/devices | grep hello 查看设备主设备号
在这里插入图片描述
mknod创建字符设备驱动文件hello0
在这里插入图片描述
编写测试程序

/* test_hello.c */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>

#define READ_SIZE 16

#define DEMO_DEV_NAME "/dev/hello0"

int main(int argc, char **argv){
	int fd,count;
	char buf[READ_SIZE+1];
	if( argc<2 ){
		printf( "[Usage: test device_name ]\n" );
		exit(0);
	}
	if(( fd=open(argv[1], O_RDWR))<0){
		printf( "Error:can not open the device: %s\n",argv[1] );
		exit(1);
	}
	printf("%s has been opened: (fd:%d).\n",argv[1],fd );
	if( (count = read(fd,buf,READ_SIZE ))<0 ){
		perror("read error.\n");
		exit(1);
	}
	printf( "read %d bytes from %s:%s\n",count,argv[1],buf );
	memcpy( buf,"Hello",6 );
	if( (count = write( fd, buf ,6 ))<0 ){
		perror("write error.\n");
		exit(1);		
	}
	printf( "write %d bytes to %s:%s\n",count,argv[1],buf );
	close(fd);
	printf("close device %s\n",argv[1] );
	return 0;
}


编译运行
在这里插入图片描述
使用dmesg命令查看系统日志
在这里插入图片描述

参考:https://www.cnblogs.com/xiaobenzhu/archive/2009/08/01/1536557.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值