source: mainline/uspace/drv/ns8250/ns8250.c@ 9880813f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9880813f was 49698fa, checked in by Jakub Jermar <jakub@…>, 15 years ago

Cstyle fixes in ns8250 driver.

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