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\n",buff);
err = copy_from_user(str, buff, count);
// printk("lk~~~~~~~read str = %s\n",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\n",buff);
err = copy_from_user(tmp, buff, count);
// printk("lk~~~~~~~write tmp = %s\n",tmp);
if (err)
return -EFAULT;
if('1' == tmp[0])
{
//LINE IN
printk("line in\n");
}
else if('0' == tmp[0])
{
//MIC IN
printk("mic in\n");
}
return count;
}
static ssize_t user_gpio_open(struct inode *inode,struct file *fp)
{
// printk("open gpio devices\n");
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\n");
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\n");
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\n");
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\n", buffer);
int len = read(s_audioInFd, buffer, 7);
// printf("get buffer = %s, len = %d\n", 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);