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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b19e892 was b19e892, checked in by Jakub Jermar <jakub@…>, 9 years ago

Merge open() with posix_open() and provide vfs_lookup_open() instead

vfs_lookup_open() is really just a convenience wrapper around
vfs_lookup() and vfs_open().

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