source: mainline/uspace/drv/ns8250/ns8250.c@ 80fdffe

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

Split driver.h into ddf/driver.h and ddf/interrupt.h.

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