source: mainline/uspace/drv/char/ns8250/ns8250.c@ 5b68e0c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5b68e0c was 5b68e0c, checked in by Jiri Svoboda <jiri@…>, 14 years ago

ns8250 device removal support.

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