• 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
    ",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);


  • 相关阅读:
    [Android Pro] 超能RecyclerView组件使用
    [Android Pro] UI设计师不可不知的安卓屏幕知识
    [Android Pro] Android Support 包里究竟有什么
    [Android Pro] fragment中嵌套viewpager,vierpager中有多个fragment,不显示
    [Android Pro] 横竖屏切换时,禁止activity重新创建,android:configChanges="keyboardHidden|orientation" 不起作用
    [Android Pro] sqlite数据库的char,varchar,text,nchar,nvarchar,ntext的区别
    [Android Pro] AndroidStudio导出jar包
    [Java] Java 打包成jar包 和 解压jar包
    [Android Pro] 通过IMSI判断手机是移动、联通、电信
    [MACOS] Mac上的抓包工具Charles
  • 原文地址:https://www.cnblogs.com/yxwkf/p/4070017.html
Copyright © 2020-2023  润新知