天天看点

字符设备驱动代码编译流程字符设备驱动代码编译流程

字符设备驱动代码编译流程

由于刚开始学习驱动开发方面的相关知识,在博客上也参考了大量的博文,但博文中的驱动代码编译过程大都相对简略一些。在这里,我根据自己电脑的工作环境,阐述一个关于字符设备驱动代码编译的详细流程,如果有不足或者需要纠正的地方,欢迎大佬们指出!

准备工作

安卓包放在对应路径下:

字符设备驱动代码编译流程字符设备驱动代码编译流程

连接好端口:

字符设备驱动代码编译流程字符设备驱动代码编译流程

1、编写好运行代码

本文的运行代码见文章最后,例:chardrv_test。

2、去 kconfig 定义(添加)

如下例:

config CHARDRV_TEST
			tristate "chardrv test program"
			default n
			help
			  Implements a shared memory based transport mechanism that allows
			  for a debugger running on a host PC to communicate with a remote
			  stub running on peripheral subsystems such as the ADSP, MODEM etc.
           

3、makefile(使用)

如下例:

在 obj-y   += misc.o 后加 obj-$(CONFIG_CHARDRV_TEST)	+= chardrv_test.o
           

4、在 MobaXterm_Personal_11.1 软件中进行编译

如下例,依次输入所列命令:

r >>> ls >>> cd android_5/LA.BR.1.2.3-10210-8x09.0/ >>> source build/envsetup.sh >>> lunch >>> msm8909-userdebug >>> (source build/envsetup.sh)make bootimage
           

5、在cmd窗口操作

如下例:

adb devices >>> adb root >>> adb remount >>> adb shell >>> exit >>> 
接着在  C:\Users\Admin\Desktop>  路径下执行如下操作:
adb reboot bootloader >>> fast flash boot (+路径,例如:W:\android_5\LA.BR.1.2.3-10210-8x09.0\out\target\product\msm8909\boot.img) >>> fastboot reboot
           

操作流程可见下图:

字符设备驱动代码编译流程字符设备驱动代码编译流程

6、创建节点结果展示

字符设备驱动代码编译流程字符设备驱动代码编译流程
字符设备驱动代码编译流程字符设备驱动代码编译流程
字符设备驱动代码编译流程字符设备驱动代码编译流程
字符设备驱动代码编译流程字符设备驱动代码编译流程

7、附录

文中所列代码:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/device.h>
#include <asm/uaccess.h>

int dev1_registers[5];    //两个数组模拟两个字符型设备
int dev2_registers[5];

struct cdev cdev;    //定义一个设备变量
dev_t devno;     //定义一个设备号变量
struct class *cdev_class;

/*文件打开函数*/
int wumany_open(struct inode *inode, struct file *filp)
{
		/*获取次设备号*/
		int num = MINOR(inode->i_rdev);
	 pr_err("wumanyuan>>>%s: enter\n", __func__);
		if (num==0)
			filp->private_data = dev1_registers;    //把设备地址赋值给filp->private_data
		else if(num == 1)
			filp->private_data = dev2_registers;
		else
			return -ENODEV;  //无效的次设备号
		
		return 0; 
}

/*文件释放函数*/
int wumany_release(struct inode *inode, struct file *filp)
{
	  pr_err("wumanyuan>>>%s: enter\n", __func__);
	  return 0;
}

/*读函数*/
static ssize_t wumany_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	  unsigned long p =  *ppos;
	  unsigned int count = size;
	  int ret = 0;
	  int *register_addr = filp->private_data; /*获取设备的寄存器基地址,//在open函数里,已经把设备的地址赋值给了filp->private_data*/
	  pr_err("wumanyuan>>>%s: enter\n", __func__);
	  /*判断读位置是否有效*/
	  if (p >= 5*sizeof(int))
		return 0;
	  if (count > 5*sizeof(int) - p)
		count = 5*sizeof(int) - p;

	  /*读数据到用户空间*/
	  if (copy_to_user(buf, register_addr+p, count))
	  {
		ret = -EFAULT;
	  }
	  else
	  {
		*ppos += count;
		ret = count;
	  }

	  return ret;
}

/*写函数*/
static ssize_t wumany_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	  unsigned long p =  *ppos;
	  unsigned int count = size;
	  int ret = 0;
	  int *register_addr = filp->private_data; /*获取设备的寄存器地址*/
	  pr_err("wumanyuan>>>%s: enter\n", __func__);
	  /*分析和获取有效的写长度*/
	  if (p >= 5*sizeof(int))
		return 0;
	  if (count > 5*sizeof(int) - p)
		count = 5*sizeof(int) - p;
		
	  /*从用户空间写入数据*/
	  if (copy_from_user(register_addr + p, buf, count))
		ret = -EFAULT;
	  else
	  {
		*ppos += count;
		ret = count;
	  }

	  return ret;
}

/* seek文件定位函数 */
static loff_t wumany_llseek(struct file *filp, loff_t offset, int whence)
	{ 
		loff_t newpos;
		pr_err("wumanyuan>>>%s: enter\n", __func__);
		switch(whence) {
		  case SEEK_SET: 
			newpos = offset;
			break;

		  case SEEK_CUR: 
			newpos = filp->f_pos + offset;
			break;

		  case SEEK_END: 
			newpos = 5*sizeof(int)-1 + offset;
			break;

		  default: 
			return -EINVAL;
		}
		if ((newpos<0) || (newpos>5*sizeof(int)))
			return -EINVAL;
			
		filp->f_pos = newpos;
		return newpos;

}

/*文件操作结构体*/
static const struct file_operations wumany_fops =
{
	.owner = THIS_MODULE,
	.llseek = wumany_llseek,
	.read = wumany_read,
	.write = wumany_write,
	.open = wumany_open,
	.release = wumany_release,
};

/*设备驱动模块加载函数*/
static int wumanydev_init(void)
{
	pr_err("wumanyuan>>>%s: enter\n", __func__);
	/*初始化cdev结构*/
	cdev_init(&cdev, &wumany_fops);
	pr_err("wumanyuan>>>%s: 1111111\n", __func__);
	/* 注册字符设备 */
	alloc_chrdev_region(&devno, 0, 2, "wumanyuan");   //注册两个设备,此设备号的起始值为0
	pr_err("wumanyuan>>>%s: 2222222\n", __func__);
	cdev_add(&cdev, devno, 2);     //向系统添加设备
	pr_err("wumanyuan>>>%s: 33333333\n", __func__);
	cdev_class=class_create(THIS_MODULE,"wumanyuan");// /sys/class/wumanyuan
	device_create(cdev_class,NULL,devno,0,"yuan");// /dev/yuan
	return 0;
}

/*模块卸载函数*/
static void wumanydev_exit(void)
{
	pr_err("wumanyuan>>>%s: enter\n", __func__);
	cdev_del(&cdev);   /*注销设备*/
	device_destroy(cdev_class, devno);
	class_destroy(cdev_class);
	unregister_chrdev_region(devno, 2); /*释放设备号*/
}

module_init(wumanydev_init);
module_exit(wumanydev_exit);
MODULE_LICENSE("GPL");

           

8、参考文献

【1】 https://blog.csdn.net/a201106107/article/details/36905159

继续阅读