Device & Driver -2- (Bus & Class)

<kernel v4.14>

버스, 디바이스, 드라이버 및 클래스의 관계

다음 그림의 위 박스에 물리적으로 버스에 두 개의 디바이스가 연결되어 있는 것을 알 수 있다. 아래 박스에는 이에 해당하는 구조체들이 구현 레벨로 표시되어 있다.

  • 버스 하나에 버스 컨트롤러 디바이스 및 Consumer 디바이스가 구성되어 있다.
  • 디바이스 하나에 device 구조체와 device_driver 구조체 두 개가 사용되어 등록되는 것을 알 수 있다.

 

디바이스는 버스와 연결될 디바이스와 클래스에 연결될 디바이스로 보통 구분하여 사용한다. 하나의 디바이스로 버스와 클래스를 동시에 지정하는 경우는 지원하지 않는다. 그리고 드라이버는 버스에 연결되는 디바이스와 매치되는 경우 바인딩되어 연동된다.

  • 버스 디바이스
    • 1개의 버스를 지정
  • 클래스 디바이스
    • 1개의 클래스를 지정
  • 드라이버
    • 버스 디바이스와 바인딩된다.

 

디바이스나 드라이버가 등록되기 이전에 버스와 클래스가 이미 등록되어 있어야 한다. 그런 후 버스에 디바이스와 드라이버가 포함될 수 있다. 클래스는 디바이스만을 포함시킬 수 있다.

  • 버스
    • 다수의 버스 디바이스들과 드라이버들을 관리한다.
  • 클래스
    • 다수의 클래스 디바이스들을 관리한다.

 

일반적으로 1개 이상의 클래스 디바이스를 만들 때, 먼저 버스 디바이스들을 만들고 그 밑으로 클래스 디바이스들을 생성하여 사용한다.

 

버스

버스를 등록하려면 다음과 같이 두 단계를 사용한다. (예: 플랫폼 버스)

  • 첫 번째, 버스 컨트롤러 디바이스(struct device) 등록
    • device_register(&platform_bus);
  • 두 번째, 버스(struct bus_type) 등록
    • bus_register(&platform_bus_type);

 

bus_type 구조체

include/linux/device.h

/**                                                                             
 * struct bus_type - The bus type of the device                                 
 *                                                                              
 * @name:   The name of the bus.                                                
 * @dev_name:   Used for subsystems to enumerate devices like ("foo%u", dev->id).
 * @dev_root:   Default device to use as the parent.                            
 * @bus_groups: Default attributes of the bus.                                  
 * @dev_groups: Default attributes of the devices on the bus.                   
 * @drv_groups: Default attributes of the device drivers on the bus.            
 * @match:  Called, perhaps multiple times, whenever a new device or driver     
 *      is added for this bus. It should return a positive value if the         
 *      given device can be handled by the given driver and zero                
 *      otherwise. It may also return error code if determining that            
 *      the driver supports the device is not possible. In case of              
 *      -EPROBE_DEFER it will queue the device for deferred probing.            
 * @uevent: Called when a device is added, removed, or a few other things       
 *      that generate uevents to add the environment variables.                 
 * @probe:  Called when a new device or driver add to this bus, and callback    
 *      the specific driver's probe to initial the matched device.              
 * @remove: Called when a device removed from this bus.                         
 * @shutdown:   Called at shut-down time to quiesce the device.                 
 *                                                                              
 * @online: Called to put the device back online (after offlining it).          
 * @offline:    Called to put the device offline for hot-removal. May fail.     
 *                                                                              
 * @suspend:    Called when a device on this bus wants to go to sleep mode.     
 * @resume: Called to bring a device on this bus out of sleep mode.             
 * @num_vf: Called to find out how many virtual functions a device on this      
 *      bus supports.                                                           
 * @pm:     Power management operations of this bus, callback the specific      
 *      device driver's pm-ops.                                                 
 * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU    
 *              driver implementations to a bus and allow the driver to do      
 *              bus-specific setup                                              
 * @p:      The private data of the driver core, only the driver core can       
 *      touch this.                                                             
 * @lock_key:   Lock class key for use by the lock validator                    
 *                                                                              
 * A bus is a channel between the processor and one or more devices. For the    
 * purposes of the device model, all devices are connected via a bus, even if   
 * it is an internal, virtual, "platform" bus. Buses can plug into each other.  
 * A USB controller is usually a PCI device, for example. The device model      
 * represents the actual connections between buses and the devices they control.
 * A bus is represented by the bus_type structure. It contains the name, the    
 * default attributes, the bus' methods, PM operations, and the driver core's   
 * private data.                                                                
 */
struct bus_type {                                                               
    const char      *name;                                                      
    const char      *dev_name;                                                  
    struct device       *dev_root;                                              
    const struct attribute_group **bus_groups;                                  
    const struct attribute_group **dev_groups;                                  
    const struct attribute_group **drv_groups;                                  
                                                                                
    int (*match)(struct device *dev, struct device_driver *drv);                
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);             
    int (*probe)(struct device *dev);                                           
    int (*remove)(struct device *dev);                                          
    void (*shutdown)(struct device *dev);                                       
                                                                                
    int (*online)(struct device *dev);                                          
    int (*offline)(struct device *dev);                                         
                                                                                
    int (*suspend)(struct device *dev, pm_message_t state);                     
    int (*resume)(struct device *dev);                                          
                                                                                
    int (*num_vf)(struct device *dev);                                          
                                                                                
    const struct dev_pm_ops *pm;                                                
                                                                                
    const struct iommu_ops *iommu_ops;                                          
                                                                                
    struct subsys_private *p;                                                   
    struct lock_class_key lock_key;                                             
};
  • *name
    • 버스명
  • *dev_name
    • 인스턴스가 포함된 버스 디바이스 명 (형태: “%s%d”, name, dev->id)
    • 예) foo1
  • *dev_root
    • 부모로 사용되는 디폴트 디바이스
  • **bus_groups
    • 버스 속성들을 지정하면 버스 디렉토리에 버스 속성들이 생성된다.
      • 예) /sys/bus/foo_bus 디렉토리에 버스 속성이 만들어진다.
  • **dev_groups
    • 버스에 디바이스가 추가되는 경우 생성되는 디바이스 디렉토리에 이 버스 디바이스 속성들이 생성된다.
      • 예) /sys/devices/foo 디렉토리에 속성들이 생성된다.
  • **drv_groups
    • 버스에 드라이버가 추가되는 경우 생성되는 드라이버 디렉토리에 이 버스 드라이버 속성들이 생성된다.
      • 예) /sys/bus/foo/drivers/foo_driver 디렉토리에 속성들이 생성된다.
  • (*match)
    • 디바이스나 드라이버가 버스에 연결될 때 이 후크 함수가 호출한다.
    • 이 후크 함수의 호출 결과가 양수면 다음 probe 과정이 진행된다.
  • (*uevent)
    • 디바이스가 추가/제거될 때 환경 변수를 추가하도록 이벤트가 발생을 위해 호출된다.
  • (*probe)
    • 버스에 id 매치된 새 디바이스 또는 드라이버가 추가될 때 이 후크 함수가 호출되어 디바이스나 드라이버가 등록되도록 한다.
  • (*remove)
    • 버스에서 디바이스 또는 드라이버가 제거될 때 호출되는 후크 함수이다.
  • (*shutdown)
    • 전원이 off되기 전에 호출되는 후크 함수이다.
  • (*online)
    • 디바이스가 연결(hot-plug)되어 online된 후 호출되는 후크 함수이다.
  • (*offline)
    • 디바이스가 분리(hot-removal)되어 offline될 때 호출되는 후크 함수이다.
  • (*suspend)
    • 절전 모드 진입 시 호출되는 후크 함수이다.
  • (*resume)
    • 절전 모드로 부터 벗어난 후 호출되는 후크 함수이다.
  • (*num_vf)
    • 버스에서 virtual 펑션들의 수를 알아내기 위해 호출되는 후크 함수이다.
  • *pm
    • 전원(절전 모드) 관리가 필요한 경우 사용되는 오퍼레이션
  • *iommu_ops
    • IOMMU 장치가 있는 경우 필요한 IOMMU 오퍼레이션
  • *p
    • 드라이버와 서브시스템간 하이라키 연결을 드라이버 core가 관리하는 내부 자료 구조이다.
  • lock_key
    • lock

 

버스 등록

인자로 받아온 bus_type 구조체를 통해 버스를 등록하는 과정을 알아본다.

 

bus_register()

drivers/base/bus.c

/**                                                                             
 * bus_register - register a driver-core subsystem                              
 * @bus: bus to register                                                        
 *                                                                              
 * Once we have that, we register the bus with the kobject                      
 * infrastructure, then register the children subsystems it has:                
 * the devices and drivers that belong to the subsystem.                        
 */                                                                             
int bus_register(struct bus_type *bus)                                          
{                                                                               
    int retval;                                                                 
    struct subsys_private *priv;                                                
    struct lock_class_key *key = &bus->lock_key;                                
                                                                                
    priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);                  
    if (!priv)                                                                  
        return -ENOMEM;                                                         
                                                                                
    priv->bus = bus;                                                            
    bus->p = priv;                                                              
                                                                                
    BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);                           
                                                                                
    retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);             
    if (retval)                                                                 
        goto out;                                                               
                                                                                
    priv->subsys.kobj.kset = bus_kset;                                          
    priv->subsys.kobj.ktype = &bus_ktype;                                       
    priv->drivers_autoprobe = 1;                                                
                                                                                
    retval = kset_register(&priv->subsys);                                      
    if (retval)                                                                 
        goto out;                                                               
                                                                                
    retval = bus_create_file(bus, &bus_attr_uevent);                            
    if (retval)                                                                 
        goto bus_uevent_fail;                                                   
                                                                                
    priv->devices_kset = kset_create_and_add("devices", NULL,                   
                         &priv->subsys.kobj);                                   
    if (!priv->devices_kset) {                                                  
        retval = -ENOMEM;                                                       
        goto bus_devices_fail;                                                  
    }                                                                           
                                                                                
    priv->drivers_kset = kset_create_and_add("drivers", NULL,                   
                         &priv->subsys.kobj);                                   
    if (!priv->drivers_kset) {                                                  
        retval = -ENOMEM;                                                       
        goto bus_drivers_fail;                                                  
    }

