source: mainline/uspace/drv/ns8250/ns8250.c@ 64d2b10

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 64d2b10 was 77429d3, checked in by Martin Decky <martin@…>, 14 years ago

remove the obsoleted SYS_INTERRUPT_ENABLE syscall

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