一. 进入到kernel/drivers目录,新建zhx_print目录:
二. 在zhx_print目录中增加zhx_print.h文件:
这个头文件定义了一些字符串常量宏,在后面我们要用到!。此外,还定义了一个字符设备结构体print_dev,这个就是我们虚拟的硬件设备了,val成员变量就代表设备里面的寄存器,它的类型为int。
三.在zhx_print目录中增加print.c文件
这是驱动程序的实现部分。驱动程序的功能主要是向上层提供访问设备的寄存器的值,包括读和写。
这里,为了让程序看起来简单明了,我们只实现普遍的字符串驱动模式。
首先是包含必要的头文件和定义三种访问设备的方法:
四:实现各个函数的功能
主要有 print_open,print_close ,print_read,print_write等
/*打开函数*/
static int print_open(struct inode*inodep,struct file*filep)
{
struct print_reg_dev *dev = NULL;
dev = container_of(inodep->i_cdev,struct print_reg_dev,print_cdev);
if(dev == NULL)
{
printk("print_open is error.\n");
return -1;
}
filep->private_data= (void*)dev;
return 0;
}
static int print_open(struct inode*inodep,struct file*filep)
{
struct print_reg_dev *dev = NULL;
dev = container_of(inodep->i_cdev,struct print_reg_dev,print_cdev);
if(dev == NULL)
{
printk("print_open is error.\n");
return -1;
}
filep->private_data= (void*)dev;
return 0;
}
/*关闭*/
static int print_close(struct inode*inodep,struct file*filep)
{
return 0;
}
static int print_close(struct inode*inodep,struct file*filep)
{
return 0;
}
/*读取*/
static ssize_t print_read(struct file*filep,char __user*buff,
size_t count,loff_t *ppos)
{
/*定义自己定义的结构体函数*/
struct print_reg_dev *dev = (struct print_reg_dev*)filep->private_data;
/*返回值*/
int ret = 0;
printk(KERN_INFO"print_read : you read value\n");
if(count != sizeof(dev->val))
{
printk("print_read : sorry error\n");
return 0;
}
if(copy_to_user(buff,&(dev->val),sizeof(dev->val)))
{
ret = -EFAULT;
}
else
{
ret = sizeof(dev->val);
}
return ret;
}
static ssize_t print_read(struct file*filep,char __user*buff,
size_t count,loff_t *ppos)
{
/*定义自己定义的结构体函数*/
struct print_reg_dev *dev = (struct print_reg_dev*)filep->private_data;
/*返回值*/
int ret = 0;
printk(KERN_INFO"print_read : you read value\n");
if(count != sizeof(dev->val))
{
printk("print_read : sorry error\n");
return 0;
}
if(copy_to_user(buff,&(dev->val),sizeof(dev->val)))
{
ret = -EFAULT;
}
else
{
ret = sizeof(dev->val);
}
return ret;
}
/*写入*/
static ssize_t print_write(struct file*filep,constchar __user *buf,
size_t count,loff_t *ppos)
{
struct print_reg_dev *dev = filep->private_data;
int ret = 0;
printk(KERN_INFO"print_write:yout write value\n");
if(count != sizeof(dev->val))
{
printk("print_write : sorry error\n");
return 0;
}
if(copy_from_user((void*)&dev->val,(void*)buf,sizeof(dev->val)))
{
ret = -EFAULT;
}
else
{
ret = sizeof(dev->val);
}
return ret;
}
static ssize_t print_write(struct file*filep,constchar __user *buf,
size_t count,loff_t *ppos)
{
struct print_reg_dev *dev = filep->private_data;
int ret = 0;
printk(KERN_INFO"print_write:yout write value\n");
if(count != sizeof(dev->val))
{
printk("print_write : sorry error\n");
return 0;
}
if(copy_from_user((void*)&dev->val,(void*)buf,sizeof(dev->val)))
{
ret = -EFAULT;
}
else
{
ret = sizeof(dev->val);
}
return ret;
}
五:定义字符驱动函数结构体
staticstruct file_operations print_fops=
{
.owner = THIS_MODULE,
.open = print_open,
.release = print_close,
.read = print_read,
.write = print_write,
}
{
.owner = THIS_MODULE,
.open = print_open,
.release = print_close,
.read = print_read,
.write = print_write,
}
六:实现初始化init函数
staticint __init print_init(void)
{
int err = 0;
dev_t devno = 0;
printk(KERN_ALERT"Initializing print device\n");
//1.动态分配主从设备号,并且注册驱动
err = alloc_chrdev_region(&devno,0,1,"zhx_print");
if(err < 0)
{
printk(KERN_ALERT"Failed to alloc char dev region.\n");
return err;
}
printk(KERN_INFO"print_init : alloc_chrdev_region sucessfully\n");
//2.分配结构体内存空间
print_dev = kmalloc(sizeof(struct print_reg_dev),GFP_KERNEL);
if(print_dev == NULL)
{
printk(KERN_ALERT"Failed to kmalloc print_dev");
err = -ENOMEM;
}
major_number = MAJOR(devno);
minor_number = MINOR(devno);
printk(KERN_INFO"print_init : kmalloc print_dev sucessfully\n");
//3.初始化和注册cdev
memset(print_dev,0,sizeof(struct print_reg_dev));//结构体初始化
cdev_init(&(print_dev->print_cdev),&print_fops);//cdev初始化
print_dev->print_cdev.owner= THIS_MODULE;
print_dev->print_cdev.ops=&print_fops;
err = cdev_add(&print_dev->print_cdev,devno,1);//注册cdev
if(err)
return err;
print_dev->val=0;
printk(KERN_INFO"print_init : cdev_init -- cdev_add sucessfully\n");
//4.创建设备节点,就是在/dev/目录下创建zhx_print
print_class = class_create(THIS_MODULE,"zhx_print");
if(IS_ERR(print_class))
{
err = PTR_ERR(print_class);
printk(KERN_ALERT"Failed to create print device class.\n");
cdev_del(&(print_dev->print_cdev));
unregister_chrdev_region(devno,1);
kfree(print_dev);
return err;
}
device_create(print_class,NULL,devno,"%s","zhx_print");//创建zhx_print
printk(KERN_INFO" init sucessfully!!!!!\n");
return 0;
}
{
int err = 0;
dev_t devno = 0;
printk(KERN_ALERT"Initializing print device\n");
//1.动态分配主从设备号,并且注册驱动
err = alloc_chrdev_region(&devno,0,1,"zhx_print");
if(err < 0)
{
printk(KERN_ALERT"Failed to alloc char dev region.\n");
return err;
}
printk(KERN_INFO"print_init : alloc_chrdev_region sucessfully\n");
//2.分配结构体内存空间
print_dev = kmalloc(sizeof(struct print_reg_dev),GFP_KERNEL);
if(print_dev == NULL)
{
printk(KERN_ALERT"Failed to kmalloc print_dev");
err = -ENOMEM;
}
major_number = MAJOR(devno);
minor_number = MINOR(devno);
printk(KERN_INFO"print_init : kmalloc print_dev sucessfully\n");
//3.初始化和注册cdev
memset(print_dev,0,sizeof(struct print_reg_dev));//结构体初始化
cdev_init(&(print_dev->print_cdev),&print_fops);//cdev初始化
print_dev->print_cdev.owner= THIS_MODULE;
print_dev->print_cdev.ops=&print_fops;
err = cdev_add(&print_dev->print_cdev,devno,1);//注册cdev
if(err)
return err;
print_dev->val=0;
printk(KERN_INFO"print_init : cdev_init -- cdev_add sucessfully\n");
//4.创建设备节点,就是在/dev/目录下创建zhx_print
print_class = class_create(THIS_MODULE,"zhx_print");
if(IS_ERR(print_class))
{
err = PTR_ERR(print_class);
printk(KERN_ALERT"Failed to create print device class.\n");
cdev_del(&(print_dev->print_cdev));
unregister_chrdev_region(devno,1);
kfree(print_dev);
return err;
}
device_create(print_class,NULL,devno,"%s","zhx_print");//创建zhx_print
printk(KERN_INFO" init sucessfully!!!!!\n");
return 0;
}
七:实现卸载函数
staticvoid __exit print_exit(void)
{
//1.卸载设备节点
device_destroy(print_class,MKDEV(major_number,minor_number));
//2.卸载cdev
if(print_dev)
{
cdev_del(&(print_dev->print_cdev));
kfree(print_dev);
}
//3.卸载
unregister_chrdev_region(MKDEV(major_number,minor_number),1);
}
{
//1.卸载设备节点
device_destroy(print_class,MKDEV(major_number,minor_number));
//2.卸载cdev
if(print_dev)
{
cdev_del(&(print_dev->print_cdev));
kfree(print_dev);
}
//3.卸载
unregister_chrdev_region(MKDEV(major_number,minor_number),1);
}
八:添加模块协议和指定模块初始化和卸载函数
MODULE_LICENSE("GPL");
module_init(print_init);
module_exit(print_exit);
module_init(print_init);
module_exit(print_exit);
九:在zhx_print目录下添加Kconfig文件,文件内容:
config ZHX_PRINT
tristate "zhx write driver"
default n
help
this is the driver
tristate "zhx write driver"
default n
help
this is the driver
十:在zhx_print目录下添加Makefile文件,文件内容:
obj-$(CONFIG_ZHX_PRINT)+= print.o
十一:在ics/kernel/drivers目录上的Kconfig 中添加粗体红色语句:
...
source "drivers/i-tv/Kconfig"
source "drivers/gps/Kconfig"
source "drivers/zhx_print/Kconfig"
endmenu
source "drivers/i-tv/Kconfig"
source "drivers/gps/Kconfig"
source "drivers/zhx_print/Kconfig"
endmenu
十二:在ics/kernel/drivers/目录下的Makefile中添加以下语句:
obj-$(CONFIG_ZHX_PRINT)+= zhx_print/
十三:在ics/kernel/目录下,敲入以下命令:
ics/kernle$ make menuconfig
到这里会弹出内核配置菜单,这里配置如下:
Device Drivers --->
[*]zhx write driver
[*]zhx write driver
然后重新编译内核!
ics/kernle$ make-j4
到这里,zhx_print的驱动就添加进入内核里面了!!!
十四:处理硬件设备访问权限问题
在硬件抽象层中,我们调用open函数来打开对应内核设备时:
if((dev->fd= open(DEVICE_NAME,O_RDWR))== -1)
{
LOGE("Failed to open device file /dev/zhx_print");
free(dev);
return -EFAULT;
}
{
LOGE("Failed to open device file /dev/zhx_print");
free(dev);
return -EFAULT;
}
如果没有修改设备节点/dev/zhx_print权限,那么,就会提示:
Failed to open device file /dev/zhx_print
这表明当前用户没有权限打开设备文件/dev/zhx_print,解决方法如下:
在板子的命令行中 执行以下命令:
board@Ubuntu:$ chmod 777 /dev/zhx_print
然后就可以操作啦!!!