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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d1582b50 was d1582b50, checked in by Jiri Svoboda <jiri@…>, 5 years ago

Fix spacing in single-line comments using latest ccheck

This found incorrectly formatted section comments (with blocks of
asterisks or dashes). I strongly believe against using section comments
but I am not simply removing them since that would probably be
controversial.

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