hidraw設備簡要分析

關鍵詞:hid、hidraw、usbhid、hidp等等。html

 

下面首先介紹hidraw設備主要用途,而後簡要分析hidraw設備驅動(可是不涉及到相關USB/Bluwtooth驅動),最後分析用戶空間接口並實例。node

1. hidraw介紹

在內核Documentation/hid/hidraw.txt中對hidra設備進行介紹,以及和hiddev的區別。linux

hidraw提供了一個經過USB/Bluetooth接口的裸數據接口,它和hiddev的區別體如今其數據不通過HID parser解析,而是直接將數據傳輸。app

若是用戶空間應用程序知道怎麼恰當和硬件設備通訊,和可以手動構建HID 報表,那麼hidraw應該被使用。這一般是在用戶控件驅動自定義HID 設備的時候。框架

Hidraw與不符合規範的HID 設備通訊也是有利 的,這些設備以一種不符合報表描述符不一致的方式發送和接收數據。由於Hiddev解析器經過他發送和接收報表,檢測設備的報表描述符,這樣的通訊是不可能使用hiddev。Hidraw是惟一的選擇,爲這些不兼容的設備編寫一個定製的內核驅動程序。異步

Hidraw一個好處是用戶空間應用程序使用獨立的底層硬件類型。當前,hidraw是經過bluetooth 和 usb實現。在未來,隨着硬件總線的發展,hidraw將支持更多的類型。async

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設備的實例。oop

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》是對上文的翻譯,附加了兩個示例。 

相關文章
相關標籤/搜索