요청한 버스를 등록한다. 이 때 sysfs의 “/sys/bus”에 버스명으로 디렉토리를 만들고 관련 디렉토리들 및 버스 속성들을 추가한다.

  • 코드 라인 15~20에서 버스와 서브시스템간의 하이 라키 관계를 구성하도록 subsys_private 구조체를 할당받고 버스와 서로 연결한다.
  • 코드 라인 22에서 bus notifier 리스트를 초기화한다.
    • 디바이스가 이 버스에 등록/제거될 때 마다 이 리스트에 담긴 notifier 블럭의 함수를 호출하는 용도로 관리한다.
  • 코드 라인 24~26에서 버스 디렉토리 이름을 버스 이름으로 동일하게 사용한다.
    • 예) “foo” 버스명 -> /sys/bus/foo
  • 코드 라인 28~30에서 버스 위치를 /sys/bus로 지정하고, 속성 보기 및 변경에 대한 오퍼레이션을 디폴트 버스 오퍼레이션으로 지정한다. 그리고 이 버스에 등록되는 드라이버를 자동 probe할 수 있도록 설정한다.
    • (*release) 후크 함수
    • (*show) 및 (*store) 후크 함수
  • 코드 라인 32~34에서 버스명에 해당하는 디렉토리를 “/sys/bus”에 생성한다.
  • 코드 라인 36~38에서 uevent 속성을 추가한다.
  • 코드 라인 40~45에서 “/sys/bus” 디렉토리 뒤에 “devices”를 생성한다.
  • 코드 라인 47~52에서 “/sys/bus” 디렉토리 뒤에 “drivers”를 생성한다.

 

    INIT_LIST_HEAD(&priv->interfaces);                                          
    __mutex_init(&priv->mutex, "subsys mutex", key);                            
    klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);     
    klist_init(&priv->klist_drivers, NULL, NULL);                               
                                                                                
    retval = add_probe_files(bus);                                              
    if (retval)                                                                 
        goto bus_probe_files_fail;                                              
                                                                                
    retval = bus_add_groups(bus, bus->bus_groups);                              
    if (retval)                                                                 
        goto bus_groups_fail;                                                   
                                                                                
    pr_debug("bus: '%s': registered\n", bus->name);                             
    return 0;                                                                   
                                                                                
bus_groups_fail:                                                                
    remove_probe_files(bus);                                                    
bus_probe_files_fail:                                                           
    kset_unregister(bus->p->drivers_kset);                                      
bus_drivers_fail:                                                               
    kset_unregister(bus->p->devices_kset);                                      
bus_devices_fail:                                                               
    bus_remove_file(bus, &bus_attr_uevent);                                     
bus_uevent_fail:                                                                
    kset_unregister(&bus->p->subsys);                                           
out:                                                                            
    kfree(bus->p);                                                              
    bus->p = NULL;                                                              
    return retval;                                                              
}                                                                               
EXPORT_SYMBOL_GPL(bus_register);
  • 코드 라인 1~4에서 버스의 인터페이스, list_devices 및 klist_dirvers들을 초기화한다.
  • 코드 라인 6~8에서 drivers_probe 및 drivers_autoprobe 속성을 생성한다.
  • 코드 라인 10~12에서 버스 속성 그룹들을 추가한다.

 

다음 그림은 foo 버스를 추가할 때의 과정을 보여준다.

 

add_probe_files()

drivers/base/bus.c

static int add_probe_files(struct bus_type *bus)                                
{                                                                               
    int retval;                                                                 
                                                                                
    retval = bus_create_file(bus, &bus_attr_drivers_probe);                     
    if (retval)                                                                 
        goto out;                                                               
                                                                                
    retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);                 
    if (retval)                                                                 
        bus_remove_file(bus, &bus_attr_drivers_probe);                          
out:                                                                            
    return retval;                                                              
}

drivers_probe 및 drivers_autoprobe 속성을 생성한다.

 

다음 그림은 플랫폼 버스에 연결된 i2c 컨트롤러와 1개의 온도 센서가 i2c 버스에 연결된 상태이다. 이 때 속성들을 살펴보자.

 

다음 그림은 위의 상황과 동일하며 다음 심볼 링크가 어느 곳에 연결되어 있는지 확인하여 보자.

  • /sys/devices
    • device 심볼 링크
    • subsystem 심볼 링크
  • /sys/bus
    • 디바이스명으로된 심볼 링크
  • /sys/class
    • 클래스 디바이스명으로된 심볼 링크

 

버스 및 클래스 하이라키 관계 관리

subsys_private 구조체

drivers/base/base.h

/**
 * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
 *
 * @subsys - the struct kset that defines this subsystem
 * @devices_kset - the subsystem's 'devices' directory
 * @interfaces - list of subsystem interfaces associated
 * @mutex - protect the devices, and interfaces lists.
 *
 * @drivers_kset - the list of drivers associated
 * @klist_devices - the klist to iterate over the @devices_kset
 * @klist_drivers - the klist to iterate over the @drivers_kset
 * @bus_notifier - the bus notifier list for anything that cares about things
 *                 on this bus.
 * @bus - pointer back to the struct bus_type that this structure is associated
 *        with.
 *
 * @glue_dirs - "glue" directory to put in-between the parent device to
 *              avoid namespace conflicts
 * @class - pointer back to the struct class that this structure is associated
 *          with.
 *
 * This structure is the one that is the actual kobject allowing struct
 * bus_type/class to be statically allocated safely.  Nothing outside of the
 * driver core should ever touch these fields.
 */
struct subsys_private {
        struct kset subsys;
        struct kset *devices_kset;
        struct list_head interfaces;
        struct mutex mutex;

        struct kset *drivers_kset;
        struct klist klist_devices;
        struct klist klist_drivers;
        struct blocking_notifier_head bus_notifier;
        unsigned int drivers_autoprobe:1;
        struct bus_type *bus;

        struct kset glue_dirs;
        struct class *class;
};

이 구조체는 다음과 bus에서도 사용되고 class에서도 사용된다.

bus에서 사용될 때
  • subsys
    • 버스 디렉토리에 해당하는 kset이다.
  • *devices_kset
    • 버스의 devices 디렉토리에 해당하는 kset를 가리킨다.
      • 예) /sys/bus/foo/devices
  • interfaces
    • 버스 인터페이스들
  • mutex
    • 리스트를 보호하기위한 mutex
  •  *drivers_kset
    • 버스의 drivers 디렉토리에 해당하는 kset를 가리킨다.
      • 예) /sys/bus/foo/drivers
  • klist_devices
    • 버스에 등록될 디바이스 리스트
  • klist_drivers
    • 버스에 등록될 드라이버 리스트
  • bus_notifier
    • 버스에 디바이스가 등록/해제 또는 드라이버가 bind/unbind될 때 마다 호출될 notifiler block 노드로 사용된다.
  • drivers_autoprobe
    • 버스가 autoprobe를 지원할지 여부
      • 디폴트: 1
  • *bus
    • 버스를 가리킨다.
class에서 사용될 때
  • subsys
    • 클래스 디렉토리에 해당하는 kset이다.
  • interfaces
    • 클래스 인터페이스들
  • mutex
    • 리스트를 보호하기위한 mutex
  • klist_devices
    • 클래스에 등록될 디바이스 리스트
  • glue_dirs
    • 이름 충돌을 피하기위해 부모 디바이스와 끼워넣을 디렉토리
  • *class
    • 클래스를 가리킨다.

 

버스에 디바이스 추가

bus_add_device()

drivers/base/bus.c

/**                                                                             
 * bus_add_device - add device to bus                                           
 * @dev: device being added                                                     
 *                                                                              
 * - Add device's bus attributes.                                               
 * - Create links to device's bus.                                              
 * - Add the device to its bus's list of devices.                               
 */                                                                             
int bus_add_device(struct device *dev)                                          
{                                                                               
    struct bus_type *bus = bus_get(dev->bus);                                   
    int error = 0;                                                              
                                                                                
    if (bus) {                                                                  
        pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));       
        error = device_add_groups(dev, bus->dev_groups);                        
        if (error)                                                              
            goto out_put;                                                       
        error = sysfs_create_link(&bus->p->devices_kset->kobj,                  
                        &dev->kobj, dev_name(dev));                             
        if (error)                                                              
            goto out_groups;                                                    
        error = sysfs_create_link(&dev->kobj,                                   
                &dev->bus->p->subsys.kobj, "subsystem");                        
        if (error)                                                              
            goto out_subsys;                                                    
        klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);             
    }                                                                           
    return 0;                                                                   
                                                                                
out_subsys:                                                                     
    sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));              
out_groups:                                                                     
    device_remove_groups(dev, bus->dev_groups);                                 
out_put:                                                                        
    bus_put(dev->bus);                                                          
    return error;                                                               
}

버스에 디바이스를 추가한다.

  • 코드 라인 14~18에서 버스에 추가될 디바이스인 경우 해당 버스에 소속된 디바이스 속성들을 추가한다.
    • 예) gpio 디바이스가 플랫폼 버스에 추가되는 경우
      • drivers_autoprobe, drivers_probe 속성이 추가된다.
  • 코드 라인 19~22에서 해당 버스의 디바이스 디렉토리에 디바이스명으로 심볼 링크를 생성하여 해당 디바이스 디렉토리를 가리키게 한다.
    • 플랫폼 버스 예) /sys/bus/platform/devices/<devname> -> /sys/devices/platform/<devname>
  • 코드 라인 23~26에서 디바이스 디렉토리에  “subsystem” 심볼 링크를 생성하여 해당 버스 디렉토리를 가리키게한다.
    • 플랫폼 버스 예) /sys/devices/platform/<devname>/subsystem -> /sys/bus/platform
  • 코드 라인 27에서 버스가 관리하는 디바이스 리스트에 디바이스를 추가한다.

 

다음 그림과 같이 디바이스 디렉토리에 버스 타입 속성을 추가한다. 그리고 디바이스와 버스 타입과 심볼 링크로 서로 연결한다.

 

버스에 드라이버 추가

bus_add_driver()

drivers/base/bus.c

/**
 * bus_add_driver - Add a driver to the bus.
 * @drv: driver.
 */
