source: mainline/uspace/drv/char/ns8250/ns8250.c

Last change on this file was b9cc81c6, checked in by Jiri Svoboda <jiri@…>, 5 months ago

Implement quiesce in NS8250 and PC-LPT.

  • Property mode set to 100644
File size: 29.0 KB
RevLine 
[68414f4a]1/*
[b9cc81c6]2 * Copyright (c) 2025 Jiri Svoboda
[5fe1c32]3 * Copyright (c) 2010 Lenka Trochtova
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/**
[4122410]31 * @addtogroup ns8250
[5fe1c32]32 * @{
33 */
34
35/** @file
36 */
37
38#include <assert.h>
39#include <stdio.h>
40#include <errno.h>
[dd8ab1c]41#include <str_error.h>
[3e6a98c5]42#include <stdbool.h>
[5fe1c32]43#include <fibril_synch.h>
44#include <stdlib.h>
[c47e1a8]45#include <str.h>
[5fe1c32]46#include <ctype.h>
47#include <macros.h>
[38d150e]48#include <stdlib.h>
[5fe1c32]49#include <dirent.h>
[1f8657b]50#include <ddi.h>
[5fe1c32]51
[af6b5157]52#include <ddf/driver.h>
53#include <ddf/interrupt.h>
[fc51296]54#include <ddf/log.h>
[74017ce]55#include <io/chardev_srv.h>
[5fe1c32]56
57#include <device/hw_res.h>
[f4ef3c2]58#include <ipc/serial_ctl.h>
[5fe1c32]59
[1f8657b]60#include "cyclic_buffer.h"
61
[04c7003f]62#define NAME "ns8250"
[5fe1c32]63
[1f8657b]64#define REG_COUNT 7
[f4ef3c2]65#define MAX_BAUD_RATE 115200
[cf8cc36]66#define DLAB_MASK (1 << 7)
67
[c7235d40]68/** Interrupt Enable Register definition. */
69#define NS8250_IER_RXREADY (1 << 0)
70#define NS8250_IER_THRE (1 << 1)
71#define NS8250_IER_RXSTATUS (1 << 2)
72#define NS8250_IER_MODEM_STATUS (1 << 3)
73
74/** Interrupt ID Register definition. */
75#define NS8250_IID_ACTIVE (1 << 0)
[e0cd9042]76#define NS8250_IID_CAUSE_MASK 0x0e
77#define NS8250_IID_CAUSE_RXSTATUS 0x06
[c7235d40]78
79/** FIFO Control Register definition. */
80#define NS8250_FCR_FIFOENABLE (1 << 0)
81#define NS8250_FCR_RXFIFORESET (1 << 1)
82#define NS8250_FCR_TXFIFORESET (1 << 2)
83#define NS8250_FCR_DMAMODE (1 << 3)
84#define NS8250_FCR_RXTRIGGERLOW (1 << 6)
85#define NS8250_FCR_RXTRIGGERHI (1 << 7)
86
87/** Line Control Register definition. */
88#define NS8250_LCR_STOPBITS (1 << 2)
89#define NS8250_LCR_PARITY (1 << 3)
90#define NS8250_LCR_SENDBREAK (1 << 6)
91#define NS8250_LCR_DLAB (1 << 7)
92
93/** Modem Control Register definition. */
94#define NS8250_MCR_DTR (1 << 0)
95#define NS8250_MCR_RTS (1 << 1)
96#define NS8250_MCR_OUT1 (1 << 2)
97#define NS8250_MCR_OUT2 (1 << 3)
98#define NS8250_MCR_LOOPBACK (1 << 4)
99#define NS8250_MCR_ALL (0x1f)
100
101/** Line Status Register definition. */
102#define NS8250_LSR_RXREADY (1 << 0)
103#define NS8250_LSR_OE (1 << 1)
104#define NS8250_LSR_PE (1 << 2)
105#define NS8250_LSR_FE (1 << 3)
106#define NS8250_LSR_BREAK (1 << 4)
107#define NS8250_LSR_THRE (1 << 5)
108#define NS8250_LSR_TSE (1 << 6)
109
110/** Modem Status Register definition. */
111#define NS8250_MSR_DELTACTS (1 << 0)
112#define NS8250_MSR_DELTADSR (1 << 1)
113#define NS8250_MSR_RITRAILING (1 << 2)
114#define NS8250_MSR_DELTADCD (1 << 3)
115#define NS8250_MSR_CTS (1 << 4)
116#define NS8250_MSR_DSR (1 << 5)
117#define NS8250_MSR_RI (1 << 6)
118#define NS8250_MSR_DCD (1 << 7)
119#define NS8250_MSR_SIGNALS (NS8250_MSR_CTS | NS8250_MSR_DSR \
120 | NS8250_MSR_RI | NS8250_MSR_DCD)
121
[49698fa]122/** The number of bits of one data unit send by the serial port. */
[cf8cc36]123typedef enum {
124 WORD_LENGTH_5,
125 WORD_LENGTH_6,
126 WORD_LENGTH_7,
[49698fa]127 WORD_LENGTH_8
[cf8cc36]128} word_length_t;
129
130/** The number of stop bits used by the serial port. */
131typedef enum {
132 /** Use one stop bit. */
133 ONE_STOP_BIT,
134 /** 1.5 stop bits for word length 5, 2 stop bits otherwise. */
[49698fa]135 TWO_STOP_BITS
[cf8cc36]136} stop_bit_t;
137
[c7235d40]138/** 8250 UART registers layout. */
139typedef struct {
140 ioport8_t data; /**< Data register. */
141 ioport8_t ier; /**< Interrupt Enable Reg. */
142 ioport8_t iid; /**< Interrupt ID Reg. */
143 ioport8_t lcr; /**< Line Control Reg. */
144 ioport8_t mcr; /**< Modem Control Reg. */
145 ioport8_t lsr; /**< Line Status Reg. */
146 ioport8_t msr; /**< Modem Status Reg. */
147} ns8250_regs_t;
148
[49698fa]149/** The driver data for the serial port devices. */
[68414f4a]150typedef struct ns8250 {
151 /** DDF device node */
[83a2f43]152 ddf_dev_t *dev;
[68414f4a]153 /** DDF function node */
[83a2f43]154 ddf_fun_t *fun;
[74017ce]155 /** Character device service */
156 chardev_srvs_t cds;
[d51838f]157 /** Parent session */
158 async_sess_t *parent_sess;
[d1582b50]159 /** I/O registers */
[c7235d40]160 ns8250_regs_t *regs;
[4510e06]161 /** Are there any clients connected to the device? */
162 unsigned client_connections;
[cf8cc36]163 /** The irq assigned to this device. */
[1f8657b]164 int irq;
[3f74275]165 /** IRQ capability handle */
[eadaeae8]166 cap_irq_handle_t irq_handle;
[cf8cc36]167 /** The base i/o address of the devices registers. */
[340513c]168 uintptr_t io_addr;
[cf8cc36]169 /** The i/o port used to access the serial ports registers. */
[1f8657b]170 ioport8_t *port;
[381ff2f]171 /** The buffer for incoming data. */
[1f8657b]172 cyclic_buffer_t input_buffer;
[49698fa]173 /** The fibril mutex for synchronizing the access to the device. */
174 fibril_mutex_t mutex;
[91ecaa10]175 /** Indicates that some data has become available */
176 fibril_condvar_t input_buffer_available;
[5b68e0c]177 /** True if device is removed. */
178 bool removed;
[68414f4a]179} ns8250_t;
[5fe1c32]180
[56fd7cf]181/** Obtain soft-state structure from device node */
182static ns8250_t *dev_ns8250(ddf_dev_t *dev)
183{
184 return ddf_dev_data_get(dev);
185}
186
187/** Obtain soft-state structure from function node */
188static ns8250_t *fun_ns8250(ddf_fun_t *fun)
189{
190 return dev_ns8250(ddf_fun_get_dev(fun));
191}
192
[74017ce]193/** Obtain soft-state structure from chardev srv */
194static ns8250_t *srv_ns8250(chardev_srv_t *srv)
195{
196 return (ns8250_t *)srv->srvs->sarg;
197}
198
[381ff2f]199/** Find out if there is some incoming data available on the serial port.
[49698fa]200 *
201 * @param port The base address of the serial port device's ports.
202 * @return True if there are data waiting to be read, false
203 * otherwise.
[cf8cc36]204 */
[c7235d40]205static bool ns8250_received(ns8250_regs_t *regs)
[ca97cad]206{
[c7235d40]207 return (pio_read_8(&regs->lsr) & NS8250_LSR_RXREADY) != 0;
[ca97cad]208}
209
[cf8cc36]210/** Read one byte from the serial port.
[49698fa]211 *
212 * @param port The base address of the serial port device's ports.
213 * @return The data read.
[cf8cc36]214 */
[c7235d40]215static uint8_t ns8250_read_8(ns8250_regs_t *regs)
[ca97cad]216{
[c7235d40]217 return pio_read_8(&regs->data);
[ca97cad]218}
219
[cf8cc36]220/** Find out wheter it is possible to send data.
[49698fa]221 *
222 * @param port The base address of the serial port device's ports.
[cf8cc36]223 */
[c7235d40]224static bool is_transmit_empty(ns8250_regs_t *regs)
[ca97cad]225{
[c7235d40]226 return (pio_read_8(&regs->lsr) & NS8250_LSR_THRE) != 0;
[ca97cad]227}
228
[cf8cc36]229/** Write one character on the serial port.
[49698fa]230 *
231 * @param port The base address of the serial port device's ports.
232 * @param c The character to be written to the serial port device.
[cf8cc36]233 */
[c7235d40]234static void ns8250_write_8(ns8250_regs_t *regs, uint8_t c)
[ca97cad]235{
[c7235d40]236 while (!is_transmit_empty(regs))
[ca97cad]237 ;
[a35b458]238
[c7235d40]239 pio_write_8(&regs->data, c);
[ca97cad]240}
241
[cf8cc36]242/** Read data from the serial port device.
[49698fa]243 *
[74017ce]244 * @param srv Server-side connection data
[956d4281]245 * @param buf The output buffer for read data.
[49698fa]246 * @param count The number of bytes to be read.
[74017ce]247 * @param nread Place to store number of bytes actually read
[f2d88f3]248 * @param flags @c chardev_f_nonblock not to block waiting for data
249 * even if no data is available
[49698fa]250 *
[74017ce]251 * @return EOK on success or non-zero error code
[cf8cc36]252 */
[f2d88f3]253static errno_t ns8250_read(chardev_srv_t *srv, void *buf, size_t count, size_t *nread,
254 chardev_flags_t flags)
[f658458]255{
[74017ce]256 ns8250_t *ns = srv_ns8250(srv);
257 char *bp = (char *) buf;
258 size_t pos = 0;
[a35b458]259
[74017ce]260 if (count == 0) {
261 *nread = 0;
262 return EOK;
263 }
[a35b458]264
[68414f4a]265 fibril_mutex_lock(&ns->mutex);
[f2d88f3]266 while ((flags & chardev_f_none) == 0 &&
267 buf_is_empty(&ns->input_buffer))
[91ecaa10]268 fibril_condvar_wait(&ns->input_buffer_available, &ns->mutex);
[74017ce]269 while (!buf_is_empty(&ns->input_buffer) && pos < count) {
270 bp[pos] = (char)buf_pop_front(&ns->input_buffer);
271 pos++;
[bb864a0]272 }
[68414f4a]273 fibril_mutex_unlock(&ns->mutex);
[a35b458]274
[74017ce]275 *nread = pos;
276 return EOK;
[f658458]277}
278
[cf8cc36]279/** Write a character to the serial port.
[49698fa]280 *
[68414f4a]281 * @param ns Serial port device
282 * @param c The character to be written
[cf8cc36]283 */
[68414f4a]284static inline void ns8250_putchar(ns8250_t *ns, uint8_t c)
[49698fa]285{
[68414f4a]286 fibril_mutex_lock(&ns->mutex);
[c7235d40]287 ns8250_write_8(ns->regs, c);
[68414f4a]288 fibril_mutex_unlock(&ns->mutex);
[ca97cad]289}
290
[cf8cc36]291/** Write data to the serial port.
[49698fa]292 *
[74017ce]293 * @param srv Server-side connection data
[68414f4a]294 * @param buf The data to be written
295 * @param count The number of bytes to be written
[74017ce]296 * @param nwritten Place to store number of bytes successfully written
297 * @return EOK on success or non-zero error code
[cf8cc36]298 */
[b7fd2a0]299static errno_t ns8250_write(chardev_srv_t *srv, const void *buf, size_t count,
[74017ce]300 size_t *nwritten)
[f658458]301{
[74017ce]302 ns8250_t *ns = srv_ns8250(srv);
[ca97cad]303 size_t idx;
[74017ce]304 uint8_t *bp = (uint8_t *) buf;
[a35b458]305
[49698fa]306 for (idx = 0; idx < count; idx++)
[74017ce]307 ns8250_putchar(ns, bp[idx]);
[a35b458]308
[74017ce]309 *nwritten = count;
310 return EOK;
[f658458]311}
312
[b7fd2a0]313static errno_t ns8250_open(chardev_srvs_t *, chardev_srv_t *);
314static errno_t ns8250_close(chardev_srv_t *);
[984a9ba]315static void ns8250_default_handler(chardev_srv_t *, ipc_call_t *);
[5fe1c32]316
[49698fa]317/** The character interface's callbacks. */
[74017ce]318static chardev_ops_t ns8250_chardev_ops = {
319 .open = ns8250_open,
320 .close = ns8250_close,
321 .read = ns8250_read,
322 .write = ns8250_write,
323 .def_handler = ns8250_default_handler
[f658458]324};
325
[984a9ba]326static void ns8250_char_conn(ipc_call_t *, void *);
[74017ce]327
[b7fd2a0]328static errno_t ns8250_dev_add(ddf_dev_t *dev);
[b9cc81c6]329static errno_t ns8250_dev_quiesce(ddf_dev_t *dev);
[b7fd2a0]330static errno_t ns8250_dev_remove(ddf_dev_t *dev);
[5fe1c32]331
[49698fa]332/** The serial port device driver's standard operations. */
[04c7003f]333static driver_ops_t ns8250_ops = {
[0c0f823b]334 .dev_add = &ns8250_dev_add,
[b9cc81c6]335 .dev_remove = &ns8250_dev_remove,
336 .dev_quiesce = &ns8250_dev_quiesce
[5fe1c32]337};
338
[49698fa]339/** The serial port device driver structure. */
[04c7003f]340static driver_t ns8250_driver = {
[5fe1c32]341 .name = NAME,
[04c7003f]342 .driver_ops = &ns8250_ops
[5fe1c32]343};
344
[68414f4a]345/** Clean up the serial port soft-state
[49698fa]346 *
[68414f4a]347 * @param ns Serial port device
[cf8cc36]348 */
[68414f4a]349static void ns8250_dev_cleanup(ns8250_t *ns)
[1f8657b]350{
351}
352
[cf8cc36]353/** Enable the i/o ports of the device.
[49698fa]354 *
[68414f4a]355 * @param ns Serial port device
356 * @return True on success, false otherwise
[cf8cc36]357 */
[68414f4a]358static bool ns8250_pio_enable(ns8250_t *ns)
[1f8657b]359{
[56fd7cf]360 ddf_msg(LVL_DEBUG, "ns8250_pio_enable %s", ddf_dev_get_name(ns->dev));
[a35b458]361
[49698fa]362 /* Gain control over port's registers. */
[340513c]363 if (pio_enable((void *) ns->io_addr, REG_COUNT,
[68414f4a]364 (void **) &ns->port)) {
[340513c]365 ddf_msg(LVL_ERROR, "Cannot map the port %#" PRIxn
[56fd7cf]366 " for device %s.", ns->io_addr, ddf_dev_get_name(ns->dev));
[1f8657b]367 return false;
368 }
[c7235d40]369
370 ns->regs = (ns8250_regs_t *)ns->port;
[a35b458]371
[1f8657b]372 return true;
373}
374
[cf8cc36]375/** Probe the serial port device for its presence.
[49698fa]376 *
[68414f4a]377 * @param ns Serial port device
378 * @return True if the device is present, false otherwise
[cf8cc36]379 */
[68414f4a]380static bool ns8250_dev_probe(ns8250_t *ns)
[1f8657b]381{
[56fd7cf]382 ddf_msg(LVL_DEBUG, "ns8250_dev_probe %s", ddf_dev_get_name(ns->dev));
[a35b458]383
[1f8657b]384 bool res = true;
385 uint8_t olddata;
[a35b458]386
[c7235d40]387 olddata = pio_read_8(&ns->regs->mcr);
[a35b458]388
[c7235d40]389 pio_write_8(&ns->regs->mcr, NS8250_MCR_LOOPBACK);
390 if (pio_read_8(&ns->regs->msr) & NS8250_MSR_SIGNALS)
[1f8657b]391 res = false;
[a35b458]392
[c7235d40]393 pio_write_8(&ns->regs->mcr, NS8250_MCR_ALL);
[3bacee1]394 if ((pio_read_8(&ns->regs->msr) & NS8250_MSR_SIGNALS) !=
395 NS8250_MSR_SIGNALS)
[1f8657b]396 res = false;
[a35b458]397
[c7235d40]398 pio_write_8(&ns->regs->mcr, olddata);
[a35b458]399
[fc51296]400 if (!res) {
[ebcb05a]401 ddf_msg(LVL_DEBUG, "Device %s is not present.",
[56fd7cf]402 ddf_dev_get_name(ns->dev));
[fc51296]403 }
[a35b458]404
[49698fa]405 return res;
[1f8657b]406}
407
[cf8cc36]408/** Initialize serial port device.
[49698fa]409 *
[68414f4a]410 * @param ns Serial port device
[cde999a]411 * @return Zero on success, error number otherwise
[cf8cc36]412 */
[b7fd2a0]413static errno_t ns8250_dev_initialize(ns8250_t *ns)
[1f8657b]414{
[b7fd2a0]415 errno_t ret = EOK;
[a35b458]416
[56fd7cf]417 ddf_msg(LVL_DEBUG, "ns8250_dev_initialize %s", ddf_dev_get_name(ns->dev));
[a35b458]418
[1f8657b]419 hw_resource_list_t hw_resources;
420 memset(&hw_resources, 0, sizeof(hw_resource_list_t));
[a35b458]421
[49698fa]422 /* Get hw resources. */
[d51838f]423 ret = hw_res_get_resource_list(ns->parent_sess, &hw_resources);
[be942bc]424 if (ret != EOK) {
[fc51296]425 ddf_msg(LVL_ERROR, "Failed to get HW resources for device "
[56fd7cf]426 "%s.", ddf_dev_get_name(ns->dev));
[1f8657b]427 goto failed;
[49698fa]428 }
[a35b458]429
[1f8657b]430 size_t i;
431 hw_resource_t *res;
432 bool irq = false;
433 bool ioport = false;
[a35b458]434
[1f8657b]435 for (i = 0; i < hw_resources.count; i++) {
436 res = &hw_resources.resources[i];
437 switch (res->type) {
438 case INTERRUPT:
[68414f4a]439 ns->irq = res->res.interrupt.irq;
[1f8657b]440 irq = true;
[3a67d63]441 ddf_msg(LVL_NOTE, "Device %s was assigned irq = 0x%x.",
[56fd7cf]442 ddf_dev_get_name(ns->dev), ns->irq);
[1f8657b]443 break;
[a35b458]444
[1f8657b]445 case IO_RANGE:
[68414f4a]446 ns->io_addr = res->res.io_range.address;
[1f8657b]447 if (res->res.io_range.size < REG_COUNT) {
[fc51296]448 ddf_msg(LVL_ERROR, "I/O range assigned to "
[56fd7cf]449 "device %s is too small.", ddf_dev_get_name(ns->dev));
[be942bc]450 ret = ELIMIT;
[1f8657b]451 goto failed;
452 }
453 ioport = true;
[3a67d63]454 ddf_msg(LVL_NOTE, "Device %s was assigned I/O address = "
[340513c]455 "0x%#" PRIxn ".", ddf_dev_get_name(ns->dev), ns->io_addr);
[3bacee1]456 break;
[a35b458]457
[dafe675]458 default:
459 break;
[1f8657b]460 }
461 }
[a35b458]462
[1f8657b]463 if (!irq || !ioport) {
[ebcb05a]464 ddf_msg(LVL_ERROR, "Missing HW resource(s) for device %s.",
[56fd7cf]465 ddf_dev_get_name(ns->dev));
[be942bc]466 ret = ENOENT;
[1f8657b]467 goto failed;
[49698fa]468 }
[a35b458]469
[f724e82]470 hw_res_clean_resource_list(&hw_resources);
[df747b9c]471 return ret;
[a35b458]472
[1f8657b]473failed:
[68414f4a]474 ns8250_dev_cleanup(ns);
[f724e82]475 hw_res_clean_resource_list(&hw_resources);
[49698fa]476 return ret;
[1f8657b]477}
478
[cf8cc36]479/** Enable interrupts on the serial port device.
[49698fa]480 *
[68414f4a]481 * Interrupt when data is received
[49698fa]482 *
483 * @param port The base address of the serial port device's ports.
[cf8cc36]484 */
[c7235d40]485static inline void ns8250_port_interrupts_enable(ns8250_regs_t *regs)
[bab6388]486{
[c7235d40]487 /* Interrupt when data received. */
[e0cd9042]488 pio_write_8(&regs->ier, NS8250_IER_RXREADY | NS8250_IER_RXSTATUS);
[3bacee1]489 pio_write_8(&regs->mcr, NS8250_MCR_DTR | NS8250_MCR_RTS |
490 NS8250_MCR_OUT2);
[f4ef3c2]491}
492
[cf8cc36]493/** Disable interrupts on the serial port device.
[49698fa]494 *
[68414f4a]495 * @param port The base address of the serial port device's ports
[cf8cc36]496 */
[c7235d40]497static inline void ns8250_port_interrupts_disable(ns8250_regs_t *regs)
[f4ef3c2]498{
[c7235d40]499 pio_write_8(&regs->ier, 0x0); /* Disable all interrupts. */
[f4ef3c2]500}
501
[cf8cc36]502/** Enable interrupts for the serial port device.
[49698fa]503 *
[68414f4a]504 * @param ns Serial port device
[cde999a]505 * @return Zero on success, error number otherwise
[cf8cc36]506 */
[b7fd2a0]507static errno_t ns8250_interrupt_enable(ns8250_t *ns)
[cfe7716]508{
[ebc9c2c]509 /* Enable interrupt using IRC service. */
[b7fd2a0]510 errno_t rc = hw_res_enable_interrupt(ns->parent_sess, ns->irq);
[ebc9c2c]511 if (rc != EOK)
[128c78b]512 return EIO;
[a35b458]513
[e0cd9042]514 /* Read LSR to clear possible previous LSR interrupt */
515 pio_read_8(&ns->regs->lsr);
[a35b458]516
[49698fa]517 /* Enable interrupt on the serial port. */
[c7235d40]518 ns8250_port_interrupts_enable(ns->regs);
[a35b458]519
[cfe7716]520 return EOK;
521}
522
[cf8cc36]523/** Set Divisor Latch Access Bit.
[49698fa]524 *
525 * When the Divisor Latch Access Bit is set, it is possible to set baud rate of
526 * the serial port device.
527 *
528 * @param port The base address of the serial port device's ports.
[cf8cc36]529 */
[c7235d40]530static inline void enable_dlab(ns8250_regs_t *regs)
[cf8cc36]531{
[c7235d40]532 uint8_t val = pio_read_8(&regs->lcr);
533 pio_write_8(&regs->lcr, val | NS8250_LCR_DLAB);
[cf8cc36]534}
535
536/** Clear Divisor Latch Access Bit.
[49698fa]537 *
538 * @param port The base address of the serial port device's ports.
[cf8cc36]539 */
[c7235d40]540static inline void clear_dlab(ns8250_regs_t *regs)
[cf8cc36]541{
[c7235d40]542 uint8_t val = pio_read_8(&regs->lcr);
543 pio_write_8(&regs->lcr, val & (~NS8250_LCR_DLAB));
[cf8cc36]544}
545
546/** Set baud rate of the serial communication on the serial device.
[49698fa]547 *
548 * @param port The base address of the serial port device's ports.
549 * @param baud_rate The baud rate to be used by the device.
[cde999a]550 * @return Zero on success, error number otherwise (EINVAL
[49698fa]551 * if the specified baud_rate is not valid).
[cf8cc36]552 */
[b7fd2a0]553static errno_t ns8250_port_set_baud_rate(ns8250_regs_t *regs, unsigned int baud_rate)
[f4ef3c2]554{
555 uint16_t divisor;
556 uint8_t div_low, div_high;
[a35b458]557
[33dbbd2]558 if (baud_rate < 50 || MAX_BAUD_RATE % baud_rate != 0) {
[ebcb05a]559 ddf_msg(LVL_ERROR, "Invalid baud rate %d requested.",
[fc51296]560 baud_rate);
[49698fa]561 return EINVAL;
[f4ef3c2]562 }
[a35b458]563
[f4ef3c2]564 divisor = MAX_BAUD_RATE / baud_rate;
565 div_low = (uint8_t)divisor;
[49698fa]566 div_high = (uint8_t)(divisor >> 8);
[a35b458]567
[49698fa]568 /* Enable DLAB to be able to access baud rate divisor. */
[c7235d40]569 enable_dlab(regs);
[a35b458]570
[49698fa]571 /* Set divisor low byte. */
[c7235d40]572 pio_write_8(&regs->data, div_low);
[49698fa]573 /* Set divisor high byte. */
[c7235d40]574 pio_write_8(&regs->ier, div_high);
[a35b458]575
[c7235d40]576 clear_dlab(regs);
[a35b458]577
[49698fa]578 return EOK;
[f4ef3c2]579}
580
[cf8cc36]581/** Get baud rate used by the serial port device.
[49698fa]582 *
583 * @param port The base address of the serial port device's ports.
584 * @param baud_rate The ouput parameter to which the baud rate is stored.
[cf8cc36]585 */
[c7235d40]586static unsigned int ns8250_port_get_baud_rate(ns8250_regs_t *regs)
[f4ef3c2]587{
[cf8cc36]588 uint16_t divisor;
589 uint8_t div_low, div_high;
[a35b458]590
[49698fa]591 /* Enable DLAB to be able to access baud rate divisor. */
[c7235d40]592 enable_dlab(regs);
[a35b458]593
[49698fa]594 /* Get divisor low byte. */
[c7235d40]595 div_low = pio_read_8(&regs->data);
[49698fa]596 /* Get divisor high byte. */
[c7235d40]597 div_high = pio_read_8(&regs->ier);
[a35b458]598
[c7235d40]599 clear_dlab(regs);
[a35b458]600
[cf8cc36]601 divisor = (div_high << 8) | div_low;
602 return MAX_BAUD_RATE / divisor;
603}
604
[49698fa]605/** Get the parameters of the serial communication set on the serial port
606 * device.
607 *
608 * @param parity The parity used.
609 * @param word_length The length of one data unit in bits.
610 * @param stop_bits The number of stop bits used (one or two).
[cf8cc36]611 */
[c7235d40]612static void ns8250_port_get_com_props(ns8250_regs_t *regs, unsigned int *parity,
[49698fa]613 unsigned int *word_length, unsigned int *stop_bits)
[cf8cc36]614{
615 uint8_t val;
[a35b458]616
[c7235d40]617 val = pio_read_8(&regs->lcr);
618 *parity = ((val >> NS8250_LCR_PARITY) & 7);
[a35b458]619
[56fd7cf]620 /* Silence warnings */
621 *word_length = 0;
622
[cf8cc36]623 switch (val & 3) {
[49698fa]624 case WORD_LENGTH_5:
625 *word_length = 5;
626 break;
627 case WORD_LENGTH_6:
628 *word_length = 6;
629 break;
630 case WORD_LENGTH_7:
631 *word_length = 7;
632 break;
633 case WORD_LENGTH_8:
634 *word_length = 8;
635 break;
[cf8cc36]636 }
[a35b458]637
[c7235d40]638 if ((val >> NS8250_LCR_STOPBITS) & 1)
[cf8cc36]639 *stop_bits = 2;
[49698fa]640 else
[cf8cc36]641 *stop_bits = 1;
[f4ef3c2]642}
643
[cf8cc36]644/** Set the parameters of the serial communication on the serial port device.
[49698fa]645 *
646 * @param parity The parity to be used.
647 * @param word_length The length of one data unit in bits.
648 * @param stop_bits The number of stop bits used (one or two).
649 * @return Zero on success, EINVAL if some of the specified values
650 * is invalid.
[cf8cc36]651 */
[b7fd2a0]652static errno_t ns8250_port_set_com_props(ns8250_regs_t *regs, unsigned int parity,
[49698fa]653 unsigned int word_length, unsigned int stop_bits)
[cf8cc36]654{
655 uint8_t val;
[a35b458]656
[cf8cc36]657 switch (word_length) {
[49698fa]658 case 5:
659 val = WORD_LENGTH_5;
660 break;
661 case 6:
662 val = WORD_LENGTH_6;
663 break;
664 case 7:
665 val = WORD_LENGTH_7;
666 break;
667 case 8:
668 val = WORD_LENGTH_8;
669 break;
670 default:
671 return EINVAL;
[cf8cc36]672 }
[a35b458]673
[cf8cc36]674 switch (stop_bits) {
[49698fa]675 case 1:
[c7235d40]676 val |= ONE_STOP_BIT << NS8250_LCR_STOPBITS;
[49698fa]677 break;
678 case 2:
[c7235d40]679 val |= TWO_STOP_BITS << NS8250_LCR_STOPBITS;
[49698fa]680 break;
681 default:
682 return EINVAL;
[cf8cc36]683 }
[a35b458]684
[cf8cc36]685 switch (parity) {
[49698fa]686 case SERIAL_NO_PARITY:
687 case SERIAL_ODD_PARITY:
688 case SERIAL_EVEN_PARITY:
689 case SERIAL_MARK_PARITY:
690 case SERIAL_SPACE_PARITY:
[c7235d40]691 val |= parity << NS8250_LCR_PARITY;
[49698fa]692 break;
693 default:
694 return EINVAL;
[cf8cc36]695 }
[a35b458]696
[c7235d40]697 pio_write_8(&regs->lcr, val);
[a35b458]698
[cf8cc36]699 return EOK;
700}
701
702/** Initialize the serial port device.
[49698fa]703 *
[cf8cc36]704 * Set the default parameters of the serial communication.
[49698fa]705 *
[68414f4a]706 * @param ns Serial port device
[cf8cc36]707 */
[68414f4a]708static void ns8250_initialize_port(ns8250_t *ns)
[cfe7716]709{
[49698fa]710 /* Disable interrupts. */
[c7235d40]711 ns8250_port_interrupts_disable(ns->regs);
[49698fa]712 /* Set baud rate. */
[c7235d40]713 ns8250_port_set_baud_rate(ns->regs, 38400);
[49698fa]714 /* 8 bits, no parity, two stop bits. */
[c7235d40]715 ns8250_port_set_com_props(ns->regs, SERIAL_NO_PARITY, 8, 2);
[188c76c]716 /*
717 * Enable FIFO, clear them, with 4-byte threshold for greater
718 * reliability.
719 */
[3bacee1]720 pio_write_8(&ns->regs->iid, NS8250_FCR_FIFOENABLE |
721 NS8250_FCR_RXFIFORESET | NS8250_FCR_TXFIFORESET |
722 NS8250_FCR_RXTRIGGERLOW);
[49698fa]723 /*
724 * RTS/DSR set (Request to Send and Data Terminal Ready lines enabled),
725 * Aux Output2 set - needed for interrupts.
726 */
[3bacee1]727 pio_write_8(&ns->regs->mcr, NS8250_MCR_DTR | NS8250_MCR_RTS |
728 NS8250_MCR_OUT2);
[49698fa]729}
730
[5b68e0c]731/** Deinitialize the serial port device.
732 *
733 * @param ns Serial port device
734 */
735static void ns8250_port_cleanup(ns8250_t *ns)
736{
737 /* Disable FIFO */
[c7235d40]738 pio_write_8(&ns->regs->iid, 0x00);
[5b68e0c]739 /* Disable DTR, RTS, OUT1, OUT2 (int. enable) */
[c7235d40]740 pio_write_8(&ns->regs->mcr, 0x00);
[5b68e0c]741 /* Disable all interrupts from the port */
[c7235d40]742 ns8250_port_interrupts_disable(ns->regs);
[5b68e0c]743}
744
[49698fa]745/** Read the data from the serial port device and store them to the input
746 * buffer.
747 *
[68414f4a]748 * @param ns Serial port device
[cf8cc36]749 */
[68414f4a]750static void ns8250_read_from_device(ns8250_t *ns)
[7f8b581]751{
[c7235d40]752 ns8250_regs_t *regs = ns->regs;
[dafe675]753 bool cont = true;
[a35b458]754
[c92e30f]755 fibril_mutex_lock(&ns->mutex);
[49698fa]756 while (cont) {
[c7235d40]757 cont = ns8250_received(regs);
[cf8cc36]758 if (cont) {
[c7235d40]759 uint8_t val = ns8250_read_8(regs);
[a35b458]760
[4510e06]761 if (ns->client_connections > 0) {
[91ecaa10]762 bool buf_was_empty = buf_is_empty(&ns->input_buffer);
[68414f4a]763 if (!buf_push_back(&ns->input_buffer, val)) {
[fc51296]764 ddf_msg(LVL_WARN, "Buffer overflow on "
[56fd7cf]765 "%s.", ddf_dev_get_name(ns->dev));
[c92e30f]766 break;
[dafe675]767 } else {
[fc51296]768 ddf_msg(LVL_DEBUG2, "Character %c saved "
[ebcb05a]769 "to the buffer of %s.",
[56fd7cf]770 val, ddf_dev_get_name(ns->dev));
[91ecaa10]771 if (buf_was_empty)
772 fibril_condvar_broadcast(&ns->input_buffer_available);
[dafe675]773 }
[49698fa]774 }
[dafe675]775 }
[49698fa]776 }
[c92e30f]777 fibril_mutex_unlock(&ns->mutex);
778 fibril_yield();
[2300b9d]779}
780
[cf8cc36]781/** The interrupt handler.
[49698fa]782 *
[e0cd9042]783 * The serial port is initialized to interrupt when some data come or line
784 * status register changes, so the interrupt is handled by reading the incoming
785 * data and reading the line status register.
[49698fa]786 *
[60744cb]787 * @pram icall IRQ event notificatoin
788 * @param arg Argument (ns8250_t *)
[cf8cc36]789 */
[60744cb]790static inline void ns8250_interrupt_handler(ipc_call_t *icall, void *arg)
[2300b9d]791{
[60744cb]792 ns8250_t *ns = (ns8250_t *)arg;
[e0cd9042]793 uint8_t iir = pio_read_8(&ns->regs->iid);
794 if ((iir & NS8250_IID_CAUSE_MASK) == NS8250_IID_CAUSE_RXSTATUS) {
795 uint8_t lsr = pio_read_8(&ns->regs->lsr);
796 if (lsr & NS8250_LSR_OE) {
[56fd7cf]797 ddf_msg(LVL_WARN, "Overrun error on %s", ddf_dev_get_name(ns->dev));
[e0cd9042]798 }
799 }
[a35b458]800
[e0cd9042]801 ns8250_read_from_device(ns);
[d51838f]802 hw_res_clear_interrupt(ns->parent_sess, ns->irq);
[7f8b581]803}
804
[cf8cc36]805/** Register the interrupt handler for the device.
[49698fa]806 *
[68414f4a]807 * @param ns Serial port device
[cf8cc36]808 */
[b7fd2a0]809static inline errno_t ns8250_register_interrupt_handler(ns8250_t *ns,
[eadaeae8]810 cap_irq_handle_t *ihandle)
[7f8b581]811{
[68414f4a]812 return register_interrupt_handler(ns->dev, ns->irq,
[60744cb]813 ns8250_interrupt_handler, (void *)ns, NULL, ihandle);
[2300b9d]814}
815
[cf8cc36]816/** Unregister the interrupt handler for the device.
[49698fa]817 *
[68414f4a]818 * @param ns Serial port device
[cf8cc36]819 */
[b7fd2a0]820static inline errno_t ns8250_unregister_interrupt_handler(ns8250_t *ns)
[2300b9d]821{
[eadaeae8]822 return unregister_interrupt_handler(ns->dev, ns->irq_handle);
[7f8b581]823}
824
[0c0f823b]825/** The dev_add callback method of the serial port driver.
[49698fa]826 *
[cf8cc36]827 * Probe and initialize the newly added device.
[49698fa]828 *
829 * @param dev The serial port device.
[cf8cc36]830 */
[b7fd2a0]831static errno_t ns8250_dev_add(ddf_dev_t *dev)
[5fe1c32]832{
[68414f4a]833 ns8250_t *ns = NULL;
[83a2f43]834 ddf_fun_t *fun = NULL;
[68414f4a]835 bool need_cleanup = false;
[dad0d2f]836 bool need_unreg_intr_handler = false;
[4f87a85a]837 bool bound = false;
[b7fd2a0]838 errno_t rc;
[a35b458]839
[0c0f823b]840 ddf_msg(LVL_DEBUG, "ns8250_dev_add %s (handle = %d)",
[56fd7cf]841 ddf_dev_get_name(dev), (int) ddf_dev_get_handle(dev));
[a35b458]842
[68414f4a]843 /* Allocate soft-state for the device */
[5f6e25e]844 ns = ddf_dev_data_alloc(dev, sizeof(ns8250_t));
[68414f4a]845 if (ns == NULL) {
846 rc = ENOMEM;
847 goto fail;
848 }
[a35b458]849
[5f6e25e]850 fibril_mutex_initialize(&ns->mutex);
[91ecaa10]851 fibril_condvar_initialize(&ns->input_buffer_available);
[68414f4a]852 ns->dev = dev;
[a35b458]853
[d51838f]854 ns->parent_sess = ddf_dev_parent_sess_get(ns->dev);
855 if (ns->parent_sess == NULL) {
856 ddf_msg(LVL_ERROR, "Failed to connect to parent driver of "
857 "device %s.", ddf_dev_get_name(ns->dev));
858 rc = EIO;
859 goto fail;
860 }
[a35b458]861
[68414f4a]862 rc = ns8250_dev_initialize(ns);
863 if (rc != EOK)
864 goto fail;
[a35b458]865
[68414f4a]866 need_cleanup = true;
[a35b458]867
[68414f4a]868 if (!ns8250_pio_enable(ns)) {
869 rc = EADDRNOTAVAIL;
870 goto fail;
[49698fa]871 }
[a35b458]872
[49698fa]873 /* Find out whether the device is present. */
[68414f4a]874 if (!ns8250_dev_probe(ns)) {
875 rc = ENOENT;
876 goto fail;
[49698fa]877 }
[a35b458]878
[49698fa]879 /* Serial port initialization (baud rate etc.). */
[68414f4a]880 ns8250_initialize_port(ns);
[a35b458]881
[49698fa]882 /* Register interrupt handler. */
[eadaeae8]883 ns->irq_handle = CAP_NIL;
884 rc = ns8250_register_interrupt_handler(ns, &ns->irq_handle);
[071a1ddb]885 if (rc != EOK) {
[ebcb05a]886 ddf_msg(LVL_ERROR, "Failed to register interrupt handler.");
[68414f4a]887 rc = EADDRNOTAVAIL;
888 goto fail;
[7f8b581]889 }
[dad0d2f]890 need_unreg_intr_handler = true;
[c4e30607]891
[49698fa]892 /* Enable interrupt. */
[68414f4a]893 rc = ns8250_interrupt_enable(ns);
894 if (rc != EOK) {
[fc51296]895 ddf_msg(LVL_ERROR, "Failed to enable the interrupt. Error code = "
[dd8ab1c]896 "%s.", str_error_name(rc));
[68414f4a]897 goto fail;
[49698fa]898 }
[a35b458]899
[97a62fe]900 fun = ddf_fun_create(dev, fun_exposed, "a");
901 if (fun == NULL) {
[ebcb05a]902 ddf_msg(LVL_ERROR, "Failed creating function.");
[97a62fe]903 goto fail;
904 }
[a35b458]905
[74017ce]906 ddf_fun_set_conn_handler(fun, ns8250_char_conn);
[a35b458]907
[74017ce]908 chardev_srvs_init(&ns->cds);
909 ns->cds.ops = &ns8250_chardev_ops;
910 ns->cds.sarg = ns;
[a35b458]911
[97a62fe]912 rc = ddf_fun_bind(fun);
913 if (rc != EOK) {
[ebcb05a]914 ddf_msg(LVL_ERROR, "Failed binding function.");
[97a62fe]915 goto fail;
916 }
917
[4f87a85a]918 bound = true;
[68414f4a]919 ns->fun = fun;
[a35b458]920
[4f87a85a]921 rc = ddf_fun_add_to_category(fun, "serial");
922 if (rc != EOK) {
923 ddf_msg(LVL_ERROR, "Error adding function to category 'serial'.");
924 goto fail;
925 }
[a35b458]926
[ebcb05a]927 ddf_msg(LVL_NOTE, "Device %s successfully initialized.",
[56fd7cf]928 ddf_dev_get_name(dev));
[a35b458]929
[df747b9c]930 return EOK;
[68414f4a]931fail:
[4f87a85a]932 if (bound)
933 ddf_fun_unbind(fun);
[97a62fe]934 if (fun != NULL)
935 ddf_fun_destroy(fun);
[dad0d2f]936 if (need_unreg_intr_handler)
937 ns8250_unregister_interrupt_handler(ns);
[68414f4a]938 if (need_cleanup)
939 ns8250_dev_cleanup(ns);
940 return rc;
[5fe1c32]941}
942
[b7fd2a0]943static errno_t ns8250_dev_remove(ddf_dev_t *dev)
[5b68e0c]944{
[56fd7cf]945 ns8250_t *ns = dev_ns8250(dev);
[b7fd2a0]946 errno_t rc;
[a35b458]947
[5b68e0c]948 fibril_mutex_lock(&ns->mutex);
[956d4281]949 if (ns->client_connections > 0) {
[5b68e0c]950 fibril_mutex_unlock(&ns->mutex);
951 return EBUSY;
952 }
953 ns->removed = true;
954 fibril_mutex_unlock(&ns->mutex);
[a35b458]955
[5b68e0c]956 rc = ddf_fun_unbind(ns->fun);
957 if (rc != EOK) {
958 ddf_msg(LVL_ERROR, "Failed to unbind function.");
959 return rc;
960 }
[a35b458]961
[5b68e0c]962 ddf_fun_destroy(ns->fun);
[a35b458]963
[5b68e0c]964 ns8250_port_cleanup(ns);
965 ns8250_unregister_interrupt_handler(ns);
966 ns8250_dev_cleanup(ns);
967 return EOK;
968}
969
[b9cc81c6]970static errno_t ns8250_dev_quiesce(ddf_dev_t *dev)
971{
972 ns8250_t *ns = dev_ns8250(dev);
973
974 ns8250_port_interrupts_disable(ns->regs);
975 return EOK;
976}
977
[25a7e11d]978/** Open the device.
[49698fa]979 *
980 * This is a callback function called when a client tries to connect to the
981 * device.
982 *
[74017ce]983 * @param srvs Service structure
984 * @param srv Server-side connection structure
[25a7e11d]985 */
[b7fd2a0]986static errno_t ns8250_open(chardev_srvs_t *srvs, chardev_srv_t *srv)
[25a7e11d]987{
[74017ce]988 ns8250_t *ns = srv_ns8250(srv);
[b7fd2a0]989 errno_t res;
[a35b458]990
[5b68e0c]991 fibril_mutex_lock(&ns->mutex);
[4510e06]992 if (ns->removed) {
[5b68e0c]993 res = ENXIO;
[25a7e11d]994 } else {
995 res = EOK;
[4510e06]996 ns->client_connections++;
[25a7e11d]997 }
[5b68e0c]998 fibril_mutex_unlock(&ns->mutex);
[a35b458]999
[25a7e11d]1000 return res;
1001}
1002
1003/** Close the device.
[49698fa]1004 *
1005 * This is a callback function called when a client tries to disconnect from
1006 * the device.
1007 *
[74017ce]1008 * @param srv Server-side connection structure
[25a7e11d]1009 */
[b7fd2a0]1010static errno_t ns8250_close(chardev_srv_t *srv)
[25a7e11d]1011{
[74017ce]1012 ns8250_t *data = srv_ns8250(srv);
[a35b458]1013
[25a7e11d]1014 fibril_mutex_lock(&data->mutex);
[a35b458]1015
[4510e06]1016 assert(data->client_connections > 0);
[a35b458]1017
[4510e06]1018 if (!(--data->client_connections))
1019 buf_clear(&data->input_buffer);
[a35b458]1020
[49698fa]1021 fibril_mutex_unlock(&data->mutex);
[a35b458]1022
[74017ce]1023 return EOK;
[25a7e11d]1024}
1025
[49698fa]1026/** Get parameters of the serial communication which are set to the specified
1027 * device.
1028 *
1029 * @param dev The serial port device.
1030 * @param baud_rate The baud rate used by the device.
1031 * @param parity The type of parity used by the device.
1032 * @param word_length The size of one data unit in bits.
1033 * @param stop_bits The number of stop bits used.
[cf8cc36]1034 */
[18b6a88]1035static void ns8250_get_props(ddf_dev_t *dev, unsigned int *baud_rate,
1036 unsigned int *parity, unsigned int *word_length, unsigned int *stop_bits)
[49698fa]1037{
[56fd7cf]1038 ns8250_t *data = dev_ns8250(dev);
[c7235d40]1039 ns8250_regs_t *regs = data->regs;
[a35b458]1040
[49698fa]1041 fibril_mutex_lock(&data->mutex);
[c7235d40]1042 ns8250_port_interrupts_disable(regs);
1043 *baud_rate = ns8250_port_get_baud_rate(regs);
1044 ns8250_port_get_com_props(regs, parity, word_length, stop_bits);
1045 ns8250_port_interrupts_enable(regs);
[49698fa]1046 fibril_mutex_unlock(&data->mutex);
[a35b458]1047
[fc51296]1048 ddf_msg(LVL_DEBUG, "ns8250_get_props: baud rate %d, parity 0x%x, word "
[ebcb05a]1049 "length %d, stop bits %d", *baud_rate, *parity, *word_length,
[49698fa]1050 *stop_bits);
[cf8cc36]1051}
1052
[49698fa]1053/** Set parameters of the serial communication to the specified serial port
1054 * device.
1055 *
1056 * @param dev The serial port device.
1057 * @param baud_rate The baud rate to be used by the device.
1058 * @param parity The type of parity to be used by the device.
1059 * @param word_length The size of one data unit in bits.
1060 * @param stop_bits The number of stop bits to be used.
[cf8cc36]1061 */
[b7fd2a0]1062static errno_t ns8250_set_props(ddf_dev_t *dev, unsigned int baud_rate,
[33dbbd2]1063 unsigned int parity, unsigned int word_length, unsigned int stop_bits)
[cf8cc36]1064{
[fc51296]1065 ddf_msg(LVL_DEBUG, "ns8250_set_props: baud rate %d, parity 0x%x, word "
[ebcb05a]1066 "length %d, stop bits %d", baud_rate, parity, word_length,
[49698fa]1067 stop_bits);
[a35b458]1068
[56fd7cf]1069 ns8250_t *data = dev_ns8250(dev);
[c7235d40]1070 ns8250_regs_t *regs = data->regs;
[b7fd2a0]1071 errno_t ret;
[a35b458]1072
[49698fa]1073 fibril_mutex_lock(&data->mutex);
[c7235d40]1074 ns8250_port_interrupts_disable(regs);
1075 ret = ns8250_port_set_baud_rate(regs, baud_rate);
[33dbbd2]1076 if (ret == EOK)
[c7235d40]1077 ret = ns8250_port_set_com_props(regs, parity, word_length, stop_bits);
1078 ns8250_port_interrupts_enable(regs);
[49698fa]1079 fibril_mutex_unlock(&data->mutex);
[a35b458]1080
[49698fa]1081 return ret;
[cf8cc36]1082}
1083
[49698fa]1084/** Default handler for client requests which are not handled by the standard
1085 * interfaces.
1086 *
[f4ef3c2]1087 * Configure the parameters of the serial communication.
1088 */
[984a9ba]1089static void ns8250_default_handler(chardev_srv_t *srv, ipc_call_t *call)
[f4ef3c2]1090{
[74017ce]1091 ns8250_t *ns8250 = srv_ns8250(srv);
[fafb8e5]1092 sysarg_t method = ipc_get_imethod(call);
[b7fd2a0]1093 errno_t ret;
[49698fa]1094 unsigned int baud_rate, parity, word_length, stop_bits;
[a35b458]1095
[49698fa]1096 switch (method) {
1097 case SERIAL_GET_COM_PROPS:
[74017ce]1098 ns8250_get_props(ns8250->dev, &baud_rate, &parity, &word_length,
[49698fa]1099 &stop_bits);
[984a9ba]1100 async_answer_4(call, EOK, baud_rate, parity, word_length,
[49698fa]1101 stop_bits);
1102 break;
[a35b458]1103
[49698fa]1104 case SERIAL_SET_COM_PROPS:
[fafb8e5]1105 baud_rate = ipc_get_arg1(call);
1106 parity = ipc_get_arg2(call);
1107 word_length = ipc_get_arg3(call);
1108 stop_bits = ipc_get_arg4(call);
[74017ce]1109 ret = ns8250_set_props(ns8250->dev, baud_rate, parity, word_length,
[49698fa]1110 stop_bits);
[984a9ba]1111 async_answer_0(call, ret);
[49698fa]1112 break;
[a35b458]1113
[49698fa]1114 default:
[984a9ba]1115 async_answer_0(call, ENOTSUP);
[49698fa]1116 }
[f4ef3c2]1117}
1118
[984a9ba]1119void ns8250_char_conn(ipc_call_t *icall, void *arg)
[74017ce]1120{
1121 ns8250_t *ns8250 = fun_ns8250((ddf_fun_t *)arg);
1122
[984a9ba]1123 chardev_conn(icall, &ns8250->cds);
[74017ce]1124}
1125
[25a7e11d]1126/** Initialize the serial port driver.
[49698fa]1127 *
1128 * Initialize device operations structures with callback methods for handling
[25a7e11d]1129 * client requests to the serial port devices.
1130 */
[49698fa]1131static void ns8250_init(void)
[5fe1c32]1132{
[267f235]1133 ddf_log_init(NAME);
[5fe1c32]1134}
1135
1136int main(int argc, char *argv[])
1137{
[49698fa]1138 printf(NAME ": HelenOS serial port driver\n");
[04c7003f]1139 ns8250_init();
[83a2f43]1140 return ddf_driver_main(&ns8250_driver);
[5fe1c32]1141}
1142
1143/**
1144 * @}
[49698fa]1145 */
Note: See TracBrowser for help on using the repository browser.