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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fd418d9 was fd418d9, checked in by Martin Sucha <sucha14@…>, 13 years ago

ns8250: Log warnings by default

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