为什么我对gpiod_set_value的调用返回“无效的GPIO”错误?

如何解决为什么我对gpiod_set_value的调用返回“无效的GPIO”错误?

我正在尝试编写OV2680摄像机传感器的驱动程序。我想打开一些GPIO引脚作为其->probe()函数的步骤之一。那些GpioIo()引脚在DSDT表中这样声明(对于OV2680所依赖的设备;请参见full DSDT table

        Device (PMI1)
        {
            Name (_ADR,Zero)  // _ADR: Address
            Name (_HID,"INT3472")  // _HID: Hardware ID
            Name (_CID,"INT3472")  // _CID: Compatible ID
            Name (_DDN,"INCL-CRDD")  // _DDN: DOS Device Name
            Name (_UID,One)  // _UID: Unique ID
            Method (_CRS,NotSerialized)  // _CRS: Current Resource Settings
            {
                Name (SBUF,ResourceTemplate ()
                {
                    GpioIo (Exclusive,PullDefault,0x0000,IoRestrictionOutputOnly,"\\_SB.PCI0.GPI0",0x00,ResourceConsumer,)
                        {   // Pin list
                            0x0079
                        }
                    GpioIo (Exclusive,)
                        {   // Pin list
                            0x007A
                        }
                    GpioIo (Exclusive,)
                        {   // Pin list
                            0x008F
                        }
                })
                Return (SBUF) /* \_SB_.PCI0.PMI1._CRS.SBUF */
            }
        }

        Device (CAM1)
        {
            Name (_ADR,"OVTI2680")  // _HID: Hardware ID
            Name (_CID,"OVTI2680")  // _CID: Compatible ID
            Name (_DDN,"OV2680-CRDD")  // _DDN: DOS Device Name
            Name (_UID,One)  // _UID: Unique ID
            Name (_DEP,Package (0x02)  // _DEP: Dependencies
            {
                PMI1,I2C2
            })
            Name (_PLD,Package (0x01)  // _PLD: Physical Location of Device
            {
                ToPLD (
                    PLD_Revision           = 0x2,PLD_IgnoreColor        = 0x1,PLD_Red                = 0x0,PLD_Green              = 0x0,PLD_Blue               = 0x0,PLD_Width              = 0x0,PLD_Height             = 0x0,PLD_UserVisible        = 0x1,PLD_Dock               = 0x0,PLD_Lid                = 0x0,PLD_Panel              = "FRONT",PLD_VerticalPosition   = "CENTER",PLD_HorizontalPosition = "RIGHT",PLD_Shape              = "VERTICALRECTANGLE",PLD_GroupOrientation   = 0x0,PLD_GroupToken         = 0x0,PLD_GroupPosition      = 0x0,PLD_Bay                = 0x0,PLD_Ejectable          = 0x1,PLD_EjectRequired      = 0x1,PLD_CabinetNumber      = 0x0,PLD_CardCageNumber     = 0x0,PLD_Reference          = 0x0,PLD_Rotation           = 0x0,PLD_Order              = 0x0,PLD_VerticalOffset     = 0xFFFF,PLD_HorizontalOffset   = 0xFFFF)

            })
            Method (_CRS,ResourceTemplate ()
                {
                    I2cSerialBusV2 (0x0010,ControllerInitiated,0x00061A80,AddressingMode7Bit,"\\_SB.PCI0.I2C2",Exclusive,)
                })
                Return (SBUF) /* \_SB_.PCI0.CAM1._CRS.SBUF */
            }
        }

请注意,没有_DSD段,这意味着我必须在驱动程序代码according to the documentation中明确声明它们。那没问题;我具有此ACPI设备的struct acpi_device(通过刮除驱动程序匹配的OV2680设备的依赖项),因此我可以这样做,并按照文档说明将它们与acpi_dev_add_driver_gpios()添加在一起。我的问题是在Getting GPIO Descriptor阶段提出的;该文档说使用gpiod_get_index(),该函数需要struct device而不是struct acpi_device。我尝试通过传递struct acpi_device::dev成员来实现此目的,但是尽管这样做时我没有收到任何错误消息,但是当我设置GPIO引脚为活动状态时,实际上似乎什么都没有发生,所以我认为它正在工作。

