Solved Issue with ioctl(2) request to ACPI battery.

Hi everybody!

I'm writing a simple C program that can read battery information. I'm sending a ACPIIO_BATT_GET_BATTINFO ioctl(2) request to /dev/acpi device. And it seems to work.

However, ioctl(2) request exits with error "Device not configured" when I put inside the loop. Here's the program sample:

C:
#include<fcntl.h>
#include<stdint.h>
#include<dev/acpica/acpiio.h>
#include<sys/ioctl.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>

int main() {
    int acpifd;
    union acpi_battery_ioctl_arg batio;
    struct acpi_battinfo* batinf;
  
    acpifd = open("/dev/acpi", O_RDONLY);
    if (acpifd == -1) {
        dprintf(2, "can't open /dev/acpi file.\n");
        return 1;
    }
  
    int i = 0;
    while(i < 2) {
        if (ioctl(acpifd, ACPIIO_BATT_GET_BATTINFO, &batio) == -1) {
            dprintf(2, "can't send an ioctl(2) request to /dev/acpi.\n");
            dprintf(2, "err: %s\n", strerror(errno));
            return 1;
        }
      
        dprintf(1, "no error.\n");
        ++i;
    }
  
    return 0;
}

When I do like this:

Code:
diff --git a/acpi1.c b/acpi2.c
index 0ebce72..e659bc4 100644
--- a/acpi1.c
+++ b/acpi2.c
@@ -18,7 +18,7 @@ int main() {
     }
  
     int i = 0;
-    while(i < 2) {
+    //while(i < 2) {
         if (ioctl(acpifd, ACPIIO_BATT_GET_BATTINFO, &batio) == -1) {
             dprintf(2, "can't send an ioctl(2) request to /dev/acpi.\n");
             dprintf(2, "err: %s\n", strerror(errno));
@@ -27,7 +27,7 @@ int main() {
      
         dprintf(1, "no error.\n");
         ++i;
-    }
+    //}
  
     return 0;
 }

i.e. remove the loop, the request is successful.

What may be the actual cause of that? Am I missing something? I consulted the acpi_battery(4) manual, but I didn't find anything that would be related to this problem. Also I looked into the actual source code - I found that this particular acpi(2) request fails with this error code in this case (the code is taken from /usr/src/sys/dev/acpica/acpi_battery.c, function acpi_battery_get_battinfo):

C:
/*
     * Get the battery devclass and max unit for battery devices.  If there
     * are none or error, return immediately.
     */
    batt_dc = devclass_find("battery");
    if (batt_dc == NULL)
    return (ENXIO);
    devcount = devclass_get_maxunit(batt_dc);
    if (devcount == 0)
    return (ENXIO);

Even though I found that, it, unfortunately, doesn't make the things clear for me - I still can't understand what's the cause of the problem and how to fix it.

Would you please help me?


Artem.
 
The problem is not the loop, but the fact that you did not initialize batio.unit. For an example of usage, see /usr/src/usr.sbin/acpi/acpiconf/acpiconf.c.
Oh, now I can see, thank you very much, eseipi!

By the way, seems that it's also written in acpi_battery(4) man page, but in not that obvious form:
Code:
IOCTLS
       Every ioctl for the acpi_battery    driver takes a    single    integer     value
       for  the     battery  unit    number    as an argument,    and returns a specific
       structure    for       each       request.    A    special    unit    number
       ACPI_BATTERY_ALL_UNITS  specifies all of    the attached units and reports
       accumulated information.

I havn't been working with ioctl's that much, so I want to ask did I get it right that in case of this ACPI request, we use the same structure acpi_battery_ioctl_arg for both input arguments (in this case it's unit member) and request response ( battinfo and others)?
 
I havn't been working with ioctl's that much, so I want to ask did I get it right that in case of this ACPI request, we use the same structure acpi_battery_ioctl_arg for both input arguments (in this case it's unit member) and request response ( battinfo and others)?
Yes, that's right. Just a small note: acpi_battery_ioctl_arg is a union, not a structure.
 
Back
Top