다음을 통해 공유


원자 버스 작업

SPB에 연결된 주변 디바이스의 특정 하드웨어 기능을 사용하려면 SPB 컨트롤러(즉, 주변 드라이버)의 클라이언트가 원자 버스 작업으로 디바이스 간에 일련의 데이터 전송을 수행해야 할 수 있습니다. 시퀀스가 완료될 때까지 다른 클라이언트가 버스의 디바이스 간에 데이터를 전송할 수 없기 때문에 전송 시퀀스는 원자성입니다.

클라이언트가 원자 버스 작업으로 전송 시퀀스를 수행하는 일반적인 방법은 대상 디바이스에 IOCTL_SPB_EXECUTE_SEQUENCE 요청을 보내는 것입니다. 이 요청에서 클라이언트는 시퀀스를 간단한 읽기 및 쓰기 전송 목록으로 지정합니다. 목록은 임의의 길이일 수 있습니다. 읽기 및 쓰기는 나열된 순서대로 수행되며 각 읽기 또는 쓰기는 임의의 바이트 수를 전송할 수 있습니다. 대부분의 SPB 컨트롤러는 IOCTL_SPB_EXECUTE_SEQUENCE 요청을 지원합니다.

SPB 컨트롤러 잠금

원자 전송 시퀀스를 수행하는 덜 일반적인 방법은 SPB 컨트롤러 잠금을 사용하는 것입니다. 클라이언트는 잠금을 획득하기 위해 IOCTL_SPB_LOCK_CONTROLLER 요청을 보내고 잠금을 해제하는 IOCTL_SPB_UNLOCK_CONTROLLER 요청을 보냅니다. 클라이언트가 컨트롤러 잠금을 보유하는 경우 클라이언트가 디바이스에 보내는 간단한 읽기 및 쓰기(IRP_MJ_READIRP_MJ_WRITE) 요청 시퀀스는 버스에서 원자성 작업으로 수행됩니다.

대부분의 SPB 연결 주변 장치는 컨트롤러 잠금이 필요하지 않으며 대부분의 SPB 컨트롤러 드라이버는 이러한 잠금에 대한 지원을 구현하지 않습니다. 그러나 일부 클라이언트는 비정상적인 기능이 있는 디바이스에 액세스하기 위해 컨트롤러 잠금을 사용해야 할 수 있습니다.

예를 들어 디바이스는 버스에서 원자성인 읽기-수정-쓰기 작업을 통해서만 액세스할 수 있는 디바이스 함수를 구현할 수 있습니다. 이러한 작업을 수행하기 위해 클라이언트는 다음과 같은 4개의 I/O 요청(표시된 순서대로)을 보냅니다.

  1. IOCTL_SPB_LOCK_CONTROLLER – 컨트롤러 잠금을 획득합니다.
  2. IRP_MJ_READ – 대상 디바이스에서 데이터 블록을 읽습니다.
  3. IRP_MJ_WRITE – 수정된 데이터를 디바이스에 다시 씁니다.
  4. IOCTL_SPB_UNLOCK_CONTROLLER – 컨트롤러 잠금을 해제합니다.

이전 목록의 읽기 작업 후에 클라이언트는 디바이스에서 읽은 데이터를 해석하고 디바이스에 다시 쓰기 전에 데이터를 수정합니다.

그러나 컨트롤러 잠금이 필요한 SPB 연결 디바이스는 거의 없습니다. 원자 버스 작업이 필요한 대부분의 디바이스의 경우 IOCTL_SPB_EXECUTE_SEQUENCE 요청으로 충분합니다.

SPB 컨트롤러 잠금과 SPB 연결 잠금을 혼동하지 마세요. 두 클라이언트가 동일한 SPB 연결 주변 장치에 대한 액세스를 공유하는 비정형의 경우 두 클라이언트는 연결 잠금을 사용하여 일시적으로 디바이스에 대한 단독 액세스를 얻을 수 있습니다. 자세한 내용은 SPB 연결 잠금을 참조하세요.

하드웨어 버스 신호

IOCTL_SPB_EXECUTE_SEQUENCE 요청을 처리하기 위해 SPB 컨트롤러 드라이버는 전송 시퀀스 중에 버스에 적절한 신호를 생성하도록 컨트롤러 하드웨어를 구성합니다. 버스에 연결된 주변 장치는 이러한 신호에 의존하여 원자 버스 작업이 진행되는 시기를 감지할 수 있습니다. SPB 컨트롤러가 원자 버스 작업으로 전송 시퀀스를 수행하는 데 사용하는 하드웨어 신호 집합은 버스 유형에 따라 달라집니다.

I2C 버스의 경우 컨트롤러는 버스에서 시작 비트를 전송하여 시퀀스를 시작하고 정지 비트를 전송하여 시퀀스를 종료합니다. 시작 비트와 중지 비트 사이에 디바이스 간 데이터 전송 시퀀스는 단일 원자 버스 작업으로 수행됩니다. 시퀀스의 최종 전송을 제외하고, 각 전송 뒤에 I2C 다시 시작 작업(중지 비트가 선행되지 않는 반복된 시작 비트)이 뒤따릅니다.

