O2 - 使用libudev获取设备信息(VirtualBox USB设备管理)

/** udev_get.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>  
#include <string.h>
#include <pthread.h>
#include <sys/stat.h>  
#include <sys/epoll.h>
#include <sys/utsname.h>
#include <libudev.h>
#include "udev_get.h"

#define DEVDEBUG

static struct user_devices *listhead = NULL;
static struct user_devices *listtail = NULL;
static struct user_devices *listread = NULL;
static pthread_mutex_t listmutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t atomicmutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_t g_pthid;
static int monitor_stop = 0;
static int initmark = 0;

enum devtype {DEVNULL = 0, DEVCHAR = 1, DEVBLK, DEVALL};
static int find_devtype(const char *devpath)
{
	struct stat devstat;
	if (NULL == devpath)
		return DEVNULL;
	if (stat(devpath, &devstat))
		return DEVNULL;

	//printf("%s (%d)\n", devpath, (int)devstat.st_mode);
	/*	必须先检查是否为块文件,根据定义来看,块设备都是字符设备
		#define	__S_IFCHR	0020000	Character device.	0010 0000 0000 0000
		#define	__S_IFBLK	0060000	Block device.		0110 0000 0000 0000
	*/
	if (S_IFBLK == (devstat.st_mode & S_IFBLK))
		return DEVBLK;
	else if(S_IFCHR == (devstat.st_mode & S_IFCHR))
		return DEVCHAR;
	else
		return DEVALL;
}

static int device_equal(struct user_devices *dev1, struct user_devices *dev2)
{
	if (NULL == dev1 || NULL == dev2) return 0;
	if (!strcmp(dev1->serial, dev2->serial)) 
		return 1;

	if (dev1->vendor_id[0] && dev2->vendor_id[0] && dev1->model_id[0] && dev2->model_id[0])
		if(!strcmp(dev1->vendor_id, dev2->vendor_id) && !strcmp(dev1->model_id, dev2->model_id))
			return 1;

	return 0;
	
}

static struct user_devices *list_find_dev_serial(struct user_devices *aimdev)
{
	pthread_mutex_lock(&listmutex);

	struct user_devices *retval = NULL;
	if (NULL == listhead || NULL == aimdev) goto out;
	struct user_devices *pdev = listhead;
	while(NULL != pdev) {
		if (device_equal(pdev, aimdev)) {
			retval = pdev;
			goto out;
		} else
			pdev = pdev->next;
	}

out:
	pthread_mutex_unlock(&listmutex);
	return retval;
}

static void insert_devnum(struct user_devices *in, struct user_devices *val)
{
	int i;
	for(i = 0; i < in->subdev_cnt; i++) {
		if (in->sdev[i].major == val->sdev[0].major && in->sdev[i].minor == val->sdev[0].minor)
			break;
	}
	if (i == in->subdev_cnt) {
		snprintf(in->sdev[i].syspath, sizeof(in->sdev[0].syspath), "%s", val->sdev[0].syspath);
		snprintf(in->sdev[i].devname, sizeof(in->sdev[0].devname), "%s", val->sdev[0].devname);
		in->sdev[i].major = val->sdev[0].major;
		in->sdev[i].minor = val->sdev[0].minor;
		in->subdev_cnt += 1;
	}
}

static void list_ergodic_dev()
{
	pthread_mutex_lock(&listmutex);

	struct user_devices *pdev = listhead;
	while(pdev != NULL) {
		printf("serial = %s\tsubdevcnt = %d\n", pdev->serial, pdev->subdev_cnt);
		pdev = pdev->next;
	}
	pthread_mutex_unlock(&listmutex);
}

static int list_add_dev(struct user_devices *pdev)
{
	pthread_mutex_lock(&listmutex);

	if (NULL == listhead || NULL == listtail) {
		listhead = listtail = pdev;
		goto out;
	}
		
	struct user_devices *p = listhead;
	struct user_devices *past = NULL;
	while (p != NULL) {
		if (device_equal(pdev, p)) {
			insert_devnum(p, pdev);
			free(pdev);
			goto out;	
		}
		past = p;
		p = p->next;
	}
	/* add a new user_devices to the listtail */
	past->next = pdev;
	listtail = pdev;
	pdev->next = NULL;

out:
	pthread_mutex_unlock(&listmutex);
	return 0;
}

