龙空技术网

ioctl 如何传递整数或地址数据到驱动?

老刘每天100字 121

前言:

此时看官们对“c语言传地址和传值”可能比较看重,我们都需要知道一些“c语言传地址和传值”的相关资讯。那么小编也在网上收集了一些有关“c语言传地址和传值””的相关内容,希望你们能喜欢,姐妹们一起来学习一下吧!

ioctl 如何传递整数或地址数据到驱动?

1. 问题提出

应用程序向驱动程序传递参数,是经常需要的,实际项目,pwm控制摄像头LED灯的亮度的一个驱动程序,要求应用程序可以设置pwm 的极性polarity、周期period、占空比duty、使能、关闭disabled等

2. 解决方法

方法一:应用程序通过ioctl函数,传递命令和整数数据,周期period和占空比duty

方法二:应用程序通过ioctl函数,传递命令和地址数据,周期&period和占空比&duty

3. 应用程序通过ioctl函数,传递命令和整数数据

这个实例只在用户程序和驱动程序中传递一个简单的整形参数,period周期,duty占空比。

1)驱动程序编写pwm-camera-led.c

#include <linux/gpio.h>

#include <linux/of_gpio.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/slab.h>

#include <linux/platform_device.h>

#include <linux/err.h>

#include <linux/uaccess.h>

#include <linux/miscdevice.h>

#include <linux/pwm.h>

#include <linux/delay.h>

#include <linux/ioctl.h>

#define PWM_CAMERA_SET_PERIOD 0x01

#define PWM_CAMERA_SET_DUTY 0x02

#define PWM_CAMERA_ENABLE 0x03

#define PWM_CAMERA_DISABLE 0x04

/*定义pwm结构体*/

struct pwm_param{

int duty_ns;

int period_ns;

struct pwm_device *pwm;

};

struct pwm_param camera;

static void pwm_camera_update(void)

{

pwm_config(camera.pwm,camera.duty_ns,camera.period_ns);

}

static int pwm_camera_open(struct inode *inode,struct file *filp)

{

int ret = 0;

pwm_set_polarity(camera.pwm,PWM_POLARITY_NORMAL);

//camera.period_ns = 5000;

//camera.duty_ns = 1000;

pwm_enable(camera.pwm);

printk("camera_led_pwm open \r\n");

return ret;

}

static int pwm_camera_release(struct inode *inode,struct file *filp)

{

pwm_disable(camera.pwm);

return 0;

}

static ssize_t pwm_camera_read(struct file *filp, char __user *buf, size_t len, loff_t *pos)

{

return 0;

}

static ssize_t pwm_camera_write(struct file *filp, const char __user *buf, size_t len, loff_t *pos)

{

return 0;

}

/*驱动的ioctl函数,接受应用程序的,命令和参数传递,并做成响应操作*/

static long pwm_camera_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

{

unsigned int period;

unsigned int duty;

int rc = 0;

void __user *argp = (void __user *)arg;

switch (cmd)

{

case PWM_CAMERA_SET_PERIOD:

if(argp == NULL){

printk("camera:invallid argument.");

return -EINVAL;

}

#if 0

if (copy_from_user(&camera.period_ns, argp, sizeof(camera.period_ns)))

{

printk("copy_from_user failed.");

return -EFAULT;

}

#endif

rc =1;

camera.period_ns = arg;

printk("%s: ioc read period = %d",__func__,camera.period_ns);

pwm_config(camera.pwm,camera.duty_ns,camera.period_ns);

break;

case PWM_CAMERA_SET_DUTY:

if (argp == NULL)

{

printk("camera: invalid argument.");

return -EINVAL;

}

#if 0

if (copy_from_user(&camera.duty_ns, argp, sizeof(camera.duty_ns))){

printk("copy_from_user failed.");

return -EFAULT;

}

#endif

rc =2;

camera.duty_ns=arg;

printk("%s: ioc read duty = %d",__func__,camera.duty_ns);

if ((camera.duty_ns<0) ||(camera.duty_ns >5000)){

printk("camera:invalid argument.");

return -EINVAL;

}

pwm_config(camera.pwm,camera.duty_ns,camera.period_ns);

break;

case PWM_CAMERA_ENABLE:

rc = 3;

pwm_enable(camera.pwm);

break;

case PWM_CAMERA_DISABLE:

rc =4;

pwm_disable(camera.pwm);

break;

default:

printk("camera: cmd error!\n");

return -EFAULT;

}

return rc;

}

struct file_operations pwm_camera_fops = {

.owner = THIS_MODULE,

.open = pwm_camera_open,

.release = pwm_camera_release,

.write = pwm_camera_write,

.unlocked_ioctl = pwm_camera_ioctl,

};

