/** 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