对《android系统源代码情景分析》书中例子
在设备驱动程序中
打开设备函数比如
freg_open(struct inode* inode,sruct file* filp)
{
struct fake_reg_dev* dev; //这是你所要访问的设备的表示结构体
。。。。
filp->private_data = dev;
return 0;
}
它的作用实质上就是将file这个文件结构体中的空指针成员变量private_data赋上你所要访问的设备的结构体的地址而已。以便下次读写的时候可以通过访问file文件结构体来访问到设备结构体。
freg_read(struct file* filp,char __user *buf,size_t count,loff_t *f_pos)
{
struct fake_reg_dev* dev = filp->private_data;
if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) {
err = -EFAULT;
goto out;
}
}
在设备驱动程序的模块加载函数中一般会实现的功能是
static int __init freg_init(void) {
int err = -1;
dev_t dev = 0;
struct device* temp = NULL;
printk(KERN_ALERT"Initializing freg device.\n");
/*
*/
err = alloc_chrdev_region(&dev, 0, 1, FREG_DEVICE_NODE_NAME); //分配设备号
if(err < 0) {
printk(KERN_ALERT"Failed to alloc char dev region.\n");
goto fail;
}
freg_major = MAJOR(dev);
freg_minor = MINOR(dev);
/*
freg
*/
freg_dev = kmalloc(sizeof(struct fake_reg_dev), GFP_KERNEL); //为描述这个设备的结构体分配存储空间,在这个设备结构体中包含字符设备结构体或者块设备结构体
if(!freg_dev) {
err = -ENOMEM;
printk(KERN_ALERT"Failed to alloc freg device.\n");
goto unregister;
}
/*
*/
err = __freg_setup_dev(freg_dev); //初始化这个设备结构体(且看下面对这个结构体中实现的功能解释)
if(err) {
printk(KERN_ALERT"Failed to setup freg device: %d.\n", err);
goto cleanup;
。。。。
}
下面是初始化这个设备结构体函数
static int __freg_setup_dev(struct fake_reg_dev* dev) {
int err;
dev_t devno = MKDEV(freg_major, freg_minor);
memset(dev, 0, sizeof(struct fake_reg_dev));
/*
*/
cdev_init(&(dev->dev), &freg_fops); //初始化字符设备
dev->dev.owner = THIS_MODULE;
dev->dev.ops = &freg_fops; //重点在这一步将设备文件操作方法函数与设备结构体中的字符设备链接了起来,所以在上面freg_open函数的时候就能够通过文件(路径)找到设备结构体,在通过设备结构中的字符设备找到操作设备文件的方法函数。将整个驱动操作就是这样联系在了一起
/*
*/
err = cdev_add(&(dev->dev),devno, 1);
if(err) {
return err;
}
/*
val
*/
init_MUTEX(&(dev->sem));
dev->val = 0;
return 0;
}
在驱动程序写完之后通过一系列的编译环境修改将驱动程序编译进内核当中,详情见《android系统源代码情景分析》一书。
在用户空间C程序中可以这样访问驱动程序
首先调用open("dev/freg",O_RDWR);//在用户空间调用了这个系统调用以后系统就会从用户态切换到内核态然后调用你写的内核函数freg_open函数(文章开头的函数),将"dev/freg"与设备驱动结构体连接起来。
再调用read()就可以读驱动程序了