Operazioni atomica degli autobus
Per usare determinate funzionalità hardware di un dispositivo periferico connesso a SPB, potrebbe essere necessario eseguire una sequenza di trasferimenti di dati da e verso il dispositivo come operazione atomica del bus. La sequenza di trasferimento è atomica perché nessun altro client può trasferire dati da o verso un dispositivo nel bus fino al termine della sequenza.
Il modo tipico per consentire a un client di eseguire una sequenza di trasferimento come operazione atomica del bus consiste nell'inviare una richiesta di IOCTL_SPB_EXECUTE_SEQUENCE al dispositivo di destinazione. In questa richiesta, il client specifica la sequenza come elenco di trasferimenti di lettura e scrittura semplici. L'elenco può essere di lunghezza arbitraria. Le letture e le scritture vengono eseguite nell'ordine in cui sono elencate e ogni lettura o scrittura può trasferire un numero arbitrario di byte. La maggior parte dei controller SPB supporta le richieste di IOCTL_SPB_EXECUTE_SEQUENCE .
Blocchi del controller SPB
Un modo meno comune per eseguire una sequenza di trasferimento atomico consiste nell'usare un blocco del controller SPB. Un client invia una richiesta di IOCTL_SPB_LOCK_CONTROLLER per acquisire il blocco e una richiesta di IOCTL_SPB_UNLOCK_CONTROLLER per rilasciare il blocco. Quando un client mantiene il blocco del controller, qualsiasi sequenza di operazioni di lettura e scrittura semplici (IRP_MJ_READ e IRP_MJ_WRITE) richiede che il client invii al dispositivo venga eseguito come operazione atomica sul bus.
La maggior parte dei dispositivi periferici connessi a SPB non richiede blocchi del controller e la maggior parte dei driver del controller SPB non implementa il supporto per questi blocchi. Tuttavia, alcuni client potrebbero dover usare blocchi controller per accedere ai dispositivi con funzionalità insolite.
Ad esempio, un dispositivo potrebbe implementare funzioni del dispositivo a cui è possibile accedere solo tramite operazioni di lettura-modifica/scrittura atomiche sul bus. Per eseguire tale operazione, il client invia le quattro richieste di I/O seguenti (nell'ordine illustrato):
- IOCTL_SPB_LOCK_CONTROLLER : acquisire il blocco del controller.
- IRP_MJ_READ : legge un blocco di dati da un dispositivo di destinazione.
- IRP_MJ_WRITE : scrivere nuovamente i dati modificati nel dispositivo.
- IOCTL_SPB_UNLOCK_CONTROLLER : rilasciare il blocco del controller.
Dopo l'operazione di lettura nell'elenco precedente, il client interpreta i dati letti dal dispositivo e modifica i dati prima di scriverlo nel dispositivo.
Tuttavia, alcuni dispositivi connessi a SPB dispongono di funzionalità che richiedono blocchi del controller. Per la maggior parte dei dispositivi che richiedono operazioni atomiche sul bus, IOCTL_SPB_EXECUTE_SEQUENCE richieste sono sufficienti.
Non confondere i blocchi del controller SPB con i blocchi di connessione SPB. Nel caso atipico in cui due client condividono l'accesso allo stesso dispositivo periferico connesso a SPB, entrambi i client possono usare un blocco di connessione per ottenere temporaneamente l'accesso esclusivo al dispositivo. Per altre informazioni, vedere Blocchi di connessione SPB.
Segnali del bus hardware
Per gestire una richiesta di IOCTL_SPB_EXECUTE_SEQUENCE , un driver controller SPB configura l'hardware del controller per generare i segnali appropriati sul bus durante la sequenza di trasferimento. I dispositivi periferici collegati al bus potrebbero basarsi su questi segnali per rilevare quando è in corso un'operazione atomica del bus. Il set di segnali hardware che un controller SPB usa per eseguire una sequenza di trasferimento come operazione atomica del bus dipende dal tipo di bus.
Per un bus I2C, il controller avvia una sequenza trasmettendo un bit di inizio sul bus e termina una sequenza trasmettendo un bit di arresto. Tra i bit di avvio e di arresto, la sequenza di trasferimenti di dati da e verso il dispositivo viene eseguita come singola operazione atomica del bus. Ad eccezione del trasferimento finale nella sequenza, ogni trasferimento è seguito da un'operazione di riavvio I2C (un bit di avvio ripetuto che non è preceduto da un bit di arresto).
Per un bus SPI, il controller avvia una sequenza asserendo la linea di selezione del chip nel dispositivo di destinazione e termina la sequenza deassertando la linea di selezione del chip. Mantenendo costante l'asserzione della linea di selezione chip durante una sequenza di trasferimenti di dati sul bus, i trasferimenti vengono eseguiti come singola operazione atomica dell'autobus.
Un esempio di dispositivo I2C
Un dispositivo periferico tipico in un bus I2C potrebbe implementare diverse funzioni interne del dispositivo. Per accedere ad alcune di queste funzioni, un client può usare IOCTL_SPB_EXECUTE_SEQUENCE richieste.
Ad esempio, un dispositivo periferico I2C potrebbe contenere i due registri interni seguenti:
- Registro dell'indirizzo della funzione in cui il client scrive l'indirizzo interno della funzione del dispositivo per accedere.
- Registro dei dati tramite il quale il client legge i dati da o scrive i dati nell'indirizzo della funzione specificato.
Il dispositivo periferico I2C in questo esempio interpreta il primo byte scritto nel dispositivo dopo un bit di avvio come indirizzo di funzione da caricare nel registro degli indirizzi della funzione. Eventuali byte aggiuntivi trasferiti da o verso il dispositivo prima della fine della sequenza (come indicato dal bit di arresto) vengono considerati dal dispositivo come dati da trasferire tramite il registro dati.
Per eseguire un'operazione di scrittura, il client invia una richiesta di scrittura (IRP_MJ_WRITE) in cui il primo byte nel buffer di scrittura è l'indirizzo della funzione e i byte rimanenti nel buffer sono dati da scrivere nell'indirizzo della funzione.
La lettura dal dispositivo è più complessa. Si supponga che il dispositivo I2C in questo esempio supporti una funzionalità di lettura veloce che reimposta automaticamente il registro degli indirizzi funzione sul valore predefinito, 0, quando viene rilevato un bit di arresto nel bus. Con questa funzionalità, il client può leggere i dati dall'indirizzo della funzione 0 senza dover prima scrivere nel registro degli indirizzi della funzione. Questa funzionalità può migliorare la velocità delle operazioni di lettura del dispositivo, soprattutto se la maggior parte delle letture proviene dall'indirizzo di funzione 0 e sono relativamente brevi.
Tuttavia, per leggere un blocco di dati da un indirizzo di funzione diverso da zero, il client deve comunque scrivere un byte nel registro degli indirizzi della funzione prima di leggere il blocco di dati dal registro dei dati. Il client deve eseguire questi trasferimenti di scrittura e lettura come operazione atomica del bus per impedire al controller del bus di trasmettere un bit di arresto dopo la scrittura al registro degli indirizzi della funzione e prima della lettura dal registro dei dati. In caso contrario, il bit di arresto causerà la lettura dei dati dall'indirizzo della funzione 0 anziché dall'indirizzo della funzione diverso da zero.
L'elenco seguente descrive la serie di richieste di I/O inviate da un client al dispositivo I2C in questo esempio per eseguire un'operazione di lettura-modifica/scrittura sui dati che si trovano in un indirizzo di funzione diverso da zero nel dispositivo:
- IOCTL_SPB_EXECUTE_SEQUENCE : eseguire una sequenza di trasferimento di I/O per leggere i dati dal dispositivo. Il primo trasferimento in questa sequenza è una scrittura di byte nel registro degli indirizzi della funzione. Il secondo trasferimento nella sequenza è una lettura di un numero di byte dall'indirizzo della funzione selezionato. Questi due trasferimenti vengono eseguiti in modo atomico sull'autobus.
- IRP_MJ_WRITE : scrivere dati nel dispositivo. Il primo byte nel buffer di scrittura per questa richiesta è il valore da scrivere nel registro degli indirizzi della funzione. I byte rimanenti nel buffer sono dati da scrivere nell'indirizzo della funzione selezionato.
È possibile usare altri modelli di richieste per eseguire questa operazione di lettura-modifica/scrittura. Ad esempio, la richiesta di IRP_MJ_WRITE nel passaggio 2 può essere sostituita da una richiesta di IOCTL_SPB_EXECUTE_SEQUENCE che specifica due trasferimenti di dati, entrambi scritti. Il primo trasferimento nella sequenza carica un byte nel registro degli indirizzi della funzione. Il secondo trasferimento scrive i byte di dati nell'indirizzo della funzione selezionato. Questa richiesta, diversamente dalla richiesta di IRP_MJ_WRITE nel passaggio 2, non richiede al client di combinare i byte di byte di indirizzo della funzione e i byte di dati nello stesso buffer di scrittura.
Per eseguire un indirizzo di funzione read-modify-write su 0 in questo dispositivo, la richiesta di IOCTL_SPB_EXECUTE_SEQUENCE nel passaggio 1 dell'elenco precedente può essere sostituita da una semplice richiesta di lettura (IRP_MJ_READ).