第一、二期銜接——4.1 字符驅動設備之按鍵驅動—查詢方式

字符驅動設備之按鍵驅動


前言

  在本片博文中,編寫按鍵驅動,通過查詢的方式得到按鍵信息。

一、框架搭建

1、編寫驅動程序

static int button_drv_open(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t button_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
	return 0;
}

2、告訴內核有這個驅動程序

2.1 構建file_operations()結構體

static struct file_operations button_drv_fops = {
	.owner  =   THIS_MODULE,    /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
    .open   =   button_drv_open,     
	.read   =	button_drv_read,	
};

2.2 構建入口函數button_drv_init()

由內核自動分配主設備節點、自動創建設備節點

static struct class *buttondrv_class;
static struct class_device	*buttondrv_class_dev;
int major;

static int button_drv_init(void)
{
	major = register_chrdev(0, "button_drv", &button_drv_fops); // 註冊, 告訴內核

	buttondrv_class = class_create(THIS_MODULE, "buttondrv");
	buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/button */

	return 0;
}

2.3 構建出口函數button_drv_exit()

static void button_drv_exit(void)
{
	unregister_chrdev(major, "button_drv"); // 卸載

	class_device_unregister(buttondrv_class_dev);
	class_destroy(buttondrv_class);
}

3、修飾入口、出口函數並添加模塊的許可證聲明

module_init(button_drv_init);
module_exit(button_drv_exit);

MODULE_LICENSE("GPL");

4、完整的框架代碼

#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>

static struct class *buttondrv_class;
static struct class_device	*buttondrv_class_dev;

static int button_drv_open(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t button_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
	return 0;
}

static struct file_operations button_drv_fops = {
	.owner  =   THIS_MODULE,    /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
    .open   =   button_drv_open,     
	.read   =	button_drv_read,	
};

int major;
static int button_drv_init(void)
{
	major = register_chrdev(0, "button_drv", &button_drv_fops); // 註冊, 告訴內核

	buttondrv_class = class_create(THIS_MODULE, "buttondrv");
	buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/button */

	return 0;
}

static void button_drv_exit(void)
{
	unregister_chrdev(major, "button_drv"); // 卸載

	class_device_unregister(buttondrv_class_dev);
	class_destroy(buttondrv_class);
}

module_init(button_drv_init);
module_exit(button_drv_exit);

MODULE_LICENSE("GPL");

5、修改Makefile文件

KERN_DIR = /work/system/linux-2.6.22.6

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= button_drv.o

5、編譯驗證

  1. 通過服務器上傳到虛擬機,執行make
  2. 通過NFS網絡系統,把button_drv.ko文件上傳到開發版的根文件系統
  3. 加載驅動,觀察功能是否可以正常實現。

在這裏插入圖片描述

二、硬件操作

1、查看原理圖

在這裏插入圖片描述

由原理圖可知
按鍵S2-EINT0-GPF0                  按鍵S3-EINT2-GPF2
按鍵S4-EINT11-GPG3                 按鍵S5-EINT19-GPG11

2、查看s3c2440芯片手冊

在這裏插入圖片描述
在這裏插入圖片描述

3、代碼編寫

3.1 代碼分佈

  1. button_drv_init()中完成地址映射
  2. button_drv_open()中完成IO口設置爲輸入
  3. button_drv_read()中完成按鍵信息的讀取

3.2 驅動代碼

#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>

int major;
static struct class *buttondrv_class;
static struct class_device	*buttondrv_class_dev;

volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;

static int button_drv_open(struct inode *inode, struct file *file)
{
	/* 配置GPF0,2爲輸入 */
	*gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));

	/* 配置GPF3、11爲輸出 */
	*gpfcon &= ~((0x3<<(3*2)) | (0x3<<(11*2)));
	
	return 0;
}

static ssize_t button_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
	int key_values[4];	//0-gdf0, 1-gdf2, 2-gdg3, 3-gdg11
	int value;
	unsigned long ret = 0;

	/* 如果字節數不對,則返回 */
	if(nbytes != sizeof(key_values))
		return -;EINVAL
	
	/* 讀取GPF0,2 */
	value = *gpfdat;;
	key_values[0] = (value & (1<<0));
	key_values[1] = (value & (1<<2));

	/* 讀取GPG3,11 */
	value = *gpgdat;
	key_values[2] = (value & (1<<3));
	key_values[3] = (value & (1<<11));

	/* 數據給到用戶?*/
	ret = copy_to_user(buf, key_values, sizeof(key_values));
	if(ret < 0){
		printk("func button_drv_read() err: copy_to_user");
		return -EFAULT;
	}
	
 	return sizeof(key_values);
}

static struct file_operations button_drv_fops = {
	.owner  =   THIS_MODULE,    /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
    .open   =   button_drv_open,     
	.read   =	button_drv_read,	
};

static int button_drv_init(void)
{
	major = register_chrdev(0, "button_drv", &button_drv_fops);	//註冊一個字符設備,名字爲button_drv

	buttondrv_class = class_create(THIS_MODULE, "button_drv");	//創建一個類,名字爲buttond_rv(/class/button_drv)
	buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); //創建一個設備節點,名爲button(/dev/button)

	/* 映射物理地址 */
	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
	gpfdat = gpfcon + 1;
	gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
	gpgdat = gpfcon + 1;
	
	return 0;
}

static void button_drv_exit(void)
{
	unregister_chrdev(major, "button_drv"); // 卸載字符設備

	class_device_unregister(buttondrv_class_dev);	//刪除設備節點
	class_destroy(buttondrv_class);	//銷燬類
	
	/* 取消映射 */
	iounmap(gpfcon);
	iounmap(gpgcon);
}

module_init(button_drv_init);
module_exit(button_drv_exit);

MODULE_LICENSE("GPL");

3.3 測試代碼

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

/* buttondrvtest on
 * buttondrvtest off
 */
int main(int argc, char **argv)
{
	int fd;
	int ret = 0;
	int key_value[4] = {0};
	int count = 0;
	
	fd = open("/dev/button", O_RDWR);
	if (fd < 0){
		printf("can't open!\n");
		return -1;
	}

	while(1){
		ret = read(fd, key_value, sizeof(key_value));
		if(ret < 0){
			printf(" func read() err\n");
		}else{
			if(!key_value[0] || !key_value[1] || !key_value[2] || !key_value[3]){
				printf("%04d key pressed: %d %d %d %d\n",
					count++, key_value[0], key_value[1], key_value[2]
					       , key_value[3]);
			}
		}
	}
	
	return ret;
}

三、燒寫驗證

  1. 通過服務器上傳到虛擬機,執行make
  2. 通過NFS網絡系統,把button_drv.ko文件上傳到開發版的根文件系統。
  3. 加載驅動,觀察功能是否可以正常實現。
  4. 交叉編譯buttondrvtest.c文件,得到buttondrvtest.o文件上傳到開發版的根文件系統。
  5. 執行./buttondrvtest

可以看到正常運行。 在這裏插入圖片描述

四、查詢方式缺點

1、不確定性高

2、CPU佔用率大

在這裏插入圖片描述
可以看到,運行測試程序的時候CPU的佔用率到達了恐怖的99%,這是因爲測試程序一直在讀取鍵值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章