源代码hwinfo分析与学习

源代码hwinfo分析与学习

源代码hwinfo分析与学习–1入口入门与目录解析

前序

一直以来都悄悄的收藏着大佬们的佳作,今天终于提笔撒网乱涂,源代码hwinfo分析与学习。
转载请记上出处 --jeffshuai 20230913

阅读hwinfo基本技能需求

  • c语言指针与链表:功力深厚
  • shell +makefile : 能看懂
  • linux 系统编程 :会勤劳查资料
  • perf :更佳

代码介绍

代码目录

一些重要的目录 和文件

hwinfo-21.63$ tree -L 2
.
├── doc
│   ├── ......
├── hwinfo     #make 后生成的bin文件
├── hwinfo.c   # main 入口
├── specifications.md # 随便看看
├── Makefile          # 根Makefile
├── Makefile.common   # 每个目录Makefile都会引用
├── src
│   ├── hd
│   │   ├── Makefile
│   │   ├── ......
│   ├── ids
│   │   ├── Makefile
│   │   ├── ......
│   ├── isdn
│   │   ├── Makefile
│   │   ├── ......
│   ├── libhd.a
│   ├── libhd.so -> libhd.so.21
│   ├── libhd.so.21 -> libhd.so.21.63.2
│   ├── libhd.so.21.63.2
│   ├── Makefile
│ ...... 省略些不重要的

代码初理解从 入口 makefile走起

LIBS		= -lhd
SLIBS		= -lhd
TLIBS		= -lhd_tiny
include Makefile.common
......
hwinfo: hwinfo.o $(LIBHD)
	$(CC) hwinfo.o $(LDFLAGS) $(CFLAGS) $(LIBS) -o $@

从上可看出hwinfo 是由hwinfo.o + libhd构成

文件 src/Makefile 解析

CFILES		= $(sort $(wildcard *.c))
OBJS		= $(CFILES:.c=.o)
LIBHD_BASE	= libhd
LIBHD		= $(TOPDIR)/src/$(LIBHD_BASE).a
LIBHD_SONAME	= $(LIBHD_BASE).so.$(LIBHD_MAJOR_VERSION)
LIBHD_NAME	= $(LIBHD_BASE).so.$(LIBHD_VERSION)
LIBHD_SO	= $(TOPDIR)/src/$(LIBHD_NAME)

$(LIBHD): $(OBJS)
	ar r $@ $?
	@rm -f $(LIBHD_D)
	
	$(CC) -shared $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -Wl,--whole-archive $(LIBHD) -Wl,--no-whole-archive \
		-Wl,-soname=$(LIBHD_SONAME) \
		-o $(LIBHD_SO) $(SO_LIBS) 

生成 libhd.a 和libhd.so*;

目录 src/isdn 解析

makefile:

isdn_cdb.h: isdn_cdb ISDN.CDB.txt
	rm -f isdn_cdb.h
	./isdn_cdb ISDN.CDB.txt isdn_cdb.h
	
ISDN.CDB.hwdb: mk_isdnhwdb ISDN.CDB.txt
	rm -f  ISDN.CDB.hwdb
	./mk_isdnhwdb ISDN.CDB.txt ISDN.CDB.hwdb

从上可看出 代码主要的功能: 生成工具 isdn_cdb和 mk_isdnhwdb,再用工具生成文件 ISDN.CDB.hwdb 和isdn_cdb.h;由 src/isdn/cdbisdn.c 》src/hd/isdn.c 调用生成API 在libhd中;详见hd_cdbisdn_**开头的API。

大约关系如下:
isdn_cdb.c+cdb_read.c 》isdn_cdb
ISDN.$(ARCH).txt 》 ISDN.CDB.txt 》isdn_cdb.h
ISDN.CDB.txt 》 ISDN.CDB.hwdb
其中 src/isdn/cdb 下的文件生成工具和 一堆数据文件,关乎vendor、device、vendor_id、device_id等

