LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

hidraw设备简要分析

关键词:hid、hidraw、usbhid、hidp等等。

 

下面首先介绍hidraw设备主要用途,然后简要分析hidraw设备驱动(但是不涉及到相关USB/Bluwtooth驱动),最后分析用户空间接口并实例。

1. hidraw介绍

在内核Documentation/hid/hidraw.txt中对hidra设备进行介绍,以及和hiddev的区别。

hidraw提供了一个通过USB/Bluetooth接口的裸数据接口,它和hiddev的区别体现在其数据不经过HID parser解析,而是直接将数据传输。

如果用户空间应用程序知道怎么恰当和硬件设备通信,和能够手动构建HID 报表,那么hidraw应该被使用。这通常是在用户控件驱动自定义HID 设备的时候。

Hidraw与不符合规范的HID 设备通信也是有利 的,这些设备以一种不符合报表描述符不一致的方式发送和接收数据。因为Hiddev解析器通过他发送和接收报表,检测设备的报表描述符,这样的通信是不可能使用hiddev。Hidraw是唯一的选择,为这些不兼容的设备编写一个定制的内核驱动程序。

Hidraw一个好处是用户空间应用程序使用独立的底层硬件类型。当前,hidraw是通过bluetooth 和 usb实现。在将来,随着硬件总线的发展,hidraw将支持更多的类型。

2. hidraw驱动

hidraw也是hid类设备,hidraw_init()被hid_init调用,在调用之前创建了虚拟的hid总线hid_bus_type,并且创建/sys/kernel/debug/hid调试接口。

static int __init hid_init(void)
{
    int ret;
...
    ret = bus_register(&hid_bus_type);
...
    ret = hidraw_init();
    if (ret)
        goto err_bus;

    hid_debug_init();
...
}

static void __exit hid_exit(void)
{
    hid_debug_exit();
    hidraw_exit();
    bus_unregister(&hid_bus_type);
}

struct hidraw是hidraw设备实例,struct hid_device是hid框架下表示hid设备的实例。

hidraw_list是hidraw设备一次传输的实例。

struct hidraw {
    unsigned int minor;----------------------从设备号。
    int exist;-------------------------------表示设备是否连接。
    int open;--------------------------------表示设备open计数。
    wait_queue_head_t wait;
    struct hid_device *hid;------------------对应hid设备实例。
    struct device *dev;
    spinlock_t list_lock;
    struct list_head list;
};

struct hidraw_report {
    __u8 *value;
    int len;
};

struct hidraw_list {
    struct hidraw_report buffer[HIDRAW_BUFFER_SIZE];
    int head;
    int tail;
    struct fasync_struct *fasync;
    struct hidraw *hidraw;
    struct list_head node;
    struct mutex read_mutex;
};

2.1 hid总线

hid_bus_type提供了hid总线上进行match、probe、remove的接口,以及uevent上报接口。

static struct bus_type hid_bus_type = {
    .name        = "hid",
    .dev_groups    = hid_dev_groups,
    .match        = hid_bus_match,
    .probe        = hid_device_probe,
    .remove        = hid_device_remove,
    .uevent        = hid_uevent,
};

static int hid_uevent(struct device *dev, struct kobj_uevent_env *env)
{
    struct hid_device *hdev = to_hid_device(dev);    

    if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X",
            hdev->bus, hdev->vendor, hdev->product))
        return -ENOMEM;

    if (add_uevent_var(env, "HID_NAME=%s", hdev->name))
        return -ENOMEM;

    if (add_uevent_var(env, "HID_PHYS=%s", hdev->phys))
        return -ENOMEM;

    if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq))
        return -ENOMEM;

    if (add_uevent_var(env, "MODALIAS=hid:b%04Xg%04Xv%08Xp%08X",
               hdev->bus, hdev->group, hdev->vendor, hdev->product))
        return -ENOMEM;

    return 0;
}

2.1 hidraw初始化

hidraw首先创建了字符设备hidraw_class,并和字符设备hidraw_cdev绑定,对应的文件操作函数集为hidraw_ops

