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

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

Leave it up to DDF to free driver-specific data. This makes it possible
to ensure soft state is not freed during calls to driver entry points.

This requires some driver changes:

  • minimum change is not to free() driver-data structures (ddf_fun_t.driver_data and ddf_dev_t.driver_data)
  • ideally allocate using ddf_dev_data_alloc(), ddf_fun_data_alloc()

I tried fixing existing drivers accordingly (mostly the minimalistic
change variant), but could have missed something.

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