install -m 644 src/isdn/cdb/ISDN.CDB.txt $(DESTDIR)/usr/share/hwinfo
install -m 644 src/isdn/cdb/ISDN.CDB.hwdb $(DESTDIR)/usr/share/hwinfo

会将文件 ISDN.CDB.txt 和ISDN.CDB.txt 安装到系统中。

而 src/isdn/isa_probe.c 函数isa_isdn_t *isdn_detect()在libhd中调用

isa_isdn_t *isdn_detect()   
{ //ISDN(综合业务数字网)是传统电话服务的替换产品,“ ISDN(Integrated Services Digital Network),即综合业务数字网。是一个数字电话网络国际标准,是一种典型的电路交换网络系统。
    isa_isdn_t *ii = NULL;
    if (iopl(3) < 0) return ii;

    avm_a1_detect(&ii);  //vendor=AVM Computersysteme  AVM是一家德国柏林成立消费电子公司,生产通讯,网络设备,例如DSL,ISDN,无线和VoIP产品。Fritz!Box
    probe_elsa(&ii);    //vendor=Elsa
    telesdetect(&ii);  //vendor=Teles

    iopl(0);
    return ii;
}

从上可看出:isdn_detect 就是侦测 这三家厂商的ISDN设备的,ISDN 对于DOS代的 j e f f 现实中没有见过这种设备,感觉原代码大佬上石器时期就很牛了。

目录 src/smp 解析

其中的总入口函数int detectSMP(void)末被 其它调用过;末知其用, 故ffej认为此目录内与其它无关,暂可不阅 仅作知识扩展。
cpu三大架构 numa smp mpp。

  • SMP (Symmetric Multiprocessing) , 对称多处理器. 顾名思义, 在SMP中所有的处理器都是对等的, 它们通过总线连接共享同一块物理内存,从linux 上也能看到:
    ls /sys/devices/system/node/# 如果只看到一个node0 那就是smp架构
    可以看到只有仅仅一个node,经过大神们的测试发现,2至4个CPU比较适合smp架构。
  • NUMA ( Non-Uniform Memory Access),非均匀访问存储模型。
  • MPP (Massive Parallel Processing) ,这个其实可以理解为刀片服务器,每个刀扇里的都是一台独立的smp架构服务器,且每个刀扇之间均有高性能的网络设备进行交互,保证了smp服务器之间的数据传输性能。相比numa 来说更适合大规模的计算。

目录 src/ids 解析

IDFILES	+= src/bus src/class src/extra src/special src/scanner src/network \
	  src/usb src/usb2 src/isapnp src/monitor src/camera src/tv2 src/tv src/dvb2 src/dvb \
	  src/chipcard src/modem src/pcmcia src/s390 src/sdio
IDFILES += src/pci src/storage src/sound src/mouse src/braille

$(LIBHD_D): hd_ids.o
	ar r $(LIBHD) $?

check_hd: check_hd.c
	$(CC) $(CFLAGS) $< -o $@

hd_ids.c: hd_ids.h hd_ids_tiny.h

hd_ids.h hd.ids: check_hd $(IDFILES)
	./check_hd --check --sort --cfile hd_ids.h $(IDFILES)

hd_ids_tiny.h: check_hd hd.ids
	./check_hd --mini --cfile hd_ids_tiny.h --log=hd_tiny.log --out=hd_tiny.ids hd.ids

上面就是将src/ids/src下的所有数据ids数据文件通过工具命令check_hd生成hd_tiny.ids hd.ids;存放在/var/lib/hardware/hd.ids中,可用 "hwinfo --dump-db 1"查看。

convert_hd

src/ids/convert_hd 这个文件的第一行是 #! /usr/bin/perl,可看出是个perl代码。

更新同步上游ids数据库

在src/ids/update_pci_usb 中

curl --fail -o pci.ids http://pci-ids.ucw.cz/v2.2/pci.ids                      //下载 各种pci.ids 
curl --fail -o usb.ids http://www.linux-usb.org/usb.ids                   //下载 各种pci.ids 
./convert_hd pci.ids && mv hd.ids src/pci                                        //转化成hwinfo格式的文件
./convert_hd usb.ids && mv hd.ids src/usb                                       //转化成hwinfo格式的文件

  src/ids/update_x11
  system "../check_hd --sort --check --join-keys-first --split --log=$out.log --out=$out hd.ids";