int bus_add_driver(struct device_driver *drv)
{
        struct bus_type *bus; 
        struct driver_private *priv;
        int error = 0;

        bus = bus_get(drv->bus);
        if (!bus)
                return -EINVAL;

        pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv) {
                error = -ENOMEM;
                goto out_put_bus;
        }
        klist_init(&priv->klist_devices, NULL, NULL);
        priv->driver = drv;
        drv->p = priv;
        priv->kobj.kset = bus->p->drivers_kset;
        error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
                                     "%s", drv->name);
        if (error)
                goto out_unregister;
        
        klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
        if (drv->bus->p->drivers_autoprobe) {
                if (driver_allows_async_probing(drv)) {
                        pr_debug("bus: '%s': probing driver %s asynchronously\n",
                                drv->bus->name, drv->name); 
                        async_schedule(driver_attach_async, drv);
                } else {
                        error = driver_attach(drv);
                        if (error)
                                goto out_unregister;
                }
        }
        module_add_driver(drv->owner, drv);

버스에 드라이버를 추가한다.

  • 코드 라인 11~13에서 드라이버에 해당되는 버스를 참조하기 위해 참조카운터를 1 증가시킨다.
  • 코드 라인 17~23에서 driver_private 구조체를 생성하고 드라이버와 서로 가리키게한다.
  • 코드 라인 24~28에서 버스의 드라이버 디렉토리에 드라이버명으로 디렉토리를 만든다.
    • 플랫폼 버스 예) sys/bus/platform/drivers/<foo>
  • 코드 라인 30에서 버스가 관리하는 드라이버 리스트에 현재 드라이버를 추가한다.
  • 코드 라인 31~41에서 버스에 drivers_autoprobe 속성이 1로 설정된 경우 드라이버에 디바이스들을 찾아 attach한다. 드라이버가 비동기 probe를 지원하는 경우에는 스레드를 사용하여 attach한다.
    • 예) /sys/bus/platform/drivers_autoprobe
  • 코드 라인 42에서 sysfs의 모듈 디렉토리에 드라이버의 모듈 디렉토리 및 속성을 추가한다.

 

        error = driver_create_file(drv, &driver_attr_uevent);
        if (error) {
                printk(KERN_ERR "%s: uevent attr (%s) failed\n",
                        __func__, drv->name);
        }
        error = driver_add_groups(drv, bus->drv_groups);
        if (error) {
                /* How the hell do we get out of this pickle? Give up */
                printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
                        __func__, drv->name);
        }

        if (!drv->suppress_bind_attrs) {
                error = add_bind_files(drv);
                if (error) {
                        /* Ditto */
                        printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
                                __func__, drv->name);
                }
        }

        return 0;

out_unregister:
        kobject_put(&priv->kobj);
        /* drv->p is freed in driver_release()  */
        drv->p = NULL;
out_put_bus:
        bus_put(bus);
        return error;
}
  • 코드 라인 1~5에서 드라이버에 uevent 속성을 추가한다.
  • 코드 라인 6~11에서 드라이버에 버스에 등록된 드라이버 속성들을 추가한다.
  • 코드 라인 13~20에서 드라이버에 suppress_bind_attrs 값이 설정되지 않은 상태인 경우 bind 및 unbind 속성을 추가한다.

 

Subsys 인터페이스

Subsys 인터페이스를 사용하는 드라이버가 일부 있으며 다음의 드라이버들에서 사용되고 있다.

  • drivers/net/rionet.c
  • drivers/cpufreq/cpufreq.c
  • drivers/cpufreq/s3c2412-cpufreq.c
  • drivers/rapidio/rio_cm.c
  • drivers/misc/mic/scif/scif_api.c

 

subsys_interface 구조체

include/linux/device.h

/**
 * struct subsys_interface - interfaces to device functions
 * @name:       name of the device function
 * @subsys:     subsytem of the devices to attach to
 * @node:       the list of functions registered at the subsystem
 * @add_dev:    device hookup to device function handler
 * @remove_dev: device hookup to device function handler
 *
 * Simple interfaces attached to a subsystem. Multiple interfaces can
 * attach to a subsystem and its devices. Unlike drivers, they do not
 * exclusively claim or control devices. Interfaces usually represent
 * a specific functionality of a subsystem/class of devices.
 */
struct subsys_interface {
        const char *name;
        struct bus_type *subsys;
        struct list_head node;
        int (*add_dev)(struct device *dev, struct subsys_interface *sif);
        void (*remove_dev)(struct device *dev, struct subsys_interface *sif);
};

이 구조체는 bus에서만 사용된다. (주의: class에서 사용하는 인터페이스는 class_interface 구조체이다)

  •  name
    • 버스 인터페이스명
  • *subsys
    • 버스 타입을 가리킨다.
  • node
    • bus_type->p->interfaces에 등록될 링크 노드로 사용된다.
  • (*add_dev)
    • 서브시스템 인터페이스가 등록될 때 버스 타입에 등록된 디바이스들을 대상으로 이 후크 함수가 호출된다.
  • (*remove_dev)
    • 서브시스템 인터페이스가 해제될 때 버스 타입에 등록된 디바이스들을 대상으로 이 후크 함수가 호출된다.

 

subsys_interface_register()

drivers/base/bus.c

int subsys_interface_register(struct subsys_interface *sif)
{
        struct bus_type *subsys;
        struct subsys_dev_iter iter;
        struct device *dev;

        if (!sif || !sif->subsys)
                return -ENODEV;

        subsys = bus_get(sif->subsys);
        if (!subsys)
                return -EINVAL;

        mutex_lock(&subsys->p->mutex);
        list_add_tail(&sif->node, &subsys->p->interfaces);
        if (sif->add_dev) {
                subsys_dev_iter_init(&iter, subsys, NULL, NULL);
                while ((dev = subsys_dev_iter_next(&iter)))
                        sif->add_dev(dev, sif);
                subsys_dev_iter_exit(&iter);
        }
        mutex_unlock(&subsys->p->mutex);

        return 0;
}
EXPORT_SYMBOL_GPL(subsys_interface_register);

sif(서브시스템 인터페이스)를 등록하고 버스 타입에 등록된 버스 디바이스들을 대상으로 (*add_dev) 후크 함수를 호출한다.

  • 코드 라인 7~8에서 인자로 전달받은 서브시스템 인터페이스가 null이거나 지정된 서브시스템이 없는 경우 -ENODEV 에러를 반환한다.
  • 코드 라인 10~12에서 버스 타입에 접근하기 위해 참조 카운터를 1 증가시킨다.
  • 코드 라인 14~15에서 버스 타입에 락을 걸고 버스 타입에 sif 인터페이스를 추가한다.
  • 코드 라인 16~21에서 sif에 (*add_dev) 후크 함수가 구현되어 있는 경우 버스 타입에 등록된 버스 디바이스들을 대상으로 (*add_dev) 후크 함수를 호출한다.

 

BUS 종류

다음과 같은 버스들이 커널에 정의되어 있다.

net/iucv/iucv.c                                 bus_register(&iucv_bus) 
sound/hda/hda_bus_type.c                        bus_register(&snd_hda_bus_type) 
sound/ac97_bus.c                                bus_register(&ac97_bus_type); 
sound/aoa/soundbus/core.c                       bus_register(&soundbus_bus_type) 
sound/core/seq_device.c                         bus_register(&snd_seq_bus_type) 
kernel/events/core.c                            bus_register(&pmu_bus)
drivers/macintosh/macio_asic.c			bus_register(&macio_bus_type)
drivers/platform/x86/wmi.c			bus_register(&wmi_bus_type)
drivers/target/loopback/tcm_loop.c		bus_register(&tcm_loop_lld_bus)
drivers/edac/edac_mc_sysfs.c			bus_register(mci->bus)
drivers/fmc/fmc-core.c				bus_register(&fmc_bus_type)
drivers/input/gameport/gameport.c		bus_register(&gameport_bus)
drivers/input/rmi4/rmi_bus.c			bus_register(&rmi_bus_type)
drivers/input/serio/serio.c			bus_register(&serio_bus)
drivers/gpu/drm/drm_mipi_dsi.c			bus_register(&mipi_dsi_bus_type)
drivers/gpu/host1x/dev.c			bus_register(&host1x_bus_type)
drivers/parport/share.c			        bus_register(&parport_bus_type)
drivers/acpi/bus.c				bus_register(&acpi_bus_type)
drivers/spmi/spmi.c				bus_register(&spmi_bus_type)
drivers/amba/bus.c				bus_register(&amba_bustype)
drivers/scsi/scsi_sysfs.c			bus_register(&scsi_bus_type)
drivers/scsi/fcoe/fcoe_sysfs.c			bus_register(&fcoe_bus_type)
drivers/scsi/scsi_debug.c			bus_register(&pseudo_lld_bus)
drivers/scsi/scsi_transport_iscsi.c		bus_register(&iscsi_flashnode_bus)
drivers/iio/industrialio-core.c		        bus_register(&iio_bus_type)
drivers/mcb/mcb-core.c				bus_register(&mcb_bus_type)
drivers/w1/w1.c				        bus_register(&w1_bus_type)
drivers/hwtracing/intel_th/core.c		bus_register(&intel_th_bus)
drivers/hwtracing/coresight/coresight.c	        bus_register(&coresight_bustype)
drivers/tty/serdev/core.c			bus_register(&serdev_bus_type)
drivers/pcmcia/ds.c				bus_register(&pcmcia_bus_type)
drivers/ssb/main.c				bus_register(&ssb_bustype)
drivers/vlynq/vlynq.c				bus_register(&vlynq_bus_type)
drivers/hid/hid-core.c				bus_register(&hid_bus_type)
drivers/hid/intel-ish-hid/ishtp/bus.c		bus_register(&ishtp_cl_bus_type)
drivers/pci/endpoint/pci-epf-core.c		bus_register(&pci_epf_bus_type)
drivers/pci/pci-driver.c			bus_register(&pci_bus_type)
drivers/pci/pcie/portdrv_bus.c			bus_register(&pcie_port_bus_type)
drivers/rapidio/rio-driver.c			bus_register(&rio_bus_type)
drivers/i2c/i2c-core-base.c			bus_register(&i2c_bus_type)
drivers/vme/vme.c				bus_register(&vme_bus_type)
drivers/eisa/eisa-bus.c			        bus_register(&eisa_bus_type)
drivers/fsi/fsi-core.c				bus_register(&fsi_bus_type)
drivers/hsi/hsi_core.c				bus_register(&hsi_bus_type)
drivers/sh/superhyway/superhyway.c		bus_register(&superhyway_bus_type)
drivers/sh/maple/maple.c			bus_register(&maple_bus_type)
drivers/zorro/zorro-driver.c			bus_register(&zorro_bus_type)
drivers/net/dummy.c				bus_register(&dummy_bus)
drivers/net/phy/mdio_bus.c			bus_register(&mdio_bus_type)
drivers/memstick/core/memstick.c		bus_register(&memstick_bus_type)
drivers/bcma/main.c				bus_register(&bcma_bus_type)
drivers/mfd/mcp-core.c				bus_register(&mcp_bus_type)
drivers/nvmem/core.c				bus_register(&nvmem_bus_type)
drivers/virtio/virtio.c			        bus_register(&virtio_bus)
drivers/spi/spi.c				bus_register(&spi_bus_type)
drivers/ipack/ipack.c				bus_register(&ipack_bus_type)
drivers/block/rbd.c				bus_register(&rbd_bus_type)
drivers/mmc/core/bus.c				bus_register(&mmc_bus_type)
drivers/mmc/core/sdio_bus.c			bus_register(&sdio_bus_type)
drivers/nvdimm/bus.c				bus_register(&nvdimm_bus_type)
drivers/rpmsg/rpmsg_core.c			bus_register(&rpmsg_bus)
drivers/gpio/gpiolib.c				bus_register(&gpio_bus_type)
drivers/firewire/core-transaction.c		bus_register(&fw_bus_type)
drivers/misc/mei/bus.c				bus_register(&mei_cl_bus_type)
drivers/misc/tifm_core.c			bus_register(&tifm_bus_type)
drivers/misc/mic/scif/scif_peer_bus.c		bus_register(&scif_peer_bus)
drivers/misc/mic/bus/mic_bus.c			bus_register(&mic_bus)
drivers/misc/mic/bus/scif_bus.c		        bus_register(&scif_bus)
drivers/misc/mic/bus/vop_bus.c			bus_register(&vop_bus)
drivers/misc/mic/bus/cosm_bus.c		        bus_register(&cosm_bus)
drivers/pnp/core.c				bus_register(&pnp_bus_type)
drivers/thunderbolt/domain.c			bus_register(&tb_bus_type)
drivers/bus/mips_cdmm.c			        bus_register(&mips_cdmm_bustype)
drivers/bus/sunxi-rsb.c			        bus_register(&sunxi_rsb_bus)
drivers/dio/dio-driver.c			bus_register(&dio_bus_type)
drivers/base/platform.c			        bus_register(&platform_bus_type)
drivers/base/soc.c				bus_register(&soc_bus_type)
drivers/base/isa.c				bus_register(&isa_bus_type)
drivers/xen/xenbus/xenbus_probe_backend.c	bus_register(&xenbus_backend.bus)
drivers/xen/xenbus/xenbus_probe_frontend.c	bus_register(&xenbus_frontend.bus)
drivers/hv/vmbus_drv.c				bus_register(&hv_bus)
drivers/usb/serial/usb-serial.c		        bus_register(&usb_serial_bus_type)
drivers/usb/core/usb.c				bus_register(&usb_bus_type)
drivers/usb/common/ulpi.c			bus_register(&ulpi_bus)
drivers/ntb/ntb.c				bus_register(&ntb_bus)
drivers/ntb/ntb_transport.c			bus_register(&ntb_transport_bus)
drivers/uwb/umc-bus.c				bus_register(&umc_bus_type)
drivers/uwb/driver.c				bus_register(&uwb_bus_type)
drivers/ide/ide.c				bus_register(&ide_bus_type)
drivers/vfio/mdev/mdev_driver.c		        bus_register(&mdev_bus_type)
drivers/tc/tc-driver.c				bus_register(&tc_bus_type)
drivers/media/pci/bt8xx/bttv-driver.c		bus_register(&bttv_sub_bus_type)
drivers/media/cec/cec-core.c			bus_register(&cec_bus_type)
drivers/media/media-devnode.c			bus_register(&media_bus_type)

 