struct miscdevice pwm_camera_dev ={

.minor = MISC_DYNAMIC_MINOR,

.fops = &pwm_camera_fops,

.name = "camera_led,pwm",

};

/*驱动自测程序,正式版本不需要*/

static void pwm_camera_test(void)

{

int i =0,led_bar=1000;

pwm_enable(camera.pwm);

//while (1)

{

for (i=0;i<5;i++)

{

camera.duty_ns=i*led_bar;

printk("%s: ioc read i = %d",__func__,i);

pwm_camera_update();

printk("%s: ioc read camera.duty = %d,camera.period =%d\n",__func__,camera.duty_ns,camera.period_ns);

msleep(1000);

}

}

}

/*系统加载,与设备树匹配ok,执行此probe函数*/

static int pwm_camera_probe(struct platform_device *pdev)

{

int ret = 0;

struct device_node *child;

struct device *dev = &pdev->dev;

printk("match pwm_camera success\n");

child = of_get_next_child(dev->of_node, NULL);

if (child){

camera.pwm = devm_of_pwm_get(dev, child, NULL);

if (IS_ERR(camera.pwm)){

dev_err(&pdev->dev,"camera: unable to request legacy PWM\n");

return ret-1;

}

}

else{

printk(KERN_ERR"pwm_camera of get_next_child error!!!\n");

return -1;

}

//set pwm

camera.period_ns = 5000;

camera.duty_ns = 1000;

pwm_config(camera.pwm,camera.duty_ns,camera.period_ns);

ret = pwm_set_polarity(camera.pwm, PWM_POLARITY_NORMAL);

if (ret < 0)

{

printk("pwm set polarity fail, ret = %d\n",ret);

return ret;

}

pwm_enable(camera.pwm) ;

misc_register(&pwm_camera_dev);

//lgh

msleep(1000);

pwm_camera_test();

return 0;

}

static int pwm_camera_remove(struct platform_device *pdev)

{

pwm_disable(camera.pwm);

pwm_free(camera.pwm);

misc_deregister(&pwm_camera_dev);

return 0;

}

int pwm_camera_suspend(struct device *dev)

{

return 0;

}

int pwm_camera_resume(struct device *dev)

{

return 0;

}

const struct dev_pm_ops pwm_camera_pm_ops ={

.suspend = pwm_camera_suspend,

.resume = pwm_camera_resume,

};

static const struct of_device_id of_pwm_camera_match[] = {

{.compatible = "camera_led,pwm", },

{},

};

static struct platform_driver pwm_camera_driver = {

.probe = pwm_camera_probe,

.remove = pwm_camera_remove,

.driver ={

.name = "camera_led,pwm",

.owner = THIS_MODULE,

//.pm = &pwm_camera_pm_ops,

.of_match_table = of_pwm_camera_match,

},

};

module_platform_driver(pwm_camera_driver);

MODULE_DESCRIPTION("pwm control driver");

MODULE_LICENSE("GPL");

MODULE_AUTHOR("lgh");

驱动程序正常运行,还需要配置设备树,将驱动放入/kernel/drivers/pwm 目录下,然后修改该目录下的makefile文件,要编译时能编进去。

具体可以参考我的另一篇文章《Linux 下 PWM控制LED灯光的方法》

这篇文章的重点是验证如何通过应用程序,传递数据给驱动程序

2)应用程序编写 pwm-test.c

step1:应用程序,调用ioctl ,来设置pwm 的period周期,duty 占空比,其中占空比用for循环实现从低到高的变化

#include <stdio.h>

#include <stdlib.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <sys/ioctl.h>

#include <unistd.h>

#include <fcntl.h>

#define PWM_CAMERA_SET_PERIOD 0x01

#define PWM_CAMERA_SET_DUTY 0x02

#define PWM_CAMERA_ENABLE 0x03

#define PWM_CAMERA_DISABLE 0x04

int main(int arg,char *argv[])

{

unsigned int i=0;

int duty,period;

int rc,fd;

fd = open("/dev/camera_led,pwm",O_RDWR);

if (fd < 0)

{

printf("open camera,pwm fail\n");

return -1;

}

/*set period*/

period = 5000;

rc = ioctl(fd,PWM_CAMERA_SET_PERIOD,period);

printf("rc =%d,Set pwm priod = %d\n",rc,period);

duty = 1000;

for(i=0;i<6;i++)

{

rc = ioctl(fd,PWM_CAMERA_SET_DUTY,duty);

printf("i=%d,rc =%d,set duty =%d\n",i,rc,duty);

duty=duty+1000;

sleep(1);

}

rc=ioctl(fd,PWM_CAMERA_DISABLE);

printf("rc=%d,\n",rc);

close(fd);

return 0;

}