其中字符设备hidraw_cdev从设备好范围为0~63,后面创建设备通过hidraw_class即可。

int __init hidraw_init(void)
{
    int result;
    dev_t dev_id;

    result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,
            HIDRAW_MAX_DEVICES, "hidraw");---------------------------------从设备号0~63。
    if (result < 0) {
        pr_warn("can't get major number\n");
        goto out;
    }

    hidraw_major = MAJOR(dev_id);

    hidraw_class = class_create(THIS_MODULE, "hidraw");------------------创建hidraw设备类。
    if (IS_ERR(hidraw_class)) {
        result = PTR_ERR(hidraw_class);
        goto error_cdev;
    }

        cdev_init(&hidraw_cdev, &hidraw_ops);------------------------------初始化一个字符设备hidraw_dev,操作函数集为hidraw_ops。
    result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
    if (result < 0)
        goto error_class;

    printk(KERN_INFO "hidraw: raw HID events driver (C) Jiri Kosina\n");
out:
    return result;

error_class:
    class_destroy(hidraw_class);
error_cdev:
    unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
    goto out;
}

void hidraw_exit(void)
{
    dev_t dev_id = MKDEV(hidraw_major, 0);

    cdev_del(&hidraw_cdev);
    class_destroy(hidraw_class);
    unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);

}

对于hidraw设备的操作,都在hidraw_ops中体现,包括open/close/read/write/ioctl,以及poll、fasync。

static const struct file_operations hidraw_ops = {
    .owner =        THIS_MODULE,
    .read =         hidraw_read,
    .write =        hidraw_write,
    .poll =         hidraw_poll,
    .open =         hidraw_open,
    .release =      hidraw_release,
    .unlocked_ioctl = hidraw_ioctl,
    .fasync =    hidraw_fasync,
#ifdef CONFIG_COMPAT
    .compat_ioctl   = hidraw_ioctl,
#endif
    .llseek =    noop_llseek,
};

static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
    struct hidraw_list *list = file->private_data;
    int ret = 0, len;
    DECLARE_WAITQUEUE(wait, current);

    mutex_lock(&list->read_mutex);

    while (ret == 0) {
        if (list->head == list->tail) {
            add_wait_queue(&list->hidraw->wait, &wait);
            set_current_state(TASK_INTERRUPTIBLE);

            while (list->head == list->tail) {
                if (signal_pending(current)) {
                    ret = -ERESTARTSYS;
                    break;
                }
                if (!list->hidraw->exist) {
                    ret = -EIO;
                    break;
                }
                if (file->f_flags & O_NONBLOCK) {
                    ret = -EAGAIN;
                    break;
                }

                /* allow O_NONBLOCK to work well from other threads */
                mutex_unlock(&list->read_mutex);
                schedule();
                mutex_lock(&list->read_mutex);
                set_current_state(TASK_INTERRUPTIBLE);
            }

            set_current_state(TASK_RUNNING);
            remove_wait_queue(&list->hidraw->wait, &wait);
        }

        if (ret)
            goto out;

        len = list->buffer[list->tail].len > count ?
            count : list->buffer[list->tail].len;

        if (list->buffer[list->tail].value) {
            if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
                ret = -EFAULT;
                goto out;
            }
            ret = len;
        }

        kfree(list->buffer[list->tail].value);
        list->buffer[list->tail].value = NULL;
        list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
    }
out:
    mutex_unlock(&list->read_mutex);
    return ret;
}

static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)
{
    unsigned int minor = iminor(file_inode(file));
    struct hid_device *dev;
    __u8 *buf;
    int ret = 0;

    if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
        ret = -ENODEV;
        goto out;
    }

    dev = hidraw_table[minor]->hid;-----------------------------------------------根据minor号找到对应hidraw设备的hid_device。
...
    buf = memdup_user(buffer, count);
    if (IS_ERR(buf)) {
        ret = PTR_ERR(buf);
        goto out;
    }

    if ((report_type == HID_OUTPUT_REPORT) &&
        !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) {
        ret = hid_hw_output_report(dev, buf, count);
        if (ret != -ENOSYS)
            goto out_free;
    }

    ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type,
                HID_REQ_SET_REPORT);----------------------------------------------调用实际硬件接口发送report,比如usb、bluetooth等。

