linux驱动说开去(一)--模块示例

本系列将以一个简单的驱动开发例子扩展讲述驱动开发各要点

一、基础知识

Linux 设备打开依次跨越层次:应用程序->系统调用接口->VFS层->当前设备文件系统(如字符设备)->驱动->硬件


Linux设备驱动分为:字符设备、块设备和网络设备。原理图如下:


  二代码实现

2.1.1驱动层代码实现

创建目录/home/workplace/sc2410_android/kernel/drivers/leon_test
将下述hello.c hello.h Makefie放于上述目录

hello.c
/* 
 *  * ===================================================================================== 
 *   * 
 *       Filename:  hello.c 
 *
 *    Description:  hello_mod
 *
 *        Version:  1.0
 *        Created:  02/22/2018 10:07:55 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Leon, pengliang2851035@163.com
 *        Company:  None
 *
 * =====================================================================================
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
#include "hello.h"
 
#define MAJOR_NUM 260
#define MINOR_NUM 0
#define IN_BUF_LEN 256
#define OUT_BUF_LEN 512
 
 MODULE_AUTHOR("Leon");
 MODULE_DESCRIPTION("Hello_mod driver by Leon");
 
 static struct class * hello_class;
 static struct cdev hello_cdev;
 static dev_t devnum = 0;
 static char * modname = "hello_mod";
 static char * devicename = "hello";
 static char * classname = "hello_class";
 
 static int open_count = 0;
 static struct semaphore sem;
 static DEFINE_SPINLOCK(spin);
 static char * inbuffer = NULL;
 static char * outbuffer = NULL;
 static lang_t langtype;
 
 static int hello_mod_open(struct inode *, struct file *);
 static int hello_mod_release(struct inode *, struct file *);
 static ssize_t hello_mod_read(struct file *, char *, size_t, loff_t *);
 static ssize_t hello_mod_write(struct file *, const char *, size_t, loff_t *);
 static long hello_mod_ioctl(struct file *, unsigned int, unsigned long);
 
 struct file_operations hello_mod_fops = 
{
	.owner = THIS_MODULE,
	.open = hello_mod_open,
	.read = hello_mod_read,
	.write = hello_mod_write,
	.unlocked_ioctl = hello_mod_ioctl,
	.release = hello_mod_release,
};

static int hello_mod_open(struct inode *inode, struct file *pfile)
{
	printk("+hello_mod_open()!\n");
	spin_lock(&spin);
	if(open_count)
	{
		spin_unlock(&spin);
		return -EBUSY;
	}
	open_count++;
	spin_unlock(&spin);
	printk("-hello_mod_open()!\n");
	return 0;
}
static int hello_mod_release(struct inode *inode, struct file *pfile)
{
	printk("+hello_mod_release()!\n");
	open_count--;
	printk("-hello_mod_release()!\n");
	return 0;
}
static ssize_t hello_mod_read(struct file *pfile, char *user_buf, size_t len, loff_t *off)
{
	printk("+hello_mod_read()!\n");
		
	if(down_interruptible(&sem))
	{
		return -ERESTARTSYS; 
	}
	memset(outbuffer, 0, OUT_BUF_LEN);
	printk("    +switch()\n");
	switch(langtype)
	{
		case english:
			printk("        >in case: english\n");
			sprintf(outbuffer, "Hello! %s.", inbuffer);
			break;
		case chinese:
		    printk("        >in case: chinese\n");
		    sprintf(outbuffer, "Ni hao %s.", inbuffer);
		    break;
		case pinyin:
		    printk("        >in case: pinyin\n");
		    sprintf(outbuffer, "ni hao! %s.", inbuffer);
		    break;
		default:
		    printk("        >in case: default\n");
		    break;
	}
	printk("    -switch()\n");
	if(copy_to_user(user_buf, outbuffer, len))
	{
		up(&sem);
		return -EFAULT;
	}
	up(&sem);
	printk("-hello_mod_read()!\n");
	return 0;
}
static ssize_t hello_mod_write(struct file *pfile, const char *user_buf, size_t len, loff_t *off)
{
	printk("+hello_mod_write()!,user_buf: %s, len: %d\n",user_buf,len);
	if(down_interruptible(&sem))
	{
		return -ERESTARTSYS;
	}
	if(len > IN_BUF_LEN)
	{
		printk("Out of input buffer\n");
		return -ERESTARTSYS;
	}
	if(copy_from_user(inbuffer, user_buf, len))
	{
		up(&sem);
		return -EFAULT;
	}
	up(&sem);    
	printk("-hello_mod_write()!, inbuffer: %s\n",inbuffer);
	return 0;
}
static long hello_mod_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
	int err = 0;
	printk("+hello_mod_ioctl()!, cmd: %d\n",cmd);
	printk("    +switch()\n");
	switch(cmd)
	{
		case HELLO_IOCTL_RESETLANG:
			printk("        >in case: HELLO_IOCTL_RESETLANG\n");
			langtype = english;
			break;
		case HELLO_IOCTL_GETLANG:
			printk("        >in case: HELLO_IOCTL_GETLANG\n");
			err = copy_to_user((int *)arg, &langtype, sizeof(int));
			break;
		case HELLO_IOCTL_SETLANG:
			printk("        >in case: HELLO_IOCTL_SETLANG\n");
			err = copy_from_user(&langtype,(int *)arg, sizeof(int));
			break;
		default:
			printk("        >in case: default\n");
			err = ENOTSUPP;
			break;
	}
	printk("    -switch()\n");
	printk("-hello_mod_ioctl()!\n");
	return err;
}
static int __init hello_mod_init(void)
{
	int result;
	printk("+hello_mod_init()!\n");
	devnum = MKDEV(MAJOR_NUM, MINOR_NUM);
	result = register_chrdev_region(devnum, 1, modname);
					
	if(result < 0)
	{
		printk("hello_mod : can't get major number!\n");
		return result;
	}    
						
	cdev_init(&hello_cdev, &hello_mod_fops);
	hello_cdev.owner = THIS_MODULE;
	hello_cdev.ops = &hello_mod_fops;
	result = cdev_add(&hello_cdev, devnum, 1);
	if(result)
		printk("Failed at cdev_add()\n");
	hello_class = class_create(THIS_MODULE, classname);
	if(IS_ERR(hello_class))
	{
		printk("Failed at class_create().Please exec [mknod] before operate the device\n");
	}
	else
	{
		device_create(hello_class, NULL, devnum,NULL, devicename);
	}
													
	open_count = 0;
	langtype = english;
	inbuffer = (char *)kmalloc(IN_BUF_LEN, GFP_KERNEL);
	outbuffer = (char *)kmalloc(OUT_BUF_LEN, GFP_KERNEL);
	sema_init(&sem, 1);
	printk("-hello_mod_init()!\n");
		return 0;
}

static void __exit hello_mod_exit(void)
{
	printk(KERN_ALERT"+hello_mod_exit!\n");
	kfree(inbuffer);
	kfree(outbuffer);
	cdev_del(&hello_cdev);
	device_destroy(hello_class, devnum);
	class_destroy(hello_class);
	unregister_chrdev_region(devnum, 1);
	printk(KERN_ALERT"-hello_mod_exit!\n");
	
	return ;
}

module_init(hello_mod_init);
module_exit(hello_mod_exit);
MODULE_LICENSE("GPL");

hello.h

/* 
* ===================================================================================== 
* 
*       Filename:  hello.h 
* 
*    Description:  define the cmd supported by hello_mod 
* 
*        Version:  1.0 
*        Created:  02/22/2018 10:24:20 AM 
*       Revision:  none 
*       Compiler:  gcc 
* 
*         Author:  Leon, pengliang2851035@163.com 
*        Company:  None
* 
* ===================================================================================== 
*/  
      