step2:上面的准备工作完成后

将应用程序用交叉编译器编译,传送到开发板测试(如何交叉编译,并传输到开发板,可以参考我的另一篇文章《rk3568应用代码如何编译与测试》链接地址:rk3568应用代码编译与测试-今日头条 (toutiao.com)

在开发下查看应用程序的权限,ls -l 命令

发现没有可执行权限,用chmod 777 pwm-test 修改权限

step3:用DAB在开发板上运行上面的应用程序./pwm-test。 注意要加"./",意思是当前路径下,否则会“not found”

程序运行后,并没有达到我们想要的结果,占空比始终没有变化,用示波器测试,是有波形,一直是20%的占空比,也并不是打印的duty=0.

分析原因:CMD的定义应该有问题,修改为如下

#define CMD_IOC_MAGIC 'a'

#define PWM_CAMERA_SET_PERIOD _IOW(CMD_IOC_MAGIC,1,int)

#define PWM_CAMERA_SET_DUTY _IOW(CMD_IOC_MAGIC,2,int)

#define PWM_CAMERA_ENABLE _IO(CMD_IOC_MAGIC,3)

#define PWM_CAMERA_DISABLE _IO(CMD_IOC_MAGIC,4)

应用程序和驱动中同时修改

用DAB在开发板上运行如下截图

在串口终端窗口中,看到log信息如下:

用示波器测试占空比是在按要求变化的,验证ok

4. 应用程序通过ioctl函数,传递命令和地址传参

前面我们介绍了ioctl接口,并举了一个简单的实例,但这个实例只在用户程序和驱动程序中传递了一个简单的整形参数。实际使用中我们可能需要传递更复杂的参数,或者传递多个参数,这时我们就只能传递参数的地址,或者将多个参数打包成一个结构体再传递该结构体的地址。

实现方法如下:

step1:在前面的驱动程序程序修改 pwm_camera_ioctl()函数为地址传参

传递pwm period 周期的命令修改如下

switch (cmd)

{

case PWM_CAMERA_SET_PERIOD:

#ifdef EN_ADDRESS //为了兼容整数传递,定义了条件编译,使能地址,用地址传参

if(argp == NULL){

printk("camera:invallid argument.");

return -EINVAL;

}

//地址传参,需要保护内核中的数据,内核驱动程序通过copy_ form_user读取应用的数据

if (copy_from_user(&camera.period_ns, argp, sizeof(camera.period_ns)))

{

printk("copy_from_user failed.");

return -EFAULT;

}

#endif

rc =1;

#ifndef EN_ADDRESS

camera.period_ns = arg;

#endif

传递pwm duty占空比的命令修改如下:

case PWM_CAMERA_SET_DUTY:

#ifdef EN_ADDRESS

if (argp == NULL)

{

printk("camera: invalid argument.");

return -EINVAL;

}

if (copy_from_user(&camera.duty_ns, argp, sizeof(camera.duty_ns))){

printk("copy_from_user failed.");

return -EFAULT;

}

#endif

rc =2;

#ifndef EN_ADDRESS

camera.duty_ns=arg;

#endif

step2:修改应用程序为地址传参

传递PWM period周期的地址参数,区别是原来的period,改为取地址&period

#ifdef EN_ADDRESS

rc = ioctl(fd,PWM_CAMERA_SET_PERIOD,&period);

#else

rc= ioctl(fd,PWM_CAMERA_SET_PERIOD,period);

#endif

传递PWM duty 占空比的地址参数,区别是原来的duty,改为取地址&duty

#ifdef EN_ADDRESS

rc = ioctl(fd,PWM_CAMERA_SET_DUTY,&duty);

#else

rc = ioctl(fd,PWM_CAMERA_SET_DUTY,duty);

#endif

修改后,分别编译驱动程序和应用程序,测试如下:

用DAB工具运行应用程序:

用串口终端程序看内核驱动打印信息

总结:至此,应用程序通过ioctl两种传递参数的方法,整数传输,和地址传输的实验和验证就做完了,整数传递适合只有一个数据值的传递,用它方便快捷;地址传递适合传递的数据较多的场景。

最重要的一点:ioctl的CMD定义,不能想当然的自定义,还是要符合Linux传递命令的协议,这部分网上有很多资料,可以参考,这里就不啰嗦了。

分享是一种美德,希望能帮助到有需要的朋友,码字不易,看到的朋友帮忙点赞和转发,希望惠及更多朋友.

深圳华科智能设计院

谢谢,关注~

标签: #c语言传地址和传值