2015.03.26 13:45


디바이스 드라이버 작업을 하다 보면 자주 보게 되는 매크로중으 히나가

바로  container_of 매크로 입니다.

이해가 가는 것 같기도 하고 아니것 같기도 하고 묻는 사람도 있고 해서

오늘은 이 매크로에 대해서 한번 알아 볼가 합니다.


일단 함수 원형을 한번 볼까요?


lxr 사이트에서 긁어 왔습니다.

위치는 include/linux/kernel.h 입니다.


 650#define container_of(ptr, type, member) ({                      \
 651        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
 652        (type *)( (char *)__mptr - offsetof(type,member) );})


이건 뭐 상당히 거시기 하군요.

뜯기 전에 좀 쉬운 예를 들어 보겠습니다.


 




위의 그림이 보이십니까?

뽀글이 structure 는 인자가 4개 입니다.

시작 번지는 1000 번지구요.  

라면 번지 1000

수프 번지 1008

가격 번지 1016

봉지 번지 1020


이렇게 되는 군요.

그런데 어떤 함수를 호출하면서 가격의 포인터를 넘겨주었습니다.


그래서...  가격포인터를 인자로 받은 함수가 아래와 같습니다.


int   how_much( short *가격)

{

     printk(" 가격위치는 0x%p\n", 가격)

     return 0;

}


자 이제 how_much 함수는 가격포인터를 프린트해줍니다.

당연히 그 값은 1016 번지가 되겠죠.


자 이제 how_much 함수는 가격의 포인터를 알고 있습니다.

처음 그림에서 보면 가격 인자는 뽀글이 자료 구조의 멤버네요?


how_much 에서 short * 형태로 인자를 받았지만

그 포인터가 struct 뽀글이 멤버라는 것을 알고 있고

struct 뽀글이 다른 멤버를 필요로 하는 경우가 생겼습니다.


쉽게 얘기해서 내가 받은 인자를 이용해서 그 인자를 포함하고 있는

자료구조가 필요한 것입니다.


직관적으로 그림에서 보면 뽀글이 자료구조의 주소는 1000 번지 입니다.

가격 포인터가 1016 이므로 16 만 빼면 1000 번지가 나오죠?

이제 우리는 struct 뽀글이 의 변수 위치를 찾은겁니다.


함수를 다시 써보죠.


int   how_much( short *내가격)

{

     struct 뽀글이 *myboggle = containerof(내가격, struct 뽀글이, 가격);


     printk(" 내가격을 멤버로 갖는 boggle 구조체의 위치는 0x%p\n", myboggle)


     myboggle->라면 = 봉지라면;


     return 0;

}


이 결과는 0x1000 이 나오게 됩니다.


컨테이너 매크로에서 필요한 것은 세가지 조건입니다.

현재 알고 있는 변수포인터.

구조체함수원형.

구조체멤버의 변수명.


복잡해 보이기도 하지만 결론적으로는


1000 = container_of(1006, struct 뽀글이, 가격)


이렇게 되는데 1006 이라는 위치를 가르쳐주고

1006 이 뽀글이의 가격이라는 멤버의 주소이다.


뽀글이 구조체에서 가격의 변수 위치는 항상 6이라는 오프셋이니까

1006 - 6 = 1000.


이렇게 되도록 짜여져 있는 매크로 입니다.


복잡한가요?


정리합니다.

container_of (prt, type, member) 

prt           :  임의의 포인터값.

type         :  ptr 을 멤버로 갖는 자료구조형(구하고자 하는 자료구조의 형).

member    :  자료구조형에서 ptr 의 멤머 이름.




출처 : http://forum.falinux.com/zbxe/index.php?document_srl=531954

신고


Posted by injunech
2015.03.24 15:01


디바이스 트리 작성법 (4편)


7 고급 주제들 

7.1 복잡한 샘플 장치

지금까지 디바이스 트리를 이해하기 위한 기본적인 정의만 다루었습니다. 
이제 좀 더 디바이스 트리를 완벽하게 사용하기 이해서 좀 더 복잡한 하드웨어를 샘플에 추가 할 필요가 있습니다. 

이런 복잡한 하드웨어로 PCI 호스트 브릿지를 추가해 보겠습니다. 
이 샘플 용 PCI 호스트 브릿지는 
0x10180000 를 제어 레지스터 주소로 갖고 
0x80000000 주소를 BARs 영역의 시작 주소를 
갖는 것으로 가정합니다. 
지금까지 사용한 예에 
다음과 같은 PCI 호스트 브릿지 노드를 추가하는 것으로 설명을 시작하겠습니다. 