static int list_del_dev(struct user_devices *pdev)
{
	pthread_mutex_lock(&listmutex);

	if (NULL == listhead || NULL == listtail)
		goto out;

	struct user_devices *cur = listhead, *past = NULL;
	while (cur != NULL) {
		if (device_equal(pdev, cur)) {
			if (cur == listread)
				listread = listread->next;
			if (listhead == cur)
				listhead = cur->next;
			else {
				if (listtail == cur) {
					listtail = past;
					past->next = NULL;
				} else {
					past->next = cur->next;
				}
			}
			free(cur);
			goto out;
		}
		past = cur;
		cur = cur->next;
	}
out:
	free(pdev);
	pthread_mutex_unlock(&listmutex);
	return 0;
}

static void list_clear_dev()
{
	pthread_mutex_lock(&atomicmutex);
	pthread_mutex_lock(&listmutex);
	struct user_devices *p = listhead;
	while(NULL != p) {
		struct user_devices *tmp = p;
		p = p->next;
		free(tmp);
	}
	listhead = listtail = listread = NULL;
	pthread_mutex_unlock(&listmutex);
	pthread_mutex_unlock(&atomicmutex);
}

static int list_node_cp(struct user_devices *dst, struct user_devices *src)
{
	if (NULL == src || NULL == dst) return 1;

	memcpy(dst, src, sizeof(struct user_devices));
	dst->next = NULL;
	return 0;
}

static struct user_devices *get_user_device(struct udev_device *device) 
{
	dev_t devnum;
	char *str;
	const char *serial, *serialshort, *vendor, *model, *revision, *bus, *syspath, *devname;
	struct utsname unamex;
	char prefixseri[128] = {0};
	
	devnum = udev_device_get_devnum(device);
	if (major(devnum) <= 0) goto errout;

	serial = udev_device_get_property_value(device, "ID_SERIAL");
	if (!serial) {
		serial = udev_device_get_property_value(device, "ID_MODEL_FROM_DATABASE");
		if (!serial) goto errout;
	}
	serialshort = udev_device_get_property_value(device, "ID_SERIAL_SHORT");
	if (serialshort) {
		if (NULL != (str = strstr(serial, serialshort)))
			str[-1] = '\0';
	}

	uname(&unamex);
	snprintf(prefixseri, sizeof(prefixseri), "Linux_%s", unamex.release);
	if (!strncmp(serial, prefixseri, strlen(prefixseri))) goto errout;
	struct user_devices *tmpdev = NULL;
	tmpdev = (struct user_devices *)calloc(1, sizeof(struct user_devices));
	if (NULL == tmpdev) goto errout;

	snprintf(tmpdev->serial, sizeof(tmpdev->serial), "%s", serial);

	vendor = udev_device_get_property_value(device, "ID_VENDOR_ID");
	if (vendor)
		snprintf(tmpdev->vendor_id, sizeof(tmpdev->vendor_id), "%s", vendor);
	model = udev_device_get_property_value(device, "ID_MODEL_ID");
	if (model)
		snprintf(tmpdev->model_id, sizeof(tmpdev->model_id), "%s", model);
	revision = udev_device_get_property_value(device, "ID_REVISION");
	if (revision)
		snprintf(tmpdev->revision, sizeof(tmpdev->revision), "%s", revision);
	bus = udev_device_get_property_value(device, "ID_BUS");
	if (bus)
		snprintf(tmpdev->bus, sizeof(tmpdev->bus), "%s", bus);