out_free:
    kfree(buf);
out:
    return ret;
}

static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
    ssize_t ret;
    mutex_lock(&minors_lock);
    ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
    mutex_unlock(&minors_lock);
    return ret;
}

static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)
{
    unsigned int minor = iminor(file_inode(file));
    struct hid_device *dev;
    __u8 *buf;
    int ret = 0, len;
    unsigned char report_number;

    dev = hidraw_table[minor]->hid;
...
    buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
    if (!buf) {
        ret = -ENOMEM;
        goto out;
    }

    if (copy_from_user(&report_number, buffer, 1)) {
        ret = -EFAULT;
        goto out_free;
    }

    ret = hid_hw_raw_request(dev, report_number, buf, count, report_type,
                 HID_REQ_GET_REPORT);---------------------------------------------从硬件接口接收数据。
...
    if (copy_to_user(buffer, buf, len)) {
        ret = -EFAULT;
        goto out_free;
    }

    ret = len;

out_free:
    kfree(buf);
out:
    return ret;
}

static unsigned int hidraw_poll(struct file *file, poll_table *wait)
{
    struct hidraw_list *list = file->private_data;

    poll_wait(file, &list->hidraw->wait, wait);
    if (list->head != list->tail)
        return POLLIN | POLLRDNORM;
    if (!list->hidraw->exist)
        return POLLERR | POLLHUP;
    return 0;
}

static int hidraw_open(struct inode *inode, struct file *file)
{
    unsigned int minor = iminor(inode);
    struct hidraw *dev;
    struct hidraw_list *list;
    unsigned long flags;
    int err = 0;

    if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
        err = -ENOMEM;
        goto out;
    }

    mutex_lock(&minors_lock);
    if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
        err = -ENODEV;
        goto out_unlock;
    }

    dev = hidraw_table[minor];
    if (!dev->open++) {
        err = hid_hw_power(dev->hid, PM_HINT_FULLON);--------------调用底层设备即具体接口的power()函数,hdev->ll_driver->power()。
        if (err < 0) {
            dev->open--;
            goto out_unlock;
        }

        err = hid_hw_open(dev->hid);-------------------------------调用底层设备的open()函数,hdev->ll_driver->open()。
        if (err < 0) {
            hid_hw_power(dev->hid, PM_HINT_NORMAL);
            dev->open--;
            goto out_unlock;
        }
    }

    list->hidraw = hidraw_table[minor];
    mutex_init(&list->read_mutex);
    spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);
    list_add_tail(&list->node, &hidraw_table[minor]->list);
    spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);
    file->private_data = list;
...
}

static int hidraw_fasync(int fd, struct file *file, int on)
{
    struct hidraw_list *list = file->private_data;

    return fasync_helper(fd, file, on, &list->fasync);--------------发送异步通知信号。
}

static void drop_ref(struct hidraw *hidraw, int exists_bit)
{
    if (exists_bit) {
        hidraw->exist = 0;
        if (hidraw->open) {
            hid_hw_close(hidraw->hid);
            wake_up_interruptible(&hidraw->wait);
        }
        device_destroy(hidraw_class,
                   MKDEV(hidraw_major, hidraw->minor));
    } else {
        --hidraw->open;---------------------------------------------打开计算减1。
    }
    if (!hidraw->open) {--------------------------------------------当计数为0后,开始释放资源。
        if (!hidraw->exist) {
            hidraw_table[hidraw->minor] = NULL;
            kfree(hidraw);
        } else {
            /* close device for last reader */
            hid_hw_power(hidraw->hid, PM_HINT_NORMAL);
            hid_hw_close(hidraw->hid);
        }
    }
}