pci@10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
};
이 예를 보면 이 PCI 호스트 브릿지는 

제어를 위한 주소가 reg 속성으로 0x10180000 번지부터 크기는 0x1000 의 범위를 가집니다. 
이 번지영역을 이용하여 호스트 브릿지를 제어하게 될겁니다. 
이 호스트 브릿지는 시스템 인터럽트에 8 번 인터럽트를 통해서 인터럽트 처리를 하게 됩니다. 

7.2 PCI 호스트 브릿지

이 섹션은 Host/PCI 브릿지 노드에 대한 설명입니다. 

사전에 양해 드리고 싶은 것은 
이 섹션을 설명할 때 여러분은 이미 PCI 에 대한 기본 지식이 
있을 것이라는 가정을 두고 있습니다 .
이 문서는 PCI 에 대한 학습문서가 아니기 때문에 
관련된 내용의 다른 곳에서 도움을 얻기 바랍니다. 
또한 ePAPR 나 Open Firmware의 PCI Bus Binding에 대한 것을 참조 하시면 많은 도움이 되실 겁니다. 
Freescale MPC5200 에 대한 완벽한 예를 거기서 찾아 보실 수 있습니다. 

7.2.1 PCI 버스 번호 매기기

우선 PCI 버스 번호를 처리하기 위한 보완된 예제를 보겠습니다.

pci@0x10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
bus-ranges = <0 0>;
};
보통 각각의 PCI 버스들은 해당 버스에 유일한 번호가 매겨집니다. 
이 버스 넘버링은 
위 예에서 보듯이 두개의 셀을 포함하는 bus-ranges 속성을 사용하여 
디바이스 트리내에 PCI 노드로 표현 됩니다. 
이 bus-ranges 속성 값의 
첫번째 셀은 이 노드에 부여된 버스 번호입니다.
즉 0 번 버스라는 의미가 됩니다. 
두번째 셀은 하부의 PCI 버스들의 중 가장 큰 번호를 기술합니다. 
샘플 장치는 하나의 PCI 버스만을 가지기 때문에 이 값은 0이 됩니다. 
7.2.2 PCI 주소 변환

앞에서 다루었던 외부 버스 공간과 비슷하게, 
PCI 주소 공간도 CPU 주소 공간과 하드웨어 적으로 완벽하게 분리되어 있습니다.
그래서 PCI 주소에서 CPU 주소를 얻기 위해서 주소 변환이 필요합니다. 
이 주소 변환에 사용하는 속성은 이전에 설명했던 
ranges
#address-cells
#size-cells 
속성이 사용됩니다. 
이걸 이용해서 주소 변환을 표현하는 예를 보겠습니다.

pci@0x10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
bus-ranges = <0 0>;

#address-cells = <3>
#size-cells = <2>;
ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
  0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
  0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;
};

위 예를 보면, 
자식 주소( PCI 주소)는 3 개의 셀을 사용합니다. 
이것은 #address-cells 속성에 정의 되어 있습니다. 
그리고 PCI 범위는 2개의 셀로 기입됩니다. 
이것은 #size-cells 셀에 정의 되어 있습니다. 
그리고 부모 주소는 1 개의 셀을 사용하고 있습니다. 
이것은 이전에 상위 노드에서 정의 하고 있습니다. 
우선 왜  PCI 주소를 사용하기 위해서 32 비트 셀이 세개가 필요한지 궁금할 것입니다.
이 세개의 셀들은 다음과 같이 phys.hi, phys.mid 그리고 phys.low 의 순서대로 나열한 자식 주소값입니다. 
phys.hi  셀 값의 의미 : npt000ss bbbbbbbb dddddfff rrrrrrrr
phys.mid 셀 값의 의미 : hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh
phys.low 셀 값의 의미 : llllllll llllllll llllllll llllllll
PCI 주소는 64 비트 주소폭을 갖습니다 그래서 phys.mid 와 phys.low. 가 ranges 속성값 중 진짜 자식주소로 사용됩니다. 
그런데, 여기서 조금 복잡하지만 관심을 가질 것은 phys.high 입니다. 
이것은 다음과 같은 비트 필드로 구성되어 있습니다. 
n : 재 배치 가능영역 표시 
p : 프리펫치 가능 영역 표시
t : 어드레스 정렬 표시 
ss : 영역 구분 코드 
00 : 환경 설정 영역
01 : I/O 영역
10 : 32 비트 메모리 영역
11 : 64 비트 메모리 영역 
bbbbbbbb : PCI 버스 번호. PCI 는 계층적으로 구성된다. 그래서 서브 버스로 정의될 수 있는
                                           PCI/PCI 브리지를 가질수도 있다. 