Note: Use the update_pci_usb script to update pci and usb ids.
总结: 上述操作是从上游网站上更新IDS文件,并转化为hwinfo格式的文件,可用命令"hwinfo --dump-db 1"查看;注意: 更新同步上游ids数据库,需要手动去运行 update_pci_usb 脚本。

目录 src/hd 解析

src/hd 是libhd库的核心代码,是hwinfo.c的依赖;需要作重点分析的

那就先从hwinfo.c 的main入口开始吧,比如执行hwinfo --mouse

main
	do_hw(hd_data, f, hw_item[i]); //如果是多个项则 do_hw_multi
	    default: hd0 = hd_list(hd_data, hw_item, 1, NULL);
	    	hd_set_probe_feature_hw(hd_data, item);
        	hd_scan(hd_data);
        		hd_scan_no_hal
					hd_scan_input
						get_input_devices
							input = read_file("/proc/bus/input/devices", 0, 0);
								ADD2LOG
								//接下来就是针对内核接口内容一一解析如"/dev/input/event%u"  "/dev/input/mouse%u" 

上面展示了一个mouse 被扫描到的详细函数调用;针对其它类型的设备也是一样的流程展开。

src/hd/hd.h 这个文件里的数据结构 会被 其它文件引用
关键数据结构:


void hd_set_probe_feature_hw(hd_data_t *hd_data, hd_hw_item_t item)
{
    switch (item) {
    case hw_cdrom:
        hd_set_probe_feature(hd_data, pr_pci);
        hd_set_probe_feature(hd_data, pr_usb);
        hd_set_probe_feature(hd_data, pr_block_mods);
        hd_set_probe_feature(hd_data, pr_scsi);
        。。。。。。
    case hw_mouse:
        hd_set_probe_feature(hd_data, pr_misc);
        if (!hd_data->flags.fast) {
            hd_set_probe_feature(hd_data, pr_serial);
        }
        hd_set_probe_feature(hd_data, pr_usb);
        hd_set_probe_feature(hd_data, pr_kbd);
        hd_set_probe_feature(hd_data, pr_sys);
        hd_set_probe_feature(hd_data, pr_bios);
        hd_set_probe_feature(hd_data, pr_mouse);
        hd_set_probe_feature(hd_data, pr_input);
        hd_set_probe_feature(hd_data, pr_pci);
        break;
        。。。。。。
    case hw_camera:
        hd_set_probe_feature(hd_data, pr_usb);
        break;
}
hd_probe_feature_t;   //flags to control the probing.
void hd_set_probe_feature(hd_data_t *hd_data, enum probe_feature feature)

//如上代码 当指定一类设备扫描时,函数hd_set_probe_feature_hw调用hd_set_probe_feature 设置只扫加入的地方。


 * Note: hw_tv _must_ be < hw_display!
 * Sync with check_hd and convert_hd!
 */
typedef enum hw_item {
    hw_none = 0, hw_sys, hw_cpu, hw_keyboard, hw_braille, hw_mouse,
    hw_joystick, hw_printer, hw_scanner, hw_chipcard, hw_monitor, hw_tv,
    hw_display, hw_framebuffer, hw_camera, hw_sound, hw_storage_ctrl,
    hw_network_ctrl, hw_isdn, hw_modem, hw_network, hw_disk, hw_partition,
    hw_cdrom, hw_floppy, hw_manual, hw_usb_ctrl, hw_usb, hw_bios, hw_pci,
    hw_isapnp, hw_bridge, hw_hub, hw_scsi, hw_ide, hw_memory, hw_dvb,
    hw_pcmcia, hw_pcmcia_ctrl, hw_ieee1394, hw_ieee1394_ctrl, hw_hotplug,
    hw_hotplug_ctrl, hw_zip, hw_pppoe, hw_wlan, hw_redasd, hw_dsl, hw_block,
    hw_tape, hw_vbe, hw_bluetooth, hw_fingerprint, hw_mmc_ctrl,
    /** append new entries here */
    hw_unknown, hw_all                /**< hw_all must be last */
} hd_hw_item_t;
//针对设备作粗略的分类


/**
 * Individual hardware item.
 * Every hardware component gets an \ref hd_t entry. A list of all hardware
 * items is in \ref hd_data_t::hd.
 */
typedef struct s_hd_t {
    struct s_hd_t *next;      /**< Link to next hardware item. */
    /**
     * Unique index, starting at 1.
     * Use \ref hd_get_device_by_idx() to look up an hardware entry by index. And don't
     * free the result!
     */
    unsigned idx;
。。。。。。。。。。。
} hd_t;


/**
 * Holds all data accumulated during hardware probing.
 */
typedef struct {
    /**
     * @brief Current hardware list.
     * The list of all currently probed hardware. This is not identical with
     * the result of \ref hd_list(). (But a superset of it.)
     */
    hd_t *hd;
    。。。。。。。。。。。。
    str_list_t *only;

    /*
     * The following entries should *not* be accessed outside of libhd!
     */
    unsigned char probe[(pr_all + 7) / 8];    /**< (Internal) bitmask of probing features. */
    unsigned char probe_set[(pr_all + 7) / 8];    /**< (Iternal) bitmask of probing features that will always be set. */
    unsigned char probe_clr[(pr_all + 7) / 8];    /**< (Internal) bitmask of probing features that will always be reset. */
    hal_prop_t *probe_val;    /**< (Internal) probing features with arbitrary values */
    unsigned last_idx;        /**< (Internal) index of the last hd entry generated */
    unsigned module;      /**< (Internal) the current probing module we are in */
    enum boot_arch boot;      /**< (Internal) boot method */
    hd_t *old_hd;         /**< (Internal) old (outdated) entries (if you scan more than once) */
    pci_t *pci;           /**< (Internal) raw PCI data */
    isapnp_t *isapnp;     /**< (Internal) raw ISA-PnP data */
    cdrom_info_t *cdrom;      /**< (Internal) CDROM devs from PROC_CDROM_INFO */
    str_list_t *net;      /**< (Internal) list of network interfaces */
    str_list_t *floppy;       /**< (Internal) contents of PROC_NVRAM, used by the floppy module */
    misc_t *misc;         /**< (Internal) data gathered in the misc module */
    serial_t *serial;     /**< (Internal) /proc's serial info */
    scsi_t *scsi;         /**< (Internal) raw SCSI data */
    ser_device_t *ser_mouse;  /**< (Internal) info about serial mice */
    ser_device_t *ser_modem;  /**< (Internal) info about serial modems */
    str_list_t *cpu;      /**< (Internal) /proc/cpuinfo */
    str_list_t *klog;     /**< (Internal) kernel log */
    str_list_t *proc_usb;     /**< (Internal) /proc/bus/usb info */
    usb_t *usb;           /**< (Internal) usb info */
    modinfo_t *modinfo_ext;   /**< (Internal) external module info */
    modinfo_t *modinfo;       /**< (Internal) module info */
    hddb2_data_t *hddb2[2];   /**< (Internal) hardware database */
    str_list_t *kmods;        /**< (Internal) list of active kernel modules */
    uint64_t used_irqs;       /**< (Internal) irq usage */
    uint64_t assigned_irqs;   /**< (Internal) irqs automatically assigned by libhd (for driver info) */
    memory_range_t bios_rom;  /**< (Internal) BIOS 0xc0000 - 0xfffff */
    memory_range_t bios_ram;  /**< (Internal) BIOS 0x00400 - 0x004ff */
    memory_range_t bios_ebda; /**< (Internal) EBDA */
} hd_data_t;

hd_data_t 里的数据反复被每个环节的作操作,往里按逻辑写入各种设备的信息数据。

hal.c hal.h

只有3个接口,扫描设备:
void hd_scan_hal(hd_data_t *hd_data);
void hd_scan_hal_basic(hd_data_t *hd_data);
void hd_scan_hal_assign_udi(hd_data_t *hd_data);

hd_int.h

这里定义着各种linux操作系统的接口文件或命令宏。

#define PROC_CMDLINE        "/proc/cmdline"
#define PROC_PCI_DEVICES    "/proc/bus/pci/devices"
#define PROC_PCI_BUS        "/proc/bus/pci"
#define PROC_CPUINFO        "/proc/cpuinfo"
#define PROC_IOPORTS        "/proc/ioports"
#define DEV_MICE        "/dev/input/mice"
#define DEV_FB          "/dev/fb"
#define DEV_FB0         "/dev/fb0"
。。。。。。
#define PROG_MODPROBE       "/sbin/modprobe"
#define PROG_RMMOD      "/sbin/rmmod"
#define PROG_CARDCTL        "/sbin/cardctl"

#define PROGRESS(a, b, c) progress(hd_data, a, b, c)
#define ADD2LOG(a...) hd_log_printf(hd_data, a)

src/hd/hd.c 是所有设备数据操作的实现。

 ADD2LOG("----- /proc/bus/input/devices end -----\n");
 ADD2LOG("  abs = %s\n", abso);
void hd_log_printf(hd_data_t *hd_data, char *format, ...)
{
    ssize_t l;
    char *s = NULL;
    va_list args;

    va_start(args, format);
    l = vasprintf(&s, format, args);
    va_end(args);
    hd_log(hd_data, s, l); //jeff 认为这里将 获取内容或日志写入了hd_data,供后续解析
    free(s);
}

hdp.h hdp.c

格式输出 显示

hddb.h hddb.c

src/hd/hddb.c db数据库的查询解析处理。

再看看其它相关文件:

src/hd$ ls
bios.c     cpu.c  edd.c     hal.c       hd.h             input.c  isapnp.c  klog.c    memory.c  monitor.c  parallel.c  pppoe.c  sbus.c    sys.c
bios.h     cpu.h  edd.h     hal.h       hd_int.h         input.h  isapnp.h  klog.h    memory.h  monitor.h  parallel.h  pppoe.h  sbus.h    sys.h
block.c    drm.c  fb.c      hd.c         int.c    isdn.c    Makefile  misc.c    mouse.c    pci.c       prom.c   serial.c  usb.c
block.h    drm.h  fb.h      hddb.c       int.h    isdn.h    manual.c  misc.h    mouse.h    pci.h       prom.h   serial.h  usb.h
braille.c  dvd.c  floppy.c  hddb.h      hwclass_names.h  isa.c    kbd.c     manual.h  modem.c   net.c      pcmcia.c    s390.c   smbios.c  wlan.c
braille.h  dvd.h  floppy.h  hddb_int.h  ibm-notebooks.h  isa.h    kbd.h     mdt.c     modem.h   net.h      pcmcia.h    s390.h   smbios.h  wlan.h

从上可看出一个 xx.c+xx.h代表一种类的设备;在xx.h中有入口函数。
因涉太多的设备,且每一样都 不一样,故在后续开章节再来一一道 来。

代码编译运行测试

make 前记得加上如下环境变量:

export LIBDIR=/usr/lib/$(shell uname -m)-linux-gnu  
export HWINFO_VERSION=21.63.10  #定义编译的版本

需要sudo make install 才能调试,否则需要自己指定hwinfo 编译链接运行的路径。

小结

其中太多的用c语言用来解析字符串文件感觉 ,感觉就是用手术刀 切牛,从编程效率和维护角度讲,太难了。
但从代码上可看出:原作者对C语言指针与链表,linux系统接口编程及硬件这块掌握的太深,很多逻辑都 需要与每块linux子框架系统相结合作了解 。
后续针对每类设备,第个系统文件作一一分析, je f f得花上好多时间与精力,写到哪算哪吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值