I2C driver provides kernel mode routines communicating with a PIC microcontroller. It encapsulates PIC's functionality and provides higher level commands which controls PIC's behavior on an I2C bus. Driver functionality is exposed via IRPs (IO request packets). Higher level driver should allocate an IRP, fill it with a code of desired operation and appropriate parameters and call kernel function IoCallDriver in order to execute that operation.
Each operation is identified by its code. The I2C driver implements IRP_MJ_READ, IRP_MJ_WRITE and IRP_MJ_DEVICE_CONTROL major function codes (and several others which purpose is entirely technical).
This call has to follow either StartFirstTransfer or StartNextTransfer operation. After any of these operations the device is waiting for execution of a read or a write operation by a driver. These operations are sending/receiving data per chunks. Each chunk is preceded by its length which is now 8bit unsigned integer but this limitation is not a fundamental one (from the device's point of view). User can read/write data of an arbitrary length and these are then send per chunks, however, this process is transparent for a caller. For details of how communication is going on the device see the PIC state chart.
When a device is switched to the sniffer mode it waits until a frame starts. Then it sniffs bits (one by one according to clocks generated on a bus) and stores them into internal 9bit buffer (8 bits for data, 1 bit for an acknowledgement). When a byte (including an ack) is read the sniffer holds I2C bus clocks low in order to get enough time to send sniffed data to a driver. Data are sent in two octets – the first one contains data themselves the second one contains an acknowledgement bit and possibly some more information. Hence the sniffer slows down a communication on the I2C bus approximately to a half of the original one but this should be a problem only in time critical applications. If that was a case some sofisticated buffering would be neccessary (but that would inevitably cause a delay of data displayed by a sniffing software).
Resets a device and waits until it becomes initialized (the driver is waiting for predefined hello sequence sent by the device to RS232; it would be suitable to add a timeout here to prevent driver's blocking when no device is connected but this is not implemented yet).
Input: none Output: none Returns: STATUS_UNSUCCESSFUL on error, STATUS_SUCCESS otherwise
Switches device to a sniffer mode. The IRP_MJ_READ operation should follow.
Input: none Output: none Returns: STATUS_UNSUCCESSFUL on error, STATUS_SUCCESS otherwise
Switches device to a master mode. The device in the master mode can address an arbitrary device on a bus and send or receive data. The frame is started by IOCTL_I2C_START_FIRST_TRANSFER, restarted by IOCTL_I2C_START_NEXT_TRANSFER and finished by IOCTL_I2C_STOP_TRANSFER device IO control commands.
If the device is currently waiting for incoming frame or reading a frame in a slave mode it sends out-of-band command which cause its switch to a master mode as soon as possible. This routine blocks while device is in a slave mode.
Input: an address of the device (of type I2C_DEVICE_ADDRESS) Output: none Returns: STATUS_INVALID_PARAMETER if the input parameter has invalid size or the address is not valid, STATUS_UNSUCCESSFUL on other error, STATUS_SUCCESS otherwise
These commands starts and restarts a frame respectively and hence can be used only in a master mode. It is an error to use any of these commands in a slave or sniffer mode.
Input: an address of target device of type I2C_DEVICE_ADDRESS ORed with the direction of transfer (I2C_TRANSFER_DIRECTION) Output: none Returns: STATUS_INVALID_PARAMETER if the input parameter has invalid size or the address is not valid, STATUS_UNSUCCESSFUL on other error, I2C_SLAVE_MODE_ENFORCED when master has lost an arbitration and switched to a slave mode, I2C_TRANSFER_CANCELED when master has lost an arbitration but not switched to a slave mode (because it was not addressed), I2C_SLAVE_NOT_RESPONDING when the target device is not responding, STATUS_SUCCESS otherwise
Completes a frame by sending a stop condition. Can be used only in a master mode.
Input: none Output: none Returns: STATUS_UNSUCCESSFUL on an error, STATUS_SUCCESS otherwise
Switches device to a slave mode.
Input: an address of the device (of type I2C_DEVICE_ADDRESS) Output: none Returns: STATUS_INVALID_PARAMETER if the input parameter has invalid size or the address is not valid, STATUS_UNSUCCESSFUL on other error, STATUS_SUCCESS otherwise
Starts waiting for a transfer. This command can be invoked only in a slave mode.
Input: none Output: direction of incoming transfer (of type I2C_TRANSFER_DIRECTION) Returns: STATUS_BUFFER_TOO_SMALL if an output buffer is too small, STATUS_UNSUCCESSFUL on other error, I2C_MASTER_MODE_ENFORCED when a device receives out-of-band command and switches to a master mode, STATUS_SUCCESS otherwise
Returns information about the current device state.
Input: none Output: state information (of type I2C_DEVICE_INFORMATION) Returns: STATUS_BUFFER_TOO_SMALL if an output buffer is too small, STATUS_SUCCESS otherwise
| Driver.c, Driver.h | driver body (DriverEntry, dispatching routine etc.) and auxiliary technicalities |
| Functions.c, Functions.h | Implemetation of all major operations (like StartFirstTransfer, SwitchToMasterMode, ...) |
| I2CErrors.mc, I2CErrors.h | Error codes (NTSTATUSes). |
| I2C.h | Interface exposed to higher level drivers or user applications. |
| Sources | A "makefile" for WinXP DDK building utility |