ddddd : 디바이스 번호, 보통 IDSEL 신호와 연결되어 있다. 
fff : 기능 번호 . PCI 디바이스가 여러 기능을 가지고 있을 경우 사용된다. 
rrrrrrrr : 레지스터 번호 : PCI 환경 설정 단계에서 사용된다. 

PCI 주소 변환의 목적상 가장 중요한 필드들은 p 와 ss 입니다. 
phys.hi 안에 있는 p 와 ss 의 값은  PCI 주소 공간이 어떤 특성이 있는지를 나타냅니다. 
이제 샘플에 기입된 ranges 속성에 값을 해석하면 
다음과 같이 세가지 영역에 대한 것을 기술하고 있습니다. 
PCI 주소 0x80000000 를 시작주소로 갖고 32 비트 프리페치 가능한 512 MByte 크기의 메모리 영역은 
                호스트 CPU의 0x80000000 에 맵핑됩니다. 
PCI 주소 0xa0000000 를 시작주소로 갖고 32 비트 프리페치가 불 가능한 256MByte 크기의 메모리 영역은 
                호스트 CPU의 0xa0000000 에 맵핑됩니다.

PCI 주소 0x00000000 를 시작주소로 갖는 16 MByte 크기의 I/O 영역 영역은 
                호스트 CPU의 0xb0000000 에 맵핑됩니다.
운영체제 입장에서 보면 phys.hi 비트 필드는 해당 노드를 PCI 브릿지의 영역으로 다룰때 
주소변환 목적에 상관없는 필드는 무시할 수 있고 이와 관련된 정보정도만 필요합니다. 
그래서 주소 변환 목적에 상관없는 필드는 무시할 수 있다. 
경우에 따라서 어떤 운영체제는 추가적인 필드의 마스크에 대한 필요할 수 있습니다 .
이때 운영체제는 해당 필드 마스크 값을 얻기 위해서 PCI 버스 노드 에서 "pci" 문자열을 이용해서 찾을 것입니다. 
이런 경우라면 필요한 phys.hi 비트값들을 기입할 필요도 있습니다. 

7.3 복잡한 인터럽트 맵핑

이제 가장 골치아픈 PCI 인터럽트 매핑을 다룰 차례입니다. 
PCI 디바이스는 #INTA, #INTB, #INTC,  #INTD. 라고 불리는 네개의 신호선을 이용하여 인터럽트를 트리거 할수 있습니다.
만약 하나의 PCI 디바이스가 여러가지 기능을 가지고 있지 않다면 인터럽트를 #INTA 만 사용할 것입니다. 
그러나, 각 PCI 슬롯 또는 디바이스 입장에서 동일한 인터럽트가 나가지만 
인터럽트 컨트롤러에는 다른 입력으로 연결되어 있습니다. 
보통 하드웨어적으로  인터럽트 네게의 라인을 한 라인씩 시프트 되는 방식으로 연결합니다. 
예를 들어 첫번째 슬롯의 #INTA 는 PCI 인터럽트 컨트롤러에 #INTA 로 연결되지만 
두번째 슬롯의 #INTA 는 PCI 인터럽트 컨트롤러에 #INTB 로 연결되고

세번째 슬롯의 #INTA 는 PCI 인터럽트 컨트롤러에 #INTC 로 연결됩니다. 
다른 인터럽트 역시 이와 같이 쉬프트 한 상태로 연결됩니다. 
이런 연결 특성을 표현하기 위해서 
디바이스 트리는 각 PCI 인터럽트 신호를 인터럽트 콘트롤러에 맵핑하는 방법이 필요합니다. 
이런 복잡한 인터럽트 매핑에 관련된 속성은 다음과 같은 것이 있습니다. 
#interrupt-cells
interrupt-map
interrupt-map-mask

이 속성들이 PCI 인터럽트 맵핑을 기술하는데 사용될 수 있습니다. 
사실, 이 속성들은 PCI 버스의 인터럽트 매핑에 한정되어 사용되는 것은 아닙니다 .
이 속성들은 복합적인 인터럽트 맵을 기술할때 필요해서 정의 된 것이지만, 
주로 PCI의 인터럽트 같이 복잡한 인터럽트 매핑을 다룰때 사용 됩니다. 
아래에 이런 복잡한 PCI 인터럽트 매핑 예를 보여 드리겠습니다. 

