source: mainline/uspace/drv/ns8250/ns8250.c@ 7e752b2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7e752b2 was 7e752b2, checked in by Martin Decky <martin@…>, 15 years ago
  • correct printf() formatting strings and corresponding arguments
  • minor cstyle changes and other small fixes
  • 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 (data != NULL)
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 (dev->driver_data != NULL) {
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 %#" PRIx32 " 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 (data == NULL)
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 (res != EOK)
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 (baud_rate < 50 || MAX_BAUD_RATE % baud_rate != 0) {
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 ns8250_port_get_com_props(ioport8_t *port, unsigned int *parity,
537 unsigned int *word_length, unsigned int *stop_bits)
538{
539 uint8_t val;
540
541 val = pio_read_8(port + 3);
542 *parity = ((val >> 3) & 7);
543
544 switch (val & 3) {
545 case WORD_LENGTH_5:
546 *word_length = 5;
547 break;
548 case WORD_LENGTH_6:
549 *word_length = 6;
550 break;
551 case WORD_LENGTH_7:
552 *word_length = 7;
553 break;
554 case WORD_LENGTH_8:
555 *word_length = 8;
556 break;
557 }
558
559 if ((val >> 2) & 1)
560 *stop_bits = 2;
561 else
562 *stop_bits = 1;
563}
564
565/** Set the parameters of the serial communication on the serial port device.
566 *
567 * @param parity The parity to be used.
568 * @param word_length The length of one data unit in bits.
569 * @param stop_bits The number of stop bits used (one or two).
570 * @return Zero on success, EINVAL if some of the specified values
571 * is invalid.
572 */
573static int ns8250_port_set_com_props(ioport8_t *port, unsigned int parity,
574 unsigned int word_length, unsigned int stop_bits)
575{
576 uint8_t val;
577
578 switch (word_length) {
579 case 5:
580 val = WORD_LENGTH_5;
581 break;
582 case 6:
583 val = WORD_LENGTH_6;
584 break;
585 case 7:
586 val = WORD_LENGTH_7;
587 break;
588 case 8:
589 val = WORD_LENGTH_8;
590 break;
591 default:
592 return EINVAL;
593 }
594
595 switch (stop_bits) {
596 case 1:
597 val |= ONE_STOP_BIT << 2;
598 break;
599 case 2:
600 val |= TWO_STOP_BITS << 2;
601 break;
602 default:
603 return EINVAL;
604 }
605
606 switch (parity) {
607 case SERIAL_NO_PARITY:
608 case SERIAL_ODD_PARITY:
609 case SERIAL_EVEN_PARITY:
610 case SERIAL_MARK_PARITY:
611 case SERIAL_SPACE_PARITY:
612 val |= parity << 3;
613 break;
614 default:
615 return EINVAL;
616 }
617
618 pio_write_8(port + 3, val);
619
620 return EOK;
621}
622
623/** Initialize the serial port device.
624 *
625 * Set the default parameters of the serial communication.
626 *
627 * @param dev The serial port device.
628 */
629static void ns8250_initialize_port(device_t *dev)
630{
631 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
632 ioport8_t *port = data->port;
633
634 /* Disable interrupts. */
635 ns8250_port_interrupts_disable(port);
636 /* Set baud rate. */
637 ns8250_port_set_baud_rate(port, 38400);
638 /* 8 bits, no parity, two stop bits. */
639 ns8250_port_set_com_props(port, SERIAL_NO_PARITY, 8, 2);
640 /* Enable FIFO, clear them, with 14-byte threshold. */
641 pio_write_8(port + 2, 0xC7);
642 /*
643 * RTS/DSR set (Request to Send and Data Terminal Ready lines enabled),
644 * Aux Output2 set - needed for interrupts.
645 */
646 pio_write_8(port + 4, 0x0B);
647}
648
649/** Read the data from the serial port device and store them to the input
650 * buffer.
651 *
652 * @param dev The serial port device.
653 */
654static void ns8250_read_from_device(device_t *dev)
655{
656 ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
657 ioport8_t *port = data->port;
658 bool cont = true;
659
660 while (cont) {
661 fibril_mutex_lock(&data->mutex);
662
663 cont = ns8250_received(port);
664 if (cont) {
665 uint8_t val = ns8250_read_8(port);
666
667 if (data->client_connected) {
668 if (!buf_push_back(&data->input_buffer, val)) {
669 printf(NAME ": buffer overflow on "
670 "%s.\n", dev->name);
671 } else {
672 printf(NAME ": the character %c saved "
673 "to the buffer of %s.\n",
674 val, dev->name);
675 }
676 }
677 }
678
679 fibril_mutex_unlock(&data->mutex);
680 fibril_yield();
681 }
682}
683
684/** The interrupt handler.
685 *
686 * The serial port is initialized to interrupt when some data come, so the
687 * interrupt is handled by reading the incomming data.
688 *
689 * @param dev The serial port device.
690 */
691static inline void ns8250_interrupt_handler(device_t *dev, ipc_callid_t iid,
692 ipc_call_t *icall)
693{
694 ns8250_read_from_device(dev);
695}
696
697/** Register the interrupt handler for the device.
698 *
699 * @param dev The serial port device.
700 */
701static inline int ns8250_register_interrupt_handler(device_t *dev)
702{
703 ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
704
705 return register_interrupt_handler(dev, data->irq,
706 ns8250_interrupt_handler, NULL);
707}
708
709/** Unregister the interrupt handler for the device.
710 *
711 * @param dev The serial port device.
712 */
713static inline int ns8250_unregister_interrupt_handler(device_t *dev)
714{
715 ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
716
717 return unregister_interrupt_handler(dev, data->irq);
718}
719
720/** The add_device callback method of the serial port driver.
721 *
722 * Probe and initialize the newly added device.
723 *
724 * @param dev The serial port device.
725 */
726static int ns8250_add_device(device_t *dev)
727{
728 printf(NAME ": ns8250_add_device %s (handle = %d)\n",
729 dev->name, dev->handle);
730
731 int res = ns8250_dev_initialize(dev);
732 if (res != EOK)
733 return res;
734
735 if (!ns8250_pio_enable(dev)) {
736 ns8250_dev_cleanup(dev);
737 return EADDRNOTAVAIL;
738 }
739
740 /* Find out whether the device is present. */
741 if (!ns8250_dev_probe(dev)) {
742 ns8250_dev_cleanup(dev);
743 return ENOENT;
744 }
745
746 /* Serial port initialization (baud rate etc.). */
747 ns8250_initialize_port(dev);
748
749 /* Register interrupt handler. */
750 if (ns8250_register_interrupt_handler(dev) != EOK) {
751 printf(NAME ": failed to register interrupt handler.\n");
752 ns8250_dev_cleanup(dev);
753 return res;
754 }
755
756 /* Enable interrupt. */
757 res = ns8250_interrupt_enable(dev);
758 if (res != EOK) {
759 printf(NAME ": failed to enable the interrupt. Error code = "
760 "%d.\n", res);
761 ns8250_dev_cleanup(dev);
762 ns8250_unregister_interrupt_handler(dev);
763 return res;
764 }
765
766 /* Set device operations. */
767 dev->ops = &ns8250_dev_ops;
768
769 add_device_to_class(dev, "serial");
770
771 printf(NAME ": the %s device has been successfully initialized.\n",
772 dev->name);
773
774 return EOK;
775}
776
777/** Open the device.
778 *
779 * This is a callback function called when a client tries to connect to the
780 * device.
781 *
782 * @param dev The device.
783 */
784static int ns8250_open(device_t *dev)
785{
786 ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
787 int res;
788
789 fibril_mutex_lock(&data->mutex);
790 if (data->client_connected) {
791 res = ELIMIT;
792 } else {
793 res = EOK;
794 data->client_connected = true;
795 }
796 fibril_mutex_unlock(&data->mutex);
797
798 return res;
799}
800
801/** Close the device.
802 *
803 * This is a callback function called when a client tries to disconnect from
804 * the device.
805 *
806 * @param dev The device.
807 */
808static void ns8250_close(device_t *dev)
809{
810 ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
811
812 fibril_mutex_lock(&data->mutex);
813
814 assert(data->client_connected);
815
816 data->client_connected = false;
817 buf_clear(&data->input_buffer);
818
819 fibril_mutex_unlock(&data->mutex);
820}
821
822/** Get parameters of the serial communication which are set to the specified
823 * device.
824 *
825 * @param dev The serial port device.
826 * @param baud_rate The baud rate used by the device.
827 * @param parity The type of parity used by the device.
828 * @param word_length The size of one data unit in bits.
829 * @param stop_bits The number of stop bits used.
830 */
831static void
832ns8250_get_props(device_t *dev, unsigned int *baud_rate, unsigned int *parity,
833 unsigned int *word_length, unsigned int* stop_bits)
834{
835 ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
836 ioport8_t *port = data->port;
837
838 fibril_mutex_lock(&data->mutex);
839 ns8250_port_interrupts_disable(port);
840 *baud_rate = ns8250_port_get_baud_rate(port);
841 ns8250_port_get_com_props(port, parity, word_length, stop_bits);
842 ns8250_port_interrupts_enable(port);
843 fibril_mutex_unlock(&data->mutex);
844
845 printf(NAME ": ns8250_get_props: baud rate %d, parity 0x%x, word "
846 "length %d, stop bits %d\n", *baud_rate, *parity, *word_length,
847 *stop_bits);
848}
849
850/** Set parameters of the serial communication to the specified serial port
851 * device.
852 *
853 * @param dev The serial port device.
854 * @param baud_rate The baud rate to be used by the device.
855 * @param parity The type of parity to be used by the device.
856 * @param word_length The size of one data unit in bits.
857 * @param stop_bits The number of stop bits to be used.
858 */
859static int ns8250_set_props(device_t *dev, unsigned int baud_rate,
860 unsigned int parity, unsigned int word_length, unsigned int stop_bits)
861{
862 printf(NAME ": ns8250_set_props: baud rate %d, parity 0x%x, word "
863 "length %d, stop bits %d\n", baud_rate, parity, word_length,
864 stop_bits);
865
866 ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
867 ioport8_t *port = data->port;
868 int ret;
869
870 fibril_mutex_lock(&data->mutex);
871 ns8250_port_interrupts_disable(port);
872 ret = ns8250_port_set_baud_rate(port, baud_rate);
873 if (ret == EOK)
874 ret = ns8250_port_set_com_props(port, parity, word_length, stop_bits);
875 ns8250_port_interrupts_enable(port);
876 fibril_mutex_unlock(&data->mutex);
877
878 return ret;
879}
880
881/** Default handler for client requests which are not handled by the standard
882 * interfaces.
883 *
884 * Configure the parameters of the serial communication.
885 */
886static void ns8250_default_handler(device_t *dev, ipc_callid_t callid,
887 ipc_call_t *call)
888{
889 ipcarg_t method = IPC_GET_METHOD(*call);
890 int ret;
891 unsigned int baud_rate, parity, word_length, stop_bits;
892
893 switch (method) {
894 case SERIAL_GET_COM_PROPS:
895 ns8250_get_props(dev, &baud_rate, &parity, &word_length,
896 &stop_bits);
897 ipc_answer_4(callid, EOK, baud_rate, parity, word_length,
898 stop_bits);
899 break;
900
901 case SERIAL_SET_COM_PROPS:
902 baud_rate = IPC_GET_ARG1(*call);
903 parity = IPC_GET_ARG2(*call);
904 word_length = IPC_GET_ARG3(*call);
905 stop_bits = IPC_GET_ARG4(*call);
906 ret = ns8250_set_props(dev, baud_rate, parity, word_length,
907 stop_bits);
908 ipc_answer_0(callid, ret);
909 break;
910
911 default:
912 ipc_answer_0(callid, ENOTSUP);
913 }
914}
915
916/** Initialize the serial port driver.
917 *
918 * Initialize device operations structures with callback methods for handling
919 * client requests to the serial port devices.
920 */
921static void ns8250_init(void)
922{
923 ns8250_dev_ops.open = &ns8250_open;
924 ns8250_dev_ops.close = &ns8250_close;
925
926 ns8250_dev_ops.interfaces[CHAR_DEV_IFACE] = &ns8250_char_iface;
927 ns8250_dev_ops.default_handler = &ns8250_default_handler;
928}
929
930int main(int argc, char *argv[])
931{
932 printf(NAME ": HelenOS serial port driver\n");
933 ns8250_init();
934 return driver_main(&ns8250_driver);
935}
936
937/**
938 * @}
939 */
Note: See TracBrowser for help on using the repository browser.