Linux应用层想要操作kernel层的API,比方想操作相关GPIO或寄存器,能够通过写一个字符设备驱动来实现。
1、先在rootfs中的 /dev/ 下生成一个字符设备。注意主设备号 和 从设备号。可用例如以下shell脚本生成:
if [ ! -e audioIN ];then sudo mknod audioIN c 240 0 fi
生成的设备为 /dev/audioIN ,主设备号240,从设备号0。
2、写audioINdriver.ko ,audioINdriver.c 基本代码框架例如以下:代码中定义了设备名audioIN,设备号240, 0 ,与之前创建的设备一致。
/************************************************************************** * audioINdriver.c * * kang_liu <liukang325@qq.com> * 2014-07-15 **************************************************************************/ #include <asm/uaccess.h> #include <asm/errno.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/module.h> #include <linux/cdev.h> #include <mach/gpio.h> //#include <mach/at91_rstc.h> /* debug */ //#include <mach/at91_pmc.h> //#include <mach/at91_rstc.h> //#include <mach/at91_shdwc.h> #include <mach/irqs.h> //#include "generic.h" //#include "clock.h" #include <mach/w55fa92_reg.h> #include <asm/io.h> #define DEV_MAJOR 240 #define DEV_MINOR 0 #define NUM_MINORS 1 #define DEVICE_NAME "audioIN" #define ERR(fmt, args...) printk(KERN_ALERT __FILE__ ": " fmt, ##args) #define MSG(fmt, args...) printk(KERN_INFO __FILE__ ": " fmt, ##args) #define DBG(fmt, args...) printk(KERN_DEBUG __FILE__ ": " fmt, ##args) static ssize_t user_gpio_read(struct file *fp, char __user *buff, size_t count, loff_t *offp) { char str[32] = {0}; char out[32] = {0}; int n, err; // printk("lk~~~~~~~read buff = %s ",buff); err = copy_from_user(str, buff, count); // printk("lk~~~~~~~read str = %s ",str); if (err) return -EFAULT; sprintf(out,"return values"); memset(buff, 0, count); err = copy_to_user(buff, out, sizeof(out)); if (err) return -EFAULT; return n; } static ssize_t user_gpio_write(struct file *fp, const char __user *buff, size_t count, loff_t *offp) { int err; char tmp[32]; // printk("lk~~~~~~~write buff = %s ",buff); err = copy_from_user(tmp, buff, count); // printk("lk~~~~~~~write tmp = %s ",tmp); if (err) return -EFAULT; if('1' == tmp[0]) { //LINE IN printk("line in "); } else if('0' == tmp[0]) { //MIC IN printk("mic in "); } return count; } static ssize_t user_gpio_open(struct inode *inode,struct file *fp) { // printk("open gpio devices "); return 0; } static struct file_operations user_gpio_file_ops = { .owner = THIS_MODULE, .write = user_gpio_write, .read = user_gpio_read, .open = user_gpio_open, }; static struct cdev *dev; static void __exit user_audioIN_exit(void) { printk("exit audioIN "); dev_t devno = MKDEV(DEV_MAJOR, DEV_MINOR); unregister_chrdev_region(devno, NUM_MINORS); cdev_del(dev); return; } static int __init user_audioIN_init(void) { printk("init audioIN "); int err = 0; int i; dev_t devno = MKDEV(DEV_MAJOR, DEV_MINOR); err = register_chrdev_region(devno, NUM_MINORS, DEVICE_NAME); if (err) goto fail_devno; dev = cdev_alloc(); dev->ops = &user_gpio_file_ops; dev->owner = THIS_MODULE; err = cdev_add(dev, devno, NUM_MINORS); if (err) goto fail_cdev; return err; fail_cdev: fail_devno: unregister_chrdev_region(devno, NUM_MINORS); fail_gpio: return err; } module_init(user_audioIN_init); module_exit(user_audioIN_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("kang_liu <liukang325@qq.com>"); MODULE_DESCRIPTION("Access GSEIO from userspace.");
这里就能够调用kernel层的一些API进行底层的操作。
Makefile:生成audioINdriver.ko
# Comment/uncomment the following line to disable/enable debugging #DEBUG = y BUILD_TOOLS_PRE = arm-linux- CC=$(BUILD_TOOLS_PRE)gcc LD=$(BUILD_TOOLS_PRE)ld # Add your debugging flag (or not) to CFLAGS ifeq ($(DEBUG),y) DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines else DEBFLAGS = -O2 endif KERNEL_DIR = ../../../linux-2.6.35.4 EXTRA_CFLAGS += $(DEBFLAGS) EXTRA_CFLAGS += -I$(LDDINC) EXTRA_CFLAGS +=-I$(KERNEL_DIR)/arch/arm/mach-w55fa92/include EXTRA_CFLAGS +=-I$(KERNEL_DIR)/arch/arm EXTRA_CFLAGS +=-I$(KERNEL_DIR)/arch/arm/include EXTRA_CFLAGS +=-I$(KERNEL_DIR)/arch/arm/include/linux ifneq ($(KERNELRELEASE),) # call from kernel build system audioIN-objs := audioINdriver.o obj-m := audioINdriver.o else KERNELDIR ?= $(KERNEL_DIR) #KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) modules: $(MAKE) ARCH=arm CROSS_COMPILE=$(BUILD_TOOLS_PRE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules endif clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules.order Module.symvers depend .depend dep: $(CC) $(CFLAGS) -M *.c > .depend ifeq (.depend,$(wildcard .depend)) include .depend endif
3. 生成好 .ko 以后,就能够在ARM板上,载入驱动。
insmod audioINdriver.ko
4、载入驱动成功后,就能够在应用层直接操作设备 /dev/audioIN,来实现相关功能,将一些參数传到驱动层,运行相关kernel层的代码。
应用层測试程序例如以下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/syscall.h> #define BUF_LEN 32 int s_audioInFd = 0; int InitAudioInDevice() { s_audioInFd = open("/dev/audioIN",O_RDWR); if (s_audioInFd > 0) { return 1; } else { printf("Can't open the GSE IO device "); return 0; } } void UninitAudioInDevice() { if (s_audioInFd > 0) close(s_audioInFd); } int getAudioIn() { char buffer[BUF_LEN] = {0}; if (s_audioInFd > 0) { memcpy(&buffer[0], "lk_test", 7); // printf("get buffer = %s ", buffer); int len = read(s_audioInFd, buffer, 7); // printf("get buffer = %s, len = %d ", buffer, len); return len; } return -1; } int setAudioIn(int micLine) { char buffer[BUF_LEN] = {0}; if (s_audioInFd > 0) { sprintf(buffer, "%d", micLine); int len = write(s_audioInFd, buffer, sizeof(buffer)); if (len > 0) return 1; } return 0; }
当中的read 和 write函数,可从驱动中获取一些返回值,也可将字符串传到驱动中。
驱动的入口为:
module_init(user_audioIN_init);
module_exit(user_audioIN_exit);