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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ceb2512 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

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