source: mainline/uspace/drv/ns8250/ns8250.c@ 83a2f43

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

Rename bunch of stuff so that it starts with 'ddf_'.

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