#ifndef __HELLO_H__  
#define __HELLO_H__  
    
#define HELLO_MAGIC    12  
#define HELLO_IOCTL_RESETLANG    _IO(HELLO_MAGIC,0)        //set langtype = english  
#define HELLO_IOCTL_GETLANG        _IOR(HELLO_MAGIC,1,int)    //get langtype  
#define HELLO_IOCTL_SETLANG        _IOW(HELLO_MAGIC,2,int)    //set langtype  
      
typedef enum _lang_t  
{  
	english, chinese, pinyin  
}lang_t;  
      
#endif 

Makefile

#Makefile 2.6  
  
obj-m :=hello.o  
KERNEL :=/home/workplace/sc2410_android/kernel
PWD :=$(shell pwd)  
$(warinning   $(MAKE))
modules :  
	$(MAKE) -C $(KERNEL) M=$(PWD) modules  

.PHONEY:clean  
clean :  
	rm -f *.o *.ko

2.1.2 编译

cd /home/workplace/sc2410_android/kernel/drivers/leon_test
root@leon:    /home/workplace/sc2410_android/kernel/drivers/leon_test# ls
Makefile  hello.c  hello.h
root@leon:    /home/workplace/sc2410_android/kernel/drivers/leon_test# make
make -C /home/workplace/sc2410_android/kernel M=/home/workplace/sc2410_android/kernel/drivers/leon_test   modules  
make[1]: 正在进入目录 `/home/workplace/sc2410_android/kernel'
  CC [M]  /home/workplace/sc2410_android/kernel/drivers/leon_test/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/workplace/sc2410_android/kernel/drivers/leon_test/hello.mod.o
  LD [M]  /home/workplace/sc2410_android/kernel/drivers/leon_test/hello.ko
make[1]:正在离开目录 `/home/workplace/sc2410_android/kernel'

