본문 바로가기

Embedded/Device Driver

linux/Documentation/driver-model/driver

struct(구조)
===========

struct device_driver {
char *name;
struct bus_type *bus;

struct completion unloaded;
stcurt kobject kobj;
list_t devices;

struct module *owner;

int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume) (struct device *dev); 
};

 
Allocation(할당)
===============


device driver는 정적으로 구조(structures)를 할당(allocate)한다.
시스템에 driver가 지원하는 여러 device가 있을 수 있다.
대개 device_driver 는 (특정 device instance 가 아니라) driver를 나타낸다.


Initialization(초기화)
===================


driver는 최소한 name과 bus fields을 초기화해야 한다.
또한 (장치가 연결됐을 때)devclass fields를 초기화 해야한다.그러면 내부적으로 적절한 결합을 받을 수 있다.
선택사항이긴 하나... 가능한 많은 callback 함수들도 초기화 해줘야 한다. 
(callback 함수 : probe, remove, suspend, resume)


Declaration(선언)
================


위에서 얘기했지만, device_driver object는 정적으로 할당된다.
아래에는 eepro100 driver를 선언하는 예제를 보여준다.
이 선언은 가상적인것이며, driver가 새로운 모델에 완벽하에 변환(convert)됐다는 전제하에 작성된 것입니다.

static struct device_driver eepro100_driver = {
.name = "eepro100",
.bus = &pci_bus_type,

.probe = eepro100_probe,
.remove = eepro100_remove,
.suspend = eepro100_suspend,
.resume = eepro100_resume,
}; 

대부분의 driver는 새로운 모델에 맞도록 완벽하게 변환(convert)될 수 없다.
왜냐하면, 버스는 각 버스에 맞는 특정 structure와 특정 field를 가지고 있기 때문에 일반적이지 않기 때문이다.

위 예제는 device ID 구조의 가장 일반적인 예이다.

driver는 일반적으로 driver가 지원하는 device ID의 배열을 정의한다.
이러한 구조 형식과 device ID를 비교하기위한 의미가 완전히 bus-specific하게 되어있다.
그것을 bus-specific하게 정의함으로서 type-safety한 희생은 있기만, 우리는 bus-specific structes를 가질 수 있다.

[[driver는 일반적으로 driver가 지원하는 device ID의 배열을 정의한다.
structure의 형식과 device ID를 비교하기위한 의미가 (버스에 의존적이기 때문에) 버스에따라 다르다.
그래서 각 버스에 따라서 특정 structure를 부여했다.]]

다음은 pci 버스 driver에 device_driver 구조체가 포함되어 있는 정의를 보여준다.

struct pci_driver {
const struct  cpi_device_id *id_table;
struct device_driver driver;
}

위의 pci_dvier에 eepro100 driver를 적용하면 아래와 같다.

