Linux实现字符设备驱动的基础步骤

Linux实现字符设备驱动的基础步骤

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

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);

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/118123.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 阿里云服务器如何配置开放端口

    阿里云服务器如何配置开放端口 1.进入ECS服务实例2.点击“资源管理”3.来到“实例”界面,点击“配置安全组规则”4.加入相应的端口号即可。5.这里说一句,避免入坑。你们别进到这个实例为0的安全组里面,这样你再

  • IIC原理超详细讲解—值得一看[通俗易懂]

    IIC原理超详细讲解—值得一看[通俗易懂]文章目录IIC简介IIC的物理层IIC的高阻态IIC物理层总结:IIC的协议层IIC总线时序图初始(空闲)状态开始信号:停止信号数据有效性应答信号IIC数据传送数据传送格式IIC发送数据IIC读数据:以AT24C02为例子软件IIC和硬件IICIIC简介    IIC(Inter-IntegratedCircuit)总线是一种由NXP…

  • CodeBlocks控制台中文乱码

    CodeBlocks控制台中文乱码中文乱码是个老问题了,涉及乱码的问题无非是编码不统一造成的;涉及编码的地方常见有:1.源文件本身编码2.编译器读取源文件编码3.数据编码(数据库,IO文件等)4.执行输出编码&amp;nbsp;知道乱码的原因有利于解决乱码问题,这些原因在哪个IDE都是通用的&amp;nbsp;关于codeblock这款IDE,这里…

  • 常量池和堆的区别_字符串常量池在堆中还是方法区

    常量池和堆的区别_字符串常量池在堆中还是方法区写在前面:博主是一位普普通通的19届二本大学生,平时最大的爱好就是听听歌,逛逛B站。博主很喜欢的一句话花开堪折直须折,莫待无花空折枝:博主的理解是头一次为人,就应该做自己想做的事,做自己不后悔的事,做自己以后不会留有遗憾的事,做自己觉得有意义的事,不浪费这大好的青春年华。博主写博客目的是记录所学到的知识并方便自己复习,在记录知识的同时获得部分浏览量,得到更多人的认可,满足小小的成就感,同时在写博客的途中结交更多志同道合的朋友,让自己在技术的路上并不孤单。目录:1.常量池与Class常量池2.运.

  • 使用CSS画一个三角形

    使用CSS画一个三角形使用CSS画一个三角形

  • 图片怎么存储到数据库里「建议收藏」

    我们存储图片到数据库里一般有两种方式将图片保存的路径存储到数据库(文件路径或者ftp路径)将图片以二进制数据流的形式直接写入数据库字段中(base64)FTP:FTP服务器(FileTransferProtocolServer)是在互联网上提供文件存储和访问服务的计算机,它们依照FTP协议提供服务。FTP是FileTransferProtocol(文件传输协议)。顾名思义,就是专门用来传输文件的协议。简单地说,支持FTP协议的服务器就是FTP服务器。关于图片或者文件在数据库.

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号