Bus 관련 API들

  • bus_create_file()
    • 버스 디렉토리를 생성하고 그 디렉토리 내부에 지정한 버스 속성 파일도 생성한다.
      • 예) /sys/bus/foo-bus 디렉토리와 속성 파일이 생성된다.
  • bus_remove_file()
    • 버스 디렉토리 및 속성들을 삭제한다.
  • bus_for_each_dev()
    • 버스에 포함된 디바이스들을 순회(iteration)
  • bus_find_device()
    • 버스에 포함된 디바이스들을 대상으로 매치되는 디바이스를 찾는다.
  • bus_find_device_by_name()
    • 버스에 포함된 디바이스들을 대상으로 요청한 디바이스명으로 start 디바이스부터 검색한다.
  • subsys_find_device_by_id()
    • 서브시스템에 포함된 디바이스들을 대상으로 id로 검색한다.
    • 디바이스를 처음 검색할 디바이스 위치를 hint로 지정할 수 있다.
  • bus_for_each_drv()
    • 버스에 포함된 드라이버들을 순회(iteration)
  • bus_rescan_devices()
    • 버스에서 디바이스를 리스캔한다.
  • device_reprobe()
    • 요청 디바이스에 대한 드라이버를 제거한 후 다시 새 드라이버로 probe한다.
  • bus_unregister()
    • 버스를 등록 해제한다.
  • bus_register_notifier()
    • 버스에 디바이스의 등록/해제 그리고 드라이버의 바운드/언바운드에 대해 notifier할 nb를 등록한다.
  • bus_unregister_notifier()
    • 버스에 등록된 notify 블럭을 등록 해제한다.
  • bus_get_kset()
    • 버스의 서브시스템에 해당하는 kset을 알아한다.
  • bus_get_device_klist()
    • 버스에 등록된 디바이스 리스트를 알아한다.
  • bus_sort_breadthfirst()
  • subsys_dev_iter_init()
    • subsys 디바이스 순회자(iterator 또는 cursor)를 초기화한다.
  • subsys_dev_iter_next()
    • subsys 다음 디바이스를 지정하고 알아온다.
  • subsys_dev_iter_exit()
    • subsys 순회자를 종료한다.
  • subsys_interface_register()
    • subsys 인터페이스를 등록한다.
  • subsys_interface_unregister()
    • subsys 인터페이스를 등록 해제한다.
  • subsys_system_register()
    • subsys 시스템을 등록한다.
      • /sys/devices/system
  • subsys_virtual_register()
    • subsys virtual을 등록한다.
      • /sys/devices/virtual

 

Class

각 디바이스가 논리적인 용도로 구분하는 개개의 클래스로 등록할 수 있다.

  • 예) 여러 회사의 온도 및 습도 센서 디바이스를 사용할 때 이들을 hwmon이라는 클래스 하나를 등록하여 비슷한 유형들을 관리할 수 있다.

 

class 구조체

include/linux/device.h

/**                                                                             
 * struct class - device classes                                                
 * @name:   Name of the class.                                                  
 * @owner:  The module owner.                                                   
 * @class_groups: Default attributes of this class.                             
 * @dev_groups: Default attributes of the devices that belong to the class.     
 * @dev_kobj:   The kobject that represents this class and links it into the hierarchy.
 * @dev_uevent: Called when a device is added, removed from this class, or a    
 *      few other things that generate uevents to add the environment           
 *      variables.                                                              
 * @devnode:    Callback to provide the devtmpfs.                               
 * @class_release: Called to release this class.                                
 * @dev_release: Called to release the device.                                  
 * @suspend:    Used to put the device to sleep mode, usually to a low power    
 *      state.                                                                  
 * @resume: Used to bring the device from the sleep mode.                       
 * @shutdown_pre: Called at shut-down time before driver shutdown.              
 * @ns_type:    Callbacks so sysfs can detemine namespaces.                     
 * @namespace:  Namespace of the device belongs to this class.                  
 * @pm:     The default device power management operations of this class.       
 * @p:      The private data of the driver core, no one other than the          
 *      driver core can touch this.                                             
 *                                                                              
 * A class is a higher-level view of a device that abstracts out low-level      
 * implementation details. Drivers may see a SCSI disk or an ATA disk, but,     
 * at the class level, they are all simply disks. Classes allow user space      
 * to work with devices based on what they do, rather than how they are         
 * connected or how they work.                                                  
 */
struct class {                                                                  
    const char      *name;                                                      
    struct module       *owner;                                                 
                                                                                
    const struct attribute_group    **class_groups;                             
    const struct attribute_group    **dev_groups;                               
    struct kobject          *dev_kobj;                                          
                                                                                
    int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);         
    char *(*devnode)(struct device *dev, umode_t *mode);                        
                                                                                
    void (*class_release)(struct class *class);                                 
    void (*dev_release)(struct device *dev);                                    
                                                                                
    int (*suspend)(struct device *dev, pm_message_t state);                     
    int (*resume)(struct device *dev);                                          
    int (*shutdown_pre)(struct device *dev);                                    
                                                                                
    const struct kobj_ns_type_operations *ns_type;                              
    const void *(*namespace)(struct device *dev);                               
                                                                                
    const struct dev_pm_ops *pm;                                                
                                                                                
    struct subsys_private *p;                                                   
};
  • name
    • 클래스 명
  • *owner
    • 모듈 오너
  • **class_groups
    • 클래스 속성들을 지정하면 클래스 디렉토리에 클래스 속성들이 생성된다.
      • 예) /sys/devices/foo_class 디렉토리에 클래스 속성들이 생성된다.
  • **dev_groups
    • 클래스 디바이스 속성들을 지정하면 클래스 디바이스가 생성되는 디렉토리에 클래스 디바이스 속성들이 생성된다.
      • 예) /sys/devices/foo/foo_class/foo_class_device 디렉토리에 클래스 디바이스 속성들이 생성된다.
  • dev_kobj
    • 디바이스 드라이버 모델에 따라 하이라키에 소속되기 위한 kobject
  • (*dev_uevent)
    • 디바이스 이벤트 후크 함수
  • (*devnode)
  • (*class_release)
    • 클래스를 해제할 때 호출되는 후크 함수
  • (*dev_release)
    • 디바이스가 클래스로부터 해제할 때 호출되는 후크 함수
  • (*suspend)
    • 절전 진입 시 호출되는 후크 함수
  • (*resume)
    • 절전이 끝난 후 정상 진입 시 호출되는 후크 함수
  • (*showdown_pre)
    • 셧다운 전에 호출되는 후크 함수
  • *ns_type
    • namespace 타입에 대한 오퍼레이션
    • 현재 KOBJ_NS_TYPE_NONE(0)과 KOBJ_NS_TYPE_NET(1) 타입만 사용한다.
  • (*namespace)
    • 네임 스페이스 후크 함수
  • (*pm)
    • 전원(절전) 관리에 대한 오퍼레이션
  • *p
    • 클래스와 서브시스템간 하이라키 연결을 class core가 관리하는 내부 자료 구조이다.

 

 

