前言:
此时看官们对“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语言传地址和传值