source: mainline/uspace/drv/char/ns8250/ns8250.c@ 6f57933

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

Replace ns8250.c magic numbers with structured register definitions.

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