root@leon:    /home/workplace/sc2410_android/kernel/drivers/leon_test# ls
Makefile  modules.order  Module.symvers  hello.c  hello.h  hello.ko  hello.mod.c  hello.mod.o  hello.o

hello.ko即为我们所需要的驱动安装文件
安装步骤如下:
root@leon:    /home/workplace/sc2410_android/kernel/drivers/leon_test# adb push test1.ko /sdcard/
[100%] /sdcard/test1.ko
root@leon:    /home/workplace/rk3229_android/kernel/drivers/test1# adb shell
shell@sc2410_box:/ $ cd sdcard/
shell@sc2410_box:/sdcard $ su
shell@sc2410_box:/sdcard # lsmod                                               
vcodec_service 33766 0 - Live 0x00000000
shell@sc2410_box:/mnt/internal_sd # insmod hello.ko
shell@sc2410_box:/sdcard # lsmod                                               
test1 3685 0 - Live 0x00000000 (O)
vcodec_service 33766 0 - Live 0x00000000
上述黑体部分即表示安装成功


2.2.1应用层代码实现

创建目录/home/workplace/sc2410_android/leon_test
将下述hello.c hello.h Android.mk放于上述目录

Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	hello.c \

LOCAL_MODULE:= hello

#include $(BUILD_SHARED_LIBRARY)
include $(BUILD_EXECUTABLE)

hello.c

/*************************************************************************
	> File Name: hello.c
	> Author: leon
	> Mail: pengliang2851035@163.com 
	> Created Time: 02/22/2018 10:24:20 AM  
************************************************************************/

#include<stdio.h>
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <unistd.h>  
#include <linux/ioctl.h>  
#include <string.h>  
#include <errno.h>  
#include "hello.h"

