Mercurial > nand-ecoscentric
view packages/io/can/current/src/can.c @ 2059:f929becf4af2
CAN driver, from Uwe Kindler
| author | gthomas |
|---|---|
| date | Thu, 25 Aug 2005 13:40:35 +0000 |
| parents | |
| children | c051df3f1083 |
line wrap: on
line source
//========================================================================== // // io/can/common/can.c // // High level CAN driver // //========================================================================== //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. // Copyright (C) 2003 Gary Thomas // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 or (at your option) any later version. // // eCos is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with eCos; if not, write to the Free Software Foundation, Inc., // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // // As a special exception, if other files instantiate templates or use macros // or inline functions from this file, or you compile this file and link it // with other works to produce a work based on this file, this file does not // by itself cause the resulting work to be covered by the GNU General Public // License. However the source code for this file must still be made available // in accordance with section (3) of the GNU General Public License. // // This exception does not invalidate any other reasons why a work based on // this file might be covered by the GNU General Public License. // // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. // at http://sources.redhat.com/ecos/ecos-license/ // ------------------------------------------- //####ECOSGPLCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): Uwe Kindler // Contributors: Uwe Kindler // Date: 2005-05-12 // Purpose: Top level CAN driver // Description: // //####DESCRIPTIONEND#### // //========================================================================== //========================================================================== // INCLUDES //========================================================================== #include <pkgconf/io.h> #include <pkgconf/io_can.h> #include <cyg/io/io.h> #include <cyg/io/devtab.h> #include <cyg/io/can.h> #include <cyg/infra/cyg_ass.h> // assertion support #include <cyg/infra/diag.h> // diagnostic output //========================================================================== // MACROS //========================================================================== #ifdef CYGOPT_IO_CAN_SUPPORT_TIMEOUTS #define CYG_DRV_COND_WAIT(_cond, _time) cyg_cond_timed_wait(_cond, cyg_current_time() + (_time)) #else #define CYG_DRV_COND_WAIT(_cond, _time) cyg_drv_cond_wait(_cond) #endif //========================================================================== // LOCAL FUNCTIONS //========================================================================== // // Device I/O functions // static Cyg_ErrNo can_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len); static Cyg_ErrNo can_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len); static cyg_bool can_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info); static Cyg_ErrNo can_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *xbuf, cyg_uint32 *len); static Cyg_ErrNo can_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *xbuf, cyg_uint32 *len); // // Callback functions into upper layer driver // static void can_init(can_channel *chan); static void can_rcv_event(can_channel *chan, void *pdata); static void can_xmt_msg(can_channel *chan, void *pdata); // // Device I/O table // DEVIO_TABLE(cyg_io_can_devio, can_write, can_read, can_select, can_get_config, can_set_config ); // // Callbacks into upper layer driver // CAN_CALLBACKS(cyg_io_can_callbacks, can_init, can_xmt_msg, can_rcv_event); //=========================================================================== // Initialize CAN driver //=========================================================================== static void can_init(can_channel *chan) { can_cbuf_t *cbuf; if (chan->init) { return; } cbuf = &chan->in_cbuf; cbuf->waiting = false; cbuf->abort = false; #ifdef CYGOPT_IO_CAN_SUPPORT_NONBLOCKING cbuf->blocking = true; #endif cyg_drv_mutex_init(&cbuf->lock); cyg_drv_cond_init(&cbuf->wait, &cbuf->lock); cbuf = &chan->out_cbuf; cbuf->waiting = false; cbuf->abort = false; #ifdef CYGOPT_IO_CAN_SUPPORT_NONBLOCKING cbuf->blocking = true; #endif cyg_drv_mutex_init(&cbuf->lock); cyg_drv_cond_init(&cbuf->wait, &cbuf->lock); chan->init = true; } //=========================================================================== // Write exactly one CAN message to CAN bus //=========================================================================== static Cyg_ErrNo can_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len) { cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; can_channel *chan = (can_channel *)t->priv; can_lowlevel_funs *funs = chan->funs; Cyg_ErrNo res = ENOERR; can_cbuf_t *cbuf = &chan->out_cbuf; cyg_uint32 size = *len; // // the user need to provide a can message buffer // if (*len != sizeof(cyg_can_message)) { return -EINVAL; } cyg_drv_mutex_lock(&cbuf->lock); cbuf->abort = false; cyg_drv_dsr_lock(); // avoid race condition while testing pointers while (size > 0) { if (cbuf->data_cnt == cbuf->len) { cbuf->waiting = true; // Buffer full - wait for space funs->start_xmit(chan); // Make sure xmit is running // // Check flag: 'start_xmit' may have obviated the need // to wait // if (cbuf->waiting) { cbuf->pending += size; // Have this much more to send [eventually] #if defined(CYGOPT_IO_CAN_SUPPORT_NONBLOCKING) #if defined(CYGOPT_IO_CAN_SUPPORT_TIMEOUTS) // // If timeouts are enabled and we use nonblocking calls then we // can use the timeout values // if (!cbuf->blocking) { if(!CYG_DRV_COND_WAIT(&cbuf->wait, cbuf->timeout)) { cbuf->abort = true; } } // if (!cbuf->blocking)# else #else // #if defined(CYGOPT_IO_CAN_SUPPORT_TIMEOUTS) // // if this is a nonblocking call then we return immediatelly // if (!cbuf->blocking) { *len = 0; res = -EAGAIN; break; } else #endif // #if defined(CYGOPT_IO_CAN_SUPPORT_TIMEOUTS) #endif //defined(CYGOPT_IO_CAN_SUPPORT_NONBLOCKING) { if(!cyg_drv_cond_wait(&cbuf->wait)) { cbuf->abort = true; } } cbuf->pending -= size; if (cbuf->abort) { // Give up! *len -= size; // number of characters actually sent cbuf->abort = false; cbuf->waiting = false; res = -EINTR; break; } // if (cbuf->abort) } // if (cbuf->waiting) } // if (cbuf->data_cnt == cbuf->len) else { // // there is enougth space left so we can store additional data // cyg_can_message *ptxbuf = (cyg_can_message *)cbuf->pdata; cyg_can_message *pbuf_message = &ptxbuf[cbuf->put]; cyg_can_message *pmessage = (cyg_can_message *)_buf; *pbuf_message = *pmessage; // copy message cbuf->put = (cbuf->put + 1) % cbuf->len; cbuf->data_cnt++; size -= sizeof(cyg_can_message); } } // while (size > 0) (funs->start_xmit)(chan); // Start output as necessary cyg_drv_dsr_unlock(); cyg_drv_mutex_unlock(&cbuf->lock); return res; } //=========================================================================== // Read one single CAN event from hw //=========================================================================== static Cyg_ErrNo can_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len) { cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; can_channel *chan = (can_channel *)t->priv; can_cbuf_t *cbuf = &chan->in_cbuf; cyg_uint32 size = 0; Cyg_ErrNo res = ENOERR; // // the user need to provide a can event buffer // if (*len != sizeof(cyg_can_event)) { return -EINVAL; } cyg_drv_mutex_lock(&cbuf->lock); cbuf->abort = false; cyg_drv_dsr_lock(); // avoid race conditions while (size < *len) { // // if message buffer contains at least one message then read the // oldest message from buffer and return // if (cbuf->data_cnt > 0) { cyg_can_event *prxbuf = (cyg_can_event *)cbuf->pdata; cyg_can_event *pbuf_event = &prxbuf[cbuf->get]; cyg_can_event *pevent = (cyg_can_event *)_buf; *pevent = *pbuf_event; // copy event cbuf->get = (cbuf->get + 1) % cbuf->len; cbuf->data_cnt--; size += sizeof(cyg_can_event); } else { // // if messaeg buffer does not contain any message, then wait until // a message arrives or return immediatelly if nonblocking calls are // supported // cbuf->waiting = true; #if defined(CYGOPT_IO_CAN_SUPPORT_NONBLOCKING) #if defined(CYGOPT_IO_CAN_SUPPORT_TIMEOUTS) // // If timeouts are enabled and we use nonblocking calls then we // can use the timeout values // if (!cbuf->blocking) { if(!CYG_DRV_COND_WAIT(&cbuf->wait, cbuf->timeout)) { cbuf->abort = true; } } // if (!cbuf->blocking)# else #else // #if defined(CYGOPT_IO_CAN_SUPPORT_TIMEOUTS) // // if this is a nonblocking call then we return immediatelly // if (!cbuf->blocking) { *len = 0; res = -EAGAIN; break; } else #endif // #if defined(CYGOPT_IO_CAN_SUPPORT_TIMEOUTS) #endif // #if defined(CYGOPT_IO_CAN_SUPPORT_NONBLOCKING) { if(!cyg_drv_cond_wait(&cbuf->wait)) { cbuf->abort = true; } } if (cbuf->abort) { *len = size; cbuf->abort = false; cbuf->waiting = false; res = -EINTR; break; } } } // while (size < *len) cyg_drv_dsr_unlock(); cyg_drv_mutex_unlock(&cbuf->lock); return res; } //=========================================================================== // Query CAN channel configuration data //=========================================================================== static Cyg_ErrNo can_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *xbuf, cyg_uint32 *len) { cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; can_channel *chan = (can_channel *)t->priv; Cyg_ErrNo res = ENOERR; cyg_can_info_t *pcan_info = (cyg_can_info_t *)xbuf; can_cbuf_t *out_cbuf = &chan->out_cbuf; can_cbuf_t *in_cbuf = &chan->in_cbuf; switch (key) { case CYG_IO_GET_CONFIG_CAN_INFO : if (*len < sizeof(cyg_can_info_t)) { return -EINVAL; } *pcan_info = chan->config; *len = sizeof(chan->config); break; // // return rx/tx buffer sizes and counts // case CYG_IO_GET_CONFIG_CAN_BUFFER_INFO : { cyg_can_buf_info_t *pbuf_info; if (*len < sizeof(cyg_can_buf_info_t)) { return -EINVAL; } *len = sizeof(cyg_can_buf_info_t); pbuf_info = (cyg_can_buf_info_t *)xbuf; pbuf_info->rx_bufsize = in_cbuf->len; if (pbuf_info->rx_bufsize) { pbuf_info->rx_count = in_cbuf->data_cnt ; } else { pbuf_info->rx_count = 0; } pbuf_info->tx_bufsize = out_cbuf->len; if (pbuf_info->tx_bufsize) { pbuf_info->tx_count = out_cbuf->data_cnt; } else { pbuf_info->tx_count = 0; } } break; // case CYG_IO_GET_CONFIG_CAN_BUFFER_INFO #ifdef CYGOPT_IO_CAN_SUPPORT_TIMEOUTS // // return current timeouts // case CYG_IO_GET_CONFIG_CAN_TIMEOUT : { cyg_can_timeout_info_t *ptimeout_info; if (*len < sizeof(cyg_can_timeout_info_t)) { return -EINVAL; } *len = sizeof(cyg_can_timeout_info_t); ptimeout_info = (cyg_can_timeout_info_t *)xbuf; ptimeout_info->rx_timeout = in_cbuf->timeout; ptimeout_info->tx_timeout = out_cbuf->timeout; } break; // case CYG_IO_GET_CONFIG_CAN_TIMEOUT_INFO #endif // CYGOPT_IO_CAN_SUPPORT_TIMEOUTS #ifdef CYGOPT_IO_CAN_SUPPORT_NONBLOCKING case CYG_IO_GET_CONFIG_READ_BLOCKING: { if (*len < sizeof(cyg_uint32)) { return -EINVAL; } *(cyg_uint32*)xbuf = (in_cbuf->blocking) ? 1 : 0; } break; case CYG_IO_GET_CONFIG_WRITE_BLOCKING: { if (*len < sizeof(cyg_uint32)) { return -EINVAL; } *(cyg_uint32*)xbuf = (out_cbuf->blocking) ? 1 : 0; } break; #endif // CYGOPT_IO_CAN_SUPPORT_NONBLOCKING default: res = -EINVAL; } // switch (key) return res; } //=========================================================================== // Set CAN channel configuration //=========================================================================== static Cyg_ErrNo can_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *xbuf, cyg_uint32 *len) { cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; can_channel *chan = (can_channel *)t->priv; Cyg_ErrNo res = ENOERR; can_lowlevel_funs *funs = chan->funs; can_cbuf_t *out_cbuf = &chan->out_cbuf; can_cbuf_t *in_cbuf = &chan->in_cbuf; switch (key) { #ifdef CYGOPT_IO_CAN_SUPPORT_NONBLOCKING case CYG_IO_SET_CONFIG_READ_BLOCKING: { if (*len < sizeof(cyg_uint32) || 0 == in_cbuf->len) { return -EINVAL; } in_cbuf->blocking = (1 == *(cyg_uint32*)xbuf) ? true : false; } break; case CYG_IO_SET_CONFIG_WRITE_BLOCKING: { if (*len < sizeof(cyg_uint32) || 0 == out_cbuf->len) { return -EINVAL; } out_cbuf->blocking = (1 == *(cyg_uint32*)xbuf) ? true : false; } break; #endif // CYGOPT_IO_CAN_SUPPORT_NONBLOCKING #ifdef CYGOPT_IO_CAN_SUPPORT_TIMEOUTS // // return current timeouts // case CYG_IO_SET_CONFIG_CAN_TIMEOUT : { cyg_can_timeout_info_t *ptimeout_info; if (*len < sizeof(cyg_can_timeout_info_t)) { return -EINVAL; } *len = sizeof(cyg_can_timeout_info_t); ptimeout_info = (cyg_can_timeout_info_t *)xbuf; in_cbuf->timeout = ptimeout_info->rx_timeout; out_cbuf->timeout = ptimeout_info->tx_timeout; } break; // case CYG_IO_GET_CONFIG_CAN_TIMEOUT_INFO #endif // CYGOPT_IO_CAN_SUPPORT_TIMEOUTS default: // // pass down to lower layers // res = (funs->set_config)(chan, key, xbuf, len); } // switch (key) return res; } //=========================================================================== // Select support for CAN channel //=========================================================================== static cyg_bool can_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info) { // // do nothing here because we currently do not support select // return true; } //=========================================================================== // Callback for received events //=========================================================================== static void can_rcv_event(can_channel *chan, void *pdata) { can_cbuf_t *cbuf = &chan->in_cbuf; cyg_can_event *prxbuf = (cyg_can_event *)cbuf->pdata; // // cbuf is a ring buffer - if the buffer is full, then we overwrite the // oldest message in buffer so the user will always get the actual and // last state of the external hardware that is connected to the // CAN bus. // if (chan->funs->getevent(chan, &prxbuf[cbuf->put], pdata)) { if (cbuf->data_cnt < cbuf->len) { cbuf->data_cnt++; } else { // // the buffer is full but a new message arrived. We store this new // message and overwrite the oldest one, but at least we tell the user // that there is an overrun in RX queue // prxbuf[cbuf->put].flags |= CYGNUM_CAN_EVENT_OVERRUN_RX; cbuf->get = (cbuf->get + 1) % cbuf->len; } cbuf->put = (cbuf->put + 1) % cbuf->len; if (cbuf->waiting) { cbuf->waiting = false; cyg_drv_cond_broadcast(&cbuf->wait); } } } //=========================================================================== // Callback function for transmit events //=========================================================================== static void can_xmt_msg(can_channel *chan, void *pdata) { can_cbuf_t *cbuf = &chan->out_cbuf; can_lowlevel_funs *funs = chan->funs; cyg_can_message *ptxbuf = (cyg_can_message *)cbuf->pdata; cyg_can_message *pbuf_txmsg; // // transmit messages as long as there are messages in the buffer // while (cbuf->data_cnt > 0) { pbuf_txmsg = &ptxbuf[cbuf->get]; if (funs->putmsg(chan, pbuf_txmsg, pdata)) { cbuf->get = (cbuf->get + 1) % cbuf->len; cbuf->data_cnt--; } else { // // we are here because the hardware is busy at the moment and // we can't send another message - now we check if there is already // some space in buffer so we can wakeup the writer // if ((cbuf->len - cbuf->data_cnt) > 0) { if (cbuf->waiting) { cbuf->waiting = false; cyg_drv_cond_broadcast(&cbuf->wait); } } return; } } // while (cbuf->data_cnt > 0) funs->stop_xmit(chan); // Done with transmit if (cbuf->waiting) { cbuf->waiting = false; cyg_drv_cond_broadcast(&cbuf->wait); } } //--------------------------------------------------------------------------- // end of can.c
