Nut/OS  4.10.3
API Reference
twif.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00034 /*
00035  * $Log$
00036  * Revision 1.7  2008/08/11 06:59:17  haraldkipp
00037  * BSD types replaced by stdint types (feature request #1282721).
00038  *
00039  * Revision 1.6  2007/07/26 13:03:05  freckle
00040  * Reduced critical section in TwSlaveListen
00041  *
00042  * Revision 1.5  2006/10/08 16:48:08  haraldkipp
00043  * Documentation fixed
00044  *
00045  * Revision 1.4  2006/06/28 14:31:24  haraldkipp
00046  * NutEventPostFromIrq() doesn't return a result anymore. We directly
00047  * inspect the queue's root.
00048  *
00049  * Revision 1.3  2005/10/24 10:56:30  haraldkipp
00050  * Added const modifier to transmit data pointer in TwMasterTransact().
00051  *
00052  * Revision 1.2  2005/10/07 22:03:29  hwmaier
00053  * Using __AVR_ENHANCED__ macro instead of __AVR_ATmega128__ to support also AT90CAN128 MCU
00054  *
00055  * Revision 1.1  2005/07/26 18:02:40  haraldkipp
00056  * Moved from dev.
00057  *
00058  * Revision 1.9  2005/01/24 21:12:05  freckle
00059  * renamed NutEventPostFromIRQ into NutEventPostFromIrq
00060  *
00061  * Revision 1.8  2005/01/21 16:49:46  freckle
00062  * Seperated calls to NutEventPostAsync between Threads and IRQs
00063  *
00064  * Revision 1.7  2004/12/16 08:40:35  haraldkipp
00065  * Late increment fixes ICCAVR bug.
00066  *
00067  * Revision 1.6  2004/11/08 18:12:59  haraldkipp
00068  * Soooo many fixes, but I'm tired...really.
00069  *
00070  * Revision 1.5  2004/09/08 10:19:14  haraldkipp
00071  * *** empty log message ***
00072  *
00073  * Revision 1.4  2003/11/03 17:03:53  haraldkipp
00074  * Many new comments added
00075  *
00076  * Revision 1.3  2003/07/20 18:28:10  haraldkipp
00077  * Explain how to disable timeout.
00078  *
00079  * Revision 1.2  2003/07/17 09:38:07  haraldkipp
00080  * Setting different speeds is now supported.
00081  *
00082  * Revision 1.1.1.1  2003/05/09 14:40:53  haraldkipp
00083  * Initial using 3.2.1
00084  *
00085  * Revision 1.2  2003/03/31 14:53:08  harald
00086  * Prepare release 3.1
00087  *
00088  * Revision 1.1  2003/02/04 17:54:59  harald
00089  * *** empty log message ***
00090  *
00091  */
00092 
00093 #include <string.h>
00094 
00095 #include <dev/irqreg.h>
00096 
00097 #include <sys/event.h>
00098 #include <sys/atom.h>
00099 #include <sys/timer.h>
00100 #include <sys/thread.h>
00101 #include <sys/heap.h>
00102 
00103 #include <dev/twif.h>
00104 
00105 #ifdef __AVR_ENHANCED__
00106 
00107 static volatile uint8_t tw_if_bsy;   /* Set while interface is busy. */
00108 
00109 HANDLE tw_mm_mutex;          /* Exclusive master access. */
00110 HANDLE tw_mm_que;            /* Threads waiting for master transfer done. */
00111 HANDLE tw_sr_que;            /* Threads waiting for slave receive. */
00112 HANDLE tw_st_que;            /* Threads waiting for slave transmit done. */
00113 
00114 static uint8_t tw_mm_sla;            /* Destination slave address. */
00115 static volatile uint8_t tw_mm_err;   /* Current master mode error. */
00116 static uint8_t tw_mm_error;          /* Last master mode error. */
00117 
00118 static CONST uint8_t *tw_mt_buf;     /* Pointer to the master transmit buffer. */
00119 static volatile uint16_t tw_mt_len;  /* Number of bytes to transmit in master mode. */
00120 static volatile uint16_t tw_mt_idx;  /* Current master transmit buffer index. */
00121 
00122 static uint8_t *tw_mr_buf;           /* Pointer to the master receive buffer. */
00123 static volatile uint16_t tw_mr_siz;  /* Size of the master receive buffer. */
00124 static volatile uint16_t tw_mr_idx;  /* Current master receive buffer index. */
00125 
00126 static volatile uint8_t tw_sm_sla;   /* Slave address received. */
00127 static volatile uint8_t tw_sm_err;   /* Current slave mode error. */
00128 static uint8_t tw_sm_error;          /* Last slave mode error. */
00129 
00130 static uint8_t *tw_st_buf;           /* Pointer to the slave transmit buffer. */
00131 static volatile uint16_t tw_st_len;  /* Number of bytes to transmit in slave mode. */
00132 static volatile uint16_t tw_st_idx;  /* Current slave transmit buffer index. */
00133 
00134 static uint8_t *tw_sr_buf;           /* Pointer to the slave receive buffer. */
00135 static volatile uint16_t tw_sr_siz;  /* Size of the master receive buffer. */
00136 static volatile uint16_t tw_sr_idx;  /* Current slave receive buffer index. */
00137 
00138 /*
00139 TWINT TWEA TWSTA TWSTO TWWC TWEN  0  TWIE
00140   C                      R        R
00141 */
00142 
00143 #define TWGO    (_BV(TWINT) | _BV(TWEN) | _BV(TWIE))
00144 
00145 /*
00146  * TWI interrupt handler.
00147  */
00148 static void TwInterrupt(void *arg)
00149 {
00150     uint8_t twsr;
00151     register uint8_t twcr = inb(TWCR);
00152 
00153     /*
00154      * Read the status and interpret its contents.
00155      */
00156     twsr = inb(TWSR) & 0xF8;
00157     switch (twsr) {
00158 
00159     /*
00160      * 0x08: Start condition has been transmitted.
00161      * 0x10: Repeated start condition has been transmitted.
00162      */
00163     case TW_START:
00164     case TW_REP_START:
00165         /* We are entering the master mode. Mark the interface busy. */
00166         tw_if_bsy = 1;
00167         tw_mt_idx = 0;
00168         tw_mr_idx = 0;
00169 
00170         /*
00171          * If outgoing data is available, transmit SLA+W. Logic is in
00172          * master transmit mode.
00173          */
00174         if (tw_mt_len) {
00175             outb(TWDR, tw_mm_sla);
00176         }
00177 
00178         /*
00179          * If outgoing data not available, transmit SLA+R. Logic will
00180          * switch to master receiver mode.
00181          */
00182         else {
00183             outb(TWDR, tw_mm_sla | 1);
00184         }
00185         outb(TWCR, TWGO | (twcr & _BV(TWEA)));
00186         break;
00187 
00188     /*
00189      * 0x18: SLA+W has been transmitted and ACK has been received.
00190      * 0x28: Data byte has been transmitted and ACK has been received.
00191      */
00192     case TW_MT_SLA_ACK:
00193     case TW_MT_DATA_ACK:
00194         /*
00195          * If outgoing data left to send, put the next byte in the data
00196          * register.
00197          */
00198         if (tw_mt_idx < tw_mt_len) {
00199             outb(TWDR, tw_mt_buf[tw_mt_idx]);
00200             /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */
00201             tw_mt_idx++;
00202             outb(TWCR, TWGO | (twcr & _BV(TWEA)));
00203             break;
00204         }
00205 
00206         /*
00207          * All outgoing data has been sent. If a response is expected,
00208          * transmit a repeated start condition.
00209          */
00210         tw_mt_len = 0;
00211         if (tw_mr_siz) {
00212             outb(TWCR, TWGO | (twcr & _BV(TWEA)) | _BV(TWSTA));
00213             break;
00214         }
00215 
00216     /*
00217      * 0x20: SLA+W has been transmitted, but not acknowledged.
00218      * 0x30: Data byte has been transmitted, but not acknowledged.
00219      * 0x48: SLA+R has been transmitted, but not acknowledged.
00220      */
00221     case TW_MT_SLA_NACK:
00222     case TW_MT_DATA_NACK:
00223     case TW_MR_SLA_NACK:
00224         /* Set unique error code. */
00225         if (twsr == TW_MT_SLA_NACK || twsr == TW_MR_SLA_NACK) {
00226             tw_mm_err = TWERR_SLA_NACK;
00227             tw_mt_len = 0;
00228             tw_mr_siz = 0;
00229         }
00230 
00231         /* Wake up the application. */
00232         NutEventPostFromIrq(&tw_mm_que);
00233 
00234         /*
00235          * Send a stop condition. If we have a listener, generate
00236          * an acknowlegde on an incoming address byte.
00237          */
00238         if(tw_sr_siz) {
00239             outb(TWCR, TWGO | _BV(TWEA) | _BV(TWSTO));
00240         }
00241         else {
00242             outb(TWCR, TWGO | _BV(TWSTO));
00243         }
00244 
00245         /* The interface is idle. */
00246         tw_if_bsy = 0;
00247         break;
00248 
00249     /*
00250      * 0x38: Arbitration lost while in master mode.
00251      */
00252     case TW_MT_ARB_LOST:
00253         /*
00254          * The start condition will be automatically resend after
00255          * the bus becomes available.
00256          */
00257         sbi(TWCR, TWSTA);
00258         /* The interface is idle. */
00259         tw_if_bsy = 0;
00260         break;
00261 
00262     /*
00263      * 0x50: Data byte has been received and acknowledged.
00264      */
00265     case TW_MR_DATA_ACK:
00266         /*
00267          * Store the data byte in the master receive buffer.
00268          */
00269         tw_mr_buf[tw_mr_idx] = inb(TWDR);
00270         /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */
00271         tw_mr_idx++;
00272 
00273     /*
00274      * 0x40: SLA+R has been transmitted and ACK has been received.
00275      */
00276     case TW_MR_SLA_ACK:
00277         /*
00278          * Acknowledge next data bytes except the last one.
00279          */
00280         if (tw_mr_idx + 1 < tw_mr_siz) {
00281             outb(TWCR, TWGO | _BV(TWEA));
00282         }
00283         else {
00284             outb(TWCR, TWGO);
00285         }
00286         break;
00287 
00288     /*
00289      * 0x58: Data byte has been received, but not acknowledged.
00290      */
00291     case TW_MR_DATA_NACK:
00292         /*
00293          * Store the last data byte.
00294          */
00295         tw_mr_buf[tw_mr_idx] = inb(TWDR);
00296         /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */
00297         tw_mr_idx++;
00298         tw_mr_siz = 0;
00299 
00300         /* Wake up the application. */
00301         NutEventPostFromIrq(&tw_mm_que);
00302 
00303         /*
00304          * Send a stop condition. If we have a listener, generate
00305          * an acknowlegde on an incoming address byte.
00306          */
00307         if(tw_sr_siz) {
00308             outb(TWCR, TWGO | _BV(TWEA) | _BV(TWSTO));
00309         }
00310         else {
00311             outb(TWCR, TWGO | _BV(TWSTO));
00312         }
00313 
00314         /* The interface is idle. */
00315         tw_if_bsy = 0;
00316         break;
00317 
00318     /*
00319      * 0x60: Own SLA+W has been received and acknowledged.
00320      * 0x68: Arbitration lost as master. Own SLA+W has been received
00321      *       and acknowledged.
00322      * 0x70: General call address has been received and acknowledged.
00323      * 0x78: Arbitration lost as master. General call address has been
00324      *       received and acknowledged.
00325      */
00326     case TW_SR_SLA_ACK:
00327     case TW_SR_ARB_LOST_SLA_ACK:
00328     case TW_SR_GCALL_ACK:
00329     case TW_SR_ARB_LOST_GCALL_ACK:
00330         /*
00331          * Do only acknowledge incoming data bytes, if we got receive
00332          * buffer space. Fetch the slave address from the data register
00333          * and reset the receive index.
00334          */
00335         if (tw_sr_siz) {
00336             /* We are entering the slave receive mode. Mark the interface busy. */
00337             tw_if_bsy = 1;
00338 
00339             tw_sm_sla = inb(TWDR);
00340             outb(TWCR, TWGO | _BV(TWEA));
00341             tw_sr_idx = 0;
00342         }
00343 
00344         /*
00345          * Do not acknowledge incoming data.
00346          */
00347         else {
00348             outb(TWCR, TWGO);
00349         }
00350         break;
00351 
00352     /*
00353      * 0x80: Data byte for own SLA has been received and acknowledged.
00354      * 0x90: Data byte for general call address has been received and
00355      *       acknowledged.
00356      */
00357     case TW_SR_DATA_ACK:
00358     case TW_SR_GCALL_DATA_ACK:
00359         /*
00360          * If the receive buffer isn't filled up, store data byte.
00361          */
00362         if (tw_sr_idx < tw_sr_siz) {
00363             tw_sr_buf[tw_sr_idx] = inb(TWDR);
00364             /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */
00365             tw_sr_idx++;
00366         }
00367         else {
00368             tw_sr_siz = 0;
00369         }
00370 
00371         /*
00372          * If more space is available for incoming data, then continue
00373          * receiving. Otherwise do not acknowledge new data bytes.
00374          */
00375         if (tw_sr_siz) {
00376             outb(TWCR, TWGO | _BV(TWEA));
00377             break;
00378         }
00379 
00380     /*
00381      * 0x88: Data byte received, but not acknowledged.
00382      * 0x98: Data byte for general call address received, but not
00383      *       acknowledged.
00384      */
00385     case TW_SR_DATA_NACK:
00386     case TW_SR_GCALL_DATA_NACK:
00387         /*
00388          * Continue not accepting more data.
00389          */
00390         if (tw_mt_len || tw_mr_siz) {
00391             outb(TWCR, inb(TWCR) | _BV(TWEA) | _BV(TWSTA));
00392         }
00393         else {
00394             outb(TWCR, inb(TWCR) | _BV(TWEA));
00395         }
00396         break;
00397 
00398     /*
00399      * 0xA0: Stop condition or repeated start condition received.
00400      */
00401     case TW_SR_STOP:
00402         /*
00403          * Wake up the application. If successful, do nothing. This
00404          * will keep SCL low and thus block the bus. The application
00405          * must now setup the transmit buffer and re-enable the
00406          * interface.
00407          */
00408         if (tw_sr_que == 0 || tw_sm_err) {
00409             /*
00410              * If no one has been waiting on the queue, the application
00411              * probably gave up waiting. So we continue on our own, either
00412              * in idle mode or switching to master mode if a master
00413              * request is waiting.
00414              */
00415             if (tw_mt_len || tw_mr_siz) {
00416                 outb(TWCR, TWGO | _BV(TWSTA));
00417             }
00418             else {
00419                 outb(TWCR, TWGO);
00420             }
00421             tw_if_bsy = 0;
00422         }
00423         else {
00424             NutEventPostFromIrq(&tw_sr_que);
00425             tw_sr_siz = 0;
00426             outb(TWCR, twcr & ~(_BV(TWINT) | _BV(TWIE)));
00427         }
00428         break;
00429 
00430     /*
00431      * 0xA8: Own SLA+R has been received and acknowledged.
00432      * 0xB0: Arbitration lost in master mode. Own SLA has been received
00433      *       and acknowledged.
00434      */
00435     case TW_ST_SLA_ACK:
00436     case TW_ST_ARB_LOST_SLA_ACK:
00437         /* Not idle. */
00438         tw_if_bsy = 1;
00439         /* Reset transmit index and fall through for outgoing data. */
00440         tw_st_idx = 0;
00441 
00442     /*
00443      * 0xB8: Data bytes has been transmitted and acknowledged.
00444      */
00445     case TW_ST_DATA_ACK:
00446         /*
00447          * If outgoing data left to send, put the next byte in the
00448          * data register. Otherwise transmit a dummy byte.
00449          */
00450         if (tw_st_idx < tw_st_len) {
00451             outb(TWDR, tw_st_buf[tw_st_idx]);
00452             /* Do not set acknowledge on the last data byte. */
00453             /* Early increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */
00454             ++tw_st_idx;
00455             if (tw_st_idx < tw_st_len) {
00456                 outb(TWCR, TWGO | _BV(TWEA));
00457             }
00458             else {
00459                 tw_st_len = 0;
00460                 outb(TWCR, TWGO);
00461             }
00462             break;
00463         }
00464 
00465         /* No more data. Continue sending dummies. */
00466         outb(TWDR, 0);
00467         outb(TWCR, TWGO);
00468         break;
00469 
00470     /*
00471      * 0xC0: Data byte has been transmitted, but not acknowledged.
00472      * 0xC8: Last data byte has been transmitted and acknowledged.
00473      */
00474     case TW_ST_DATA_NACK:
00475     case TW_ST_LAST_DATA:
00476         NutEventPostFromIrq(&tw_st_que);
00477 
00478         /* Transmit start condition, if a master transfer is waiting. */
00479         if (tw_mt_len || tw_mr_siz) {
00480             outb(TWCR, TWGO | _BV(TWSTA) |  _BV(TWEA));
00481         }
00482         /* Otherwise enter idle state. */
00483         else {
00484             outb(TWCR, TWGO | _BV(TWEA));
00485         }
00486         tw_if_bsy = 0;
00487         break;
00488 
00489     /*
00490      * 0x00: Bus error.
00491      */
00492     case TW_BUS_ERROR:
00493         outb(TWCR, inb(TWCR) | _BV(TWSTO));
00494 #if 1
00495         tw_if_bsy = 0;
00496         tw_mm_err = TWERR_BUS;
00497         tw_sm_err = TWERR_BUS;
00498         NutEventPostFromIrq(&tw_sr_que);
00499         NutEventPostFromIrq(&tw_st_que);
00500         NutEventPostFromIrq(&tw_mm_que);
00501 #endif
00502         break;
00503     }
00504 }
00505 
00506 #endif /* __AVR_ENHANCED__ */
00507 
00535 int TwMasterTransact(uint8_t sla, CONST void *txdata, uint16_t txlen, void *rxdata, uint16_t rxsiz, uint32_t tmo)
00536 {
00537     int rc = -1;
00538 
00539 #ifdef __AVR_ENHANCED__
00540     /* This routine is marked reentrant, so lock the interface. */
00541     if(NutEventWait(&tw_mm_mutex, 500)) {
00542         tw_mm_err = TWERR_IF_LOCKED;
00543         NutEventPost(&tw_mm_mutex);
00544         return -1;
00545     }
00546 
00547     while(tw_if_bsy) {
00548         NutSleep(63);
00549     }
00550     NutEnterCritical();
00551     /*
00552      * Set all parameters for master mode.
00553      */
00554     tw_mm_sla = sla << 1;
00555     tw_mm_err = 0;
00556     tw_mt_len = txlen;
00557     tw_mt_buf = txdata;
00558     tw_mr_siz = rxsiz;
00559     tw_mr_buf = rxdata;
00560 
00561     /*
00562      * Send a start condition if the interface is idle. If busy, then
00563      * the interrupt routine will automatically initiate the transfer
00564      * as soon as the interface becomes ready again.
00565      */
00566     if(tw_if_bsy == 0) {
00567         uint8_t twcr = inb(TWCR);
00568         uint8_t twsr = inb(TWSR);
00569         if((twsr & 0xF8) == TW_NO_INFO) {
00570             if(tw_sr_siz) {
00571                 outb(TWCR, _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWSTA) | (twcr & _BV(TWSTO)));
00572             }
00573             else {
00574                 outb(TWCR, _BV(TWEN) | _BV(TWIE) | _BV(TWSTA) | (twcr & _BV(TWSTO)));
00575             }
00576         }
00577     }
00578 
00579     /* Clear the queue. */
00580     //*broken?! NutEventBroadcastAsync(&tw_mm_que);
00581     if (tw_mm_que == SIGNALED) {
00582         tw_mm_que = 0;
00583     }
00584     NutExitCritical();
00585 
00586     /*
00587      * Wait for master transmission done.
00588      */
00589     rc = -1;
00590     if (NutEventWait(&tw_mm_que, tmo)) {
00591         tw_mm_error = TWERR_TIMEOUT;
00592     } else {
00593         NutEnterCritical();
00594         if (tw_mm_err) {
00595             tw_mm_error = tw_mm_err;
00596         } else {
00597             rc = tw_mr_idx;
00598         }
00599         NutExitCritical();
00600     }
00601 
00602     /*
00603      * Release the interface.
00604      */
00605     NutEventPost(&tw_mm_mutex);
00606 
00607 #endif /* __AVR_ENHANCED__ */
00608     return rc;
00609 }
00610 
00620 int TwMasterError(void)
00621 {
00622 #ifndef __AVR_ENHANCED__
00623     return -1;
00624 #else
00625     int rc = (int) tw_mm_error;
00626     tw_mm_error = 0;
00627     return rc;
00628 #endif
00629 }
00630 
00653 int TwSlaveListen(uint8_t * sla, void *rxdata, uint16_t rxsiz, uint32_t tmo)
00654 {
00655 #ifndef __AVR_ENHANCED__
00656     return -1;
00657 #else
00658     int rc = -1;
00659 
00660     NutEnterCritical();
00661 
00662     /* Initialize parameters for slave receive. */
00663     tw_sm_err = 0;
00664     tw_sr_siz = rxsiz;
00665     tw_sr_buf = rxdata;
00666 
00667     /*
00668      * If the interface is currently not busy then enable it for
00669      * address recognition.
00670      */
00671     if(tw_if_bsy == 0) {
00672         uint8_t twsr = inb(TWSR);
00673         if((twsr & 0xF8) == TW_NO_INFO) {
00674             if(tw_mt_len || tw_mr_siz)
00675                 outb(TWCR, _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA));
00676             else
00677                 outb(TWCR, _BV(TWEA) | _BV(TWEN) | _BV(TWIE));
00678         }
00679     }
00680 
00681     /* Clear the queue. */
00682     //*broken?! NutEventBroadcastAsync(&tw_sr_que);
00683     if (tw_sr_que == SIGNALED) {
00684         tw_sr_que = 0;
00685     }
00686 
00687     NutExitCritical();
00688 
00689     /* Wait for a frame on the slave mode queue. */
00690     if (NutEventWait(&tw_sr_que, tmo)) {
00691         NutEnterCritical();
00692         tw_sm_err = TWERR_TIMEOUT;
00693         tw_sr_siz = 0;
00694         NutExitCritical();
00695     }
00696 
00697     /*
00698      * Return the number of bytes received and the destination slave
00699      * address, if no slave error occured. In this case the bus is
00700      * blocked.
00701      */
00702     if(tw_sm_err == 0) {
00703         rc = tw_sr_idx;
00704         *sla = tw_sm_sla;
00705     }
00706     return rc;
00707 #endif /* __AVR_ENHANCED__ */
00708 }
00709 
00727 int TwSlaveRespond(void *txdata, uint16_t txlen, uint32_t tmo)
00728 {
00729     int rc = -1;
00730 #ifdef __AVR_ENHANCED__
00731 
00732     /* The bus is blocked. No critical section required. */
00733     tw_st_buf = txdata;
00734     tw_st_len = txlen;
00735 
00736     /*
00737      * If there is anything to transmit, start the interface.
00738      */
00739     if (txlen) {
00740         NutEnterCritical();
00741         /* Clear the queue. */
00742         //*broken?! NutEventBroadcastAsync(&tw_st_que);
00743         if (tw_st_que == SIGNALED) {
00744             tw_st_que = 0;
00745         }
00746 
00747         /* Release the bus, accepting SLA+R. */
00748         outb(TWCR, TWGO | _BV(TWEA));
00749 
00750         NutExitCritical();
00751         if (NutEventWait(&tw_st_que, tmo)) {
00752             tw_sm_err = TWERR_TIMEOUT;
00753         }
00754 
00755         NutEnterCritical();
00756         tw_st_len = 0;
00757         if (tw_sm_err) {
00758             tw_sm_error = tw_sm_err;
00759         } else {
00760             rc = tw_st_idx;
00761         }
00762         NutExitCritical();
00763     }
00764 
00765     /*
00766      * Nothing to transmit.
00767      */
00768     else {
00769         uint8_t twcr;
00770         uint8_t twsr;
00771         rc = 0;
00772         /* Release the bus, not accepting SLA+R. */
00773 
00774         NutEnterCritical();
00775         twcr = inb(TWCR);
00776         twsr = inb(TWSR);
00777         /* Transmit start condition, if a master transfer is waiting. */
00778         if (tw_mt_len || tw_mr_siz) {
00779             outb(TWCR, TWGO | _BV(TWSTA));
00780         }
00781         /* Otherwise enter idle state. */
00782         else {
00783             tw_if_bsy = 0;
00784             outb(TWCR, TWGO);
00785         }
00786 
00787         NutExitCritical();
00788     }
00789 #endif /* __AVR_ENHANCED__ */
00790     return rc;
00791 }
00792 
00802 int TwSlaveError(void)
00803 {
00804 #ifndef __AVR_ENHANCED__
00805     return -1;
00806 #else
00807     int rc = (int) tw_sm_error;
00808     tw_sm_error = 0;
00809     return rc;
00810 #endif
00811 }
00812 
00830 int TwIOCtl(int req, void *conf)
00831 {
00832 #ifndef __AVR_ENHANCED__
00833     return -1;
00834 #else
00835     int rc = 0;
00836     uint32_t lval;
00837 
00838     switch (req) {
00839     case TWI_SETSLAVEADDRESS:
00840         TWAR = (*((uint8_t *) conf) << 1) | 1;
00841         break;
00842     case TWI_GETSLAVEADDRESS:
00843         *((uint8_t *) conf) = TWAR;
00844         break;
00845 
00846     case TWI_SETSPEED:
00847         lval = ((2UL * NutGetCpuClock() / (*((uint32_t *) conf)) + 1UL) / 2UL - 16UL) / 2UL;
00848         if (lval > 1020UL) {
00849             lval /= 16UL;
00850             sbi(TWSR, TWPS1);
00851         } else {
00852             cbi(TWSR, TWPS1);
00853         }
00854         if (lval > 255UL) {
00855             lval /= 4UL;
00856             sbi(TWSR, TWPS0);
00857         } else {
00858             cbi(TWSR, TWPS0);
00859         }
00860         if (lval > 9UL && lval < 256UL) {
00861             outb(TWBR, (uint8_t) lval);
00862         } else {
00863             rc = -1;
00864         }
00865         break;
00866     case TWI_GETSPEED:
00867         lval = 2UL;
00868         if (bit_is_set(TWSR, TWPS0)) {
00869             lval *= 4UL;
00870         }
00871         if (bit_is_set(TWSR, TWPS1)) {
00872             lval *= 16UL;
00873         }
00874         *((uint32_t *) conf) = NutGetCpuClock() / (16UL + lval * (uint32_t) inb(TWBR));
00875         break;
00876 
00877     case TWI_GETSTATUS:
00878         break;
00879     case TWI_SETSTATUS:
00880         break;
00881 
00882     default:
00883         rc = -1;
00884         break;
00885     }
00886     return rc;
00887 #endif /* __AVR_ENHANCED__ */
00888 }
00889 
00902 int TwInit(uint8_t sla)
00903 {
00904 #ifndef __AVR_ENHANCED__
00905     return -1;
00906 #else
00907     uint32_t speed = 2400;
00908 
00909     if (NutRegisterIrqHandler(&sig_2WIRE_SERIAL, TwInterrupt, 0)) {
00910         return -1;
00911     }
00912 
00913     /*
00914      * Set address register, enable general call address, set transfer
00915      * speed and enable interface.
00916      */
00917     outb(TWAR, (sla << 1) | 1);
00918     TwIOCtl(TWI_SETSPEED, &speed);
00919     outb(TWCR, _BV(TWINT));
00920     outb(TWCR, _BV(TWEN) | _BV(TWIE));
00921 
00922     /*
00923      * Initialize mutex semaphores.
00924      */
00925     NutEventPost(&tw_mm_mutex);
00926 
00927     return 0;
00928 #endif /* __AVR_ENHANCED__ */
00929 }