static int hidraw_release(struct inode * inode, struct file * file)
{
    unsigned int minor = iminor(inode);
    struct hidraw_list *list = file->private_data;
    unsigned long flags;

    mutex_lock(&minors_lock);

    spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);
    list_del(&list->node);
    spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);
    kfree(list);

    drop_ref(hidraw_table[minor], 0);

    mutex_unlock(&minors_lock);
    return 0;
}

static long hidraw_ioctl(struct file *file, unsigned int cmd,
                            unsigned long arg)
{
    struct inode *inode = file_inode(file);
    unsigned int minor = iminor(inode);
    long ret = 0;
    struct hidraw *dev;
    void __user *user_arg = (void __user*) arg;

    mutex_lock(&minors_lock);
    dev = hidraw_table[minor];
    if (!dev) {
        ret = -ENODEV;
        goto out;
    }

    switch (cmd) {
        case HIDIOCGRDESCSIZE:----------------------------------------------Get report descriptor size。
            if (put_user(dev->hid->rsize, (int __user *)arg))
                ret = -EFAULT;
            break;

        case HIDIOCGRDESC:--------------------------------------------------Get report descriptor。
            {
                __u32 len;

                if (get_user(len, (int __user *)arg))
                    ret = -EFAULT;
                else if (len > HID_MAX_DESCRIPTOR_SIZE - 1)
                    ret = -EINVAL;
                else if (copy_to_user(user_arg + offsetof(
                    struct hidraw_report_descriptor,
                    value[0]),
                    dev->hid->rdesc,
                    min(dev->hid->rsize, len)))
                    ret = -EFAULT;
                break;
            }
        case HIDIOCGRAWINFO:------------------------------------------------Get raw info,包括bus类型,vid和pid。
            {
                struct hidraw_devinfo dinfo;

                dinfo.bustype = dev->hid->bus;
                dinfo.vendor = dev->hid->vendor;
                dinfo.product = dev->hid->product;
                if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
                    ret = -EFAULT;
                break;
            }
        default:
            {
                struct hid_device *hid = dev->hid;
                if (_IOC_TYPE(cmd) != 'H') {
                    ret = -EINVAL;
                    break;
                }

                if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) {-------------Send a feature report。
                    int len = _IOC_SIZE(cmd);
                    ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT);
                    break;
                }
                if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) {-------------Get a feature report。
                    int len = _IOC_SIZE(cmd);
                    ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT);
                    break;
                }

                /* Begin Read-only ioctls. */
                if (_IOC_DIR(cmd) != _IOC_READ) {
                    ret = -EINVAL;
                    break;
                }

                if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) {--------------Get raw name。
                    int len = strlen(hid->name) + 1;
                    if (len > _IOC_SIZE(cmd))
                        len = _IOC_SIZE(cmd);
                    ret = copy_to_user(user_arg, hid->name, len) ?
                        -EFAULT : len;
                    break;
                }

                if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) {---------------Get physical address。
                    int len = strlen(hid->phys) + 1;
                    if (len > _IOC_SIZE(cmd))
                        len = _IOC_SIZE(cmd);
                    ret = copy_to_user(user_arg, hid->phys, len) ?
                        -EFAULT : len;
                    break;
                }
            }

        ret = -ENOTTY;
    }
out:
    mutex_unlock(&minors_lock);
    return ret;
}

由于一个系统下可能存在多个hidraw设备,常通过HIDIOCGRAWINFO获取信息,判断对应的hidraw设备。

2.3 创建hidraw设备

int hidraw_connect(struct hid_device *hid)
{
    int minor, result;
    struct hidraw *dev;

    /* we accept any HID device, all applications */

    dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;

    result = -EINVAL;

    mutex_lock(&minors_lock);

    for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {--------------------分配hidraw设备的minor号,并将dev赋给hidraw_table[]。
        if (hidraw_table[minor])
            continue;
        hidraw_table[minor] = dev;
        result = 0;
        break;
    }

    if (result) {
        mutex_unlock(&minors_lock);
        kfree(dev);
        goto out;
    }

    dev->dev = device_create(hidraw_class, &hid->dev, MKDEV(hidraw_major, minor),
                 NULL, "%s%d", "hidraw", minor);------------------------------创建/dev/hidrawX设备节点。

    if (IS_ERR(dev->dev)) {
        hidraw_table[minor] = NULL;
        mutex_unlock(&minors_lock);
        result = PTR_ERR(dev->dev);
        kfree(dev);
        goto out;
    }

    init_waitqueue_head(&dev->wait);
    spin_lock_init(&dev->list_lock);
    INIT_LIST_HEAD(&dev->list);

    dev->hid = hid;
    dev->minor = minor;

    dev->exist = 1;
    hid->hidraw = dev;

    mutex_unlock(&minors_lock);
out:
    return result;

}
EXPORT_SYMBOL_GPL(hidraw_connect);