鉴于这是特定于硬件的,所以我不确定MRE是否有用,但这是一个应编译并成功插入的驱动程序:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/acpi.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>

static const struct acpi_gpio_params gpio1 = {0,false};
static const struct acpi_gpio_params gpio2 = {1,false};
static const struct acpi_gpio_params gpio3 = {2,false};

static const struct acpi_gpio_mapping int3472_acpi_gpios[] = {
    {"gpio1",&gpio1,1},{"gpio2",&gpio2,{"gpio3",&gpio3,{}
};

static int ov2680_probe(struct i2c_client *client)
{    
    /*
     * The driver will match the OV2680 device,but the GPIO
     * pins lie in its dependent INT3472,so we need to walk
     * up the dependencies to find that device.
    */
   struct acpi_device *int3472_device;

   /* get ACPI handle of OV2680 device */
   struct acpi_handle *dev_handle = ACPI_HANDLE(&client->dev);

   /* Get dependent devices */
   struct acpi_handle_list dep_devices;
   acpi_evaluate_reference(dev_handle,"_DEP",NULL,&dep_devices);

   int i;
   for (i=0; i < dep_devices.count; i++) {
       struct acpi_device_info *devinfo;
       acpi_get_object_info(dep_devices.handles[i],&devinfo);

       if (devinfo->valid & ACPI_VALID_HID && !strcmp(devinfo->hardware_id.string,"INT3472")) {
           acpi_bus_get_device(dep_devices.handles[i],&int3472_device);
       }
   }

   int ret;

   ret = acpi_dev_add_driver_gpios(int3472_device,int3472_acpi_gpios);

   struct gpio_desc *gpiod1,*gpiod2,*gpiod3;

   gpiod1 = gpiod_get_index(&int3472_device->dev,GPIOD_ASIS);
   gpiod2 = gpiod_get_index(&int3472_device->dev,1,GPIOD_ASIS);
   gpiod3 = gpiod_get_index(&int3472_device->dev,2,GPIOD_ASIS);
   
   gpiod_set_value_cansleep(gpiod1,1);
   gpiod_set_value_cansleep(gpiod2,1);
   gpiod_set_value_cansleep(gpiod3,1);

   return 0;
}

static int ov2680_remove(struct i2c_client *client)
{
    /*
     * Code goes here to get acpi_device,turn off all
     * the GPIO pins,remove them from the ACPI device
     * and whatnot
     */

    return 0;
}

static const struct acpi_device_id ov2680_acpi_match[] = {
    {"OVTI2680",0},{ }
};
MODULE_DEVICE_TABLE(acpi,ov2680_acpi_match);

static struct i2c_driver ov2680_driver = {
    .driver = {
        .name = "ov2680",.acpi_match_table = ov2680_acpi_match,},.probe_new = ov2680_probe,.remove = ov2680_remove,};
module_i2c_driver(ov2680_driver);

MODULE_AUTHOR("Dan Scally");
MODULE_DESCRIPTION("A driver for OmniVision 2680 sensors");
MODULE_LICENSE("GPL");

dmesg在添加图钉或其他任何东西时均未报告任何问题,但是对gpiod_set_value_cansleep()的调用在此处引发了错误:

[4840.774633] gpiod_set_value_cansleep:无效的GPIO(错误指针)

这可能是因为对gpiod_get_index()的调用失败,因此GPIO描述符无效。

问题:

  1. 我使用&int3472->device作为gpiod_get_index()的参数是正确的方法吗?
  2. 如果是这样,什么可能导致对gpiod_get_index()的调用失败?

编辑:

grep -H 15 /sys/bus/acpi/devices/*/status

的输出
/sys/bus/acpi/devices/ACPI000C:00/status:15
/sys/bus/acpi/devices/BOSC0200:00/status:15
/sys/bus/acpi/devices/device:16/status:15
/sys/bus/acpi/devices/device:17/status:15
/sys/bus/acpi/devices/device:32/status:15
/sys/bus/acpi/devices/INT33D3:00/status:15
/sys/bus/acpi/devices/INT33D6:00/status:15
/sys/bus/acpi/devices/INT3400:00/status:15
/sys/bus/acpi/devices/INT340E:00/status:15
/sys/bus/acpi/devices/INT344B:00/status:15
/sys/bus/acpi/devices/INT3472:08/status:15
/sys/bus/acpi/devices/INT3472:09/status:15
/sys/bus/acpi/devices/INT3F0D:00/status:15
/sys/bus/acpi/devices/MSFT0001:00/status:15
/sys/bus/acpi/devices/MSFT0101:00/status:15
/sys/bus/acpi/devices/OVTI2680:00/status:15
/sys/bus/acpi/devices/OVTI5648:00/status:15
/sys/bus/acpi/devices/PNP0103:00/status:15
/sys/bus/acpi/devices/PNP0401:01/status:15
/sys/bus/acpi/devices/PNP0A05:04/status:15
/sys/bus/acpi/devices/PNP0C09:00/status:15
/sys/bus/acpi/devices/PNP0C0C:00/status:15
/sys/bus/acpi/devices/PNP0C0D:00/status:15
/sys/bus/acpi/devices/VPC2004:00/status:15
/sys/bus/acpi/devices/WCOM508C:00/status:15

解决方法

(根据我之前给出的评论收集答案)

为澄清起见,我必须说,从您的DSDT中我们可以获得以下信息。有3组PMIC,即DSCxCLPxPMIx。我相信它们是基于模型的,例如 Desktop Laptop 2-in-1 。并且在每种情况下,同一组中的所有PMIC都有不同的_UID。从grep -H 15 ...提供的输出中,我们仅列举了实例中的2个,分别是 INT3472:08 INT3472:09 (在DSDT中最后定义的两个实例) )。它们是PMIx,您可以通过grep -H . /sys/bus/acpi/devices/INT3472:*/path进行检查。

您感兴趣的是PMI1,它{strong>消耗来自Intel GPIO driver的三条GPIO线,即引脚121、122和143(您可以将它们解码为Community#2,Group# 5或GPP_F(相对于组引脚1、2和23),这可以帮助您了解通过DSDT中的其他方法触及这些行的_INI方法),并提供 3 + 7 = 10个根据its driver的引脚。

现在输入代码。 _DEP ACPI方法仅用于链接电源,Linux内核还有其他方法可以从其他设备中劫持资源,因为您要执行的操作是使用与您的设备无关的资源正在为其创建驱动程序。

方法是通过ACPI HID查找设备:

struct acpi_device *adev;
struct device *phys_dev;
struct gpio_desc *desc;

...

adev = acpi_dev_get_first_match_dev("INT3472","1",-1);
if (!adev) {
  pr_err("Oops,we didn't find an ACPI device!\n");
  return -ENODEV;
}

phys_dev = get_device(acpi_get_first_physical_node(adev));
acpi_dev_put(adev);

if (!phys_dev) {
  pr_err("Oops,we didn't find a physical device!\n");
  return -ENODEV;
}

desc = gpiod_get_index(phys_dev,NULL,GPIOD_ASIS);
if (IS_ERR(desc)) {
  pr_err("Something went wrong when retrieving GPIO\n");
  put_device(phys_dev);
  return PTR_ERR(desc);
}

...

gpiod_put(desc);
put_device(phys_dev);

简化这种错误的方法(因为您知道设备实例的总线类型和确切名称,但是Linux不保证在启动时保持相同)

struct device *phys_dev;
struct gpio_desc *desc;

...

phys_dev = bus_find_device_by_name(&i2c_bus_type,"i2c-INT3472:09");
if (!phys_dev) {
  pr_err("Oops,GPIOD_ASIS);
if (IS_ERR(desc)) {
  pr_err("Something went wrong when retrieving GPIO\n");
  put_device(phys_dev);
  return PTR_ERR(desc);
}

...

gpiod_put(desc);
put_device(phys_dev);

旁注:

  1. OV2680摄像机传感器已经有existing driver,可以扩展它而不是为ACPI情况创建特定的分叉。
  2. 正确的方法是在没有任何黑客攻击的情况下使用资源,即在PMIC MFD driver中使用资源。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res