Class 등록

class_register()

include/linux/device.h

/* This is a #define to keep the compiler from merging different                
 * instances of the __key variable */                                           
#define class_register(class)           \                                       
({                      \                                                       
    static struct lock_class_key __key; \                                       
    __class_register(class, &__key);    \                                       
})

요청한 클래스를 등록한다.

 

__class_register()

drivers/base/class.c

int __class_register(struct class *cls, struct lock_class_key *key)             
{                                                                               
    struct subsys_private *cp;                                                  
    int error;                                                                  
                                                                                
    pr_debug("device class '%s': registering\n", cls->name);                    
                                                                                
    cp = kzalloc(sizeof(*cp), GFP_KERNEL);                                      
    if (!cp)                                                                    
        return -ENOMEM;                                                         
    klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);   
    INIT_LIST_HEAD(&cp->interfaces);                                            
    kset_init(&cp->glue_dirs);                                                  
    __mutex_init(&cp->mutex, "subsys mutex", key);                              
    error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);                
    if (error) {                                                                
        kfree(cp);                                                              
        return error;                                                           
    }                                                                           
                                                                                
    /* set the default /sys/dev directory for devices of this class */          
    if (!cls->dev_kobj)                                                         
        cls->dev_kobj = sysfs_dev_char_kobj;                                    
                                                                                
#if defined(CONFIG_BLOCK)                                                       
    /* let the block class directory show up in the root of sysfs */            
    if (!sysfs_deprecated || cls != &block_class)                               
        cp->subsys.kobj.kset = class_kset;                                      
#else                                                                           
    cp->subsys.kobj.kset = class_kset;                                          
#endif                                                                          
    cp->subsys.kobj.ktype = &class_ktype;                                       
    cp->class = cls;                                                            
    cls->p = cp;                                                                
                                                                                
    error = kset_register(&cp->subsys);                                         
    if (error) {                                                                
        kfree(cp);                                                              
        return error;                                                           
    }                                                                           
    error = class_add_groups(class_get(cls), cls->class_groups);                
    class_put(cls);                                                             
    return error;                                                               
}                                                                               
EXPORT_SYMBOL_GPL(__class_register);

요청한 클래스를 등록한다. 이 때 sysfs의 “/sys/class”에 클래스명으로 디렉토리를 만들고 관련 디렉토리들 및 클래스 속성들을 추가한다.

  • 코드 라인 8~14에서 버스와 서브시스템간의 하이 라키 관계를 구성하도록 subsys_private 구조체를 할당받고 멤버들을 초기화한다.
  • 코드 라인 15~19에서 생성할 클래스 디렉토리에 사용할 이름을 클래스 이름으로 동일하게 사용한다.
    • 예) “foo” 클래스명 -> /sys/class/foo
  • 코드 라인 22~23에서 클래스의 디바이스로 디폴트인 “/sys/dev”를 가리키게한다.
  • 코드 라인 25~31에서 서브시스템이 “/sys/class”를 가리키게 한다. 단 sysfs _deprecated 설정이 되어 있고 추가할 클래스가  block 클래스 인경우는 지정하지 않는다.
  • 코드 라인 32~34에서 서브시스템이 디폴트 class 오퍼레이션을 사용하게 한다. 그리고 서브시스템과 클래스를 서로 연결한다.
  • 코드 라인 36~40에서 sysfs에 클래스를 등록한다.
  • 코드 라인 41에서 클래스에 클래스 속성들을 추가한다.
  • 코드 라인 42에서 클래스 사용이 루틴에서 완료되었으므로 참조 카운터를 1 감소시킨다.

 

다음 그림은 foo 클래스를 추가할 때의 과정을 보여준다.

 

클래스 디바이스 생성

클래스에 연동

디바이스에 클래스를 연결하기 위해 사용되는 일반적인 패턴이 있다. 디바이스 하나가 하나의 클래스를 가질 수 있는 제약이 있으므로, 1 개 이상의 클래스에 연동하기 위해서는 특정 디바이스의 아래에 클래스용 자식 디바이스를 만들어 사용한다. 클래스용 자식 디바이스의 디렉토리 구조는 클래스 명으로 빈 디렉토리를 하나 만들고 그 아래에 클래스 연동된 자식 디바이스가 위치한다. 클래스 디바이스들은 속성을 통해 추상화된 기능을 작성하여 사용하므로 드라이버와 연결하여 사용하는 경우가 없는 것이 특징이다.

 

다음 그림과 같이 클래스용 디바이스를 쉽게 생성하기 위해 device_create() 함수를 사용하였고, 그룹으로 이루어진 속성들도 추가할 수 있는 device_create_with_groups() 함수를 사용한 모습을 보여준다.

 

다음 그림은 2개의 클래스에 연결된 foo 디바이스를 보여준다. 클래스와 연결하기 위해 생성되는 서브 디바이스명 들은 foo_sub_device1과 foo_sub_device2로 하였다.

 

속성 없이 클래스 디바이스 생성과 등록

device_create()

drivers/base/core.c

/**                                                                             
 * device_create - creates a device and registers it with sysfs                 
 * @class: pointer to the struct class that this device should be registered to 
 * @parent: pointer to the parent struct device of this new device, if any      
 * @devt: the dev_t for the char device to be added                             
 * @drvdata: the data to be added to the device for callbacks                   
 * @fmt: string for the device's name                                           
 *                                                                              
 * This function can be used by char device classes.  A struct device           
 * will be created in sysfs, registered to the specified class.                 
 *                                                                              
 * A "dev" file will be created, showing the dev_t for the device, if           
 * the dev_t is not 0,0.                                                        
 * If a pointer to a parent struct device is passed in, the newly created       
 * struct device will be a child of that device in sysfs.                       
 * The pointer to the struct device will be returned from the call.             
 * Any further sysfs files that might be required can be created using this     
 * pointer.                                                                     
 *                                                                              
 * Returns &struct device pointer on success, or ERR_PTR() on error.            
 *                                                                              
 * Note: the struct class passed to this function must have previously          
 * been created with a call to class_create().                                  
 */
struct device *device_create(struct class *class, struct device *parent,        
                 dev_t devt, void *drvdata, const char *fmt, ...)               
{                                                                               
    va_list vargs;                                                              
    struct device *dev;                                                         
                                                                                
    va_start(vargs, fmt);                                                       
    dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);        
    va_end(vargs);                                                              
    return dev;                                                                 
}                                                                               
EXPORT_SYMBOL_GPL(device_create);

포맷과 가변인수를 사용한 디바이스명으로 클래스용 디바이스를 생성하고 등록한다. 부모 디바이스가 지정된 경우 부모 디바이스 아래에 생성한다.

 

device_create_vargs()

drivers/base/core.c

/**                                                                             
 * device_create_vargs - creates a device and registers it with sysfs           
 * @class: pointer to the struct class that this device should be registered to 
 * @parent: pointer to the parent struct device of this new device, if any      
 * @devt: the dev_t for the char device to be added                             
 * @drvdata: the data to be added to the device for callbacks                   
 * @fmt: string for the device's name                                           
 * @args: va_list for the device's name                                         
 *                                                                              
 * This function can be used by char device classes.  A struct device           
 * will be created in sysfs, registered to the specified class.                 
 *                                                                              
 * A "dev" file will be created, showing the dev_t for the device, if           
 * the dev_t is not 0,0.                                                        
 * If a pointer to a parent struct device is passed in, the newly created       
 * struct device will be a child of that device in sysfs.                       
 * The pointer to the struct device will be returned from the call.             
 * Any further sysfs files that might be required can be created using this     
 * pointer.                                                                     
 *                                                                              
 * Returns &struct device pointer on success, or ERR_PTR() on error.            
 *                                                                              
 * Note: the struct class passed to this function must have previously          
 * been created with a call to class_create().                                  
 */
struct device *device_create_vargs(struct class *class, struct device *parent,  
                   dev_t devt, void *drvdata, const char *fmt,                  
                   va_list args)                                                
{                                                                               
    return device_create_groups_vargs(class, parent, devt, drvdata, NULL,       
                      fmt, args);                                               
}                                                                               
EXPORT_SYMBOL_GPL(device_create_vargs);

포맷과 가변인수를 사용한 디바이스명으로 클래스용 디바이스를 생성하고 등록한다. 부모 디바이스가 지정된 경우 부모 디바이스 아래에 생성한다.

 

device_create_groups_vargs()

drivers/base/core.c

static struct device *                                                          
device_create_groups_vargs(struct class *class, struct device *parent,          
               dev_t devt, void *drvdata,                                       
               const struct attribute_group **groups,                           
               const char *fmt, va_list args)                                   
{                                                                               
    struct device *dev = NULL;                                                  
    int retval = -ENODEV;                                                       
                                                                                
    if (class == NULL || IS_ERR(class))                                         
        goto error;                                                             
                                                                                
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);                                    
    if (!dev) {                                                                 
        retval = -ENOMEM;                                                       
        goto error;                                                             
    }                                                                           
                                                                                
    device_initialize(dev);                                                     
    dev->devt = devt;                                                           
    dev->class = class;                                                         
    dev->parent = parent;                                                       
    dev->groups = groups;                                                       
    dev->release = device_create_release;                                       
    dev_set_drvdata(dev, drvdata);                                              
                                                                                
    retval = kobject_set_name_vargs(&dev->kobj, fmt, args);                     
    if (retval)                                                                 
        goto error;                                                             
                                                                                
    retval = device_add(dev);                                                   
    if (retval)                                                                 
        goto error;                                                             
                                                                                
    return dev;                                                                 
                                                                                
error:                                                                          
    put_device(dev);                                                            
    return ERR_PTR(retval);                                                     
}

포맷과 가변인수를 사용한 디바이스명으로 클래스용 디바이스를 생성하고 등록한다. 부모 디바이스가 지정된 경우 부모 디바이스 아래에 생성한다.

  • 코드 라인 10~11에서 클래스가 지정되지 않은 경우 에러로 함수를 빠져나간다.
  • 코드 라인 13~17에서 디바이스를 할당한다.
  • 코드 라인 19~25에서 디바이스를 초기화하고, 캐릭터 디바이스용 번호, 클래스, 부모 디바이스, 그룹 속성들을 지정한다. 그리고 디폴트 디바이스 해제 함수를 연결하고 드라이버 데이터를 연결한다.
  • 코드 라인 27~29에서 인자로 전달받은 포맷과 가변인수로 디바이스명을 설정한다.
  • 코드 라인 31~33에서 디바이스를 추가 등록한다.

 

