source: mainline/uspace/drv/ns8250/ns8250.c@ 175ad13e

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

Logging functions should append newline automatically. Since one has no
choice but to end log message with a newline, there is no need to do it
manually in every invocation.

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