static struct pci_driver eepro100_driver = {
.id_table = eepro100_pci_tlb,
.driver =  {
.name = "eepro100",
.bus = &pci_bus_type,
.probe = eepro100_probe,
.remove = eepro100_remove,
.suspend = eepro100_suspend,
.resume = eepro100_resume, 
}

Some may find the syntax of embedded struct initialization awkward or
even a bit ugly. So far, it's the best way we've found to do what we want... 


Registration(등록)
=================


int driver_register(struct device_driver *drv);
 
driver는 시작할때 device_driver 구조체를 등록한다.
특정 버스driver가 없는 driver는 driver_register에 사용되고 그들의 device_driver 구조체 포인터를 전달한다.

그러나 대부분의 driver는 bus_specific structure를 가지고 있다.
그렇기 때문에 그 bus driver에 맞는 등록 함수를 사용해야 한다.
(pci 버스의 경우 pci_driver_register함수를 사용) 

driver register에게 그들의 driver구조체를 빨리 등록하는 것은 중요하다.
등록시에 몇몇 중요한 field들의 초기화와, reference count과 lock등록을 같이 수행한다.
초기화되는 field들은 항상 유효한 것으로 간주된다. 그리고 device model core나 bus driver에의해 사용될 수 있다.


Transition Bus Drivers (버스 드라이버의 전이)
==========================================


wrapper 함수를 정의함으로써, 새로운 모델로 쉽게 전환될 수 있다.
driver는 모든 일반적인 구조를 무시하고  bus wrapper로 field를 입력할 수 있다.
callback 때문에, 버스는 generic callback을 드라이버의 bus-specific callback보다 이전에 호출되도록 정의할 수 있다.

이 솔루션은 임시적으로 만들어진 것이다.
driver에서 클래스 정보를 얻기 위해서는 driver는 수정되어야만 한다.
driver를 새 모델로 변환하는 것은 기초적인 복잡성(infrastructural complexity)과 코드 크기를 줄이기 때문에
클래스 정보가 추가될 수록 driver를 새 모델로 변환할 것을 추천한다.


Access(접근)
=============


object가 한번 등록되면 그 객체는 lock과 device list같은 객체의 일반 field에 접근할수 있다.

int driver_for_each_dev(struct device_driver * drv, void * data, int (*callback) (struct device *dev, void * data));

device field는 driver에 바인딩되어있는 모든 device의 목록이다.
LDM core는 driver controls을 모든 device위에서 작동하기위해 도우미 함수를 제공한다.
이 도우미 함수는 각 노드에 접근할때 driver를 잠그고, 접근시에 reference count를 제공한다.


sysfs
=====

driver가 등록될때 sysfs 디렉토리는 그 장치의 bus 디렉토리에 생성된다.
driver는 유저스페이스에 인터페이스를 export할 수 있다.
인터페이스를 이용해서 driver의 작동을 제어할 수 있다.

When a driver is registered, a sysfs directory is created in its bus's directory.
In this directory, the driver can export an interface to userspace to control operation of the driver on a global basis;
e.g. toggling debugging output in the driver.


A future feature of this directory will be a 'devices' directory.
This directory will contain symlinks to the directories of devices it supports.



Callbacks
========== 

int (*probe) (struct device * dev);

The probe() entry is called in task context, with the bus's rwsem locked and the driver partially bound to the device.
Drivers commonly use container_of() to convert "dev" to a bus-specific type, both in probe() and other routines.
That type often provides device resource data, such as pci_dev.resource[] or platform_device.resources, which is used in
addition to dev->platform_data to initialize the driver.

This callback holds the driver-specific logic to bind the driver to a given device.
That includes verifying that the device is present, that it's a version the driver can handle, that driver data structures can be allocated and initialized, and that any hardware can be initialized.
Drivers often store a pointer to their state with dev_set_drvdata().
When the driver has successfully bound itself to that device, then probe() returns zero and the driver model code will finish its part of binding the driver to that device.

A driver's probe() may return a negative errno value to indicate that the driver did not bind to this device, in which case it should have released all resources it allocated.

int  (*remove) (struct device * dev);

remove is called to unbind a driver from a device.
This may be called if a device is physically removed from the system, if the driver module is being unloaded, during a reboot sequence, or in other cases.

It is up to the driver to determine if the device is present or not. It should free any resources allocated specifically for the
device; i.e. anything in the device's driver_data field. 

If the device is still present, it should quiesce the device and place it into a supported low-power state.

int (*suspend) (struct device * dev, pm_message_t state);

suspend is called to put the device in a low power state.

int (*resume) (struct device * dev);

Resume is used to bring a device back from a low power state.


Attributes
========= 

struct driver_attribute {
        struct attribute        attr;
        ssize_t (*show)(struct device_driver *driver, char *buf);
        ssize_t (*store)(struct device_driver *, const char * buf, size_t count);
};

Device drivers can export attributes via their sysfs directories. 
Drivers can declare attributes using a DRIVER_ATTR macro that works identically to the DEVICE_ATTR macro. 

Example:

DRIVER_ATTR(debug,0644,show_debug,store_debug);

This is equivalent to declaring:

struct driver_attribute driver_attr_debug;

This can then be used to add and remove the attribute from the driver's directory using:

int driver_create_file(struct device_driver *, struct driver_attribute *);
void driver_remove_file(struct device_driver *, struct driver_attribute *);

'Embedded > Device Driver' 카테고리의 다른 글

udev  (1) 2011.05.03
Device Driver] 인터럽트_Interrupt  (0) 2011.05.03
Kernel Timer 사용하기  (0) 2011.04.30
Device Driver] Kernel Facilities  (0) 2011.04.30
Device Driver] 커널 메시지 출력  (0) 2011.04.25