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

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

Rename DDF entry point add_device to dev_add.

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