속성을 포함하여 디바이스 생성과 등록

device_create_with_groups()

drivers/base/core.c

/**                                                                             
 * device_create_with_groups - creates a device and registers it with sysfs     
 * @class: pointer to the struct class that this device should be registered to 
 * @parent: pointer to the parent struct device of this new device, if any      
 * @devt: the dev_t for the char device to be added                             
 * @drvdata: the data to be added to the device for callbacks                   
 * @groups: NULL-terminated list of attribute groups to be created              
 * @fmt: string for the device's name                                           
 *                                                                              
 * This function can be used by char device classes.  A struct device           
 * will be created in sysfs, registered to the specified class.                 
 * Additional attributes specified in the groups parameter will also            
 * be created automatically.                                                    
 *                                                                              
 * A "dev" file will be created, showing the dev_t for the device, if           
 * the dev_t is not 0,0.                                                        
 * If a pointer to a parent struct device is passed in, the newly created       
 * struct device will be a child of that device in sysfs.                       
 * The pointer to the struct device will be returned from the call.             
 * Any further sysfs files that might be required can be created using this     
 * pointer.                                                                     
 *                                                                              
 * Returns &struct device pointer on success, or ERR_PTR() on error.            
 *                                                                              
 * Note: the struct class passed to this function must have previously          
 * been created with a call to class_create().                                  
 */
struct device *device_create_with_groups(struct class *class,                   
                     struct device *parent, dev_t devt,                         
                     void *drvdata,                                             
                     const struct attribute_group **groups,                     
                     const char *fmt, ...)                                      
{                                                                               
    va_list vargs;                                                              
    struct device *dev;                                                         
                                                                                
    va_start(vargs, fmt);                                                       
    dev = device_create_groups_vargs(class, parent, devt, drvdata, groups,      
                     fmt, vargs);                                               
    va_end(vargs);                                                              
    return dev;                                                                 
}                                                                               
EXPORT_SYMBOL_GPL(device_create_with_groups);

포맷과 가변인수를 사용한 디바이스명으로 클래스용 디바이스를 생성하고 등록한다. 부모 디바이스가 지정된 경우 부모 디바이스 아래에 생성한다. 생성된 클래스용 디바이스에 속성 파일들을 추가한다.

 

Class 인터페이스

Class 인터페이스를 사용하는 드라이버가 일부 있으며 다음의 드라이버들에서 사용되고 있다.

  • drivers/net/rionet.c
  • drivers/pcmcia/ds.c
  • drivers/pcmcia/rsrc_nonstatic.c
  • drivers/rapidio/devices/rio_mport_cdev.c
  • drivers/rapidio/rio_cm.c
  • drivers/scsi/ses.c
  • drivers/scsi/scsi_sysfs.c
  • drivers/scsi/sg.c

 

class_interface 구조체

include/linux/device.h

struct class_interface {
        struct list_head        node;
        struct class            *class;

        int (*add_dev)          (struct device *, struct class_interface *);
        void (*remove_dev)      (struct device *, struct class_interface *);
};
  • node
    • class->interfaces에 등록될 링크 노드로 사용된다.
  • *class
    • 부모 클래스
  • (*add_dev)
    • 클래스 인터페이스가 등록될 때 클래스에 등록된 디바이스들을 대상으로 이 후크 함수가 호출된다.
  • (*remove_dev)
    • 클래스 인터페이스가 해제될 때 클래스에 등록된 디바이스들을 대상으로 이 후크 함수가 호출된다.

 

Class 인터페이스 등록

class_interface_register()

drivers/base/class.c

int class_interface_register(struct class_interface *class_intf)
{
        struct class *parent;
        struct class_dev_iter iter;
        struct device *dev;

        if (!class_intf || !class_intf->class)
                return -ENODEV;

        parent = class_get(class_intf->class);
        if (!parent)
                return -EINVAL;

        mutex_lock(&parent->p->mutex);
        list_add_tail(&class_intf->node, &parent->p->interfaces);
        if (class_intf->add_dev) {
                class_dev_iter_init(&iter, parent, NULL, NULL);
                while ((dev = class_dev_iter_next(&iter)))
                        class_intf->add_dev(dev, class_intf);
                class_dev_iter_exit(&iter);
        }
        mutex_unlock(&parent->p->mutex);

        return 0;
}

클래스 인터페이스를 등록하고 클래스에 등록된 클래스 디바이스들을 대상으로 (*add_dev) 후크 함수를 호출한다.

  • 코드 라인 7~8에서 인자로 전달받은 클래스 인터페이스가 null이거나 지정된 클래스가 없는 경우 -ENODEV 에러를 반환한다.
  • 코드 라인 10~12에서 클래스에 접근하기 위해 참조 카운터를 1 증가시킨다.
  • 코드 라인 14~15에서 클래스에 락을 걸고 클래스 인터페이스를 추가한다.
  • 코드 라인 16~21에서 클래스 인터페이스에 (*add_dev) 후크 함수가 구현되어 있는 경우 클래스에 등록된 클래스 디바이스들을 대상으로 (*add_dev) 후크 함수를 호출한다.

 

Class 종류

다음과 같은 클래스들이 커널에 정의되어 있다.

./net/ieee802154/sysfs.c			class_register(&wpan_phy_class)
./net/atm/atm_sysfs.c				class_register(&atm_class)
./net/rfkill/core.c				class_register(&rfkill_class)
./net/nfc/core.c				class_register(&nfc_class)
./net/core/net-sysfs.c				class_register(&net_class)
./net/wireless/sysfs.c				class_register(&ieee80211_class)
./block/genhd.c					class_register(&block_class)
./drivers/platform/x86/wmi.c			class_register(&wmi_bus_class)
./drivers/platform/chrome/cros_ec_dev.c		class_register(&cros_class)
./drivers/iommu/iommu-sysfs.c			class_register(&iommu_class)
./drivers/input/input.c				class_register(&input_class)
./drivers/uio/uio.c				class_register(&uio_class)
./drivers/hwmon/hwmon.c				class_register(&hwmon_class)
./drivers/scsi/scsi_sysfs.c			class_register(&sdev_class)
./drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c	class_register(&ibmvscsis_class)
./drivers/scsi/scsi_transport_sas.c		transport_class_register(&sas_host_class)
./drivers/scsi/scsi_transport_sas.c		transport_class_register(&sas_phy_class)
./drivers/scsi/scsi_transport_sas.c		transport_class_register(&sas_port_class)
./drivers/scsi/scsi_transport_sas.c		transport_class_register(&sas_rphy_class)
./drivers/scsi/scsi_transport_sas.c		transport_class_register(&sas_end_dev_class)
./drivers/scsi/scsi_transport_sas.c		transport_class_register(&sas_expander_class)
./drivers/scsi/hosts.c				class_register(&shost_class)
./drivers/scsi/osd/osd_uld.c			class_register(&osd_uld_class)
./drivers/scsi/scsi_transport_srp.c		transport_class_register(&srp_host_class)
./drivers/scsi/scsi_transport_srp.c		transport_class_register(&srp_rport_class)
./drivers/scsi/sd.c				class_register(&sd_disk_class)
./drivers/scsi/scsi_transport_fc.c		transport_class_register(&fc_host_class)
./drivers/scsi/scsi_transport_fc.c		transport_class_register(&fc_vport_class)
./drivers/scsi/scsi_transport_fc.c		transport_class_register(&fc_rport_class)
./drivers/scsi/scsi_transport_fc.c		transport_class_register(&fc_transport_class)
./drivers/scsi/raid_class.c			transport_class_register(&raid_class)
./drivers/scsi/scsi_transport_iscsi.c		class_register(&iscsi_transport_class)
./drivers/scsi/scsi_transport_iscsi.c		class_register(&iscsi_endpoint_class)
./drivers/scsi/scsi_transport_iscsi.c		class_register(&iscsi_iface_class)
./drivers/scsi/scsi_transport_iscsi.c		transport_class_register(&iscsi_host_class)
./drivers/scsi/scsi_transport_iscsi.c		transport_class_register(&iscsi_connection_class)
./drivers/scsi/scsi_transport_iscsi.c		transport_class_register(&iscsi_session_class)
./drivers/scsi/scsi_transport_spi.c		transport_class_register(&spi_transport_class)
./drivers/scsi/scsi_transport_spi.c		transport_class_register(&spi_host_class)
./drivers/scsi/st.c				class_register(&st_sysfs_class)
./drivers/pwm/sysfs.c				class_register(&pwm_class)
./drivers/mux/core.c				class_register(&mux_class)
./drivers/regulator/core.c			class_register(&regulator_class)
./drivers/hwtracing/stm/core.c			class_register(&stm_class)
./drivers/hwtracing/stm/core.c			class_register(&stm_source_class)
./drivers/isdn/mISDN/core.c			class_register(&mISDN_class)
./drivers/pcmcia/cs.c				class_register(&pcmcia_socket_class)
./drivers/mtd/mtdcore.c				class_register(&mtd_class)
./drivers/mtd/ubi/build.c			class_register(&ubi_class)
./drivers/ata/libata-transport.c		transport_class_register(&ata_link_class)
./drivers/ata/libata-transport.c		transport_class_register(&ata_port_class)
./drivers/ata/libata-transport.c		transport_class_register(&ata_dev_class)
./drivers/pci/probe.c				class_register(&pcibus_class)
./drivers/rapidio/rio-driver.c			class_register(&rio_mport_class)
./drivers/staging/greybus/loopback.c		class_register(&loopback_class)
./drivers/staging/greybus/vibrator.c		class_register(&vibrator_class)
./drivers/net/ipvlan/ipvtap.c			class_register(&ipvtap_class)
./drivers/net/phy/mdio_bus.c			class_register(&mdio_bus_class)
./drivers/net/macvtap.c				class_register(&macvtap_class)
./drivers/memstick/core/memstick.c		class_register(&memstick_host_class)
./drivers/mfd/ucb1x00-core.c			class_register(&ucb1x00_class)
./drivers/powercap/powercap_sys.c		class_register(&powercap_class)
./drivers/spi/spi.c				class_register(&spi_master_class)
./drivers/spi/spi.c				class_register(&spi_slave_class)
./drivers/remoteproc/remoteproc_sysfs.c		class_register(&rproc_class)
./drivers/firmware/dmi-id.c			class_register(&dmi_class)
./drivers/watchdog/watchdog_dev.c		class_register(&watchdog_class)
./drivers/block/zram/zram_drv.c			class_register(&zram_control_class)
./drivers/block/pktcdvd.c			class_register(class_pktcdvd)
./drivers/mmc/core/host.c			class_register(&mmc_host_class)
./drivers/gpio/gpiolib-sysfs.c			class_register(&gpio_class)
./drivers/misc/tifm_core.c			class_register(&tifm_adapter_class)
./drivers/misc/enclosure.c			class_register(&enclosure_class)
./drivers/mailbox/omap-mailbox.c		class_register(&omap_mbox_class)
./drivers/base/devcoredump.c			class_register(&devcd_class)
./drivers/base/firmware_class.c			class_register(&firmware_class)
./drivers/uwb/driver.c				class_register(&uwb_rc_class)
./drivers/infiniband/ulp/srp/ib_srp.c		class_register(&srp_class)
./drivers/infiniband/core/device.c		class_register(&ib_class)
./drivers/infiniband/core/cm.c			class_register(&cm_class)
./drivers/thermal/thermal_core.c		class_register(&thermal_class)
./drivers/media/rc/rc-main.c			class_register(&rc_class)
./drivers/media/v4l2-core/v4l2-dev.c		class_register(&video_class)
./drivers/media/pci/ddbridge/ddbridge-core.c	class_register(&ddb_class)
./drivers/media/usb/pvrusb2/pvrusb2-sysfs.c	class_register(&clp->class) 
./drivers/dma/dmaengine.c			class_register(&dma_devclass)

 