void hidraw_disconnect(struct hid_device *hid)
{
    struct hidraw *hidraw = hid->hidraw;

    mutex_lock(&minors_lock);

    drop_ref(hidraw, 1);

    mutex_unlock(&minors_lock);
}
EXPORT_SYMBOL_GPL(hidraw_disconnect);

所以hidraw的驱动分为两部分,一是hidraw_init()发起的整个hidraw设备驱动的初始化,二是底层驱动检测到hidraw设备后通过hidraw_connect()/hidraw_disconnect()创建或者销毁设备。

当USB/Bluetooth检查到设备是hidraw类型之后,就会调用hidraw提供的接口创建设备,表现为创建节点/dev/hidrawx。

后续用户空间程序对/dev/hidrawx进行read/write/ioctl等操作。

3. hidraw测试程序

内核提供了一个示例程序hid-example.c,下面结合内核代码简单分析一下。遍历/dev下所有的hidraw设备,然后读取信息。

/* Linux */
#include <linux/types.h>
#include <linux/input.h>
#include <linux/hidraw.h>
#include <dirent.h>

#ifndef HIDIOCSFEATURE
#warning Please have your distro update the userspace kernel headers
#define HIDIOCSFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
#define HIDIOCGFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
#endif

/* Unix */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

/* C */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

const char *bus_str(int bus);

