Linux led 驱动



在飞凌的入门手册中的LED驱动,比照着修改了一下,出现了奇怪的问题,源程序为:

static long s3c6410_leds_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	switch(cmd) {
		unsigned tmp;
	case 0:
	case 1:
                 if (arg > 4) 
                 {
		 	return -EINVAL;
		 }
		tmp = readl(S3C64XX_GPMDAT);
            
		if(cmd==0) //close light
                  { 
			tmp &= (~(1<<arg));
                  }
		else  //open light
                  { 
			tmp |= (1<<arg);
                  }

                writel(tmp,S3C64XX_GPMDAT);

		//printk (DEVICE_NAME": %d %d\n", arg, cmd);
		return 0;
	default:
		return -EINVAL;
	}
}

增加了一个cmd 2,但是返回错误. 初次学习不知道错误为什么会出现。

开始看对应的程序,该函数 s3c6410_leds_ioctl 赋值给 unlocked_ioctl , unlocked_ioctl 是ioctl 的改版。

尝试在 s3c6410_leds_ioctl 加入打印,发现cmd = 2 时,该函数并没有执行,对linux 代码不熟悉,那我只能从上层往下跟踪了,

在应用层的代码如下:

	fd = open("/dev/leds",0);
	
	if(fd < 0)
	{
		printf("can not open leds device\n");
		exit(1);
	}

 	ret = ioctl(fd, cmd, led_num);
	printf("return:%d\n",ret);
	close(fd);

调用 ioctl 时,系统会调用 sys_ioctl 函数,关于ioctl 调用的介绍在《深入Linux设备驱动程序内核设计机制》中介绍的比较清楚,在这里简单描述一下.

sys_ioctl 函数 fs/ioctl.c

SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{
	struct file *filp;
	int error = -EBADF;
	int fput_needed;

	filp = fget_light(fd, &fput_needed);
	if (!filp)
		goto out;

	error = security_file_ioctl(filp, cmd, arg);
	if (error)
		goto out_fput;

	error = do_vfs_ioctl(filp, fd, cmd, arg);
 out_fput:
	fput_light(filp, fput_needed);
 out:
	return error;
}

这里可以看到由设备描述符 fd 通过 fget_light 函数 映射到文件 filp, 然后调用 do_vfs_ioctl 函数:

int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
	     unsigned long arg)
{
	int error = 0;
	int __user *argp = (int __user *)arg;
	struct inode *inode = filp->f_path.dentry->d_inode;
	printk("<1> do_vfs_ioctl fd:%d,cmd:%d\n",fd,cmd);
	switch (cmd) {
	case FIOCLEX:
		set_close_on_exec(fd, 1);
		break;

	case FIONCLEX:
		set_close_on_exec(fd, 0);
		break;

	case FIONBIO:
		error = ioctl_fionbio(filp, argp);
		break;

	case FIOASYNC:
		error = ioctl_fioasync(fd, filp, argp);
		break;

	case FIOQSIZE:
		if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) ||
		    S_ISLNK(inode->i_mode)) {
			loff_t res = inode_get_bytes(inode);
			error = copy_to_user(argp, &res, sizeof(res)) ?
					-EFAULT : 0;
		} else
			error = -ENOTTY;
		break;

	case FIFREEZE:
		error = ioctl_fsfreeze(filp);
		break;

	case FITHAW:
		error = ioctl_fsthaw(filp);
		break;

	case FS_IOC_FIEMAP:
		return ioctl_fiemap(filp, arg);

	case FIGETBSZ:
		return put_user(inode->i_sb->s_blocksize, argp);

	default:
		if (S_ISREG(inode->i_mode))
			error = file_ioctl(filp, cmd, arg);
		else
			error = vfs_ioctl(filp, cmd, arg);
		break;
	}
	return error;
}

在这里我们看到对cmd 进行了一系列的处理,这样看来,我们的cmd 值并不是任意的。依次察看各个case 发现 FIGETBSZ 的值 为2,这样的话,执行了 put_user 函数而没有执行s3c6410_leds_ioctl 函数,因为我们期待的函数是 vfs_ioctl ,

static long vfs_ioctl(struct file *filp, unsigned int cmd,
		      unsigned long arg)
{
	int error = -ENOTTY;
	printk("<1>vfs_ioctl\n");
	if (!filp->f_op || !filp->f_op->unlocked_ioctl)
		goto out;

	error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
	if (error == -ENOIOCTLCMD)
		error = -EINVAL;
 out:
	return error;
}

显然,


未完待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值