在飞凌的入门手册中的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;
}
显然,
未完待续。。。