int main(int argc, char **argv)
{
    int fd;
    int i, res, desc_size = 0;
    char buf[256];
    struct hidraw_report_descriptor rpt_desc;
    struct hidraw_devinfo info;
    char device[32] = "/dev/hidraw0";
    DIR *dir = NULL;
    struct dirent *ptr;

    /* Open dir /dev. */
    dir = opendir("/dev");-------------------------------------------------打开/dev目录。
    if(!dir) {
        perror("Popen dir failed...");
        return -ENOENT;
    }

    /*  */
    while(ptr = readdir(dir)) {--------------------------------------------遍历/dev目录下所有的文件。
        if(ptr->d_type != DT_CHR)------------------------------------------判断设备是否为字符设备,其他设备包括DT_UNKNOWN/DT_FIFO/DT_CHR DT_DIR/DT_BLK/DT_REG/DT_LNK/DT_SOCK/DT_WHT。
continue;
if(!strncmp(ptr->d_name, "hidraw", 6)) {

            snprintf(device, sizeof(device), "%s/%s", "/dev", ptr->d_name);

            /* Open the Device with non-blocking reads. In real life,
               don't use a hard coded path; use libudev instead. */
            fd = open(device, O_RDWR|O_NONBLOCK);--------------------------打开hidraw设备,对应内核的hidraw_open()if (fd < 0) {
                printf("Unable to open device %s.\n", device);
                return 1;
            }

            memset(&rpt_desc, 0x0, sizeof(rpt_desc));
            memset(&info, 0x0, sizeof(info));
            memset(buf, 0x0, sizeof(buf));

            printf("\n\n=================================Device %s info=================================\n", device);
            /* Get Raw Name */
            res = ioctl(fd, HIDIOCGRAWNAME(256), buf);---------------------对应hidraw_ioctl()的HIDIOCGRAWNAMEif (res < 0)
                perror("HIDIOCGRAWNAME");
            else
                printf("Raw Name: %s\n", buf);

            /* Get Physical Location */
            res = ioctl(fd, HIDIOCGRAWPHYS(256), buf);---------------------对应HIDIOCGRAWPHYSif (res < 0)
                perror("HIDIOCGRAWPHYS");
            else
                printf("Raw Phys: %s\n", buf);

            /* Get Raw Info */
            res = ioctl(fd, HIDIOCGRAWINFO, &info);-------------------------对应HIDIOCGRAWINFOif (res < 0) {
                perror("HIDIOCGRAWINFO");
            } else {
                printf("Raw Info:\n");
                printf("\tbustype: %d (%s)\n",
                    info.bustype, bus_str(info.bustype));
                printf("\tvendor: 0x%04hx\n", info.vendor);
                printf("\tproduct: 0x%04hx\n", info.product);
            }

            /* Get Report Descriptor Size */
            res = ioctl(fd, HIDIOCGRDESCSIZE, &desc_size);------------------对应HIDIOCGRDESCSIZE
            if (res < 0)
                perror("HIDIOCGRDESCSIZE");
            else
                printf("Report Descriptor Size: %d\n", desc_size);

            /* Get Report Descriptor */
            rpt_desc.size = desc_size;
            res = ioctl(fd, HIDIOCGRDESC, &rpt_desc);------------------------对应HIDIOCGRDESCif (res < 0) {
                perror("HIDIOCGRDESC");
            } else {
                printf("Report Descriptor:\n");
                for (i = 0; i < rpt_desc.size; i++)
                    printf("%hhx ", rpt_desc.value[i]);
                puts("\n");
            }

            /* Set Feature */
            buf[0] = 0x9; /* Report Number */
            buf[1] = 0xff;
            buf[2] = 0xff;
            buf[3] = 0xff;
            res = ioctl(fd, HIDIOCSFEATURE(4), buf);-------------------------对应HIDIOCSFEATUREif (res < 0)
                perror("HIDIOCSFEATURE");
            else
                printf("ioctl HIDIOCGFEATURE returned: %d\n", res);

            /* Get Feature */
            buf[0] = 0x9; /* Report Number */
            res = ioctl(fd, HIDIOCGFEATURE(256), buf);-----------------------对应HIDIOCGFEATUREif (res < 0) {
                perror("HIDIOCGFEATURE");
            } else {
                printf("ioctl HIDIOCGFEATURE returned: %d\n", res);
                printf("Report data (not containing the report number):\n\t");
                for (i = 0; i < res; i++)
                    printf("%hhx ", buf[i]);
                puts("\n");
            }

            /* Send a Report to the Device */
            buf[0] = 0x1; /* Report Number */
            buf[1] = 0x77;
            res = write(fd, buf, 2);
            if (res < 0) {
                printf("Error: %d\n", errno);
                perror("write");
            } else {
                printf("write() wrote %d bytes\n", res);
            }

            /* Get a report from the device */
            res = read(fd, buf, 16);
            if (res < 0) {
                perror("read");
            } else {
                printf("read() read %d bytes:\n\t", res);
                for (i = 0; i < res; i++)
                    printf("%hhx ", buf[i]);
                puts("\n");
            }

            close(fd);
        }
    }
    return 0;
}

const char *
bus_str(int bus)
{
    switch (bus) {
    case BUS_USB:
        return "USB";
        break;
    case BUS_HIL:
        return "HIL";
        break;
    case BUS_BLUETOOTH:
        return "Bluetooth";
        break;
    case BUS_VIRTUAL:
        return "Virtual";
        break;
    default:
        return "Other";
        break;
    }
} 

综合来看hidraw设备是hid的一种,传输裸数据,对应的底层硬件可能是USB/Bluetooth等。

通过对hidraw设备的操作,ioctl可以配置hidraw设备,read/write可以对hidraw设备进行读写。

 

参考文档:

HIDRAW - Raw Access to USB and Bluetooth Human Interface Devices》:包括简要介绍以及hidraw相关API介绍,尤其是ioctl命令。

Linux之访问/dev/hidraw》是对上文的翻译,附加了两个示例。 

posted on 2019-09-08 00:00  ArnoldLu  阅读(8996)  评论(0编辑  收藏  举报

导航