pci@0x10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
bus-ranges = <0 0>;

#address-cells = <3>
#size-cells = <2>;
ranges = <0x42000000 0 0x80000000  0x80000000  0 0x20000000
0x02000000 0 0xa0000000  0xa0000000  0 0x10000000
0x01000000 0 0x00000000  0xb0000000  0 0x01000000>;

#interrupt-cells = <1>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <0xc000 0 0 1 &intc  9 3 // 1st slot
 0xc000 0 0 2 &intc 10 3
 0xc000 0 0 3 &intc 11 3
 0xc000 0 0 4 &intc 12 3

 0xc800 0 0 1 &intc 10 3 // 2nd slot
 0xc800 0 0 2 &intc 11 3
 0xc800 0 0 3 &intc 12 3
 0xc800 0 0 4 &intc  9 3>;
};

이 중에서 가장 먼저 interrupt-map 속성을 설명할 것입니다. 

다음 내용을 이해하기 전에 
복잡한 인터럽트 매핑에 대한 정확한 작업 표현 방법은 
        "Open Firmware Recommended Practice: Interrupt Mapping" 를 참조 하시기 바랍니다.
이 내용을 살펴보면 
interrupt-map 속성은 다음과 같은 형식을 가집니다.
interrupt-map = < 자식_인터럽트_정보1  부모_인터럽트_컨트롤러_정보1  부모_인터럽트_정보1"
                  자식_인터럽트_정보2  부모_인터럽트_컨트롤러_정보2  부모_인터럽트_정보2
                  자식_인터럽트_정보3  부모_인터럽트_컨트롤러_정보3  부모_인터럽트_정보3
   ...
>;   
  
위 예제의 한 부분인 
interrupt-map = <0xc000 0 0 1 &intc  9 3 
에서 정의 한 엔트리
0xc000 0 0 1 &intc  9 3 
를 위 정의에 대입하면 

0xc000 0 0 1  <-- 자식 인터럽트 정보1
&intc <-- 부모 인터럽트 컨트롤러 정보1
9 3 <-- 부모 인터럽트 정보 1
에 해당합니다. 
자식 인터럽트 정보1를 표현하기 위해서 사용하는 셀 수는 
#address-cells
#interrupt-cells
에 의해서 정해집니다. 위 예에서는 다음과 같이 지정되어 있는 것을 보실 수 있습니다. 
#address-cells = <3>
#interrupt-cells = <1>;
여기서 #address-cells 셀수로 표현된 주소 정보가 어떤 의미인지는 뒤에서 자세하게 설명합니다. 
#interrupt-cells 셀 수로 표현되는 것은 자식 인터럽트의 번호를 의미 합니다. 
보통 PCI 디바이스의 하드웨어의 인터럽트 번호를 의미 하는데 0으로 시작하지 않고 
        1 로 시작되는 것에 주의 하시기 바랍니다.
PCI 하드웨어 인터럽트 번호는 1 부터 시작합니다. 
이 PCI 주제 이전에 시스템 인터럽트를 설명할 때, 
시스템 인터럽트는 IRQ  번호 와 해당 인터럽트의 검출 방법에 정보가 필요하기 때문에 
인터럽트의 정보를 표현할 셀 수를 #interrupt-cells 속성을 이용하여 2 개의 셀을 지정했습니다. 
그러나 PCI 인터럽트의 검출은 항상 로우 레벨일 경우만 검출되는 것으로 규정되어 있기 때문에 
위 예에서 보듯이 PCI 인터럽트는 1 개의 셀만 지정하고 있습니다. 
이제 이 자식 디바이스 인터럽트를 받는 부모 인터럽트 컨트롤러 정보를 표현하고 있는 부분을 살펴 보겠습니다. 
예를 보듯이 
&intc
와 같이 표현하고 있습니다. 

이것은 이전 예제에 다음과 같이 표현했을때 
intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};
사용했던 라벨 intc 를 참조 하도록 표현한 것입니다. 

이 표현에서 주의 깊게 보아야 하는 것은 
#interrupt-cells 속성입니다. 
이것은 interrupt-map 속성의 엔트리 중 
부모_인터럽트_정보1 를 지정하는 셀 수로 사용됩니다. 
#interrupt-cells = <2> 
로 지정했기 때문에 

