source: mainline/uspace/drv/ns8250/ns8250.c@ 68414f4a

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

Refactor drivers

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