int main() 
{
	char outbuf[512];
	char * myname = "leon";  
    lang_t langtype = english;  
    int fd = open("/dev/hello", O_RDWR, S_IRUSR|S_IWUSR);  
    if(fd != -1)  
    {  
        write(fd, myname, strlen(myname)+1);  
        langtype = chinese;  
        ioctl(fd, HELLO_IOCTL_SETLANG, &langtype);  
        read(fd, outbuf, 512);  
        printf("langtype=chinese:%s\n", outbuf);  
        memset(outbuf, 0, 512);  
        langtype = pinyin;  
        ioctl(fd, HELLO_IOCTL_SETLANG, &langtype);  
        read(fd, outbuf, 512);      
        printf("langtype=pinyin:%s\n", outbuf);  
        memset(outbuf, 0, 512);  
        ioctl(fd, HELLO_IOCTL_RESETLANG, &langtype);      
        read(fd, outbuf, 512);  
        printf("langtype=english:%s\n", outbuf);  
    }  
    else  
    {  
        perror("Failed at open():");  
    }  
    close(fd);
    return 0;
}

hello.h

/* 
* ===================================================================================== 
* 
*       Filename:  hello.h 
* 
*    Description:  define the cmd supported by hello_mod 
* 
*        Version:  1.0 
*        Created:  02/22/2018 10:24:20 AM 
*       Revision:  none 
*       Compiler:  gcc 
* 
*         Author:  Leon, pengliang2851035@163.com 
*        Company:  None 
* 
* ===================================================================================== 
*/  
      
#ifndef __HELLO_H__  
#define __HELLO_H__  
    
#define HELLO_MAGIC    12  
#define HELLO_IOCTL_RESETLANG    _IO(HELLO_MAGIC,0)        //set langtype = english  
#define HELLO_IOCTL_GETLANG        _IOR(HELLO_MAGIC,1,int)    //get langtype  
#define HELLO_IOCTL_SETLANG        _IOW(HELLO_MAGIC,2,int)    //set langtype  
      
typedef enum _lang_t  
{  
	english, chinese, pinyin  
}lang_t;  
      
#endif  

2.2.2 编译运行

root@leon:    /home/workplace/sc2410_android/leon_test# mm
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=5.1.1
TARGET_PRODUCT=sc2410_box
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
TARGET_CPU_VARIANT=cortex-a7
TARGET_2ND_ARCH=
TARGET_2ND_ARCH_VARIANT=
TARGET_2ND_CPU_VARIANT=
HOST_ARCH=x86_64
HOST_OS=linux
HOST_OS_EXTRA=Linux-3.13.0-100-generic-x86_64-with-Ubuntu-14.04-trusty
HOST_BUILD_TYPE=release
BUILD_ID=OTCoA1_000_010
OUT_DIR=out
============================================
PRODUCT_COPY_FILES device/sc2410/common/ueventd.sc2410.rc:root/ueventd.rk30board.rc ignored.
PRODUCT_COPY_FILES device/sc2410/common/fstab.sc2410board.bootmode.unknown:root/fstab.sc2410board.bootmode.unknown ignored.
PRODUCT_COPY_FILES device/sc2410/common/fstab.sc2410board.bootmode.emmc:root/fstab.sc2410board.bootmode.emmc ignored.
PRODUCT_COPY_FILES device/sc2410/common/performance_info.xml:system/etc/performance_info.xml ignored.
make:进入目录'/home/workplace/sc2410_android'
target thumb C: hello <= leon_test/hello.c
target Executable: hello (out/target/product/sc2410_box/obj/EXECUTABLES/hello_intermediates/LINKED/hello)
target Symbolic: hello (out/target/product/sc2410_box/symbols/system/bin/hello)
target Strip: hello (out/target/product/sc2410_box/obj/EXECUTABLES/hello_intermediates/hello)
Install: out/target/product/sc2410_box/system/bin/hello
make:离开目录“/home/workplace/sc2410_android”

#### make completed successfully (2 seconds) ####

root@leon:    /home/workplace/sc2410_android/leon_test/# adb push ../out/target/product/rk322x_box/system/bin/hello /sdcard/
root@leon:    /home/workplace/sc2410_android/leon_test/# adb shell
shell@sc2410_box:# cp /sdcard/hello /system/bin
shell@sc2410_box:/system/bin # ./test1                                         
langtype=chinese:Ni hao tishion.
langtype=pinyin:ni hao! tishion.
langtype=english:Hello! tishion.




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值