source: mainline/uspace/drv/char/ns8250/ns8250.c@ 224174f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 224174f was 3a67d63, checked in by Maurizio Lombardi <m.lombardi85@…>, 12 years ago

ns8250: fix typo

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