부모_인터럽트_정보1 을 표현하기 위해서  2 개의 셀을 사용한다는 것을 알 수 있습니다.
위 예에서 보듯이 2 개의 셀을 사용해서 표현하고 있고 
9 3
이란 의미는 9 번 인터럽트 번호이며 
PCI 의 인터럽트 검출 레벨은 항상 로우 액티브 이므로 
이에 대한 표현 값으로 3을 지정하고 있습니다. 
이제 #address-cells 셀수로 표현된 주소 정보가 어떤 의미인지를 설명하겠습니다. 
위 예를 보면 이 디바이스 트리가 표현하는 샘플 보드는 
interrupt-map 속성의 주석을 보시면 알수 있듯이 
4개의 인터럽트를 가지는 2개의 PCI 슬롯을 가지고 있음을 알수 있습니다. 
그래서 인터럽트 콘트롤러에 8개의 인터럽트 라인에 대한 맵으로 표현하고 있습니다. 
하나의 PCI 버스상에서 각 PCI 디바이스들은 #INTA 등 과 같은 인터럽트 라인을 4 개밖에 없습니다. 
그래서 인터럽트가 발생했을때 어떤 디바이스의 인터럽트인지 구별하는데 문제가 있습니다. 
다행스럽게도, 모든 PCI 디바이스는 사용가능한 유일한 디바이스 번호를 가지고 있습니다.
그래서 이 디바이스 번호를 추출해야 하는데 
이것은 앞에서 다룬 다음과 같은
phys.hi  셀 값의 의미 : npt000ss bbbbbbbb dddddfff rrrrrrrr
phys.mid 셀 값의 의미 : hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh
phys.low 셀 값의 의미 : llllllll llllllll llllllll llllllll

주소 정보 중에서 phys.hi 의 정보중 디바이스 번호의 비트 필드 정보가 필요합니다. 
이 부분을 앞에서 설명했던 부분을 다시 한번 나열해 보겠습니다. 
n : 재 배치 가능영역 표시 
p : 프리펫치 가능 영역 표시
t : 어드레스 정렬 표시 
ss : 영역 구분 코드 
00 : 환경 설정 영역
01 : I/O 영역
10 : 32 비트 메모리 영역
11 : 64 비트 메모리 영역 
bbbbbbbb : PCI 버스 번호. PCI 는 계층적으로 구성된다. 그래서 서브 버스로 정의될 수 있는
                                          PCI/PCI 브리지를 가질수도 있다. 
ddddd : 디바이스 번호, 보통 IDSEL 신호와 연결되어 있다. 
fff : 기능 번호 . PCI 디바이스가 여러 기능을 가지고 있을 경우 사용된다. 
rrrrrrrr : 레지스터 번호 : PCI 환경 설정 단계에서 사용된다. 

이 중에 
ddddd : 디바이스 번호, 보통 IDSEL 신호와 연결되어 있다. 
부분이 필요합니다. 

이 부분의 정보를 다루기 위해서 
#address-cells

에 지정된 셀 수의 정보를 나열한 것입니다. 
이때 디바이스 번호의 비트 필드 이외에는 필요 없는 정보이므로 
필요한 ddddd 정보 영역에 대한 것만 다음과 같이 나열한 것입니다 .
0xc000 0 0 
여기서 이 주소 정보의 셀은 
#address-cells = <3>
로 지정하고 있습니다. 

여기에 이 주소를 비트 마스크 하여 원하는 비트의 유효범위를 지정하기 위해서 

interrupt-map-mask = <0xf800 0 0 7>;
와 같이 지정하고 있는데 

0xf800 0 0
는 주소 정보를 추출하기 위한 비트 마스크 값이고 
7
은 인터럽트 번호를 추출하기 위한 비트 마스크 값이 됩니다

이제 interrupt-map 속성을 좀 더 설명해 보겠습니다. 

개별적인 PCI 디바이스의 인터럽트를 구별하기 위해서는 PCI 디바이스 번호와 PCI 인터럽트 번호로 
        구성된 묶음이 필요합니다. 
좀 더 쉽게 이야기 하면, 4개의 셀을 가지고 있는 자식 디바이스의 인터럽트 정보 값을 만들어야 합니다.
phys.hi, phys.mid, phys.low 의 각각에 해당하는 값을 기술하고 이를 위해서 
        #address-cells 속성 값은 3 으로 지정하고 있습니다. 
#INTA, #INTB, #INTC, #INTD 중 하나를 지정하기 위해서 #interrupt-cell 속성값은 1로 지정하고 있습니다. 