	int loop;
	for (loop = 0; loop < tmpdev->subdev_cnt; loop ++) {
		if (tmpdev->sdev[loop].major == major(devnum)
				&& tmpdev->sdev[loop].minor == minor(devnum))
			return tmpdev;
	}
	syspath = udev_device_get_syspath(device);
	if (syspath)
		snprintf(tmpdev->sdev[tmpdev->subdev_cnt].syspath, 
				sizeof(tmpdev->sdev[0].syspath), "%s", syspath);
	devname = udev_device_get_devnode(device);
	if (devname) {
		snprintf(tmpdev->sdev[tmpdev->subdev_cnt].devname, 
				sizeof(tmpdev->sdev[0].devname), "%s", devname);
		tmpdev->sdev[tmpdev->subdev_cnt].type = find_devtype(devname);
	}
	tmpdev->sdev[tmpdev->subdev_cnt].major = major(devnum);
	tmpdev->sdev[tmpdev->subdev_cnt].minor = minor(devnum);
	tmpdev->subdev_cnt += 1;

	return tmpdev;
errout:
	return NULL;
}

static int enumerate_devices(const char *subsystem) 
{
	struct udev *udev = NULL;
	struct udev_enumerate *udev_enumerate = NULL;
	struct udev_list_entry *list_entry;

	udev = udev_new();
	if (udev == NULL)
		return 1;

	udev_enumerate = udev_enumerate_new(udev);
	if (udev_enumerate == NULL)
		return -1;
	udev_enumerate_add_match_subsystem(udev_enumerate, subsystem);
	udev_enumerate_scan_devices(udev_enumerate);

	udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
		struct udev_device *device;

		device = udev_device_new_from_syspath(udev_enumerate_get_udev(udev_enumerate),
							  udev_list_entry_get_name(list_entry));
		if (device != NULL) {
			pthread_mutex_lock(&atomicmutex);
			struct user_devices *d = get_user_device(device);
			if (d)
				list_add_dev(d);
			pthread_mutex_unlock(&atomicmutex);
			udev_device_unref(device);
		}
	}

	udev_enumerate_unref(udev_enumerate);
	udev_unref(udev);

	return 0;
}

static void *devices_monitor(void *param) 
{
	struct udev *udev = NULL;
	udev = udev_new();
	struct udev_monitor *mon = NULL;
	int fd;

	mon = udev_monitor_new_from_netlink(udev, "udev");
	udev_monitor_enable_receiving(mon);
	fd = udev_monitor_get_fd(mon);
	while (!monitor_stop) {
		fd_set fds;
		struct timeval tv;
		int ret;
		
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
		tv.tv_sec = 0;
		tv.tv_usec = 1000 * 100;/* select timeout : 100ms */
		
		ret = select(fd+1, &fds, NULL, NULL, &tv);
		
		/* Check if our file descriptor has received data. */
		if (ret > 0 && FD_ISSET(fd, &fds)) {
			struct udev_device *dev;
			dev = udev_monitor_receive_device(mon);
			if (dev) {
				const char *str = udev_device_get_action(dev);
				if (str != NULL) {
					if (!strcmp(str, "remove")) {
						pthread_mutex_lock(&atomicmutex);
						struct user_devices *p = get_user_device(dev);
						if (p) list_del_dev(p);
						pthread_mutex_unlock(&atomicmutex);
					} else if (!strcmp(str, "add")) {
						pthread_mutex_lock(&atomicmutex);
						struct user_devices *p = get_user_device(dev);
						if (p) list_add_dev(p);
						pthread_mutex_unlock(&atomicmutex);
					}
				}
				
				udev_device_unref(dev);
			}				
		}
	}

	udev_monitor_unref(mon);
	udev_unref(udev);
	return NULL;
}

void udev_init()
{
	if (initmark) return;
	initmark = 1;
	listhead = NULL;
	listtail = NULL;
	listread = NULL;
	monitor_stop = 0;
	pthread_create(&g_pthid, NULL, devices_monitor, NULL);
}

void udev_uninit()
{
	monitor_stop = 1;
	initmark = 0;
	pthread_join(g_pthid, NULL);
	list_clear_dev();
}