Classs 관련 API들

  • class_dev_iter_init()
    • 클래스 디바이스 순회자를 초기화한다.
  • class_dev_iter_next()
    • 클래스 디바이스에서 다음 디바이스를 지정하고 얻어온다.
  • class_dev_iter_exit()
    • 클래스 디바이스 순회자를 종료한다.
  • class_for_each_device()
    • 클래스 디바이스를 순회한다.
  • class_find_device()
    • 클래스에서 start 디바이스부터 시작하여 매치되는 디바이스를 검색한다.
  • show_class_attr_string()
    • 클래스 속성 문자열을 출력한다.
  • class_compat_register()
    • compatibility 클래스를 등록한다.
  • class_compat_unregister()
    • compatibility 클래스를 등록 해제한다.
  • class_compat_create_link()
    • compatibility 클래스 링크 심볼을 생성한다.
  • class_compat_remove_link()
    • compatibility 클래스 링크 심볼을 삭제한다.

 

Device Binding

디바이스가 드라이버를 probing하는 과정은 다음과 같다.

  • 디바이스가 등록될 때 드라이버가 준비된 상태인 경우 자동으로 probe 한다.
  • 드라이버가 등록될 때 역시 디바이스가 준비된 상태인 경우 자동으로 probe 한다.
    • insmod에 의해 사용자 드라이버가 등록될 때 probe되는 과정과 동일하다.
  • 매치된 디바이스와 드라이버가 probe되지 못한 경우 스레드를 사용하여 probe를 시도한다.
  • pci 또는 usb와 같이 plug & plug를 지원하여 버스를 스캔하며 디바이스 드라이버를 등록할 수 있다.
    • 이 과정은 pci 드라이버를 다루는 과정에서 알아보자.

 

bus_probe_device()

drivers/base/bus.c

/**                                                                             
 * bus_probe_device - probe drivers for a new device                            
 * @dev: device to probe                                                        
 *                                                                              
 * - Automatically probe for a driver if the bus allows it.                     
 */                                                                             
void bus_probe_device(struct device *dev)                                       
{                                                                               
    struct bus_type *bus = dev->bus;                                            
    struct subsys_interface *sif;                                               
                                                                                
    if (!bus)                                                                   
        return;                                                                 
                                                                                
    if (bus->p->drivers_autoprobe)                                              
        device_initial_probe(dev);                                              
                                                                                
    mutex_lock(&bus->p->mutex);                                                 
    list_for_each_entry(sif, &bus->p->interfaces, node)                         
        if (sif->add_dev)                                                       
            sif->add_dev(dev, sif);                                             
    mutex_unlock(&bus->p->mutex);                                               
}

버스가 autoprobe  상태인 경우 디바이스를 probe한다.  (디폴트로 bus는 항상 autoprobe 상태이다)

 

device_initial_probe()

drivers/base/dd.c

void device_initial_probe(struct device *dev)                                   
{                                                                               
    __device_attach(dev, true);                                                 
}

디바이스가 드라이버와의 동기 attach를 시도한다.

 

__device_attach()

drivers/base/dd.c

static int __device_attach(struct device *dev, bool allow_async)                
{                                                                               
    int ret = 0;                                                                
                                                                                
    device_lock(dev);                                                           
    if (dev->driver) {                                                          
        if (device_is_bound(dev)) {                                             
            ret = 1;                                                            
            goto out_unlock;                                                    
        }                                                                       
        ret = device_bind_driver(dev);                                          
        if (ret == 0)                                                           
            ret = 1;                                                            
        else {                                                                  
            dev->driver = NULL;                                                 
            ret = 0;                                                            
        }                                                                       
    } else {                                                                    
        struct device_attach_data data = {                                      
            .dev = dev,                                                         
            .check_async = allow_async,                                         
            .want_async = false,                                                
        };                                                                      
                                                                                
        if (dev->parent)                                                        
            pm_runtime_get_sync(dev->parent);                                   
                                                                                
        ret = bus_for_each_drv(dev->bus, NULL, &data,                           
                    __device_attach_driver);                                    
        if (!ret && allow_async && data.have_async) {                           
            /*                                                                  
             * If we could not find appropriate driver                          
             * synchronously and we are allowed to do                           
             * async probes and there are drivers that                          
             * want to probe asynchronously, we'll                              
             * try them.                                                        
             */                                                                 
            dev_dbg(dev, "scheduling asynchronous probe\n");                    
            get_device(dev);                                                    
            async_schedule(__device_attach_async_helper, dev);                  
        } else {                                                                
            pm_request_idle(dev);                                               
        }                                                                       
                                                                                
        if (dev->parent)                                                        
            pm_runtime_put(dev->parent);                                        
    }                                                                           
out_unlock:                                                                     
    device_unlock(dev);                                                         
    return ret;                                                                 
}

디바이스가 드라이버의 attach를 시도한다. 인자로 비동기 및 동기 attach 요청을 지정할 수 있다. attach 시도 전에 부모 디바이스가 절전 모드인 경우 절전에서 빠져나온다.

  • 코드 라인 6~17에서 디바이스에 이미 드라이버가 지정된 경우 드라이버를 바인딩 시도한다. 성공(1) 또는 실패(0)를 반환한다. 만일 이미 디바이스와 드라이버가 바인딩 되어 있는 경우 성공(1)을 반환한다.
  • 코드 라인 18~29에서 버스에 등록되어있는 드라이버들을 대상으로 매치 체크하여 매치된 드라이버를 attach 시도한다.
  • 코드 라인 30~40에서 부모 디바이스가 절전 상태에서 빠져나오도록 대기(sync)한다. 그리고 비동기 요청으로 드라이버의 attach가 성공된 경우 __device_attach_async_helper 워크를 언바운드 워커스레드로 동작시킨다.
    • 이 때 async probe가 완료되도록 한다.
  • 코드 라인 41~47에서 디바이스를 idle 상태로 바꾼다. 그리고 부모 디바이스의 참조 카운터를 1 감소시킨다.

 

__driver_attach()

drivers/base/dd.c

static int __driver_attach(struct device *dev, void *data)                      
{                                                                               
    struct device_driver *drv = data;                                           
    int ret;                                                                    
                                                                                
    /*                                                                          
     * Lock device and try to bind to it. We drop the error                     
     * here and always return 0, because we need to keep trying                 
     * to bind to devices and some drivers will return an error                 
     * simply if it didn't support the device.                                  
     *                                                                          
     * driver_probe_device() will spit a warning if there                       
     * is an error.                                                             
     */                                                                         
                                                                                
    ret = driver_match_device(drv, dev);                                        
    if (ret == 0) {                                                             
        /* no match */                                                          
        return 0;                                                               
    } else if (ret == -EPROBE_DEFER) {                                          
        dev_dbg(dev, "Device match requests probe deferral\n");                 
        driver_deferred_probe_add(dev);                                         
    } else if (ret < 0) {                                                       
        dev_dbg(dev, "Bus failed to match device: %d", ret);                    
        return ret;                                                             
    } /* ret > 0 means positive match */                                        
                                                                                
    if (dev->parent)    /* Needed for USB */                                    
        device_lock(dev->parent);                                               
    device_lock(dev);                                                           
    if (!dev->driver)                                                           
        driver_probe_device(drv, dev);                                          
    device_unlock(dev);                                                         
    if (dev->parent)                                                            
        device_unlock(dev->parent);                                             
                                                                                
    return 0;                                                                   
}

인자로 전달받은 디바이스와 드라이버가 매치되는 경우 attach 시도한다.

  • 코드 라인 16~19에서 디바이스와 드라이버가 서로 매치되는 것이 없으면 성공(0)을 반환한다.
  • 코드 라인 20~26에서 probe 유예인 경우 deferred_probe_pending_list에 디바이스를 추가한다. 그 외 에러의 경우 함수를 빠져나간다.
  • 코드 라인 28~35에서 부모 디바이스의 락을 건채 디바이스와 드라이버를 probe 시도 한 후 성공(0)을 반환한다.

 

driver_probe_device()

drivers/base/dd.c

/**                                                                             
 * driver_probe_device - attempt to bind device & driver together               
 * @drv: driver to bind a device to                                             
 * @dev: device to try to bind to the driver                                    
 *                                                                              
 * This function returns -ENODEV if the device is not registered,               
 * 1 if the device is bound successfully and 0 otherwise.                       
 *                                                                              
 * This function must be called with @dev lock held.  When called for a         
 * USB interface, @dev->parent lock must be held as well.                       
 *                                                                              
 * If the device has a parent, runtime-resume the parent before driver probing. 
 */                                                                             
