2 * Copyright (c) 2010 Lenka Trochtova
3 * Copyright (c) 2017 Jiri Svoboda
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
31 * @defgroup ns8250 Serial port driver.
32 * @brief HelenOS serial port driver.
43 #include <fibril_synch.h>
52 #include <ddf/driver.h>
53 #include <ddf/interrupt.h>
55 #include <io/chardev_srv.h>
57 #include <device/hw_res.h>
58 #include <ipc/serial_ctl.h>
60 #include "cyclic_buffer.h"
65 #define MAX_BAUD_RATE 115200
66 #define DLAB_MASK (1 << 7)
68 /** Interrupt Enable Register definition. */
69 #define NS8250_IER_RXREADY (1 << 0)
70 #define NS8250_IER_THRE (1 << 1)
71 #define NS8250_IER_RXSTATUS (1 << 2)
72 #define NS8250_IER_MODEM_STATUS (1 << 3)
74 /** Interrupt ID Register definition. */
75 #define NS8250_IID_ACTIVE (1 << 0)
76 #define NS8250_IID_CAUSE_MASK 0x0e
77 #define NS8250_IID_CAUSE_RXSTATUS 0x06
79 /** FIFO Control Register definition. */
80 #define NS8250_FCR_FIFOENABLE (1 << 0)
81 #define NS8250_FCR_RXFIFORESET (1 << 1)
82 #define NS8250_FCR_TXFIFORESET (1 << 2)
83 #define NS8250_FCR_DMAMODE (1 << 3)
84 #define NS8250_FCR_RXTRIGGERLOW (1 << 6)
85 #define NS8250_FCR_RXTRIGGERHI (1 << 7)
87 /** Line Control Register definition. */
88 #define NS8250_LCR_STOPBITS (1 << 2)
89 #define NS8250_LCR_PARITY (1 << 3)
90 #define NS8250_LCR_SENDBREAK (1 << 6)
91 #define NS8250_LCR_DLAB (1 << 7)
93 /** Modem Control Register definition. */
94 #define NS8250_MCR_DTR (1 << 0)
95 #define NS8250_MCR_RTS (1 << 1)
96 #define NS8250_MCR_OUT1 (1 << 2)
97 #define NS8250_MCR_OUT2 (1 << 3)
98 #define NS8250_MCR_LOOPBACK (1 << 4)
99 #define NS8250_MCR_ALL (0x1f)
101 /** Line Status Register definition. */
102 #define NS8250_LSR_RXREADY (1 << 0)
103 #define NS8250_LSR_OE (1 << 1)
104 #define NS8250_LSR_PE (1 << 2)
105 #define NS8250_LSR_FE (1 << 3)
106 #define NS8250_LSR_BREAK (1 << 4)
107 #define NS8250_LSR_THRE (1 << 5)
108 #define NS8250_LSR_TSE (1 << 6)
110 /** Modem Status Register definition. */
111 #define NS8250_MSR_DELTACTS (1 << 0)
112 #define NS8250_MSR_DELTADSR (1 << 1)
113 #define NS8250_MSR_RITRAILING (1 << 2)
114 #define NS8250_MSR_DELTADCD (1 << 3)
115 #define NS8250_MSR_CTS (1 << 4)
116 #define NS8250_MSR_DSR (1 << 5)
117 #define NS8250_MSR_RI (1 << 6)
118 #define NS8250_MSR_DCD (1 << 7)
119 #define NS8250_MSR_SIGNALS (NS8250_MSR_CTS | NS8250_MSR_DSR \
120 | NS8250_MSR_RI | NS8250_MSR_DCD)
122 /** The number of bits of one data unit send by the serial port. */
130 /** The number of stop bits used by the serial port. */
132 /** Use one stop bit. */
134 /** 1.5 stop bits for word length 5, 2 stop bits otherwise. */
138 /** 8250 UART registers layout. */
140 ioport8_t data
; /**< Data register. */
141 ioport8_t ier
; /**< Interrupt Enable Reg. */
142 ioport8_t iid
; /**< Interrupt ID Reg. */
143 ioport8_t lcr
; /**< Line Control Reg. */
144 ioport8_t mcr
; /**< Modem Control Reg. */
145 ioport8_t lsr
; /**< Line Status Reg. */
146 ioport8_t msr
; /**< Modem Status Reg. */
149 /** The driver data for the serial port devices. */
150 typedef struct ns8250
{
151 /** DDF device node */
153 /** DDF function node */
155 /** Character device service */
157 /** Parent session */
158 async_sess_t
*parent_sess
;
159 /** I/O registers **/
161 /** Are there any clients connected to the device? */
162 unsigned client_connections
;
163 /** The irq assigned to this device. */
165 /** IRQ capability handle */
167 /** The base i/o address of the devices registers. */
169 /** The i/o port used to access the serial ports registers. */
171 /** The buffer for incoming data. */
172 cyclic_buffer_t input_buffer
;
173 /** The fibril mutex for synchronizing the access to the device. */
174 fibril_mutex_t mutex
;
175 /** Indicates that some data has become available */
176 fibril_condvar_t input_buffer_available
;
177 /** True if device is removed. */
181 /** Obtain soft-state structure from device node */
182 static ns8250_t
*dev_ns8250(ddf_dev_t
*dev
)
184 return ddf_dev_data_get(dev
);
187 /** Obtain soft-state structure from function node */
188 static ns8250_t
*fun_ns8250(ddf_fun_t
*fun
)
190 return dev_ns8250(ddf_fun_get_dev(fun
));
193 /** Obtain soft-state structure from chardev srv */
194 static ns8250_t
*srv_ns8250(chardev_srv_t
*srv
)
196 return (ns8250_t
*)srv
->srvs
->sarg
;
200 /** Find out if there is some incoming data available on the serial port.
202 * @param port The base address of the serial port device's ports.
203 * @return True if there are data waiting to be read, false
206 static bool ns8250_received(ns8250_regs_t
*regs
)
208 return (pio_read_8(®s
->lsr
) & NS8250_LSR_RXREADY
) != 0;
211 /** Read one byte from the serial port.
213 * @param port The base address of the serial port device's ports.
214 * @return The data read.
216 static uint8_t ns8250_read_8(ns8250_regs_t
*regs
)
218 return pio_read_8(®s
->data
);
221 /** Find out wheter it is possible to send data.
223 * @param port The base address of the serial port device's ports.
225 static bool is_transmit_empty(ns8250_regs_t
*regs
)
227 return (pio_read_8(®s
->lsr
) & NS8250_LSR_THRE
) != 0;
230 /** Write one character on the serial port.
232 * @param port The base address of the serial port device's ports.
233 * @param c The character to be written to the serial port device.
235 static void ns8250_write_8(ns8250_regs_t
*regs
, uint8_t c
)
237 while (!is_transmit_empty(regs
))
240 pio_write_8(®s
->data
, c
);
243 /** Read data from the serial port device.
245 * @param srv Server-side connection data
246 * @param buf The output buffer for read data.
247 * @param count The number of bytes to be read.
248 * @param nread Place to store number of bytes actually read
250 * @return EOK on success or non-zero error code
252 static int ns8250_read(chardev_srv_t
*srv
, void *buf
, size_t count
, size_t *nread
)
254 ns8250_t
*ns
= srv_ns8250(srv
);
255 char *bp
= (char *) buf
;
263 fibril_mutex_lock(&ns
->mutex
);
264 while (buf_is_empty(&ns
->input_buffer
))
265 fibril_condvar_wait(&ns
->input_buffer_available
, &ns
->mutex
);
266 while (!buf_is_empty(&ns
->input_buffer
) && pos
< count
) {
267 bp
[pos
] = (char)buf_pop_front(&ns
->input_buffer
);
270 fibril_mutex_unlock(&ns
->mutex
);
276 /** Write a character to the serial port.
278 * @param ns Serial port device
279 * @param c The character to be written
281 static inline void ns8250_putchar(ns8250_t
*ns
, uint8_t c
)
283 fibril_mutex_lock(&ns
->mutex
);
284 ns8250_write_8(ns
->regs
, c
);
285 fibril_mutex_unlock(&ns
->mutex
);
288 /** Write data to the serial port.
290 * @param srv Server-side connection data
291 * @param buf The data to be written
292 * @param count The number of bytes to be written
293 * @param nwritten Place to store number of bytes successfully written
294 * @return EOK on success or non-zero error code
296 static int ns8250_write(chardev_srv_t
*srv
, const void *buf
, size_t count
,
299 ns8250_t
*ns
= srv_ns8250(srv
);
301 uint8_t *bp
= (uint8_t *) buf
;
303 for (idx
= 0; idx
< count
; idx
++)
304 ns8250_putchar(ns
, bp
[idx
]);
310 static int ns8250_open(chardev_srvs_t
*, chardev_srv_t
*);
311 static int ns8250_close(chardev_srv_t
*);
312 static void ns8250_default_handler(chardev_srv_t
*, ipc_callid_t
, ipc_call_t
*);
314 /** The character interface's callbacks. */
315 static chardev_ops_t ns8250_chardev_ops
= {
317 .close
= ns8250_close
,
319 .write
= ns8250_write
,
320 .def_handler
= ns8250_default_handler
323 static void ns8250_char_conn(ipc_callid_t
, ipc_call_t
*, void *);
325 static int ns8250_dev_add(ddf_dev_t
*dev
);
326 static int ns8250_dev_remove(ddf_dev_t
*dev
);
328 /** The serial port device driver's standard operations. */
329 static driver_ops_t ns8250_ops
= {
330 .dev_add
= &ns8250_dev_add
,
331 .dev_remove
= &ns8250_dev_remove
334 /** The serial port device driver structure. */
335 static driver_t ns8250_driver
= {
337 .driver_ops
= &ns8250_ops
340 /** Clean up the serial port soft-state
342 * @param ns Serial port device
344 static void ns8250_dev_cleanup(ns8250_t
*ns
)
348 /** Enable the i/o ports of the device.
350 * @param ns Serial port device
351 * @return True on success, false otherwise
353 static bool ns8250_pio_enable(ns8250_t
*ns
)
355 ddf_msg(LVL_DEBUG
, "ns8250_pio_enable %s", ddf_dev_get_name(ns
->dev
));
357 /* Gain control over port's registers. */
358 if (pio_enable((void *) ns
->io_addr
, REG_COUNT
,
359 (void **) &ns
->port
)) {
360 ddf_msg(LVL_ERROR
, "Cannot map the port %#" PRIxn
361 " for device %s.", ns
->io_addr
, ddf_dev_get_name(ns
->dev
));
365 ns
->regs
= (ns8250_regs_t
*)ns
->port
;
370 /** Probe the serial port device for its presence.
372 * @param ns Serial port device
373 * @return True if the device is present, false otherwise
375 static bool ns8250_dev_probe(ns8250_t
*ns
)
377 ddf_msg(LVL_DEBUG
, "ns8250_dev_probe %s", ddf_dev_get_name(ns
->dev
));
382 olddata
= pio_read_8(&ns
->regs
->mcr
);
384 pio_write_8(&ns
->regs
->mcr
, NS8250_MCR_LOOPBACK
);
385 if (pio_read_8(&ns
->regs
->msr
) & NS8250_MSR_SIGNALS
)
388 pio_write_8(&ns
->regs
->mcr
, NS8250_MCR_ALL
);
389 if ((pio_read_8(&ns
->regs
->msr
) & NS8250_MSR_SIGNALS
)
390 != NS8250_MSR_SIGNALS
)
393 pio_write_8(&ns
->regs
->mcr
, olddata
);
396 ddf_msg(LVL_DEBUG
, "Device %s is not present.",
397 ddf_dev_get_name(ns
->dev
));
403 /** Initialize serial port device.
405 * @param ns Serial port device
406 * @return Zero on success, negative error number otherwise
408 static int ns8250_dev_initialize(ns8250_t
*ns
)
412 ddf_msg(LVL_DEBUG
, "ns8250_dev_initialize %s", ddf_dev_get_name(ns
->dev
));
414 hw_resource_list_t hw_resources
;
415 memset(&hw_resources
, 0, sizeof(hw_resource_list_t
));
417 /* Get hw resources. */
418 ret
= hw_res_get_resource_list(ns
->parent_sess
, &hw_resources
);
420 ddf_msg(LVL_ERROR
, "Failed to get HW resources for device "
421 "%s.", ddf_dev_get_name(ns
->dev
));
430 for (i
= 0; i
< hw_resources
.count
; i
++) {
431 res
= &hw_resources
.resources
[i
];
434 ns
->irq
= res
->res
.interrupt
.irq
;
436 ddf_msg(LVL_NOTE
, "Device %s was assigned irq = 0x%x.",
437 ddf_dev_get_name(ns
->dev
), ns
->irq
);
441 ns
->io_addr
= res
->res
.io_range
.address
;
442 if (res
->res
.io_range
.size
< REG_COUNT
) {
443 ddf_msg(LVL_ERROR
, "I/O range assigned to "
444 "device %s is too small.", ddf_dev_get_name(ns
->dev
));
449 ddf_msg(LVL_NOTE
, "Device %s was assigned I/O address = "
450 "0x%#" PRIxn
".", ddf_dev_get_name(ns
->dev
), ns
->io_addr
);
458 if (!irq
|| !ioport
) {
459 ddf_msg(LVL_ERROR
, "Missing HW resource(s) for device %s.",
460 ddf_dev_get_name(ns
->dev
));
465 hw_res_clean_resource_list(&hw_resources
);
469 ns8250_dev_cleanup(ns
);
470 hw_res_clean_resource_list(&hw_resources
);
474 /** Enable interrupts on the serial port device.
476 * Interrupt when data is received
478 * @param port The base address of the serial port device's ports.
480 static inline void ns8250_port_interrupts_enable(ns8250_regs_t
*regs
)
482 /* Interrupt when data received. */
483 pio_write_8(®s
->ier
, NS8250_IER_RXREADY
| NS8250_IER_RXSTATUS
);
484 pio_write_8(®s
->mcr
, NS8250_MCR_DTR
| NS8250_MCR_RTS
488 /** Disable interrupts on the serial port device.
490 * @param port The base address of the serial port device's ports
492 static inline void ns8250_port_interrupts_disable(ns8250_regs_t
*regs
)
494 pio_write_8(®s
->ier
, 0x0); /* Disable all interrupts. */
497 /** Enable interrupts for the serial port device.
499 * @param ns Serial port device
500 * @return Zero on success, negative error number otherwise
502 static int ns8250_interrupt_enable(ns8250_t
*ns
)
504 /* Enable interrupt using IRC service. */
505 int rc
= hw_res_enable_interrupt(ns
->parent_sess
, ns
->irq
);
509 /* Read LSR to clear possible previous LSR interrupt */
510 pio_read_8(&ns
->regs
->lsr
);
512 /* Enable interrupt on the serial port. */
513 ns8250_port_interrupts_enable(ns
->regs
);
518 /** Set Divisor Latch Access Bit.
520 * When the Divisor Latch Access Bit is set, it is possible to set baud rate of
521 * the serial port device.
523 * @param port The base address of the serial port device's ports.
525 static inline void enable_dlab(ns8250_regs_t
*regs
)
527 uint8_t val
= pio_read_8(®s
->lcr
);
528 pio_write_8(®s
->lcr
, val
| NS8250_LCR_DLAB
);
531 /** Clear Divisor Latch Access Bit.
533 * @param port The base address of the serial port device's ports.
535 static inline void clear_dlab(ns8250_regs_t
*regs
)
537 uint8_t val
= pio_read_8(®s
->lcr
);
538 pio_write_8(®s
->lcr
, val
& (~NS8250_LCR_DLAB
));
541 /** Set baud rate of the serial communication on the serial device.
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).
548 static int ns8250_port_set_baud_rate(ns8250_regs_t
*regs
, unsigned int baud_rate
)
551 uint8_t div_low
, div_high
;
553 if (baud_rate
< 50 || MAX_BAUD_RATE
% baud_rate
!= 0) {
554 ddf_msg(LVL_ERROR
, "Invalid baud rate %d requested.",
559 divisor
= MAX_BAUD_RATE
/ baud_rate
;
560 div_low
= (uint8_t)divisor
;
561 div_high
= (uint8_t)(divisor
>> 8);
563 /* Enable DLAB to be able to access baud rate divisor. */
566 /* Set divisor low byte. */
567 pio_write_8(®s
->data
, div_low
);
568 /* Set divisor high byte. */
569 pio_write_8(®s
->ier
, div_high
);
576 /** Get baud rate used by the serial port device.
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.
581 static unsigned int ns8250_port_get_baud_rate(ns8250_regs_t
*regs
)
584 uint8_t div_low
, div_high
;
586 /* Enable DLAB to be able to access baud rate divisor. */
589 /* Get divisor low byte. */
590 div_low
= pio_read_8(®s
->data
);
591 /* Get divisor high byte. */
592 div_high
= pio_read_8(®s
->ier
);
596 divisor
= (div_high
<< 8) | div_low
;
597 return MAX_BAUD_RATE
/ divisor
;
600 /** Get the parameters of the serial communication set on the serial port
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).
607 static void ns8250_port_get_com_props(ns8250_regs_t
*regs
, unsigned int *parity
,
608 unsigned int *word_length
, unsigned int *stop_bits
)
612 val
= pio_read_8(®s
->lcr
);
613 *parity
= ((val
>> NS8250_LCR_PARITY
) & 7);
615 /* Silence warnings */
633 if ((val
>> NS8250_LCR_STOPBITS
) & 1)
639 /** Set the parameters of the serial communication on the serial port device.
641 * @param parity The parity to be used.
642 * @param word_length The length of one data unit in bits.
643 * @param stop_bits The number of stop bits used (one or two).
644 * @return Zero on success, EINVAL if some of the specified values
647 static int ns8250_port_set_com_props(ns8250_regs_t
*regs
, unsigned int parity
,
648 unsigned int word_length
, unsigned int stop_bits
)
652 switch (word_length
) {
671 val
|= ONE_STOP_BIT
<< NS8250_LCR_STOPBITS
;
674 val
|= TWO_STOP_BITS
<< NS8250_LCR_STOPBITS
;
681 case SERIAL_NO_PARITY
:
682 case SERIAL_ODD_PARITY
:
683 case SERIAL_EVEN_PARITY
:
684 case SERIAL_MARK_PARITY
:
685 case SERIAL_SPACE_PARITY
:
686 val
|= parity
<< NS8250_LCR_PARITY
;
692 pio_write_8(®s
->lcr
, val
);
697 /** Initialize the serial port device.
699 * Set the default parameters of the serial communication.
701 * @param ns Serial port device
703 static void ns8250_initialize_port(ns8250_t
*ns
)
705 /* Disable interrupts. */
706 ns8250_port_interrupts_disable(ns
->regs
);
708 ns8250_port_set_baud_rate(ns
->regs
, 38400);
709 /* 8 bits, no parity, two stop bits. */
710 ns8250_port_set_com_props(ns
->regs
, SERIAL_NO_PARITY
, 8, 2);
712 * Enable FIFO, clear them, with 4-byte threshold for greater
715 pio_write_8(&ns
->regs
->iid
, NS8250_FCR_FIFOENABLE
716 | NS8250_FCR_RXFIFORESET
| NS8250_FCR_TXFIFORESET
717 | NS8250_FCR_RXTRIGGERLOW
);
719 * RTS/DSR set (Request to Send and Data Terminal Ready lines enabled),
720 * Aux Output2 set - needed for interrupts.
722 pio_write_8(&ns
->regs
->mcr
, NS8250_MCR_DTR
| NS8250_MCR_RTS
726 /** Deinitialize the serial port device.
728 * @param ns Serial port device
730 static void ns8250_port_cleanup(ns8250_t
*ns
)
733 pio_write_8(&ns
->regs
->iid
, 0x00);
734 /* Disable DTR, RTS, OUT1, OUT2 (int. enable) */
735 pio_write_8(&ns
->regs
->mcr
, 0x00);
736 /* Disable all interrupts from the port */
737 ns8250_port_interrupts_disable(ns
->regs
);
740 /** Read the data from the serial port device and store them to the input
743 * @param ns Serial port device
745 static void ns8250_read_from_device(ns8250_t
*ns
)
747 ns8250_regs_t
*regs
= ns
->regs
;
750 fibril_mutex_lock(&ns
->mutex
);
752 cont
= ns8250_received(regs
);
754 uint8_t val
= ns8250_read_8(regs
);
756 if (ns
->client_connections
> 0) {
757 bool buf_was_empty
= buf_is_empty(&ns
->input_buffer
);
758 if (!buf_push_back(&ns
->input_buffer
, val
)) {
759 ddf_msg(LVL_WARN
, "Buffer overflow on "
760 "%s.", ddf_dev_get_name(ns
->dev
));
763 ddf_msg(LVL_DEBUG2
, "Character %c saved "
764 "to the buffer of %s.",
765 val
, ddf_dev_get_name(ns
->dev
));
767 fibril_condvar_broadcast(&ns
->input_buffer_available
);
772 fibril_mutex_unlock(&ns
->mutex
);
776 /** The interrupt handler.
778 * The serial port is initialized to interrupt when some data come or line
779 * status register changes, so the interrupt is handled by reading the incoming
780 * data and reading the line status register.
782 * @param dev The serial port device.
785 static inline void ns8250_interrupt_handler(ipc_call_t
*icall
, ddf_dev_t
*dev
)
787 ns8250_t
*ns
= dev_ns8250(dev
);
788 uint8_t iir
= pio_read_8(&ns
->regs
->iid
);
789 if ((iir
& NS8250_IID_CAUSE_MASK
) == NS8250_IID_CAUSE_RXSTATUS
) {
790 uint8_t lsr
= pio_read_8(&ns
->regs
->lsr
);
791 if (lsr
& NS8250_LSR_OE
) {
792 ddf_msg(LVL_WARN
, "Overrun error on %s", ddf_dev_get_name(ns
->dev
));
796 ns8250_read_from_device(ns
);
797 hw_res_clear_interrupt(ns
->parent_sess
, ns
->irq
);
800 /** Register the interrupt handler for the device.
802 * @param ns Serial port device
804 static inline int ns8250_register_interrupt_handler(ns8250_t
*ns
)
806 return register_interrupt_handler(ns
->dev
, ns
->irq
,
807 ns8250_interrupt_handler
, NULL
);
810 /** Unregister the interrupt handler for the device.
812 * @param ns Serial port device
814 static inline int ns8250_unregister_interrupt_handler(ns8250_t
*ns
)
816 return unregister_interrupt_handler(ns
->dev
, ns
->irq_cap
);
819 /** The dev_add callback method of the serial port driver.
821 * Probe and initialize the newly added device.
823 * @param dev The serial port device.
825 static int ns8250_dev_add(ddf_dev_t
*dev
)
828 ddf_fun_t
*fun
= NULL
;
829 bool need_cleanup
= false;
830 bool need_unreg_intr_handler
= false;
833 ddf_msg(LVL_DEBUG
, "ns8250_dev_add %s (handle = %d)",
834 ddf_dev_get_name(dev
), (int) ddf_dev_get_handle(dev
));
836 /* Allocate soft-state for the device */
837 ns
= ddf_dev_data_alloc(dev
, sizeof(ns8250_t
));
843 fibril_mutex_initialize(&ns
->mutex
);
844 fibril_condvar_initialize(&ns
->input_buffer_available
);
847 ns
->parent_sess
= ddf_dev_parent_sess_get(ns
->dev
);
848 if (ns
->parent_sess
== NULL
) {
849 ddf_msg(LVL_ERROR
, "Failed to connect to parent driver of "
850 "device %s.", ddf_dev_get_name(ns
->dev
));
855 rc
= ns8250_dev_initialize(ns
);
861 if (!ns8250_pio_enable(ns
)) {
866 /* Find out whether the device is present. */
867 if (!ns8250_dev_probe(ns
)) {
872 /* Serial port initialization (baud rate etc.). */
873 ns8250_initialize_port(ns
);
875 /* Register interrupt handler. */
876 ns
->irq_cap
= ns8250_register_interrupt_handler(ns
);
877 if (ns
->irq_cap
< 0) {
878 ddf_msg(LVL_ERROR
, "Failed to register interrupt handler.");
882 need_unreg_intr_handler
= true;
884 /* Enable interrupt. */
885 rc
= ns8250_interrupt_enable(ns
);
887 ddf_msg(LVL_ERROR
, "Failed to enable the interrupt. Error code = "
892 fun
= ddf_fun_create(dev
, fun_exposed
, "a");
894 ddf_msg(LVL_ERROR
, "Failed creating function.");
898 ddf_fun_set_conn_handler(fun
, ns8250_char_conn
);
900 chardev_srvs_init(&ns
->cds
);
901 ns
->cds
.ops
= &ns8250_chardev_ops
;
904 rc
= ddf_fun_bind(fun
);
906 ddf_msg(LVL_ERROR
, "Failed binding function.");
912 ddf_fun_add_to_category(fun
, "serial");
914 ddf_msg(LVL_NOTE
, "Device %s successfully initialized.",
915 ddf_dev_get_name(dev
));
920 ddf_fun_destroy(fun
);
921 if (need_unreg_intr_handler
)
922 ns8250_unregister_interrupt_handler(ns
);
924 ns8250_dev_cleanup(ns
);
928 static int ns8250_dev_remove(ddf_dev_t
*dev
)
930 ns8250_t
*ns
= dev_ns8250(dev
);
933 fibril_mutex_lock(&ns
->mutex
);
934 if (ns
->client_connections
> 0) {
935 fibril_mutex_unlock(&ns
->mutex
);
939 fibril_mutex_unlock(&ns
->mutex
);
941 rc
= ddf_fun_unbind(ns
->fun
);
943 ddf_msg(LVL_ERROR
, "Failed to unbind function.");
947 ddf_fun_destroy(ns
->fun
);
949 ns8250_port_cleanup(ns
);
950 ns8250_unregister_interrupt_handler(ns
);
951 ns8250_dev_cleanup(ns
);
957 * This is a callback function called when a client tries to connect to the
960 * @param srvs Service structure
961 * @param srv Server-side connection structure
963 static int ns8250_open(chardev_srvs_t
*srvs
, chardev_srv_t
*srv
)
965 ns8250_t
*ns
= srv_ns8250(srv
);
968 fibril_mutex_lock(&ns
->mutex
);
973 ns
->client_connections
++;
975 fibril_mutex_unlock(&ns
->mutex
);
980 /** Close the device.
982 * This is a callback function called when a client tries to disconnect from
985 * @param srv Server-side connection structure
987 static int ns8250_close(chardev_srv_t
*srv
)
989 ns8250_t
*data
= srv_ns8250(srv
);
991 fibril_mutex_lock(&data
->mutex
);
993 assert(data
->client_connections
> 0);
995 if (!(--data
->client_connections
))
996 buf_clear(&data
->input_buffer
);
998 fibril_mutex_unlock(&data
->mutex
);
1003 /** Get parameters of the serial communication which are set to the specified
1006 * @param dev The serial port device.
1007 * @param baud_rate The baud rate used by the device.
1008 * @param parity The type of parity used by the device.
1009 * @param word_length The size of one data unit in bits.
1010 * @param stop_bits The number of stop bits used.
1013 ns8250_get_props(ddf_dev_t
*dev
, unsigned int *baud_rate
, unsigned int *parity
,
1014 unsigned int *word_length
, unsigned int* stop_bits
)
1016 ns8250_t
*data
= dev_ns8250(dev
);
1017 ns8250_regs_t
*regs
= data
->regs
;
1019 fibril_mutex_lock(&data
->mutex
);
1020 ns8250_port_interrupts_disable(regs
);
1021 *baud_rate
= ns8250_port_get_baud_rate(regs
);
1022 ns8250_port_get_com_props(regs
, parity
, word_length
, stop_bits
);
1023 ns8250_port_interrupts_enable(regs
);
1024 fibril_mutex_unlock(&data
->mutex
);
1026 ddf_msg(LVL_DEBUG
, "ns8250_get_props: baud rate %d, parity 0x%x, word "
1027 "length %d, stop bits %d", *baud_rate
, *parity
, *word_length
,
1031 /** Set parameters of the serial communication to the specified serial port
1034 * @param dev The serial port device.
1035 * @param baud_rate The baud rate to be used by the device.
1036 * @param parity The type of parity to be used by the device.
1037 * @param word_length The size of one data unit in bits.
1038 * @param stop_bits The number of stop bits to be used.
1040 static int ns8250_set_props(ddf_dev_t
*dev
, unsigned int baud_rate
,
1041 unsigned int parity
, unsigned int word_length
, unsigned int stop_bits
)
1043 ddf_msg(LVL_DEBUG
, "ns8250_set_props: baud rate %d, parity 0x%x, word "
1044 "length %d, stop bits %d", baud_rate
, parity
, word_length
,
1047 ns8250_t
*data
= dev_ns8250(dev
);
1048 ns8250_regs_t
*regs
= data
->regs
;
1051 fibril_mutex_lock(&data
->mutex
);
1052 ns8250_port_interrupts_disable(regs
);
1053 ret
= ns8250_port_set_baud_rate(regs
, baud_rate
);
1055 ret
= ns8250_port_set_com_props(regs
, parity
, word_length
, stop_bits
);
1056 ns8250_port_interrupts_enable(regs
);
1057 fibril_mutex_unlock(&data
->mutex
);
1062 /** Default handler for client requests which are not handled by the standard
1065 * Configure the parameters of the serial communication.
1067 static void ns8250_default_handler(chardev_srv_t
*srv
, ipc_callid_t callid
,
1070 ns8250_t
*ns8250
= srv_ns8250(srv
);
1071 sysarg_t method
= IPC_GET_IMETHOD(*call
);
1073 unsigned int baud_rate
, parity
, word_length
, stop_bits
;
1076 case SERIAL_GET_COM_PROPS
:
1077 ns8250_get_props(ns8250
->dev
, &baud_rate
, &parity
, &word_length
,
1079 async_answer_4(callid
, EOK
, baud_rate
, parity
, word_length
,
1083 case SERIAL_SET_COM_PROPS
:
1084 baud_rate
= IPC_GET_ARG1(*call
);
1085 parity
= IPC_GET_ARG2(*call
);
1086 word_length
= IPC_GET_ARG3(*call
);
1087 stop_bits
= IPC_GET_ARG4(*call
);
1088 ret
= ns8250_set_props(ns8250
->dev
, baud_rate
, parity
, word_length
,
1090 async_answer_0(callid
, ret
);
1094 async_answer_0(callid
, ENOTSUP
);
1098 void ns8250_char_conn(ipc_callid_t iid
, ipc_call_t
*icall
, void *arg
)
1100 ns8250_t
*ns8250
= fun_ns8250((ddf_fun_t
*)arg
);
1102 chardev_conn(iid
, icall
, &ns8250
->cds
);
1105 /** Initialize the serial port driver.
1107 * Initialize device operations structures with callback methods for handling
1108 * client requests to the serial port devices.
1110 static void ns8250_init(void)
1115 int main(int argc
, char *argv
[])
1117 printf(NAME
": HelenOS serial port driver\n");
1119 return ddf_driver_main(&ns8250_driver
);