interrupt-map-mask 속성은 자식 디바이스 인터럽트 정보와 같이 4개의 셀로 구성됩니다. 
마스크 값의 각 비트들을 1로 사용하여 필드의 비트중 유요한 비트임을 표시합니다. 
이제 위 예제에 표현된 것을 정리 해 보겠습니다. 
이 예에서는 , PCI 슬롯 1 은 디바이스 id 24(0x18)을 부여합니다. 
그리고 PCI 슬롯 2 는 디바이스 id 25(0x19)를 부여합니다. 
이 값에 의해서 각 슬롯별 phys.hi 의 값은 결정합니다 
아래처럼 비트 필드의 ddddd 섹션 을 11 비트 만큼 쉬프트해서 디바이스 번호를 얻게 됩니다. 
그래서 
슬롯 1 에 대한 phys.hi 는 0xC000 이고 
슬롯 2 에 대한 phys.hi 는 0xC800 입니다. 
이 모든 것을 담은 interrupt-map 속성은 아래의 내용을 보여 준다. 
슬롯 1 의 #INTA 는 IRQ9  입니다. 주 인터럽트 콘트롤에서 로우 레벨일때 인식됩니다. 
슬롯 1 의 #INTB 는 IRQ10 입니다. 주 인터럽트 콘트롤에서 로우 레벨일때 인식됩니다. 
슬롯 1 의 #INTC 는 IRQ11 입니다. 주 인터럽트 콘트롤에서 로우 레벨일때 인식됩니다. 
슬롯 1 의 #INTD 는 IRQ12 입니다. 주 인터럽트 콘트롤에서 로우 레벨일때 인식됩니다. 
그리고 
슬롯 2 의 #INTA 는 IRQ10 입니다. 주 인터럽트 콘트롤에서 로우 레벨일때 인식됩니다. 
슬롯 2 의 #INTB 는 IRQ11 입니다. 주 인터럽트 콘트롤에서 로우 레벨일때 인식됩니다. 
슬롯 2 의 #INTC 는 IRQ12 입니다. 주 인터럽트 콘트롤에서 로우 레벨일때 인식됩니다. 
슬롯 2 의 #INTD 는 IRQ9  입니다. 주 인터럽트 콘트롤에서 로우 레벨일때 인식됩니다. 
interrupts = <8 0>; 라고 표현된 속성은 호스트/PCI-브릿지 컨트롤러 자신에게 트리거 될 수 있는 인터럽트들을 기술합니다.
이런 인터럽트들은 PCI 디바이스들이 트리거 할 수 있는 인터럽트들과 혼합하지 마시기 바랍니다. 
마지막으로 설명할 것은. 
interrupt-parent 속성인데 
interrupt-map 속성은 모든 자식 및 손자들 노드들에 대해서 디폴트 인터럽트 콘트롤러를 바꿀 수 있습니다. 
이 PCI 예제에서는, PCI 호스트 브릿지가 디폴트 인터럽트 컨트롤러가 된다는 것을 표현하고 있음을 앞에서 설명했습니다. 
만약에 PCI 버스를 통해 연결된 디바이스가 또 다른 인터럽트 컨트롤러에 바로 연결되어 있다면,
interrupt-parent 속성을 가지도록 기술되어야 합니다 .


신고


Posted by injunech
2015.03.24 15:00


디바이스 트리 작성법 (3편)

4.  인터럽트 처리 방법

디바이스 트리 구조내에서 디바이스들은 트리 구조로 계층화 표현이 가능합니다. 
인터럽트는 이런 계층 구조가 곤란 합니다. 
보통 인터럽트는 디바이스의 하드웨어가 인터럽트를 발생하고 
인터럽트 컨트롤러가 해당 신호를 수신하는 구조로 되어 있습니다. 
그래서 인터럽트는 디바이스 트리의 구조와 별도로 디바이스 노드간에 링크 구조로 표현됩니다. 
인터럽트는 디바이스 노드의 속성의 형태로 표현합니다. 
네개의 속성이 인터럽트 간의 연결을 표현하기 위해서 준비 되어 있습니다.
다음은  네개의 속성에 대한 간단한 설명입니다. 
- interrupt-controller 속성
interrupt-controller 속성 은 값이 없는 빈 속성으로 해당 노드의 디바이스가 
인터럽트 신호를 수신하는 인터럽트 컨트롤러 디바이스 임을 표현합니다. 

