天天看點

簡單的linux雜項裝置驅動的寫法

linux将裝置驅動分成幾大類:字元裝置、雜項裝置、塊裝置、網絡裝置······

本篇文章介紹雜項裝置驅動的編寫,雜項裝置與字元裝置本質上沒什麼差別,但是寫法和相關函數的使用上有差別。

除此之外雜項裝置主裝置号都為10,裝置間通過次裝置号來進行區分,與字元裝置相比節約了主裝置号。

雜項裝置驅動編寫模式一般如下:

在linux系統下一切皆檔案,裝置驅動同樣秉承此“”大法“”。

對檔案操作就少不了打開、讀寫、關閉等操作。

是以雜項裝置驅動第一步就是進行檔案操作函數的編寫。

驅動子產品最終需要加載到核心上運作,核心對檔案的操作在内部有标準的系統調用,

那麼怎樣将自己寫的檔案操作函數挂接到系統标準的函數調用上呢?

這就是檔案操作集合這個結構體所要做的工作了。

首先看一下該結構體的結構

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
};
           

其實我們仔細觀察,該結構體裡除了第一個成員外,其餘的成員都是函數指針,當我們在進一步觀察。原來他們當中很多都很熟悉。

有許多跟原來經常調用的檔案操作函數很類似,其實他們作用就是将自己書寫的檔案操作函數與系統标準檔案操作函數調用銜接起來。

也可以把他們說成是檔案操作的函數的聲明,隻有在這個結構體裡聲明過的函數,在對驅動操作時才能使用該函數。

什麼意思呢?

如果我們沒有在這個結構體中聲明read,也就是讀取檔案的函數,我們在調用該驅動子產品時就不能對該子產品進行讀操作。

該結構體成員很多,我們不必全部都要聲明,我們在實際使用時,用到那個寫哪個就可以了。

例如我們隻寫一個簡單的LED燈的驅動,隻涉及簡單的IO控制,是以其餘的函數對我們沒有意義,我們就可以不寫。

對于第一個成員owner,它的定義有很多,在早期簡單的驅動編寫中,我們統一将它指派“”THIS_MODULE“”。

可以防止驅動在運作中被意外解除安裝。至于其他用法自己可以在以後深入學習中慢慢探索。

寫完檔案操作集合,還有另外一個重要結構體miscdevice,它記錄了該驅動裝置的相關資訊,為裝置注冊提供必要資訊。

struct miscdevice  {
	int minor;
	const char *name;
	const struct file_operations *fops;
	struct list_head list;
	struct device *parent;
	struct device *this_device;
	const char *nodename;
	umode_t mode;
};
           

對于我們比較關注的就是前三項

minor 次裝置号,若果讓系統自動配置設定裝置号 應指派為MISC_DUNAMIC_MINOR

name 裝置名稱

fops 操作集合的聲明 将檔案操作集合結構體位址指派給它即可

将以上内容完成後,就是驅動的初始化和解除安裝函數的書寫。

子產品初始化函數中主要完成的内容,主要是進行雜項裝置的注冊。

子產品解除安裝函數,主要是對雜項裝置的解除安裝。

最後聲明子產品的初始化、解除安裝函數的入口。聲明遵循的開源協定。

最後可編寫一個控制函數驗證驅動的正确性

下面附上一段示例代碼,此段并沒有實質性操作,隻簡單示範雜項裝置驅動編寫流程

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/miscdevice.h>
#include<linux/fs.h>
#include<linux/export.h>

//打開函數
static int  misc_open(struct inode *node, struct file *fp)
{
    printk("this dev is open\r\n");
	return 0;
}
//關閉函數
static int misc_close(struct inode *node, struct file *fp)
{
    printk("this dev is close\r\n");
	return 0;
}

//讀函數
ssize_t misc_read(struct file *fp, char __user *buf, size_t size, loff_t *loff)
{
   printk("this dev is read\r\n");
   return 0;
}

//寫函數
ssize_t misc_write(struct file *fp, const char __user *buf, size_t size, loff_t *loff)
{
  printk("this dev is write\r\n");
  return 0;
}
           