int driver_probe_device(struct device_driver *drv, struct device *dev)          
{                                                                               
    int ret = 0;                                                                
                                                                                
    if (!device_is_registered(dev))                                             
        return -ENODEV;                                                         
                                                                                
    pr_debug("bus: '%s': %s: matched device %s with driver %s\n",               
         drv->bus->name, __func__, dev_name(dev), drv->name);                   
                                                                                
    pm_runtime_get_suppliers(dev);                                              
    if (dev->parent)                                                            
        pm_runtime_get_sync(dev->parent);                                       
                                                                                
    pm_runtime_barrier(dev);                                                    
    ret = really_probe(dev, drv);                                               
    pm_request_idle(dev);                                                       
                                                                                
    if (dev->parent)                                                            
        pm_runtime_put(dev->parent);                                            
                                                                                
    pm_runtime_put_suppliers(dev);                                              
    return ret;                                                                 
}

디바이스와 드라이버를 서로 bind 시도한다. 반환되는 값이 -ENODEV인 경우 디바이스가 등록되지 않았음을 의미한다. 1인 경우 binding이 성공이고, 0인 경우 실패이다.

  • 코드 라인 18~19에서 디바이스가 등록되지 않은 경우 -ENODEV 결과를 반환한다.
  • 코드 라인 24~26에서 디바이스의 suppliers를 대상으로 DL_FLAG_PM_RUNTIME 플래그가 설정된 경우 pm 참조 카운터를 감소시키고 resunme 처리한다.
  • 코드 라인 28~30에서 pm 기능(suspend)이 정상 완료될 때까지 대기한 후 드라이버를 probe 요청한다. 그런 후 디바이스가 suspend되어야 하는 경우 pm idle 요청한다.
  • 코드 라인 32~33에서 부모 디바이스의 참조 카운터를 1 감소시킨다.
  • 코드 라인 35에서 suppolier 디바이스들의 참조 카운터를 1 감소시킨다.

 

really_probe()

drivers/base/dd.c

static int really_probe(struct device *dev, struct device_driver *drv)          
{                                                                               
    int ret = -EPROBE_DEFER;                                                    
    int local_trigger_count = atomic_read(&deferred_trigger_count);             
    bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&           
               !drv->suppress_bind_attrs;                                       
                                                                                
    if (defer_all_probes) {                                                     
        /*                                                                      
         * Value of defer_all_probes can be set only by                         
         * device_defer_all_probes_enable() which, in turn, will call           
         * wait_for_device_probe() right after that to avoid any races.         
         */                                                                     
        dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);            
        driver_deferred_probe_add(dev);                                         
        return ret;                                                             
    }                                                                           
                                                                                
    ret = device_links_check_suppliers(dev);                                    
    if (ret)                                                                    
        return ret;                                                             
                                                                                
    atomic_inc(&probe_count);                                                   
    pr_debug("bus: '%s': %s: probing driver %s with device %s\n",               
         drv->bus->name, __func__, drv->name, dev_name(dev));                   
    WARN_ON(!list_empty(&dev->devres_head));                                    
                                                                                
re_probe:                                                                       
    dev->driver = drv;                                                          
                                                                                
    /* If using pinctrl, bind pins now before probing */                        
    ret = pinctrl_bind_pins(dev);                                               
    if (ret)                                                                    
        goto pinctrl_bind_failed;                                               
                                                                                
    ret = dma_configure(dev);                                                   
    if (ret)                                                                    
        goto dma_failed;                                                        
                                                                                
    if (driver_sysfs_add(dev)) {                                                
        printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",                    
            __func__, dev_name(dev));                                           
        goto probe_failed;                                                      
    }                                                                           
                                                                                
    if (dev->pm_domain && dev->pm_domain->activate) {                           
        ret = dev->pm_domain->activate(dev);                                    
        if (ret)                                                                
            goto probe_failed;                                                  
    }

디바이스가 드라이버를 probe 한다.

  • 코드 라인 8~17에서 dpm 절전(suspend) 모드로 진입 시에는 디바이스를 전역 deferred_probe_pending_list에 추가하여  probe를 유예시킨다.
  • 코드 라인 19~21에서 supplier 디바이스들 중 DL_STATE_AVAILABLE 상태인 디바이스를 DL_STATE_CONSUMER_PROBE 상태로 변경한다. 그 후 요청한 디바이스의 상태는 DL_DEV_PROBING 상태로 변경한다.
  • 코드 라인 23~26에서 probe 카운터를 1 증가시키고 디버그 정보를 출력한다.
  • 코드 라인 32~34에서 디바이스를 사용하기 위해 pinctrl로 bind한다.
  • 코드 라인 36~38에서 디바이스가 dma를 사용하는 경우 디바이스 트리 또는 ACPI를 사용하여 dma 설정을 한다.
  • 코드 라인 40~44에서 sysfs에 드라이버와 디바이스간의 심볼 링크를 생성한다.
  • 코드 라인 46~50에서 디바이스의 pm 도메인에 있는 (*active) 후크 함수를 호출한다.

 

.   /*                                                                          
     * Ensure devices are listed in devices_kset in correct order               
     * It's important to move Dev to the end of devices_kset before             
     * calling .probe, because it could be recursive and parent Dev             
     * should always go first                                                   
     */                                                                         
    devices_kset_move_last(dev);                                                
                                                                                
    if (dev->bus->probe) {                                                      
        ret = dev->bus->probe(dev);                                             
        if (ret)                                                                
            goto probe_failed;                                                  
    } else if (drv->probe) {                                                    
        ret = drv->probe(dev);                                                  
        if (ret)                                                                
            goto probe_failed;                                                  
    }                                                                           
                                                                                
    if (test_remove) {                                                          
        test_remove = false;                                                    
                                                                                
        if (dev->bus->remove)                                                   
            dev->bus->remove(dev);                                              
        else if (drv->remove)                                                   
            drv->remove(dev);                                                   
                                                                                
        devres_release_all(dev);                                                
        driver_sysfs_remove(dev);                                               
        dev->driver = NULL;                                                     
        dev_set_drvdata(dev, NULL);                                             
        if (dev->pm_domain && dev->pm_domain->dismiss)                          
            dev->pm_domain->dismiss(dev);                                       
        pm_runtime_reinit(dev);                                                 
                                                                                
        goto re_probe;                                                          
    }                                                                           
                                                                                
    pinctrl_init_done(dev);                                                     
                                                                                
    if (dev->pm_domain && dev->pm_domain->sync)                                 
        dev->pm_domain->sync(dev);                                              
                                                                                
    driver_bound(dev);                                                          
    ret = 1;                                                                    
    pr_debug("bus: '%s': %s: bound device %s to driver %s\n",                   
         drv->bus->name, __func__, dev_name(dev), drv->name);                   
    goto done;
  • 코드 라인 7에서 디바이스를 devices_kset 리스트의 마지막으로 옮긴다.
  • 코드 라인 9~17에서 디바이스의 버스에 있는 (*probe) 후크 함수를 호출한다. 만일 없으면 드라이버의 (*probe) 후크 함수를 호출한다.
  • 코드 라인 19~36에서probe 시 테스트 드라이버 기능을 사용하는 경우 드라이버를 제거한 후 다시 probe하는 과정을 거친다.
  • 코드 라인 38에서 probe가 완료되었음을 pinctrl에 전한다.
  • 코드 라인 40~41에서 디바이스의 pm_domain에 있는 (*sync) 후크를 호출한다.
  • 코드 라인 43~47에서 디바이스가 드라이버를 바운드한 후 done 레이블로 이동하여 probe_waitqueue에 있는 스레드를 깨우고 정상적으로 함수를 빠져나간다.

 

probe_failed:                                                                   
    dma_deconfigure(dev);                                                       
dma_failed:                                                                     
    if (dev->bus)                                                               
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,                
                         BUS_NOTIFY_DRIVER_NOT_BOUND, dev);                     
pinctrl_bind_failed:                                                            
    device_links_no_driver(dev);                                                
    devres_release_all(dev);                                                    
    driver_sysfs_remove(dev);                                                   
    dev->driver = NULL;                                                         
    dev_set_drvdata(dev, NULL);                                                 
    if (dev->pm_domain && dev->pm_domain->dismiss)                              
        dev->pm_domain->dismiss(dev);                                           
    pm_runtime_reinit(dev);                                                     
                                                                                
    switch (ret) {                                                              
    case -EPROBE_DEFER:                                                         
        /* Driver requested deferred probing */                                 
        dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);         
        driver_deferred_probe_add(dev);                                         
        /* Did a trigger occur while probing? Need to re-trigger if yes */      
        if (local_trigger_count != atomic_read(&deferred_trigger_count))        
            driver_deferred_probe_trigger();                                    
        break;                                                                  
    case -ENODEV:                                                               
    case -ENXIO:                                                                
        pr_debug("%s: probe of %s rejects match %d\n",                          
             drv->name, dev_name(dev), ret);                                    
        break;                                                                  
    default:                                                                    
        /* driver matched but the probe failed */                               
        printk(KERN_WARNING                                                     
               "%s: probe of %s failed with error %d\n",                        
               drv->name, dev_name(dev), ret);                                  
    }                                                                           
    /*                                                                          
     * Ignore errors returned by ->probe so that the next driver can try        
     * its luck.                                                                
     */                                                                         
    ret = 0;                                                                    
done:                                                                           
    atomic_dec(&probe_count);                                                   
    wake_up(&probe_waitqueue);                                                  
    return ret;                                                                 
}

sysfs 속성을 사용하여 바인딩하는 방법은 다음을 참고한다.참고:

 

디바이스 드라이버 바인딩 관련 API들

  • device_bind_driver()
    • 디바이스에 대응하는 드라이버를 바인딩한다.
  • wait_for_device_probe()
    • 디바이스 probe의 완료를 기다린다. (deferred probe 포함)
  • device_attach()
    • 디바이스에 대응하는 드라이버를 동기 attach 한다.
  • driver_attach()
    • 드라이버에 대응하는 디바이스 찾아 attach 한다.
  • device_release_driver()
    • 디바이스에 attach된 드라이버를 분리시킨다.

 

디바이스, 드라이버, 버스, 클래스 및 모듈 간의 관계 관리

다음 그림에서 sysfs에서 관계 관리를 위해 kobject, kset 및 klist 이외에도 다음의 구조체들이 사용되는 것을 알 수 있다.

  • device
    • device_private
  • device_driver
    • driver_private
  • bus & class
    • subsys_private
  • module
    • module_kobject

 

다음 그림은 각 구조체에서 정의한 속성들에 대해 생성되는 디렉토리 위치를 보여준다.

 

참고

 

 

댓글 남기기