int udev_get_dev(struct user_devices *pdev)
{
	int retval = 1;
	if (NULL == listhead)
		enumerate_devices(NULL);

	pthread_mutex_lock(&listmutex);
	if (NULL == pdev) 
		goto out;
	listread = listhead;
	if (NULL == listread)
		goto out;

	if (!list_node_cp(pdev, listread)) {
		listread = listread->next;
		retval = 0;
	}
out:
	pthread_mutex_unlock(&listmutex);
	return retval;
}

int udev_get_nextdev(struct user_devices *pdev)
{
	int retval = 1;
	pthread_mutex_lock(&listmutex);
	if (NULL == pdev || NULL == listread) 
		goto out;

	if (!list_node_cp(pdev, listread)) {
		listread = listread->next;
		retval = 0;
		goto out;
	}
out:
	pthread_mutex_unlock(&listmutex);
	return retval;
}

#ifdef DEVDEBUG
void printdev(struct user_devices *dev)
{
	int i;
	printf("serial = %s\n", dev->serial);
	printf("vendor_id = %s\n", dev->vendor_id);
	printf("model_id = %s\n", dev->model_id);
	printf("revision = %s\n", dev->revision);
	printf("bus = %s\n", dev->bus);
	
	for (i = 0; i < dev->subdev_cnt; i++) {
		printf("syspath = %s\n", dev->sdev[i].syspath);
		printf("devname = %s\n", dev->sdev[i].devname);
		printf("devnum = (%d:%d) (%d)\n", dev->sdev[i].major, dev->sdev[i].minor, dev->sdev[i].type);
	}
	printf("\n");
}

/* gcc udev_get.c -o udev_get -g -Wall -ludev -lpthread */
int main(int argc, char *argv[]) 
{
	int glob = 0;
while (1) {
	udev_init();
	struct user_devices dev;
	int loop = 100;
	while (--loop) {
		if (!udev_get_dev(&dev))
			printdev(&dev);
		while(!udev_get_nextdev(&dev))
			printdev(&dev);
		list_clear_dev();
		usleep(1000 * 1);
		printf("############ ((%d))\n\n", ++glob);
	}
	udev_uninit();
}
	return 0;
}
#endif

/** udev_get.h */
#ifndef UDEV_GET_H
#define UDEV_GET_H

struct subdev {
	char syspath[256];		/* /sys目录下的路径 */
	char devname[128];		/* /dev目录下的路径 */
	int major;			/* 主设备号 */
	int minor;			/* 次设备号 */
	int type;			/* 设备类型, 1表示字符设备,2表示块设备,其他值表示其他类型设备 */
};

struct user_devices {
	char serial[256];		/* 序列号,即设备的名字 */
	char vendor_id[8];		/* 产商ID */
	char model_id[8];		/* 模块ID */
	char revision[32];		/* 版本ID */
	char bus[32];			/* 总线类型 */
	int subdev_cnt;
	struct subdev sdev[32];
	struct user_devices *next;	/* NULL */
};

/* udev_init
* description:调用其他接口前调用该函数进行初始化
* param:无
* return:无
*/
void udev_init();

/* udev_uninit
* description:不再调用其他接口后,调用该函数进行反初始化
* param:无
* return:无
*/
void udev_uninit();

/* udev_get_dev
* description:遍历所有设备,先调用该函数,再调用udev_get_nextdev函数获取剩余的设备
* param:用户传入结构体指针用来存储获取的设备信息
* return:0表示正常,其他值表示错误
*/
int udev_get_dev(struct user_devices *pdev);

/* udev_get_nextdev
* description:遍历所有设备,在调用udev_get_dev后调用该函数
* param:用户传入结构体指针用来存储获取的设备信息
* return:0表示正常,其他值表示错误
*/
int udev_get_nextdev(struct user_devices *pdev);

#endif

#Makefile
udev:udev_get.o
	gcc udev_get.c -o udev -g -Wall -Wunused-function -ludev -lpthread
clean:
	rm -rf udev_get.o udev



参考文档:http://www.signal11.us/oss/udev/

udev_example.c

参考代码:systemd-221/src/test/test-libudev.c

你可能感兴趣的:(O2 - 使用libudev获取设备信息(VirtualBox USB设备管理))