//檔案操作集合
struct file_operations fops={
  .owner=THIS_MODULE,
  .open=misc_open,
  .read=misc_read,
  .write=misc_write,
  .release=misc_close,
};
  //裝置相關資訊
struct miscdevice mymisc={
  .minor=MISC_DYNAMIC_MINOR,
  .name="mymisc",
  .fops=fops,
};

//驅動初始化
static int __init misc_init(void)
{
  if((misc_register(&mymisc))
  	{
    printk("this module is insmod fail\r\n");
	return -1;
  }
  printk("this module is success\r\n");
  return 0;
}
//驅動解除安裝
static void __exit misc_exit(void)
{
  printk("this module is exit\r\n");
}

module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");
           

驅動控制函數代碼

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

//主函數  參數為:傳入裝置名稱(含有路徑)
int main(int argc,char *argv[])  
{
	int fd = open(argv[1],O_RDWR);
	if(fd == -1)
	{
		printf("打開失敗\n");
		return 0;
	}
	read(fd,buf,0);
	write(fd,NULL,0);
	close(fd);
	return 0;
}
           

驅動控制代碼的作用主要是将,裝置節點打開對其進行讀寫關閉等操作(這裡沒有實質操作,隻列印一句話),隻是來驗證驅動架構的正确性

makefile代碼如下:

KERN_DIR = /zhangchao/linux3.5/linux-3.5
all:
	make -C $(KERN_DIR) M=`pwd` modules
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
obj-m += misc.o
           

編譯子產品

[[email protected] zhangchao]# make
make -C /zhangchao/linux3.5/linux-3.5 M=`pwd` modules
make[1]: 進入目錄“/zhangchao/linux3.5/linux-3.5”
  Building modules, stage 2.
  MODPOST 1 modules
make[1]: 離開目錄“/zhangchao/linux3.5/linux-3.5”
[[email protected] zhangchao]# ls
Makefile    misc.c   misc.mod.c  misc.o         Module.symvers
misc_app.c  misc.ko  misc.mod.o  modules.order
[[email protected] zhangchao]# 
           

可以看到.ko子產品檔案已經生成

安裝子產品

[[email protected]/zhangchao]#insmod misc.ko 
[  857.560000] this module is success
           

列印相關資訊子產品安裝成功

檢視子產品

[[email protected]/zhangchao]#lsmod
Module                  Size  Used by    Tainted: G  
misc                    1440  0 
           

子產品資訊存在,子產品已經成功安裝

編譯驅動控制代碼

[[email protected] zhangchao]# arm-linux-gcc misc_app.c -o run
[[email protected] zhangchao]# ls
main.c    man         misc.c   misc.mod.c  misc.o         Module.symvers
Makefile  misc_app.c  misc.ko  misc.mod.o  modules.order  run
           

編譯沒有報錯,生成了可執行檔案 run

檢視裝置

[[email protected]/zhangchao]#ls -al /dev/mymisc 
crw-rw----    1 root     root       10,  47 Mar 16 17:00 /dev/mymisc
           

裝置已經存在,證明雜項裝置注冊成功,在裝置清單内部能夠檢視到裝置。主裝置号10 次裝置号47

運作控制 程式,傳入參數為注冊的裝置節點,控制程式可以通過裝置節點對裝置進行讀寫等操作。(在開發闆上執行)

[[email protected]/zhangchao]#./run /dev/mymisc 
[ 1487.635000] this dev is open
[ 1487.635000] this dev is read
[ 1487.635000] this dev is write
[ 1487.635000] this dev is close
[[email protected]/zhangchao]#
           

驅動解除安裝

[[email protected]/zhangchao]#rmmod misc.ko 
[ 1577.995000] this module is exit
           

子產品編譯,控制程式的編譯在主控端上進行,開發闆上隻有核心,沒有編譯工具,子產品的裝載、檢視、解除安裝在開發闆上運作。

主控端: VMware12版本下的CentOS7

開發闆: 友善之臂Tiny4412開發闆

交叉編譯器: arm-linux-gcc 4.5.1

核心版本:linux-3.5

繼續閱讀