diff options
author | marha <marha@users.sourceforge.net> | 2012-03-26 14:23:28 +0200 |
---|---|---|
committer | marha <marha@users.sourceforge.net> | 2012-03-26 14:23:28 +0200 |
commit | 76bcc36ed305418a3ddc5752d287ede894243e1b (patch) | |
tree | bacb320c825768471ce56f058f17ce863d592376 /xorg-server/hw/xfree86/i2c/xf86i2c.c | |
parent | 7d894e32566b710952c44cbc71939ad1d9e2fa8d (diff) | |
parent | 0f834b91a4768673833ab4917e87d86c237bb1a6 (diff) | |
download | vcxsrv-76bcc36ed305418a3ddc5752d287ede894243e1b.tar.gz vcxsrv-76bcc36ed305418a3ddc5752d287ede894243e1b.tar.bz2 vcxsrv-76bcc36ed305418a3ddc5752d287ede894243e1b.zip |
Merge remote-tracking branch 'origin/released'
Conflicts:
pixman/pixman/pixman-mmx.c
xorg-server/Xext/shm.c
xorg-server/Xext/syncsrv.h
xorg-server/Xext/xvmain.c
xorg-server/Xi/exevents.c
xorg-server/Xi/opendev.c
xorg-server/composite/compalloc.c
xorg-server/composite/compoverlay.c
xorg-server/dix/colormap.c
xorg-server/dix/devices.c
xorg-server/dix/dispatch.c
xorg-server/dix/dixfonts.c
xorg-server/dix/eventconvert.c
xorg-server/dix/events.c
xorg-server/dix/gc.c
xorg-server/dix/getevents.c
xorg-server/dix/main.c
xorg-server/dix/privates.c
xorg-server/dix/registry.c
xorg-server/dix/resource.c
xorg-server/exa/exa_accel.c
xorg-server/exa/exa_migration_classic.c
xorg-server/exa/exa_unaccel.c
xorg-server/fb/fb.h
xorg-server/fb/fbcopy.c
xorg-server/fb/fbpixmap.c
xorg-server/glx/dispatch.h
xorg-server/glx/glapi.h
xorg-server/glx/glapi_gentable.c
xorg-server/glx/glapitable.h
xorg-server/glx/glprocs.h
xorg-server/glx/glxcmds.c
xorg-server/glx/glxcmdsswap.c
xorg-server/glx/glxdricommon.c
xorg-server/glx/glxdriswrast.c
xorg-server/glx/glxext.c
xorg-server/glx/indirect_dispatch.c
xorg-server/glx/indirect_dispatch.h
xorg-server/glx/indirect_dispatch_swap.c
xorg-server/glx/indirect_size.h
xorg-server/glx/indirect_size_get.h
xorg-server/glx/indirect_table.c
xorg-server/glx/indirect_util.c
xorg-server/glx/rensize.c
xorg-server/glx/single2swap.c
xorg-server/glx/singlepix.c
xorg-server/glx/singlepixswap.c
xorg-server/glx/singlesize.c
xorg-server/hw/dmx/dmxinit.c
xorg-server/hw/kdrive/ephyr/ephyr.c
xorg-server/hw/kdrive/ephyr/hostx.c
xorg-server/hw/kdrive/ephyr/hostx.h
xorg-server/hw/kdrive/src/kinput.c
xorg-server/hw/xfree86/common/compiler.h
xorg-server/hw/xwin/InitInput.c
xorg-server/hw/xwin/InitOutput.c
xorg-server/hw/xwin/ddraw.h
xorg-server/hw/xwin/glx/glwrap.c
xorg-server/hw/xwin/glx/indirect.c
xorg-server/hw/xwin/glx/wgl_ext_api.h
xorg-server/hw/xwin/glx/winpriv.c
xorg-server/hw/xwin/win.h
xorg-server/hw/xwin/winallpriv.c
xorg-server/hw/xwin/winauth.c
xorg-server/hw/xwin/winclipboard.h
xorg-server/hw/xwin/winclipboardinit.c
xorg-server/hw/xwin/winclipboardthread.c
xorg-server/hw/xwin/winclipboardunicode.c
xorg-server/hw/xwin/winclipboardwndproc.c
xorg-server/hw/xwin/winclipboardwrappers.c
xorg-server/hw/xwin/winclipboardxevents.c
xorg-server/hw/xwin/wincmap.c
xorg-server/hw/xwin/winconfig.c
xorg-server/hw/xwin/wincreatewnd.c
xorg-server/hw/xwin/wincursor.c
xorg-server/hw/xwin/windialogs.c
xorg-server/hw/xwin/winengine.c
xorg-server/hw/xwin/winerror.c
xorg-server/hw/xwin/wingc.c
xorg-server/hw/xwin/wingetsp.c
xorg-server/hw/xwin/winkeybd.c
xorg-server/hw/xwin/winkeybd.h
xorg-server/hw/xwin/winlayouts.h
xorg-server/hw/xwin/winmisc.c
xorg-server/hw/xwin/winmonitors.c
xorg-server/hw/xwin/winmouse.c
xorg-server/hw/xwin/winmsg.c
xorg-server/hw/xwin/winmsg.h
xorg-server/hw/xwin/winmultiwindowclass.c
xorg-server/hw/xwin/winmultiwindowicons.c
xorg-server/hw/xwin/winmultiwindowshape.c
xorg-server/hw/xwin/winmultiwindowwindow.c
xorg-server/hw/xwin/winmultiwindowwm.c
xorg-server/hw/xwin/winmultiwindowwndproc.c
xorg-server/hw/xwin/winnativegdi.c
xorg-server/hw/xwin/winpfbdd.c
xorg-server/hw/xwin/winpixmap.c
xorg-server/hw/xwin/winpolyline.c
xorg-server/hw/xwin/winprefs.c
xorg-server/hw/xwin/winprocarg.c
xorg-server/hw/xwin/winregistry.c
xorg-server/hw/xwin/winscrinit.c
xorg-server/hw/xwin/winsetsp.c
xorg-server/hw/xwin/winshaddd.c
xorg-server/hw/xwin/winshadddnl.c
xorg-server/hw/xwin/winshadgdi.c
xorg-server/hw/xwin/wintrayicon.c
xorg-server/hw/xwin/winwin32rootless.c
xorg-server/hw/xwin/winwin32rootlesswindow.c
xorg-server/hw/xwin/winwin32rootlesswndproc.c
xorg-server/hw/xwin/winwindow.c
xorg-server/hw/xwin/winwindow.h
xorg-server/hw/xwin/winwindowswm.c
xorg-server/hw/xwin/winwndproc.c
xorg-server/include/callback.h
xorg-server/include/dixstruct.h
xorg-server/include/misc.h
xorg-server/include/os.h
xorg-server/include/scrnintstr.h
xorg-server/mi/micmap.c
xorg-server/mi/miinitext.c
xorg-server/mi/mioverlay.c
xorg-server/mi/misprite.c
xorg-server/mi/mivaltree.c
xorg-server/mi/miwindow.c
xorg-server/miext/damage/damage.c
xorg-server/miext/rootless/rootlessGC.c
xorg-server/miext/rootless/rootlessWindow.c
xorg-server/os/WaitFor.c
xorg-server/os/access.c
xorg-server/os/connection.c
xorg-server/os/io.c
xorg-server/os/log.c
xorg-server/os/osinit.c
xorg-server/os/utils.c
xorg-server/os/xdmcp.c
xorg-server/os/xprintf.c
xorg-server/os/xstrans.c
xorg-server/render/mipict.c
xorg-server/xkb/xkbActions.c
xorg-server/xkb/xkbInit.c
xorg-server/xkeyboard-config/compat/default.in
Diffstat (limited to 'xorg-server/hw/xfree86/i2c/xf86i2c.c')
-rw-r--r-- | xorg-server/hw/xfree86/i2c/xf86i2c.c | 1745 |
1 files changed, 879 insertions, 866 deletions
diff --git a/xorg-server/hw/xfree86/i2c/xf86i2c.c b/xorg-server/hw/xfree86/i2c/xf86i2c.c index 488b6815f..1273f4bab 100644 --- a/xorg-server/hw/xfree86/i2c/xf86i2c.c +++ b/xorg-server/hw/xfree86/i2c/xf86i2c.c @@ -1,866 +1,879 @@ -/*
- * Copyright (C) 1998 Itai Nahshon, Michael Schimek
- *
- * The original code was derived from and inspired by
- * the I2C driver from the Linux kernel.
- * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
- */
-
-
-#ifdef HAVE_XORG_CONFIG_H
-#include <xorg-config.h>
-#endif
-
-#include <sys/time.h>
-#include <string.h>
-
-#include "misc.h"
-#include "xf86.h"
-#include "xf86_OSproc.h"
-
-#include <X11/X.h>
-#include <X11/Xos.h>
-#include <X11/Xproto.h>
-#include "scrnintstr.h"
-#include "regionstr.h"
-#include "windowstr.h"
-#include "pixmapstr.h"
-#include "validate.h"
-#include "resource.h"
-#include "gcstruct.h"
-#include "dixstruct.h"
-
-#include "xf86i2c.h"
-
-#define I2C_TIMEOUT(x) /*(x)*/ /* Report timeouts */
-#define I2C_TRACE(x) /*(x)*/ /* Report progress */
-
-/* This is the default I2CUDelay function if not supplied by the driver.
- * High level I2C interfaces implementing the bus protocol in hardware
- * should supply this function too.
- *
- * Delay execution at least usec microseconds.
- * All values 0 to 1e6 inclusive must be expected.
- */
-
-static void
-I2CUDelay(I2CBusPtr b, int usec)
-{
- struct timeval begin, cur;
- long d_secs, d_usecs;
- long diff;
-
- if (usec > 0) {
- X_GETTIMEOFDAY(&begin);
- do {
- /* It would be nice to use {xf86}usleep,
- * but usleep (1) takes >10000 usec !
- */
- X_GETTIMEOFDAY(&cur);
- d_secs = (cur.tv_sec - begin.tv_sec);
- d_usecs = (cur.tv_usec - begin.tv_usec);
- diff = d_secs*1000000 + d_usecs;
- } while (diff>=0 && diff< (usec + 1));
- }
-}
-
-/* Most drivers will register just with GetBits/PutBits functions.
- * The following functions implement a software I2C protocol
- * by using the promitive functions given by the driver.
- * ================================================================
- *
- * It is assumed that there is just one master on the I2C bus, therefore
- * there is no explicit test for conflits.
- */
-
-#define RISEFALLTIME 2 /* usec, actually 300 to 1000 ns according to the i2c specs */
-
-/* Some devices will hold SCL low to slow down the bus or until
- * ready for transmission.
- *
- * This condition will be noticed when the master tries to raise
- * the SCL line. You can set the timeout to zero if the slave device
- * does not support this clock synchronization.
- */
-
-static Bool
-I2CRaiseSCL(I2CBusPtr b, int sda, int timeout)
-{
- int i, scl;
-
- b->I2CPutBits(b, 1, sda);
- b->I2CUDelay(b, b->RiseFallTime);
-
- for (i = timeout; i > 0; i -= b->RiseFallTime) {
- b->I2CGetBits(b, &scl, &sda);
- if (scl) break;
- b->I2CUDelay(b, b->RiseFallTime);
- }
-
- if (i <= 0) {
- I2C_TIMEOUT(ErrorF("[I2CRaiseSCL(<%s>, %d, %d) timeout]", b->BusName, sda, timeout));
- return FALSE;
- }
-
- return TRUE;
-}
-
-/* Send a start signal on the I2C bus. The start signal notifies
- * devices that a new transaction is initiated by the bus master.
- *
- * The start signal is always followed by a slave address.
- * Slave addresses are 8+ bits. The first 7 bits identify the
- * device and the last bit signals if this is a read (1) or
- * write (0) operation.
- *
- * There may be more than one start signal on one transaction.
- * This happens for example on some devices that allow reading
- * of registers. First send a start bit followed by the device
- * address (with the last bit 0) and the register number. Then send
- * a new start bit with the device address (with the last bit 1)
- * and then read the value from the device.
- *
- * Note this is function does not implement a multiple master
- * arbitration procedure.
- */
-
-static Bool
-I2CStart(I2CBusPtr b, int timeout)
-{
- if (!I2CRaiseSCL(b, 1, timeout))
- return FALSE;
-
- b->I2CPutBits(b, 1, 0);
- b->I2CUDelay(b, b->HoldTime);
- b->I2CPutBits(b, 0, 0);
- b->I2CUDelay(b, b->HoldTime);
-
- I2C_TRACE(ErrorF("\ni2c: <"));
-
- return TRUE;
-}
-
-/* This is the default I2CStop function if not supplied by the driver.
- *
- * Signal devices on the I2C bus that a transaction on the
- * bus has finished. There may be more than one start signal
- * on a transaction but only one stop signal.
- */
-
-static void
-I2CStop(I2CDevPtr d)
-{
- I2CBusPtr b = d->pI2CBus;
-
- b->I2CPutBits(b, 0, 0);
- b->I2CUDelay(b, b->RiseFallTime);
-
- b->I2CPutBits(b, 1, 0);
- b->I2CUDelay(b, b->HoldTime);
- b->I2CPutBits(b, 1, 1);
- b->I2CUDelay(b, b->HoldTime);
-
- I2C_TRACE(ErrorF(">\n"));
-}
-
-/* Write/Read a single bit to/from a device.
- * Return FALSE if a timeout occurs.
- */
-
-static Bool
-I2CWriteBit(I2CBusPtr b, int sda, int timeout)
-{
- Bool r;
-
- b->I2CPutBits(b, 0, sda);
- b->I2CUDelay(b, b->RiseFallTime);
-
- r = I2CRaiseSCL(b, sda, timeout);
- b->I2CUDelay(b, b->HoldTime);
-
- b->I2CPutBits(b, 0, sda);
- b->I2CUDelay(b, b->HoldTime);
-
- return r;
-}
-
-static Bool
-I2CReadBit(I2CBusPtr b, int *psda, int timeout)
-{
- Bool r;
- int scl;
-
- r = I2CRaiseSCL(b, 1, timeout);
- b->I2CUDelay(b, b->HoldTime);
-
- b->I2CGetBits(b, &scl, psda);
-
- b->I2CPutBits(b, 0, 1);
- b->I2CUDelay(b, b->HoldTime);
-
- return r;
-}
-
-/* This is the default I2CPutByte function if not supplied by the driver.
- *
- * A single byte is sent to the device.
- * The function returns FALSE if a timeout occurs, you should send
- * a stop condition afterwards to reset the bus.
- *
- * A timeout occurs,
- * if the slave pulls SCL to slow down the bus more than ByteTimeout usecs,
- * or slows down the bus for more than BitTimeout usecs for each bit,
- * or does not send an ACK bit (0) to acknowledge the transmission within
- * AcknTimeout usecs, but a NACK (1) bit.
- *
- * AcknTimeout must be at least b->HoldTime, the other timeouts can be
- * zero according to the comment on I2CRaiseSCL.
- */
-
-static Bool
-I2CPutByte(I2CDevPtr d, I2CByte data)
-{
- Bool r;
- int i, scl, sda;
- I2CBusPtr b = d->pI2CBus;
-
- if (!I2CWriteBit(b, (data >> 7) & 1, d->ByteTimeout))
- return FALSE;
-
- for (i = 6; i >= 0; i--)
- if (!I2CWriteBit(b, (data >> i) & 1, d->BitTimeout))
- return FALSE;
-
- b->I2CPutBits(b, 0, 1);
- b->I2CUDelay(b, b->RiseFallTime);
-
- r = I2CRaiseSCL(b, 1, b->HoldTime);
-
- if (r) {
- for (i = d->AcknTimeout; i > 0; i -= b->HoldTime) {
- b->I2CUDelay(b, b->HoldTime);
- b->I2CGetBits(b, &scl, &sda);
- if (sda == 0) break;
- }
-
- if (i <= 0) {
- I2C_TIMEOUT(ErrorF("[I2CPutByte(<%s>, 0x%02x, %d, %d, %d) timeout]",
- b->BusName, data, d->BitTimeout,
- d->ByteTimeout, d->AcknTimeout));
- r = FALSE;
- }
-
- I2C_TRACE(ErrorF("W%02x%c ", (int) data, sda ? '-' : '+'));
- }
-
- b->I2CPutBits(b, 0, 1);
- b->I2CUDelay(b, b->HoldTime);
-
- return r;
-}
-
-/* This is the default I2CGetByte function if not supplied by the driver.
- *
- * A single byte is read from the device.
- * The function returns FALSE if a timeout occurs, you should send
- * a stop condition afterwards to reset the bus.
- *
- * A timeout occurs,
- * if the slave pulls SCL to slow down the bus more than ByteTimeout usecs,
- * or slows down the bus for more than b->BitTimeout usecs for each bit.
- *
- * ByteTimeout must be at least b->HoldTime, the other timeouts can be
- * zero according to the comment on I2CRaiseSCL.
- *
- * For the <last> byte in a sequence the acknowledge bit NACK (1),
- * otherwise ACK (0) will be sent.
- */
-
-static Bool
-I2CGetByte(I2CDevPtr d, I2CByte *data, Bool last)
-{
- int i, sda;
- I2CBusPtr b = d->pI2CBus;
-
- b->I2CPutBits(b, 0, 1);
- b->I2CUDelay(b, b->RiseFallTime);
-
- if (!I2CReadBit(b, &sda, d->ByteTimeout))
- return FALSE;
-
- *data = (sda > 0) << 7;
-
- for (i = 6; i >= 0; i--)
- if (!I2CReadBit(b, &sda, d->BitTimeout))
- return FALSE;
- else
- *data |= (sda > 0) << i;
-
- if (!I2CWriteBit(b, last ? 1 : 0, d->BitTimeout))
- return FALSE;
-
- I2C_TRACE(ErrorF("R%02x%c ", (int) *data, last ? '+' : '-'));
-
- return TRUE;
-}
-
-/* This is the default I2CAddress function if not supplied by the driver.
- *
- * It creates the start condition, followed by the d->SlaveAddr.
- * Higher level functions must call this routine rather than
- * I2CStart/PutByte because a hardware I2C master may not be able
- * to send a slave address without a start condition.
- *
- * The same timeouts apply as with I2CPutByte and additional a
- * StartTimeout, similar to the ByteTimeout but for the start
- * condition.
- *
- * In case of a timeout, the bus is left in a clean idle condition.
- * I. e. you *must not* send a Stop. If this function succeeds, you *must*.
- *
- * The slave address format is 16 bit, with the legacy _8_bit_ slave address
- * in the least significant byte. This is, the slave address must include the
- * R/_W flag as least significant bit.
- *
- * The most significant byte of the address will be sent _after_ the LSB,
- * but only if the LSB indicates:
- * a) an 11 bit address, this is LSB = 1111 0xxx.
- * b) a 'general call address', this is LSB = 0000 000x - see the I2C specs
- * for more.
- */
-
-static Bool
-I2CAddress(I2CDevPtr d, I2CSlaveAddr addr)
-{
- if (I2CStart(d->pI2CBus, d->StartTimeout)) {
- if (I2CPutByte(d, addr & 0xFF)) {
- if ((addr & 0xF8) != 0xF0 &&
- (addr & 0xFE) != 0x00)
- return TRUE;
-
- if (I2CPutByte(d, (addr >> 8) & 0xFF))
- return TRUE;
- }
-
- I2CStop(d);
- }
-
- return FALSE;
-}
-
-/* These are the hardware independent I2C helper functions.
- * ========================================================
- */
-
-/* Function for probing. Just send the slave address
- * and return true if the device responds. The slave address
- * must have the lsb set to reflect a read (1) or write (0) access.
- * Don't expect a read- or write-only device will respond otherwise.
- */
-
-Bool
-xf86I2CProbeAddress(I2CBusPtr b, I2CSlaveAddr addr)
-{
- int r;
- I2CDevRec d;
-
- d.DevName = "Probing";
- d.BitTimeout = b->BitTimeout;
- d.ByteTimeout = b->ByteTimeout;
- d.AcknTimeout = b->AcknTimeout;
- d.StartTimeout = b->StartTimeout;
- d.SlaveAddr = addr;
- d.pI2CBus = b;
- d.NextDev = NULL;
-
- r = b->I2CAddress(&d, addr);
-
- if (r) b->I2CStop(&d);
-
- return r;
-}
-
-/* All functions below are related to devices and take the
- * slave address and timeout values from an I2CDevRec. They
- * return FALSE in case of an error (presumably a timeout).
- */
-
-/* General purpose read and write function.
- *
- * 1st, if nWrite > 0
- * Send a start condition
- * Send the slave address (1 or 2 bytes) with write flag
- * Write n bytes from WriteBuffer
- * 2nd, if nRead > 0
- * Send a start condition [again]
- * Send the slave address (1 or 2 bytes) with read flag
- * Read n bytes to ReadBuffer
- * 3rd, if a Start condition has been successfully sent,
- * Send a Stop condition.
- *
- * The functions exits immediately when an error occures,
- * not proceeding any data left. However, step 3 will
- * be executed anyway to leave the bus in clean idle state.
- */
-
-static Bool
-I2CWriteRead(I2CDevPtr d,
- I2CByte *WriteBuffer, int nWrite,
- I2CByte *ReadBuffer, int nRead)
-{
- Bool r = TRUE;
- I2CBusPtr b = d->pI2CBus;
- int s = 0;
-
- if (r && nWrite > 0) {
- r = b->I2CAddress(d, d->SlaveAddr & ~1);
- if (r) {
- for (; nWrite > 0; WriteBuffer++, nWrite--)
- if (!(r = b->I2CPutByte(d, *WriteBuffer)))
- break;
- s++;
- }
- }
-
- if (r && nRead > 0) {
- r = b->I2CAddress(d, d->SlaveAddr | 1);
- if (r) {
- for (; nRead > 0; ReadBuffer++, nRead--)
- if (!(r = b->I2CGetByte(d, ReadBuffer, nRead == 1)))
- break;
- s++;
- }
- }
-
- if (s) b->I2CStop(d);
-
- return r;
-}
-
-/* wrapper - for compatibility and convinience */
-
-Bool
-xf86I2CWriteRead(I2CDevPtr d,
- I2CByte *WriteBuffer, int nWrite,
- I2CByte *ReadBuffer, int nRead)
-{
- I2CBusPtr b = d->pI2CBus;
- return b->I2CWriteRead(d,WriteBuffer,nWrite,ReadBuffer,nRead);
-}
-
-/* Read a byte, the only readable register of a device.
- */
-
-Bool
-xf86I2CReadStatus(I2CDevPtr d, I2CByte *pbyte)
-{
- return xf86I2CWriteRead(d, NULL, 0, pbyte, 1);
-}
-
-/* Read a byte from one of the registers determined by its sub-address.
- */
-
-Bool
-xf86I2CReadByte(I2CDevPtr d, I2CByte subaddr, I2CByte *pbyte)
-{
- return xf86I2CWriteRead(d, &subaddr, 1, pbyte, 1);
-}
-
-/* Read bytes from subsequent registers determined by the
- * sub-address of the first register.
- */
-
-Bool
-xf86I2CReadBytes(I2CDevPtr d, I2CByte subaddr, I2CByte *pbyte, int n)
-{
- return xf86I2CWriteRead(d, &subaddr, 1, pbyte, n);
-}
-
-/* Read a word (high byte, then low byte) from one of the registers
- * determined by its sub-address.
- */
-
-Bool
-xf86I2CReadWord(I2CDevPtr d, I2CByte subaddr, unsigned short *pword)
-{
- I2CByte rb[2];
-
- if (!xf86I2CWriteRead(d, &subaddr, 1, rb, 2)) return FALSE;
-
- *pword = (rb[0] << 8) | rb[1];
-
- return TRUE;
-}
-
-/* Write a byte to one of the registers determined by its sub-address.
- */
-
-Bool
-xf86I2CWriteByte(I2CDevPtr d, I2CByte subaddr, I2CByte byte)
-{
- I2CByte wb[2];
-
- wb[0] = subaddr;
- wb[1] = byte;
-
- return xf86I2CWriteRead(d, wb, 2, NULL, 0);
-}
-
-/* Write bytes to subsequent registers determined by the
- * sub-address of the first register.
- */
-
-Bool
-xf86I2CWriteBytes(I2CDevPtr d, I2CByte subaddr,
- I2CByte *WriteBuffer, int nWrite)
-{
- I2CBusPtr b = d->pI2CBus;
- Bool r = TRUE;
-
- if (nWrite > 0) {
- r = b->I2CAddress(d, d->SlaveAddr & ~1);
- if (r){
- if ((r = b->I2CPutByte(d, subaddr)))
- for (; nWrite > 0; WriteBuffer++, nWrite--)
- if (!(r = b->I2CPutByte(d, *WriteBuffer)))
- break;
-
- b->I2CStop(d);
- }
- }
-
- return r;
-}
-
-/* Write a word (high byte, then low byte) to one of the registers
- * determined by its sub-address.
- */
-
-Bool
-xf86I2CWriteWord(I2CDevPtr d, I2CByte subaddr, unsigned short word)
-{
- I2CByte wb[3];
-
- wb[0] = subaddr;
- wb[1] = word >> 8;
- wb[2] = word & 0xFF;
-
- return xf86I2CWriteRead(d, wb, 3, NULL, 0);
-}
-
-/* Write a vector of bytes to not adjacent registers. This vector is,
- * 1st byte sub-address, 2nd byte value, 3rd byte sub-address asf.
- * This function is intended to initialize devices. Note this function
- * exits immediately when an error occurs, some registers may
- * remain uninitialized.
- */
-
-Bool
-xf86I2CWriteVec(I2CDevPtr d, I2CByte *vec, int nValues)
-{
- I2CBusPtr b = d->pI2CBus;
- Bool r = TRUE;
- int s = 0;
-
- if (nValues > 0) {
- for (; nValues > 0; nValues--, vec += 2) {
- if (!(r = b->I2CAddress(d, d->SlaveAddr & ~1)))
- break;
-
- s++;
-
- if (!(r = b->I2CPutByte(d, vec[0])))
- break;
-
- if (!(r = b->I2CPutByte(d, vec[1])))
- break;
- }
-
- if (s > 0) b->I2CStop(d);
- }
-
- return r;
-}
-
-/* Administrative functions.
- * =========================
- */
-
-/* Allocates an I2CDevRec for you and initializes with propper defaults
- * you may modify before calling xf86I2CDevInit. Your I2CDevRec must
- * contain at least a SlaveAddr, and a pI2CBus pointer to the bus this
- * device shall be linked to.
- *
- * See function I2CAddress for the slave address format. Always set
- * the least significant bit, indicating a read or write access, to zero.
- */
-
-I2CDevPtr
-xf86CreateI2CDevRec(void)
-{
- return calloc(1, sizeof(I2CDevRec));
-}
-
-/* Unlink an I2C device. If you got the I2CDevRec from xf86CreateI2CDevRec
- * you should set <unalloc> to free it.
- */
-
-void
-xf86DestroyI2CDevRec(I2CDevPtr d, Bool unalloc)
-{
- if (d) {
- I2CDevPtr *p;
-
- /* Remove this from the list of active I2C devices. */
-
- for (p = &d->pI2CBus->FirstDev; *p != NULL; p = &(*p)->NextDev)
- if (*p == d) {
- *p = (*p)->NextDev;
- break;
- }
-
- xf86DrvMsg(d->pI2CBus->scrnIndex, X_INFO,
- "I2C device \"%s:%s\" removed.\n",
- d->pI2CBus->BusName, d->DevName);
-
- if (unalloc) free(d);
- }
-}
-
-/* I2C transmissions are related to an I2CDevRec you must link to a
- * previously registered bus (see xf86I2CBusInit) before attempting
- * to read and write data. You may call xf86I2CProbeAddress first to
- * see if the device in question is present on this bus.
- *
- * xf86I2CDevInit will not allocate an I2CBusRec for you, instead you
- * may enter a pointer to a statically allocated I2CDevRec or the (modified)
- * result of xf86CreateI2CDevRec.
- *
- * If you don't specify timeouts for the device (n <= 0), it will inherit
- * the bus-wide defaults. The function returns TRUE on success.
- */
-
-Bool
-xf86I2CDevInit(I2CDevPtr d)
-{
- I2CBusPtr b;
-
- if (d == NULL ||
- (b = d->pI2CBus) == NULL ||
- (d->SlaveAddr & 1) ||
- xf86I2CFindDev(b, d->SlaveAddr) != NULL)
- return FALSE;
-
- if (d->BitTimeout <= 0) d->BitTimeout = b->BitTimeout;
- if (d->ByteTimeout <= 0) d->ByteTimeout = b->ByteTimeout;
- if (d->AcknTimeout <= 0) d->AcknTimeout = b->AcknTimeout;
- if (d->StartTimeout <= 0) d->StartTimeout = b->StartTimeout;
-
- d->NextDev = b->FirstDev;
- b->FirstDev = d;
-
- xf86DrvMsg(b->scrnIndex, X_INFO,
- "I2C device \"%s:%s\" registered at address 0x%02X.\n",
- b->BusName, d->DevName, d->SlaveAddr);
-
- return TRUE;
-}
-
-I2CDevPtr
-xf86I2CFindDev(I2CBusPtr b, I2CSlaveAddr addr)
-{
- I2CDevPtr d;
-
- if (b) {
- for (d = b->FirstDev; d != NULL; d = d->NextDev)
- if (d->SlaveAddr == addr)
- return d;
- }
-
- return NULL;
-}
-
-static I2CBusPtr I2CBusList;
-
-/* Allocates an I2CBusRec for you and initializes with propper defaults
- * you may modify before calling xf86I2CBusInit. Your I2CBusRec must
- * contain at least a BusName, a scrnIndex (or -1), and a complete set
- * of either high or low level I2C function pointers. You may pass
- * bus-wide timeouts, otherwise inplausible values will be replaced
- * with safe defaults.
- */
-
-I2CBusPtr
-xf86CreateI2CBusRec(void)
-{
- I2CBusPtr b;
-
- b = (I2CBusPtr) calloc(1, sizeof(I2CBusRec));
-
- if (b != NULL) {
- b->scrnIndex = -1;
- b->HoldTime = 5; /* 100 kHz bus */
- b->BitTimeout = 5;
- b->ByteTimeout = 5;
- b->AcknTimeout = 5;
- b->StartTimeout = 5;
- b->RiseFallTime = RISEFALLTIME;
- }
-
- return b;
-}
-
-/* Unregister an I2C bus. If you got the I2CBusRec from xf86CreateI2CBusRec
- * you should set <unalloc> to free it. If you set <devs_too>, the function
- * xf86DestroyI2CDevRec will be called for all devices linked to the bus
- * first, passing down the <unalloc> option.
- */
-
-void
-xf86DestroyI2CBusRec(I2CBusPtr b, Bool unalloc, Bool devs_too)
-{
- if (b) {
- I2CBusPtr *p;
-
- /* Remove this from the list of active I2C buses */
-
- for (p = &I2CBusList; *p != NULL; p = &(*p)->NextBus)
- if (*p == b) {
- *p = (*p)->NextBus;
- break;
- }
-
- if (b->FirstDev != NULL) {
- if (devs_too) {
- I2CDevPtr d;
-
- while ((d = b->FirstDev) != NULL) {
- b->FirstDev = d->NextDev;
- xf86DestroyI2CDevRec(d, unalloc);
- }
- } else {
- if (unalloc) {
- xf86Msg(X_ERROR, "i2c bug: Attempt to remove I2C bus \"%s\", "
- "but device list is not empty.\n",
- b->BusName);
- return;
- }
- }
- }
-
- xf86DrvMsg(b->scrnIndex, X_INFO, "I2C bus \"%s\" removed.\n",
- b->BusName);
-
- if (unalloc) free(b);
- }
-}
-
-/* I2C masters have to register themselves using this function.
- * It will not allocate an I2CBusRec for you, instead you may enter
- * a pointer to a statically allocated I2CBusRec or the (modified)
- * result of xf86CreateI2CBusRec. Returns TRUE on success.
- *
- * At this point there won't be any traffic on the I2C bus.
- */
-
-Bool
-xf86I2CBusInit(I2CBusPtr b)
-{
- /* I2C buses must be identified by a unique scrnIndex
- * and name. If scrnIndex is unspecified (a negative value),
- * then the name must be unique throughout the server.
- */
-
- if (b->BusName == NULL ||
- xf86I2CFindBus(b->scrnIndex, b->BusName) != NULL)
- return FALSE;
-
- /* If the high level functions are not
- * supplied, use the generic functions.
- * In this case we need the low-level
- * function.
- */
- if (b->I2CWriteRead == NULL)
- {
- b->I2CWriteRead=I2CWriteRead;
-
- if (b->I2CPutBits == NULL ||
- b->I2CGetBits == NULL)
- {
- if (b->I2CPutByte == NULL ||
- b->I2CGetByte == NULL ||
- b->I2CAddress == NULL ||
- b->I2CStart == NULL ||
- b->I2CStop == NULL)
- return FALSE;
- } else {
- b->I2CPutByte = I2CPutByte;
- b->I2CGetByte = I2CGetByte;
- b->I2CAddress = I2CAddress;
- b->I2CStop = I2CStop;
- b->I2CStart = I2CStart;
- }
- }
-
- if (b->I2CUDelay == NULL)
- b->I2CUDelay = I2CUDelay;
-
- if (b->HoldTime < 2) b->HoldTime = 5;
- if (b->BitTimeout <= 0) b->BitTimeout = b->HoldTime;
- if (b->ByteTimeout <= 0) b->ByteTimeout = b->HoldTime;
- if (b->AcknTimeout <= 0) b->AcknTimeout = b->HoldTime;
- if (b->StartTimeout <= 0) b->StartTimeout = b->HoldTime;
-
- /* Put new bus on list. */
-
- b->NextBus = I2CBusList;
- I2CBusList = b;
-
- xf86DrvMsg(b->scrnIndex, X_INFO, "I2C bus \"%s\" initialized.\n",
- b->BusName);
-
- return TRUE;
-}
-
-I2CBusPtr
-xf86I2CFindBus(int scrnIndex, char *name)
-{
- I2CBusPtr p;
-
- if (name != NULL)
- for (p = I2CBusList; p != NULL; p = p->NextBus)
- if (scrnIndex < 0 || p->scrnIndex == scrnIndex)
- if (!strcmp(p->BusName, name))
- return p;
-
- return NULL;
-}
-
-/*
- * Return an array of I2CBusPtr's related to a screen. The caller is
- * responsible for freeing the array.
- */
-int
-xf86I2CGetScreenBuses(int scrnIndex, I2CBusPtr **pppI2CBus)
-{
- I2CBusPtr pI2CBus;
- int n = 0;
-
- if (pppI2CBus)
- *pppI2CBus = NULL;
-
- for (pI2CBus = I2CBusList; pI2CBus; pI2CBus = pI2CBus->NextBus) {
- if ((pI2CBus->scrnIndex >= 0) && (pI2CBus->scrnIndex != scrnIndex))
- continue;
-
- n++;
-
- if (!pppI2CBus)
- continue;
-
- *pppI2CBus = xnfrealloc(*pppI2CBus, n * sizeof(I2CBusPtr));
- (*pppI2CBus)[n - 1] = pI2CBus;
- }
-
- return n;
-}
+/* + * Copyright (C) 1998 Itai Nahshon, Michael Schimek + * + * The original code was derived from and inspired by + * the I2C driver from the Linux kernel. + * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> + */ + +#ifdef HAVE_XORG_CONFIG_H +#include <xorg-config.h> +#endif + +#include <sys/time.h> +#include <string.h> + +#include "misc.h" +#include "xf86.h" +#include "xf86_OSproc.h" + +#include <X11/X.h> +#include <X11/Xos.h> +#include <X11/Xproto.h> +#include "scrnintstr.h" +#include "regionstr.h" +#include "windowstr.h" +#include "pixmapstr.h" +#include "validate.h" +#include "resource.h" +#include "gcstruct.h" +#include "dixstruct.h" + +#include "xf86i2c.h" + +#define I2C_TIMEOUT(x) /*(x)*/ /* Report timeouts */ +#define I2C_TRACE(x) /*(x)*/ /* Report progress */ + +/* This is the default I2CUDelay function if not supplied by the driver. + * High level I2C interfaces implementing the bus protocol in hardware + * should supply this function too. + * + * Delay execution at least usec microseconds. + * All values 0 to 1e6 inclusive must be expected. + */ + +static void +I2CUDelay(I2CBusPtr b, int usec) +{ + struct timeval begin, cur; + long d_secs, d_usecs; + long diff; + + if (usec > 0) { + X_GETTIMEOFDAY(&begin); + do { + /* It would be nice to use {xf86}usleep, + * but usleep (1) takes >10000 usec ! + */ + X_GETTIMEOFDAY(&cur); + d_secs = (cur.tv_sec - begin.tv_sec); + d_usecs = (cur.tv_usec - begin.tv_usec); + diff = d_secs * 1000000 + d_usecs; + } while (diff >= 0 && diff < (usec + 1)); + } +} + +/* Most drivers will register just with GetBits/PutBits functions. + * The following functions implement a software I2C protocol + * by using the promitive functions given by the driver. + * ================================================================ + * + * It is assumed that there is just one master on the I2C bus, therefore + * there is no explicit test for conflits. + */ + +#define RISEFALLTIME 2 /* usec, actually 300 to 1000 ns according to the i2c specs */ + +/* Some devices will hold SCL low to slow down the bus or until + * ready for transmission. + * + * This condition will be noticed when the master tries to raise + * the SCL line. You can set the timeout to zero if the slave device + * does not support this clock synchronization. + */ + +static Bool +I2CRaiseSCL(I2CBusPtr b, int sda, int timeout) +{ + int i, scl; + + b->I2CPutBits(b, 1, sda); + b->I2CUDelay(b, b->RiseFallTime); + + for (i = timeout; i > 0; i -= b->RiseFallTime) { + b->I2CGetBits(b, &scl, &sda); + if (scl) + break; + b->I2CUDelay(b, b->RiseFallTime); + } + + if (i <= 0) { + I2C_TIMEOUT(ErrorF + ("[I2CRaiseSCL(<%s>, %d, %d) timeout]", b->BusName, sda, + timeout)); + return FALSE; + } + + return TRUE; +} + +/* Send a start signal on the I2C bus. The start signal notifies + * devices that a new transaction is initiated by the bus master. + * + * The start signal is always followed by a slave address. + * Slave addresses are 8+ bits. The first 7 bits identify the + * device and the last bit signals if this is a read (1) or + * write (0) operation. + * + * There may be more than one start signal on one transaction. + * This happens for example on some devices that allow reading + * of registers. First send a start bit followed by the device + * address (with the last bit 0) and the register number. Then send + * a new start bit with the device address (with the last bit 1) + * and then read the value from the device. + * + * Note this is function does not implement a multiple master + * arbitration procedure. + */ + +static Bool +I2CStart(I2CBusPtr b, int timeout) +{ + if (!I2CRaiseSCL(b, 1, timeout)) + return FALSE; + + b->I2CPutBits(b, 1, 0); + b->I2CUDelay(b, b->HoldTime); + b->I2CPutBits(b, 0, 0); + b->I2CUDelay(b, b->HoldTime); + + I2C_TRACE(ErrorF("\ni2c: <")); + + return TRUE; +} + +/* This is the default I2CStop function if not supplied by the driver. + * + * Signal devices on the I2C bus that a transaction on the + * bus has finished. There may be more than one start signal + * on a transaction but only one stop signal. + */ + +static void +I2CStop(I2CDevPtr d) +{ + I2CBusPtr b = d->pI2CBus; + + b->I2CPutBits(b, 0, 0); + b->I2CUDelay(b, b->RiseFallTime); + + b->I2CPutBits(b, 1, 0); + b->I2CUDelay(b, b->HoldTime); + b->I2CPutBits(b, 1, 1); + b->I2CUDelay(b, b->HoldTime); + + I2C_TRACE(ErrorF(">\n")); +} + +/* Write/Read a single bit to/from a device. + * Return FALSE if a timeout occurs. + */ + +static Bool +I2CWriteBit(I2CBusPtr b, int sda, int timeout) +{ + Bool r; + + b->I2CPutBits(b, 0, sda); + b->I2CUDelay(b, b->RiseFallTime); + + r = I2CRaiseSCL(b, sda, timeout); + b->I2CUDelay(b, b->HoldTime); + + b->I2CPutBits(b, 0, sda); + b->I2CUDelay(b, b->HoldTime); + + return r; +} + +static Bool +I2CReadBit(I2CBusPtr b, int *psda, int timeout) +{ + Bool r; + int scl; + + r = I2CRaiseSCL(b, 1, timeout); + b->I2CUDelay(b, b->HoldTime); + + b->I2CGetBits(b, &scl, psda); + + b->I2CPutBits(b, 0, 1); + b->I2CUDelay(b, b->HoldTime); + + return r; +} + +/* This is the default I2CPutByte function if not supplied by the driver. + * + * A single byte is sent to the device. + * The function returns FALSE if a timeout occurs, you should send + * a stop condition afterwards to reset the bus. + * + * A timeout occurs, + * if the slave pulls SCL to slow down the bus more than ByteTimeout usecs, + * or slows down the bus for more than BitTimeout usecs for each bit, + * or does not send an ACK bit (0) to acknowledge the transmission within + * AcknTimeout usecs, but a NACK (1) bit. + * + * AcknTimeout must be at least b->HoldTime, the other timeouts can be + * zero according to the comment on I2CRaiseSCL. + */ + +static Bool +I2CPutByte(I2CDevPtr d, I2CByte data) +{ + Bool r; + int i, scl, sda; + I2CBusPtr b = d->pI2CBus; + + if (!I2CWriteBit(b, (data >> 7) & 1, d->ByteTimeout)) + return FALSE; + + for (i = 6; i >= 0; i--) + if (!I2CWriteBit(b, (data >> i) & 1, d->BitTimeout)) + return FALSE; + + b->I2CPutBits(b, 0, 1); + b->I2CUDelay(b, b->RiseFallTime); + + r = I2CRaiseSCL(b, 1, b->HoldTime); + + if (r) { + for (i = d->AcknTimeout; i > 0; i -= b->HoldTime) { + b->I2CUDelay(b, b->HoldTime); + b->I2CGetBits(b, &scl, &sda); + if (sda == 0) + break; + } + + if (i <= 0) { + I2C_TIMEOUT(ErrorF("[I2CPutByte(<%s>, 0x%02x, %d, %d, %d) timeout]", + b->BusName, data, d->BitTimeout, + d->ByteTimeout, d->AcknTimeout)); + r = FALSE; + } + + I2C_TRACE(ErrorF("W%02x%c ", (int) data, sda ? '-' : '+')); + } + + b->I2CPutBits(b, 0, 1); + b->I2CUDelay(b, b->HoldTime); + + return r; +} + +/* This is the default I2CGetByte function if not supplied by the driver. + * + * A single byte is read from the device. + * The function returns FALSE if a timeout occurs, you should send + * a stop condition afterwards to reset the bus. + * + * A timeout occurs, + * if the slave pulls SCL to slow down the bus more than ByteTimeout usecs, + * or slows down the bus for more than b->BitTimeout usecs for each bit. + * + * ByteTimeout must be at least b->HoldTime, the other timeouts can be + * zero according to the comment on I2CRaiseSCL. + * + * For the <last> byte in a sequence the acknowledge bit NACK (1), + * otherwise ACK (0) will be sent. + */ + +static Bool +I2CGetByte(I2CDevPtr d, I2CByte * data, Bool last) +{ + int i, sda; + I2CBusPtr b = d->pI2CBus; + + b->I2CPutBits(b, 0, 1); + b->I2CUDelay(b, b->RiseFallTime); + + if (!I2CReadBit(b, &sda, d->ByteTimeout)) + return FALSE; + + *data = (sda > 0) << 7; + + for (i = 6; i >= 0; i--) + if (!I2CReadBit(b, &sda, d->BitTimeout)) + return FALSE; + else + *data |= (sda > 0) << i; + + if (!I2CWriteBit(b, last ? 1 : 0, d->BitTimeout)) + return FALSE; + + I2C_TRACE(ErrorF("R%02x%c ", (int) *data, last ? '+' : '-')); + + return TRUE; +} + +/* This is the default I2CAddress function if not supplied by the driver. + * + * It creates the start condition, followed by the d->SlaveAddr. + * Higher level functions must call this routine rather than + * I2CStart/PutByte because a hardware I2C master may not be able + * to send a slave address without a start condition. + * + * The same timeouts apply as with I2CPutByte and additional a + * StartTimeout, similar to the ByteTimeout but for the start + * condition. + * + * In case of a timeout, the bus is left in a clean idle condition. + * I. e. you *must not* send a Stop. If this function succeeds, you *must*. + * + * The slave address format is 16 bit, with the legacy _8_bit_ slave address + * in the least significant byte. This is, the slave address must include the + * R/_W flag as least significant bit. + * + * The most significant byte of the address will be sent _after_ the LSB, + * but only if the LSB indicates: + * a) an 11 bit address, this is LSB = 1111 0xxx. + * b) a 'general call address', this is LSB = 0000 000x - see the I2C specs + * for more. + */ + +static Bool +I2CAddress(I2CDevPtr d, I2CSlaveAddr addr) +{ + if (I2CStart(d->pI2CBus, d->StartTimeout)) { + if (I2CPutByte(d, addr & 0xFF)) { + if ((addr & 0xF8) != 0xF0 && (addr & 0xFE) != 0x00) + return TRUE; + + if (I2CPutByte(d, (addr >> 8) & 0xFF)) + return TRUE; + } + + I2CStop(d); + } + + return FALSE; +} + +/* These are the hardware independent I2C helper functions. + * ======================================================== + */ + +/* Function for probing. Just send the slave address + * and return true if the device responds. The slave address + * must have the lsb set to reflect a read (1) or write (0) access. + * Don't expect a read- or write-only device will respond otherwise. + */ + +Bool +xf86I2CProbeAddress(I2CBusPtr b, I2CSlaveAddr addr) +{ + int r; + I2CDevRec d; + + d.DevName = "Probing"; + d.BitTimeout = b->BitTimeout; + d.ByteTimeout = b->ByteTimeout; + d.AcknTimeout = b->AcknTimeout; + d.StartTimeout = b->StartTimeout; + d.SlaveAddr = addr; + d.pI2CBus = b; + d.NextDev = NULL; + + r = b->I2CAddress(&d, addr); + + if (r) + b->I2CStop(&d); + + return r; +} + +/* All functions below are related to devices and take the + * slave address and timeout values from an I2CDevRec. They + * return FALSE in case of an error (presumably a timeout). + */ + +/* General purpose read and write function. + * + * 1st, if nWrite > 0 + * Send a start condition + * Send the slave address (1 or 2 bytes) with write flag + * Write n bytes from WriteBuffer + * 2nd, if nRead > 0 + * Send a start condition [again] + * Send the slave address (1 or 2 bytes) with read flag + * Read n bytes to ReadBuffer + * 3rd, if a Start condition has been successfully sent, + * Send a Stop condition. + * + * The functions exits immediately when an error occures, + * not proceeding any data left. However, step 3 will + * be executed anyway to leave the bus in clean idle state. + */ + +static Bool +I2CWriteRead(I2CDevPtr d, + I2CByte * WriteBuffer, int nWrite, I2CByte * ReadBuffer, int nRead) +{ + Bool r = TRUE; + I2CBusPtr b = d->pI2CBus; + int s = 0; + + if (r && nWrite > 0) { + r = b->I2CAddress(d, d->SlaveAddr & ~1); + if (r) { + for (; nWrite > 0; WriteBuffer++, nWrite--) + if (!(r = b->I2CPutByte(d, *WriteBuffer))) + break; + s++; + } + } + + if (r && nRead > 0) { + r = b->I2CAddress(d, d->SlaveAddr | 1); + if (r) { + for (; nRead > 0; ReadBuffer++, nRead--) + if (!(r = b->I2CGetByte(d, ReadBuffer, nRead == 1))) + break; + s++; + } + } + + if (s) + b->I2CStop(d); + + return r; +} + +/* wrapper - for compatibility and convinience */ + +Bool +xf86I2CWriteRead(I2CDevPtr d, + I2CByte * WriteBuffer, int nWrite, + I2CByte * ReadBuffer, int nRead) +{ + I2CBusPtr b = d->pI2CBus; + + return b->I2CWriteRead(d, WriteBuffer, nWrite, ReadBuffer, nRead); +} + +/* Read a byte, the only readable register of a device. + */ + +Bool +xf86I2CReadStatus(I2CDevPtr d, I2CByte * pbyte) +{ + return xf86I2CWriteRead(d, NULL, 0, pbyte, 1); +} + +/* Read a byte from one of the registers determined by its sub-address. + */ + +Bool +xf86I2CReadByte(I2CDevPtr d, I2CByte subaddr, I2CByte * pbyte) +{ + return xf86I2CWriteRead(d, &subaddr, 1, pbyte, 1); +} + +/* Read bytes from subsequent registers determined by the + * sub-address of the first register. + */ + +Bool +xf86I2CReadBytes(I2CDevPtr d, I2CByte subaddr, I2CByte * pbyte, int n) +{ + return xf86I2CWriteRead(d, &subaddr, 1, pbyte, n); +} + +/* Read a word (high byte, then low byte) from one of the registers + * determined by its sub-address. + */ + +Bool +xf86I2CReadWord(I2CDevPtr d, I2CByte subaddr, unsigned short *pword) +{ + I2CByte rb[2]; + + if (!xf86I2CWriteRead(d, &subaddr, 1, rb, 2)) + return FALSE; + + *pword = (rb[0] << 8) | rb[1]; + + return TRUE; +} + +/* Write a byte to one of the registers determined by its sub-address. + */ + +Bool +xf86I2CWriteByte(I2CDevPtr d, I2CByte subaddr, I2CByte byte) +{ + I2CByte wb[2]; + + wb[0] = subaddr; + wb[1] = byte; + + return xf86I2CWriteRead(d, wb, 2, NULL, 0); +} + +/* Write bytes to subsequent registers determined by the + * sub-address of the first register. + */ + +Bool +xf86I2CWriteBytes(I2CDevPtr d, I2CByte subaddr, + I2CByte * WriteBuffer, int nWrite) +{ + I2CBusPtr b = d->pI2CBus; + Bool r = TRUE; + + if (nWrite > 0) { + r = b->I2CAddress(d, d->SlaveAddr & ~1); + if (r) { + if ((r = b->I2CPutByte(d, subaddr))) + for (; nWrite > 0; WriteBuffer++, nWrite--) + if (!(r = b->I2CPutByte(d, *WriteBuffer))) + break; + + b->I2CStop(d); + } + } + + return r; +} + +/* Write a word (high byte, then low byte) to one of the registers + * determined by its sub-address. + */ + +Bool +xf86I2CWriteWord(I2CDevPtr d, I2CByte subaddr, unsigned short word) +{ + I2CByte wb[3]; + + wb[0] = subaddr; + wb[1] = word >> 8; + wb[2] = word & 0xFF; + + return xf86I2CWriteRead(d, wb, 3, NULL, 0); +} + +/* Write a vector of bytes to not adjacent registers. This vector is, + * 1st byte sub-address, 2nd byte value, 3rd byte sub-address asf. + * This function is intended to initialize devices. Note this function + * exits immediately when an error occurs, some registers may + * remain uninitialized. + */ + +Bool +xf86I2CWriteVec(I2CDevPtr d, I2CByte * vec, int nValues) +{ + I2CBusPtr b = d->pI2CBus; + Bool r = TRUE; + int s = 0; + + if (nValues > 0) { + for (; nValues > 0; nValues--, vec += 2) { + if (!(r = b->I2CAddress(d, d->SlaveAddr & ~1))) + break; + + s++; + + if (!(r = b->I2CPutByte(d, vec[0]))) + break; + + if (!(r = b->I2CPutByte(d, vec[1]))) + break; + } + + if (s > 0) + b->I2CStop(d); + } + + return r; +} + +/* Administrative functions. + * ========================= + */ + +/* Allocates an I2CDevRec for you and initializes with propper defaults + * you may modify before calling xf86I2CDevInit. Your I2CDevRec must + * contain at least a SlaveAddr, and a pI2CBus pointer to the bus this + * device shall be linked to. + * + * See function I2CAddress for the slave address format. Always set + * the least significant bit, indicating a read or write access, to zero. + */ + +I2CDevPtr +xf86CreateI2CDevRec(void) +{ + return calloc(1, sizeof(I2CDevRec)); +} + +/* Unlink an I2C device. If you got the I2CDevRec from xf86CreateI2CDevRec + * you should set <unalloc> to free it. + */ + +void +xf86DestroyI2CDevRec(I2CDevPtr d, Bool unalloc) +{ + if (d) { + I2CDevPtr *p; + + /* Remove this from the list of active I2C devices. */ + + for (p = &d->pI2CBus->FirstDev; *p != NULL; p = &(*p)->NextDev) + if (*p == d) { + *p = (*p)->NextDev; + break; + } + + xf86DrvMsg(d->pI2CBus->scrnIndex, X_INFO, + "I2C device \"%s:%s\" removed.\n", + d->pI2CBus->BusName, d->DevName); + + if (unalloc) + free(d); + } +} + +/* I2C transmissions are related to an I2CDevRec you must link to a + * previously registered bus (see xf86I2CBusInit) before attempting + * to read and write data. You may call xf86I2CProbeAddress first to + * see if the device in question is present on this bus. + * + * xf86I2CDevInit will not allocate an I2CBusRec for you, instead you + * may enter a pointer to a statically allocated I2CDevRec or the (modified) + * result of xf86CreateI2CDevRec. + * + * If you don't specify timeouts for the device (n <= 0), it will inherit + * the bus-wide defaults. The function returns TRUE on success. + */ + +Bool +xf86I2CDevInit(I2CDevPtr d) +{ + I2CBusPtr b; + + if (d == NULL || + (b = d->pI2CBus) == NULL || + (d->SlaveAddr & 1) || xf86I2CFindDev(b, d->SlaveAddr) != NULL) + return FALSE; + + if (d->BitTimeout <= 0) + d->BitTimeout = b->BitTimeout; + if (d->ByteTimeout <= 0) + d->ByteTimeout = b->ByteTimeout; + if (d->AcknTimeout <= 0) + d->AcknTimeout = b->AcknTimeout; + if (d->StartTimeout <= 0) + d->StartTimeout = b->StartTimeout; + + d->NextDev = b->FirstDev; + b->FirstDev = d; + + xf86DrvMsg(b->scrnIndex, X_INFO, + "I2C device \"%s:%s\" registered at address 0x%02X.\n", + b->BusName, d->DevName, d->SlaveAddr); + + return TRUE; +} + +I2CDevPtr +xf86I2CFindDev(I2CBusPtr b, I2CSlaveAddr addr) +{ + I2CDevPtr d; + + if (b) { + for (d = b->FirstDev; d != NULL; d = d->NextDev) + if (d->SlaveAddr == addr) + return d; + } + + return NULL; +} + +static I2CBusPtr I2CBusList; + +/* Allocates an I2CBusRec for you and initializes with propper defaults + * you may modify before calling xf86I2CBusInit. Your I2CBusRec must + * contain at least a BusName, a scrnIndex (or -1), and a complete set + * of either high or low level I2C function pointers. You may pass + * bus-wide timeouts, otherwise inplausible values will be replaced + * with safe defaults. + */ + +I2CBusPtr +xf86CreateI2CBusRec(void) +{ + I2CBusPtr b; + + b = (I2CBusPtr) calloc(1, sizeof(I2CBusRec)); + + if (b != NULL) { + b->scrnIndex = -1; + b->HoldTime = 5; /* 100 kHz bus */ + b->BitTimeout = 5; + b->ByteTimeout = 5; + b->AcknTimeout = 5; + b->StartTimeout = 5; + b->RiseFallTime = RISEFALLTIME; + } + + return b; +} + +/* Unregister an I2C bus. If you got the I2CBusRec from xf86CreateI2CBusRec + * you should set <unalloc> to free it. If you set <devs_too>, the function + * xf86DestroyI2CDevRec will be called for all devices linked to the bus + * first, passing down the <unalloc> option. + */ + +void +xf86DestroyI2CBusRec(I2CBusPtr b, Bool unalloc, Bool devs_too) +{ + if (b) { + I2CBusPtr *p; + + /* Remove this from the list of active I2C buses */ + + for (p = &I2CBusList; *p != NULL; p = &(*p)->NextBus) + if (*p == b) { + *p = (*p)->NextBus; + break; + } + + if (b->FirstDev != NULL) { + if (devs_too) { + I2CDevPtr d; + + while ((d = b->FirstDev) != NULL) { + b->FirstDev = d->NextDev; + xf86DestroyI2CDevRec(d, unalloc); + } + } + else { + if (unalloc) { + xf86Msg(X_ERROR, + "i2c bug: Attempt to remove I2C bus \"%s\", " + "but device list is not empty.\n", b->BusName); + return; + } + } + } + + xf86DrvMsg(b->scrnIndex, X_INFO, "I2C bus \"%s\" removed.\n", + b->BusName); + + if (unalloc) + free(b); + } +} + +/* I2C masters have to register themselves using this function. + * It will not allocate an I2CBusRec for you, instead you may enter + * a pointer to a statically allocated I2CBusRec or the (modified) + * result of xf86CreateI2CBusRec. Returns TRUE on success. + * + * At this point there won't be any traffic on the I2C bus. + */ + +Bool +xf86I2CBusInit(I2CBusPtr b) +{ + /* I2C buses must be identified by a unique scrnIndex + * and name. If scrnIndex is unspecified (a negative value), + * then the name must be unique throughout the server. + */ + + if (b->BusName == NULL || xf86I2CFindBus(b->scrnIndex, b->BusName) != NULL) + return FALSE; + + /* If the high level functions are not + * supplied, use the generic functions. + * In this case we need the low-level + * function. + */ + if (b->I2CWriteRead == NULL) { + b->I2CWriteRead = I2CWriteRead; + + if (b->I2CPutBits == NULL || b->I2CGetBits == NULL) { + if (b->I2CPutByte == NULL || + b->I2CGetByte == NULL || + b->I2CAddress == NULL || + b->I2CStart == NULL || b->I2CStop == NULL) + return FALSE; + } + else { + b->I2CPutByte = I2CPutByte; + b->I2CGetByte = I2CGetByte; + b->I2CAddress = I2CAddress; + b->I2CStop = I2CStop; + b->I2CStart = I2CStart; + } + } + + if (b->I2CUDelay == NULL) + b->I2CUDelay = I2CUDelay; + + if (b->HoldTime < 2) + b->HoldTime = 5; + if (b->BitTimeout <= 0) + b->BitTimeout = b->HoldTime; + if (b->ByteTimeout <= 0) + b->ByteTimeout = b->HoldTime; + if (b->AcknTimeout <= 0) + b->AcknTimeout = b->HoldTime; + if (b->StartTimeout <= 0) + b->StartTimeout = b->HoldTime; + + /* Put new bus on list. */ + + b->NextBus = I2CBusList; + I2CBusList = b; + + xf86DrvMsg(b->scrnIndex, X_INFO, "I2C bus \"%s\" initialized.\n", + b->BusName); + + return TRUE; +} + +I2CBusPtr +xf86I2CFindBus(int scrnIndex, char *name) +{ + I2CBusPtr p; + + if (name != NULL) + for (p = I2CBusList; p != NULL; p = p->NextBus) + if (scrnIndex < 0 || p->scrnIndex == scrnIndex) + if (!strcmp(p->BusName, name)) + return p; + + return NULL; +} + +/* + * Return an array of I2CBusPtr's related to a screen. The caller is + * responsible for freeing the array. + */ +int +xf86I2CGetScreenBuses(int scrnIndex, I2CBusPtr ** pppI2CBus) +{ + I2CBusPtr pI2CBus; + int n = 0; + + if (pppI2CBus) + *pppI2CBus = NULL; + + for (pI2CBus = I2CBusList; pI2CBus; pI2CBus = pI2CBus->NextBus) { + if ((pI2CBus->scrnIndex >= 0) && (pI2CBus->scrnIndex != scrnIndex)) + continue; + + n++; + + if (!pppI2CBus) + continue; + + *pppI2CBus = xnfrealloc(*pppI2CBus, n * sizeof(I2CBusPtr)); + (*pppI2CBus)[n - 1] = pI2CBus; + } + + return n; +} |