- interrupt-parent
interrupt-parent 속성을 통해서 라벨 참조 형태로 지정하는 디바이스를 
디바이스 트리의 계층 구조에서 
가장 상위의 인터럽트 컨트롤러로 정의 하게 됩니다. 
interrupt-parent 속성을 갖지 않은 노드들은 그들의 부모 노드중 
interrupt-parent 속성이 선언된 노드의 interrupt-parent 속성을 상속 받습니다. 
- #interrupt-cells 속성
#interrupt-cells 속성에 지정된 값은 interrupts 속성에서 인터럽트에 대한 정보를 속성값으로 표현할때 
사용될 셀수를 나타냅니다. ( #address-cells 와 #size-cells 과 비슷한 역활을 합니다.  )
interrupt-controller 속성이 선언된 노드에 같이 선언됩니다. 
- interrupts 
interrupts  속성은 디바이스가 발생하는 인터럽트 출력 신호에 대한 정보의 리스트를 값으로 표현합니다. 
interrupts 의 속성에 표현되는 한개의 인터럽트에 대한 정보는 여러개의 셀로 구성됩니다. 
이 인터럽트는 인터럽트 컨트롤러에 연결된 인터럽트 입력에 정보 입니다. 
아래 예에 보듯이 대부분의 디바이스는 보통 하나의 인터럽트 출력만 있습니다. 
그러나 어떤 디바이스는 여러개의 인터럽트 출력이 있을 수 있습니다. 
interrupts 의 속성 값에 지정되는 정보의 의미는 인터럽트 컨트롤러에 의존적입니다. 
인터럽트 콘트롤러 마다 인터럽트를 다루기 위한 나름대로의 특성이 있으므로 이에 맞는 것으로 정의하고 
이에 따라서 셀수가 결정됩니다. 

아래 코드는 Coyote's Revenge 샘플 장치에 인터럽트와 관련된 것을 추가 하였습니다. 

/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
interrupts = < 1 0 >;
};
serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
interrupts = < 2 0 >;
};
gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
interrupts = < 3 0 >;
};
intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};
spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
interrupts = < 4 0 >;
};
external-bus {
#address-cells = <2>
#size-cells = <1>;
ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = < 5 2 >;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
interrupts = < 6 2 >;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
interrupts = < 7 3 >;
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
주목해야 할 몇 가지:
위 코드에 표현하고 있는 샘플 장치에는 
노드 이름이 interrupt-controller@10140000 로 지정된 한개의 인터럽트 컨트롤러를 가지고 있다. 

여기서 특이하게 'intc:' 라고 하는 라벨이 인터럽트 컨트롤러 노드에 추가된 것을 볼 수 있습니다. 
이 라벨은 루트 노드에 interrupt-parent 속성에서 사용합니다. 
interrupt-parent = <&intc>;
이 interrupt-parent 속성 값은 시스템의 디폴트 인터럽트 컨트롤러를 지정합니다. 
자식 노드들은 해당 인터럽트를 수신하는 것이 어떤 것인가를 지정하지 않고 
상위 노드에서 interrupt-parent 속성으로 지정된 인터럽트 컨트롤러를 해당 디바이스 노드에 연결된 
인터럽트 컨트롤러를 디폴트로 지정합니다. 
왜냐하면 모든 자식 노드들은 해당 속성을 쓰지 않아도 상위 노드에 지정된  interrupt-parent 속성이 상속되기 때문이다. 
각 디바이스의 interrupts 속성은 각각의 인터럽트 입력 라인을 기술하기 위해서 사용합니다.
이때 인터럽트 컨트롤러를 표현한 노드에 정의된 #interrupt-cells 속성은 
interrupts 속성에서 하나의 인터럽트에 대한 기술을 하기 위한 셀 수를 지정합니다. 
위 예에서 "#interrupt-cells" 은 2 입니다. 그래서 하나의 인터럽트 입력을 표현하기 위해서는 2 개의 셀이 필요합니다. 
이 예에서는 인터럽트 라인 번호를 표현하기 위해서 첫번째 셀이 쓰입니다. 
그리고 두번째 셀은 액티브 되는 인터럽트 레벨의 조건이 레벨 하이인지 로우인지 엣지인지를 나타냅니다. 
여러분이 다루고자 하는 인터럽트 컨트롤러에 인터럽트 입력에 대한 부분을 다루고자 한다면 
해당 인터럽트 컨트롤러의 매뉴얼을 참조해야 합니다. 
5.  디바이스 표현 데이터

지금까지 다룬 공통된 속성이외에, 여러분이 필요하다고 생각되는 임의의 속성이나 자식 노드들을 
노드에 포함시킬 수 있습니다. 
만약에 디바이스 트리에 여러분이 다루는 운영체제에서 필요로 하는 어떤 데이터를 추가 하고 싶다면
다음과 같은 규칙 하에 추가 하시기 바랍니다. 
첫째, 새로운 디바이스 노드에 추가할 속성 이름은 표준 속성 이름과 충돌을 방지하기 위해서 제조사 접두사를 붙이싶시오.
두번째, 속성의 의미와 자식 노드에 대한 것은 
운영체제의 디바이스 드라이버 개발자가 해당 내용을 이해할 수 있도록 바인딩 문서에 문서화 하시기 바랍니다. 
여기서 디바이스 트리에서 바인딩이라고 하는 의미는 
디바이스 트리 안에서 디바이스가 표현되는 것을 의미합니다. 
바인딩관 관련되어 기술되는 문서에는 해당 디바이스 노드에 
새로 만들어지는 속성은 어떤 의미인지, 속성은 어떤 값을 갖을 수 있는지 , 자식 노드를 가지고 있는지, 
기술하고자 하는 디바이스가 어떤 디바이스인지가 표현되어야 합니다. 
디바이스를 표현하는 노드 각각은 compatible 속성 값에 각각이 서로 구별될 수 있는 고유의 문자열을 사용하여야 합니다. 
새로운 디바이스들에 대한 바인딩은 이 문서가 있는 사이트의 위키에 문서화 되어야 합니다.
문서 형식과 검토 프로세스에 대해서 설명하는 메인 페이지를 보시기 바랍니다 
세번째, devicetree-discuss@lists.ozlabs.org 메일링 리스트에 새로 만들어진 바인딩과 관련된 검토 내용을 
        포스팅하시기 바랍니다.
포스팅을 하시면 나중에 발생할 수 있는 많은 예상되는 문제들 중 공통된 실수를 잡을 수 있습니다. 

6.  특별한 노드들 

6.1 aliases 노드

어떤 노드는 노드 이름으로 /external-bus/ethernet@0,0 와 같이 풀 패쓰 즉 긴 이름을 사용합니다. 
또는 우리는 이런 긴 이름보다는 eth0  와 같은 짧은 이름을 종종 사용합니다. 
그래서 디바이스 트리를 볼 때 "eth0 가 무슨 디바이스일까?" 라는 생각으로 보게 됩니다 .
이런 경우들에 대해서 디바이스 트리는 aliases 라는 노드를 제공하여 
쉽게 긴이름을 짧은 이름으로 대치하게 하거나 일반적으로 알수 있는 용어로 바꾸어 사용할 수 있도록 
해 줍니다. 
예를 들면:

aliases {
ethernet0 = &eth0;
serial0 = &serial0;
};
즉 ethernet0 는 eth0 라는 이름과 같다는 것을 표현해 줄 수 있습니다. 

aliases 안에 기술되는 형식은 

별명  = &라벨;
형태가 됩니다. 

이런 문법 표현은 별명에 해당하는 문자열을 라벨에 대한 참조로 사용할 수 있도록 합니다. 
이런 표현은 운영체제가 디바이스 의 식별자를 부여하는 방법으로 환영할 만한 것입니다. 

그리고 위 표현 방법은 의미는 interrupt-parent 속성 값을 표현 할때 사용했던 "< &label >" 와는 다른 것임을 주의 하십시오. 
6.2 chosen 노드

chosen 노드는 실제 디바이스를 디바이스 트리에 표현하는 것은 아닙니다 .

단지, 부트 아큐먼트와 같은, 펌웨어에서 운영 체제에 전달된 데이터를 파씽하기 위한 방법으로 제공됩니다.
일반적으로 .dts 소스 파일들내에 기술된 chosen 노드는 왼쪽 키 부분이 비어 있습니다. 
         그리고 그 정보는 부팅 동안만 유효합니다.
예제 시스템에서, 펌웨어는 chosen 노드를 다음과 같이 추가 하도록 하겠습니다.

chosen {
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};


신고

'Programming > Linux/Unix' 카테고리의 다른 글

[Kernel Macro] container_of 매크로  (0) 2015.03.26
디바이스 트리 작성법 (4편)  (0) 2015.03.24
디바이스 트리 작성법 (3편)  (0) 2015.03.24
디바이스 트리 작성법 (2편)  (1) 2015.03.24
디바이스 트리 작성법 (1편)  (0) 2015.03.24
[Linux] Module 관련  (0) 2015.03.13


Posted by injunech

티스토리 툴바