forked

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers UARTSerial.cpp Source File

UARTSerial.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2006-2017 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #if (DEVICE_SERIAL && DEVICE_INTERRUPTIN)
00018 
00019 #include <errno.h>
00020 #include "UARTSerial.h"
00021 #include "platform/mbed_poll.h"
00022 #include "platform/mbed_wait_api.h"
00023 
00024 namespace mbed {
00025 
00026 UARTSerial::UARTSerial(PinName tx, PinName rx, int baud) :
00027         SerialBase(tx, rx, baud),
00028         _blocking(true),
00029         _tx_irq_enabled(false),
00030         _dcd_irq(NULL)
00031 {
00032     /* Attatch IRQ routines to the serial device. */
00033     SerialBase::attach(callback(this, &UARTSerial::rx_irq), RxIrq);
00034 }
00035 
00036 UARTSerial::~UARTSerial()
00037 {
00038     delete _dcd_irq;
00039 }
00040 
00041 void UARTSerial::dcd_irq()
00042 {
00043     wake();
00044 }
00045 
00046 void UARTSerial::set_baud(int baud)
00047 {
00048     SerialBase::baud(baud);
00049 }
00050 
00051 void UARTSerial::set_data_carrier_detect(PinName dcd_pin, bool active_high)
00052 {
00053      delete _dcd_irq;
00054     _dcd_irq = NULL;
00055 
00056     if (dcd_pin != NC) {
00057         _dcd_irq = new InterruptIn(dcd_pin);
00058         if (active_high) {
00059             _dcd_irq->fall(callback(this, &UARTSerial::dcd_irq));
00060         } else {
00061             _dcd_irq->rise(callback(this, &UARTSerial::dcd_irq));
00062         }
00063     }
00064 }
00065 
00066 int UARTSerial::close()
00067 {
00068     /* Does not let us pass a file descriptor. So how to close ?
00069      * Also, does it make sense to close a device type file descriptor*/
00070     return 0;
00071 }
00072 
00073 int UARTSerial::isatty()
00074 {
00075     return 1;
00076 
00077 }
00078 
00079 off_t UARTSerial::seek(off_t offset, int whence)
00080 {
00081     /*XXX lseek can be done theoratically, but is it sane to mark positions on a dynamically growing/shrinking
00082      * buffer system (from an interrupt context) */
00083     return -ESPIPE;
00084 }
00085 
00086 int UARTSerial::sync()
00087 {
00088     api_lock();
00089 
00090     while (!_txbuf.empty()) {
00091         api_unlock();
00092         // Doing better than wait would require TxIRQ to also do wake() when becoming empty. Worth it?
00093         wait_ms(1);
00094         api_lock();
00095     }
00096 
00097     api_unlock();
00098 
00099     return 0;
00100 }
00101 
00102 void UARTSerial::sigio(Callback<void()> func) {
00103     core_util_critical_section_enter();
00104     _sigio_cb = func;
00105     if (_sigio_cb) {
00106         short current_events = poll(0x7FFF);
00107         if (current_events) {
00108             _sigio_cb();
00109         }
00110     }
00111     core_util_critical_section_exit();
00112 }
00113 
00114 ssize_t UARTSerial::write(const void* buffer, size_t length)
00115 {
00116     size_t data_written = 0;
00117     const char *buf_ptr = static_cast<const char *>(buffer);
00118 
00119     api_lock();
00120 
00121     while (_txbuf.full()) {
00122         if (!_blocking) {
00123             api_unlock();
00124             return -EAGAIN;
00125         }
00126         api_unlock();
00127         wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
00128         api_lock();
00129     }
00130 
00131     while (data_written < length && !_txbuf.full()) {
00132         _txbuf.push(*buf_ptr++);
00133         data_written++;
00134     }
00135 
00136     core_util_critical_section_enter();
00137     if (!_tx_irq_enabled) {
00138         UARTSerial::tx_irq();                // only write to hardware in one place
00139         if (!_txbuf.empty()) {
00140             SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq);
00141             _tx_irq_enabled = true;
00142         }
00143     }
00144     core_util_critical_section_exit();
00145 
00146     api_unlock();
00147 
00148     return data_written;
00149 }
00150 
00151 ssize_t UARTSerial::read(void* buffer, size_t length)
00152 {
00153     size_t data_read = 0;
00154 
00155     char *ptr = static_cast<char *>(buffer);
00156 
00157     api_lock();
00158 
00159     while (_rxbuf.empty()) {
00160         if (!_blocking) {
00161             api_unlock();
00162             return -EAGAIN;
00163         }
00164         api_unlock();
00165         wait_ms(1);  // XXX todo - proper wait, WFE for non-rtos ?
00166         api_lock();
00167     }
00168 
00169     while (data_read < length && !_rxbuf.empty()) {
00170         _rxbuf.pop(*ptr++);
00171         data_read++;
00172     }
00173 
00174     api_unlock();
00175 
00176     return data_read;
00177 }
00178 
00179 bool UARTSerial::hup() const
00180 {
00181     return _dcd_irq && _dcd_irq->read() != 0;
00182 }
00183 
00184 void UARTSerial::wake()
00185 {
00186     if (_sigio_cb) {
00187         _sigio_cb();
00188     }
00189 }
00190 
00191 short UARTSerial::poll(short events) const {
00192 
00193     short revents = 0;
00194     /* Check the Circular Buffer if space available for writing out */
00195 
00196 
00197     if (!_rxbuf.empty()) {
00198         revents |= POLLIN;
00199     }
00200 
00201     /* POLLHUP and POLLOUT are mutually exclusive */
00202     if (hup()) {
00203         revents |= POLLHUP;
00204     } else if (!_txbuf.full()) {
00205         revents |= POLLOUT;
00206     }
00207 
00208     /*TODO Handle other event types */
00209 
00210     return revents;
00211 }
00212 
00213 void UARTSerial::lock()
00214 {
00215     // This is the override for SerialBase.
00216     // No lock required as we only use SerialBase from interrupt or from
00217     // inside our own critical section.
00218 }
00219 
00220 void UARTSerial::unlock()
00221 {
00222     // This is the override for SerialBase.
00223 }
00224 
00225 void UARTSerial::api_lock(void)
00226 {
00227     _mutex.lock();
00228 }
00229 
00230 void UARTSerial::api_unlock(void)
00231 {
00232     _mutex.unlock();
00233 }
00234 
00235 void UARTSerial::rx_irq(void)
00236 {
00237     bool was_empty = _rxbuf.empty();
00238 
00239     /* Fill in the receive buffer if the peripheral is readable
00240      * and receive buffer is not full. */
00241     while (SerialBase::readable()) {
00242         char data = SerialBase::_base_getc();
00243         if (!_rxbuf.full()) {
00244             _rxbuf.push(data);
00245         } else {
00246             /* Drop - can we report in some way? */
00247         }
00248     }
00249 
00250     /* Report the File handler that data is ready to be read from the buffer. */
00251     if (was_empty && !_rxbuf.empty()) {
00252         wake();
00253     }
00254 }
00255 
00256 // Also called from write to start transfer
00257 void UARTSerial::tx_irq(void)
00258 {
00259     bool was_full = _txbuf.full();
00260 
00261     /* Write to the peripheral if there is something to write
00262      * and if the peripheral is available to write. */
00263     while (!_txbuf.empty() && SerialBase::writeable()) {
00264         char data;
00265         _txbuf.pop(data);
00266         SerialBase::_base_putc(data);
00267     }
00268 
00269     if (_tx_irq_enabled && _txbuf.empty()) {
00270         SerialBase::attach(NULL, TxIrq);
00271         _tx_irq_enabled = false;
00272     }
00273 
00274     /* Report the File handler that data can be written to peripheral. */
00275     if (was_full && !_txbuf.full() && !hup()) {
00276         wake();
00277     }
00278 }
00279 
00280 } //namespace mbed
00281 
00282 #endif //(DEVICE_SERIAL && DEVICE_INTERRUPTIN)