相關知識:Linux設備驅動開發
insmod 編譯好的.ko文件后再運行beep_app.c編譯完成的可執行文件即可使板子蜂鳴。
beep_drv.c:
#include <linux/module.h> //包含了加載模塊時需要使用的大量符號和函數聲明
#include <linux/kernel.h> //包含了printk內核打印函數等函數聲明
#include <linux/init.h> //包含了模塊加載函數和模塊釋放函數的宏定義
#include <linux/errno.h>
#include <linux/cdev.h>
#include<linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/uaccess.h>int devno_major=0;//beep的主設備號
int devno_minor=0;//beep的次設備號
struct class *beep_class;
struct device *beep_device;
struct resource *beep_resource;
void __iomem *gpioc_base_va;
void __iomem *gpiocout_va;
void __iomem *gpiocoutenb_va;
void __iomem *gpiocaltfn0_va;
void __iomem *gpiocaltfn1_va;
void __iomem *gpiocpad_va; module_param(devno_major,int,0660);
module_param(devno_minor,int,0660);enum beep_state{BEEP_OFF,BEEP_ON};int beep_state=BEEP_OFF;/*1、定義一個字符設備 ---> struct cdev*/
struct cdev beep_cdev;/*2、定義并初始化字符設備的文件操作集 ---> struct file_operations*/
int gec6818_beep_open(struct inode *inode, struct file *filp)
{printk("beep have been opened!\n");return 0;
}
ssize_t gec6818_beep_read (struct file *filp, char __user *user_buf,size_t size, loff_t *off)
{int r = copy_to_user(user_buf, (void*)&beep_state, size);if(r != 0)return EINVAL;return size;
}
ssize_t gec6818_beep_write (struct file *filp, const char __user *user_buf, size_t size, loff_t *off)
{int r = copy_from_user((void*)&beep_state, user_buf, size);if(r != 0)return EINVAL;if(beep_state == BEEP_ON)writel(readl(gpiocout_va)|(0x01<<14),gpiocout_va);elsewritel(readl(gpiocout_va)&(~(0x01<<14)),gpiocout_va);return size;
}
int gec6818_beep_release (struct inode *inode, struct file *filp)
{printk("beep have been closed!\n");return 0;
}static const struct file_operations gec6818_beep_fops = {.owner = THIS_MODULE,.open = gec6818_beep_open,.read = gec6818_beep_read,.write = gec6818_beep_write,.release = gec6818_beep_release
};//入口函數 -->安裝驅動
static int __init gec6818_beep_init(void)
{dev_t devno;//beep的設備號int ret=0;/*3、給字符設備申請一個設備號 ---> 設備號=主設備號<<20 + 次設備號*/if(devno_major != 0){devno = MKDEV(devno_major,devno_minor);if(register_chrdev_region(devno, 1, "gec6818_beep")){printk("register_chrdev_region error!\n");if(alloc_chrdev_region(&devno, devno_minor, 1, "gec6818_beep")){printk("alloc_chrdev_region error!\n");ret = EINVAL;goto alloc_chrdev_region_err;}}}else{if(alloc_chrdev_region(&devno, devno_minor, 1, "gec6818_beep")){printk("alloc_chrdev_region error!\n");ret = EINVAL;goto alloc_chrdev_region_err;}}devno_major = MAJOR(devno);devno_minor = MINOR(devno);/*4、初始化字符設備*/cdev_init(&beep_cdev, &gec6818_beep_fops);/*5、將字符設備加入內核*/if(cdev_add(&beep_cdev, devno, 1)<0){ret = EINVAL;goto cdev_add_err; }/*6、創建class*/beep_class = class_create(THIS_MODULE, "gec6818_beep");if(beep_class == NULL){ret = EBUSY;goto class_create_err;}/*7、創建device*/beep_device = device_create(beep_class, NULL,devno, NULL, "gec6818_beep");if(beep_device == NULL){ret = EBUSY;goto device_create_err;}/*8、申請物理內存區*/beep_resource = request_mem_region(0xC001C000, 0x1000, "GPIOC_MEM");if(beep_resource == NULL){ret = EBUSY;goto request_mem_region_err;}/*9、IO內存動態映射,得到虛擬地址*/gpioc_base_va = ioremap(0xC001C000, 0x1000);if(gpioc_base_va == NULL){ret = EBUSY;goto ioremap_err;}/*10、使用虛擬地址,把beep進行初始化*/gpiocout_va = gpioc_base_va + 0x00;gpiocoutenb_va = gpioc_base_va + 0x04;gpiocaltfn0_va = gpioc_base_va + 0x20;gpiocaltfn1_va = gpioc_base_va + 0x24;gpiocpad_va = gpioc_base_va + 0x18;//GPIOCALTFN0 &= ~(0x03<<28);//把GPIOC14設置為GPIO功能writel(readl(gpiocaltfn0_va)&(~(0x03<<28)),gpiocaltfn0_va);//GPIOCALTFN0 |= (0x01<<28);writel(readl(gpiocaltfn0_va)|((0x01<<28)),gpiocaltfn0_va);//GPIOCOUTENB |= 1<<14;//把GPIOC14設置為輸出模式writel(readl(gpiocoutenb_va)|((0x01<<14)),gpiocoutenb_va);//GPIOCOUT &= ~(1<<14);//把GPIOC14輸出低電平,默認beep不響writel(readl(gpiocout_va)&(~(0x01<<14)),gpiocout_va);printk("gec6818 beep init success!\n");return 0;
ioremap_err:release_mem_region(0xC001C000, 0x1000);
request_mem_region_err:device_destroy(beep_class, devno);
device_create_err:class_destroy(beep_class);
class_create_err:
cdev_add_err:unregister_chrdev_region(devno, 1);//注銷設備號
alloc_chrdev_region_err:return ret;
}//出口函數 -->卸載驅動
static void __exit gec6818_beep_exit(void)
{iounmap(gpioc_base_va);release_mem_region(0xC001C000, 0x1000);device_destroy(beep_class, MKDEV(devno_major,devno_minor));class_destroy(beep_class);unregister_chrdev_region(MKDEV(devno_major,devno_minor), 1);//注銷設備號printk("6818gec beep exit\n ");
}//驅動程序的入口: #insmod hello.ko ==> module_init() ==> gec6868_hello_init();
module_init(gec6818_beep_init);
//驅動程序的出口: #rmmod hello.ko ==> module_exit() ==> gec6818_hello_exit();
module_exit(gec6818_beep_exit);
//module的描述: #modinfo hello.ko
MODULE_AUTHOR("GEC_Liudehua@163.com");
MODULE_DESCRIPTION("beep driver for GEC6818");
MODULE_LICENSE("GPL");
MODULE_VERSION("V1.0");
?beep_app.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int beep_state;
int readbuf;int main()
{int fd = open("/dev/gec6818_beep",O_RDWR);if(fd == -1){printf("open failed!\n");return -1;}while(1){beep_state=1;int r = write(fd,&beep_state,4);if(r !=4 ){printf("write error!\n");}read(fd,&readbuf,4);printf("read buf is %d\n",readbuf);sleep(2);beep_state=0;write(fd,&beep_state,4);read(fd,&readbuf,4);printf("read buf is %d\n",readbuf);sleep(2);}
}
makefile:
#KERNELRELEASE這個變量,在內核源碼的根目錄下面的Makefile會初初始化的ifeq ($(KERNELRELEASE),)KERN_DIR := /home/china/6818GEC/kernel#在Makefile中可以調用shell的命令,調用方法如下:
# $(shell shell命令) -> 整個這個表達式,表示調用該shell命令的輸出結果
PWD := $(shell pwd)CROSS_COMPILE := /home/china/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-modules:make -C $(KERN_DIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules
clean:rm -rf *.orm -rf modules.order .tmp_versions *.ko Module.symversrm -f *.cmd .*.cmd *.mod.celseobj-m += beep_drv.oendif