SPI 버스의 경우 컨트롤러는 대상 디바이스에 칩 선택 선을 어설션하여 시퀀스를 시작하고 칩 선택 선을 해제하여 시퀀스를 종료합니다. 버스에 대한 일련의 데이터 전송 중에 칩 선택 라인을 지속적으로 어설션하여 전송은 단일 원자 버스 작업으로 수행됩니다.

예제 I2C 디바이스

I2C 버스의 일반적인 주변 장치는 여러 내부 디바이스 기능을 구현할 수 있습니다. 이러한 함수 중 일부에 액세스하기 위해 클라이언트는 IOCTL_SPB_EXECUTE_SEQUENCE 요청을 사용할 수 있습니다.

예를 들어 I2C 주변 장치 디바이스에는 다음 두 개의 내부 레지스터가 포함될 수 있습니다.

  • 클라이언트가 액세스할 디바이스 함수의 내부 주소를 쓰는 함수 주소 레지스터 입니다.
  • 클라이언트에서 데이터를 읽거나 지정된 함수 주소에 데이터를 쓰는 데이터 레지스터 입니다.

이 예제의 I2C 주변 장치는 시작 비트 이후 디바이스에 기록된 첫 번째 바이트를 함수 주소 레지스터에 로드하는 함수 주소로 해석합니다. 시퀀스가 종료되기 전에 디바이스에서 전송된 모든 추가 바이트(중지 비트로 표시됨)는 디바이스에서 데이터 레지스터를 통해 전송할 데이터로 처리됩니다.

쓰기 작업을 수행하기 위해 클라이언트는 쓰기 버퍼의 첫 번째 바이트가 함수 주소이고 버퍼의 나머지 바이트는 함수 주소에 쓸 데이터인 쓰기(IRP_MJ_WRITE) 요청을 보냅니다.

디바이스에서 읽는 것은 더 복잡합니다. 이 예제의 I2C 디바이스는 버스에서 정지 비트가 감지될 때 함수 주소 레지스터를 기본값인 0으로 자동으로 다시 설정하는 "빠른 읽기" 기능을 지원한다고 가정합니다. 이 기능을 사용하면 클라이언트는 먼저 함수 주소 레지스터에 쓸 필요 없이 함수 주소 0에서 데이터를 읽을 수 있습니다. 이 기능은 특히 대부분의 읽기가 함수 주소 0에서 제공되고 상대적으로 짧은 경우 디바이스 읽기 작업의 속도를 향상시킬 수 있습니다.

그러나 0이 아닌 함수 주소에서 데이터 블록을 읽으려면 클라이언트는 데이터 레지스터에서 데이터 블록을 읽기 전에 함수 주소 레지스터에 바이트를 기록해야 합니다. 클라이언트는 이러한 쓰기 및 읽기 전송을 원자 버스 작업으로 수행하여 버스 컨트롤러가 함수 주소 레지스터에 쓰기 후 및 데이터 레지스터에서 읽기 전에 정지 비트를 전송하지 않도록 해야 합니다. 그렇지 않으면 중지 비트로 인해 0이 아닌 함수 주소 대신 함수 주소 0에서 데이터가 읽히게 됩니다.

다음 목록에서는 클라이언트가 디바이스의 0이 아닌 함수 주소에 있는 데이터에 대해 읽기-수정-쓰기 작업을 수행하기 위해 이 예제에서I2C 디바이스로 보내는 일련의 I/O 요청에 대해 설명합니다.

  1. IOCTL_SPB_EXECUTE_SEQUENCE - I/O 전송 시퀀스를 수행하여 디바이스에서 데이터를 읽습니다. 이 시퀀스의 첫 번째 전송은 함수 주소 레지스터에 대한 바이트 쓰기입니다. 시퀀스의 두 번째 전송은 선택한 함수 주소에서 몇 바이트의 읽기입니다. 이 두 전송은 버스에서 원자성으로 수행됩니다.
  2. IRP_MJ_WRITE - 디바이스에 데이터를 씁니다. 이 요청에 대한 쓰기 버퍼의 첫 번째 바이트는 함수 주소 레지스터에 쓸 값입니다. 버퍼의 나머지 바이트는 선택한 함수 주소에 쓸 데이터입니다.

이 읽기-수정-쓰기 작업을 수행하는 대신 다른 요청 패턴을 사용할 수 있습니다. 예를 들어 2단계의 IRP_MJ_WRITE 요청은 두 번의 쓰기 작업이 포함된 두 개의 데이터 전송을 지정하는 IOCTL_SPB_EXECUTE_SEQUENCE 요청으로 바꿀 수 있습니다. 시퀀스의 첫 번째 전송은 바이트를 함수 주소 레지스터에 로드합니다. 두 번째 전송은 데이터 바이트를 선택한 함수 주소에 씁니다. 이 요청은 2단계의 IRP_MJ_WRITE 요청과 달리 클라이언트가 동일한 쓰기 버퍼에서 함수-주소 바이트와 데이터 바이트를 결합할 필요가 없습니다.

이 디바이스의 함수 주소 0에서 읽기-수정-쓰기를 수행하려면 이전 목록의 1단계에서 IOCTL_SPB_EXECUTE_SEQUENCE 요청을 간단한 읽기(IRP_MJ_READ) 요청으로 바꿀 수 있습니다.