source: mainline/uspace/drv/ns8250/ns8250.c@ 2391aaf

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

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

  • Property mode set to 100644
File size: 23.1 KB
RevLine 
[68414f4a]1/*
[5fe1c32]2 * Copyright (c) 2010 Lenka Trochtova
[68414f4a]3 * Copyright (c) 2011 Jiri Svoboda
[5fe1c32]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/**
[04c7003f]31 * @defgroup ns8250 Serial port driver.
[5fe1c32]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>
[c47e1a8]45#include <str.h>
[5fe1c32]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>
[1f8657b]52#include <ddi.h>
53#include <libarch/ddi.h>
[5fe1c32]54
[af6b5157]55#include <ddf/driver.h>
56#include <ddf/interrupt.h>
[41b56084]57#include <ops/char_dev.h>
[5fe1c32]58
59#include <devman.h>
60#include <ipc/devman.h>
61#include <device/hw_res.h>
[f4ef3c2]62#include <ipc/serial_ctl.h>
[5fe1c32]63
[1f8657b]64#include "cyclic_buffer.h"
65
[04c7003f]66#define NAME "ns8250"
[5fe1c32]67
[1f8657b]68#define REG_COUNT 7
[f4ef3c2]69#define MAX_BAUD_RATE 115200
[cf8cc36]70#define DLAB_MASK (1 << 7)
71
[68414f4a]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
[49698fa]78/** The number of bits of one data unit send by the serial port. */
[cf8cc36]79typedef enum {
80 WORD_LENGTH_5,
81 WORD_LENGTH_6,
82 WORD_LENGTH_7,
[49698fa]83 WORD_LENGTH_8
[cf8cc36]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. */
[49698fa]91 TWO_STOP_BITS
[cf8cc36]92} stop_bit_t;
93
[49698fa]94/** The driver data for the serial port devices. */
[68414f4a]95typedef struct ns8250 {
96 /** DDF device node */
[83a2f43]97 ddf_dev_t *dev;
[68414f4a]98 /** DDF function node */
[83a2f43]99 ddf_fun_t *fun;
[cf8cc36]100 /** Is there any client conntected to the device? */
[1f8657b]101 bool client_connected;
[cf8cc36]102 /** The irq assigned to this device. */
[1f8657b]103 int irq;
[cf8cc36]104 /** The base i/o address of the devices registers. */
[1f8657b]105 uint32_t io_addr;
[cf8cc36]106 /** The i/o port used to access the serial ports registers. */
[1f8657b]107 ioport8_t *port;
[49698fa]108 /** The buffer for incomming data. */
[1f8657b]109 cyclic_buffer_t input_buffer;
[49698fa]110 /** The fibril mutex for synchronizing the access to the device. */
111 fibril_mutex_t mutex;
[68414f4a]112} ns8250_t;
[5fe1c32]113
[68414f4a]114/** Create per-device soft-state structure.
[49698fa]115 *
[68414f4a]116 * @return Pointer to soft-state structure.
[cf8cc36]117 */
[68414f4a]118static ns8250_t *ns8250_new(void)
[1f8657b]119{
[68414f4a]120 ns8250_t *ns;
[49698fa]121
[68414f4a]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;
[1f8657b]128}
129
[68414f4a]130/** Delete soft-state structure.
[49698fa]131 *
[68414f4a]132 * @param ns The driver data structure.
[cf8cc36]133 */
[68414f4a]134static void ns8250_delete(ns8250_t *ns)
[1f8657b]135{
[bab6388]136 assert(ns != NULL);
[68414f4a]137 free(ns);
[1f8657b]138}
[5fe1c32]139
[cf8cc36]140/** Find out if there is some incomming data available on the serial port.
[49698fa]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.
[cf8cc36]145 */
[49698fa]146static bool ns8250_received(ioport8_t *port)
[ca97cad]147{
[49698fa]148 return (pio_read_8(port + 5) & 1) != 0;
[ca97cad]149}
150
[cf8cc36]151/** Read one byte from the serial port.
[49698fa]152 *
153 * @param port The base address of the serial port device's ports.
154 * @return The data read.
[cf8cc36]155 */
[49698fa]156static uint8_t ns8250_read_8(ioport8_t *port)
[ca97cad]157{
158 return pio_read_8(port);
159}
160
[cf8cc36]161/** Find out wheter it is possible to send data.
[49698fa]162 *
163 * @param port The base address of the serial port device's ports.
[cf8cc36]164 */
[49698fa]165static bool is_transmit_empty(ioport8_t *port)
[ca97cad]166{
[49698fa]167 return (pio_read_8(port + 5) & 0x20) != 0;
[ca97cad]168}
169
[cf8cc36]170/** Write one character on the serial port.
[49698fa]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.
[cf8cc36]174 */
[49698fa]175static void ns8250_write_8(ioport8_t *port, uint8_t c)
[ca97cad]176{
[49698fa]177 while (!is_transmit_empty(port))
[ca97cad]178 ;
179
180 pio_write_8(port, c);
181}
182
[cf8cc36]183/** Read data from the serial port device.
[49698fa]184 *
[68414f4a]185 * @param fun The serial port function
[49698fa]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.
[cf8cc36]191 */
[83a2f43]192static int ns8250_read(ddf_fun_t *fun, char *buf, size_t count)
[f658458]193{
[68414f4a]194 ns8250_t *ns = NS8250(fun);
[cf8cc36]195 int ret = EOK;
[bb864a0]196
[68414f4a]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);
[bb864a0]200 ret++;
201 }
[68414f4a]202 fibril_mutex_unlock(&ns->mutex);
[bb864a0]203
204 return ret;
[f658458]205}
206
[cf8cc36]207/** Write a character to the serial port.
[49698fa]208 *
[68414f4a]209 * @param ns Serial port device
210 * @param c The character to be written
[cf8cc36]211 */
[68414f4a]212static inline void ns8250_putchar(ns8250_t *ns, uint8_t c)
[49698fa]213{
[68414f4a]214 fibril_mutex_lock(&ns->mutex);
215 ns8250_write_8(ns->port, c);
216 fibril_mutex_unlock(&ns->mutex);
[ca97cad]217}
218
[cf8cc36]219/** Write data to the serial port.
[49698fa]220 *
[68414f4a]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
[cf8cc36]225 */
[83a2f43]226static int ns8250_write(ddf_fun_t *fun, char *buf, size_t count)
[f658458]227{
[68414f4a]228 ns8250_t *ns = NS8250(fun);
[ca97cad]229 size_t idx;
[49698fa]230
231 for (idx = 0; idx < count; idx++)
[68414f4a]232 ns8250_putchar(ns, (uint8_t) buf[idx]);
[ca97cad]233
[f658458]234 return 0;
235}
236
[83a2f43]237static ddf_dev_ops_t ns8250_dev_ops;
[5fe1c32]238
[49698fa]239/** The character interface's callbacks. */
[50c57df]240static char_dev_ops_t ns8250_char_dev_ops = {
[04c7003f]241 .read = &ns8250_read,
242 .write = &ns8250_write
[f658458]243};
244
[83a2f43]245static int ns8250_add_device(ddf_dev_t *dev);
[5fe1c32]246
[49698fa]247/** The serial port device driver's standard operations. */
[04c7003f]248static driver_ops_t ns8250_ops = {
249 .add_device = &ns8250_add_device
[5fe1c32]250};
251
[49698fa]252/** The serial port device driver structure. */
[04c7003f]253static driver_t ns8250_driver = {
[5fe1c32]254 .name = NAME,
[04c7003f]255 .driver_ops = &ns8250_ops
[5fe1c32]256};
257
[68414f4a]258/** Clean up the serial port soft-state
[49698fa]259 *
[68414f4a]260 * @param ns Serial port device
[cf8cc36]261 */
[68414f4a]262static void ns8250_dev_cleanup(ns8250_t *ns)
[1f8657b]263{
[68414f4a]264 if (ns->dev->parent_phone > 0) {
265 async_hangup(ns->dev->parent_phone);
266 ns->dev->parent_phone = 0;
[49698fa]267 }
[1f8657b]268}
269
[cf8cc36]270/** Enable the i/o ports of the device.
[49698fa]271 *
[68414f4a]272 * @param ns Serial port device
273 * @return True on success, false otherwise
[cf8cc36]274 */
[68414f4a]275static bool ns8250_pio_enable(ns8250_t *ns)
[1f8657b]276{
[68414f4a]277 printf(NAME ": ns8250_pio_enable %s\n", ns->dev->name);
[1f8657b]278
[49698fa]279 /* Gain control over port's registers. */
[68414f4a]280 if (pio_enable((void *)(uintptr_t) ns->io_addr, REG_COUNT,
281 (void **) &ns->port)) {
[7e752b2]282 printf(NAME ": error - cannot gain the port %#" PRIx32 " for device "
[68414f4a]283 "%s.\n", ns->io_addr, ns->dev->name);
[1f8657b]284 return false;
285 }
286
287 return true;
288}
289
[cf8cc36]290/** Probe the serial port device for its presence.
[49698fa]291 *
[68414f4a]292 * @param ns Serial port device
293 * @return True if the device is present, false otherwise
[cf8cc36]294 */
[68414f4a]295static bool ns8250_dev_probe(ns8250_t *ns)
[1f8657b]296{
[68414f4a]297 printf(NAME ": ns8250_dev_probe %s\n", ns->dev->name);
[1f8657b]298
[68414f4a]299 ioport8_t *port_addr = ns->port;
[1f8657b]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);
[49698fa]306 if (pio_read_8(port_addr + 6) & 0xf0)
[1f8657b]307 res = false;
308
309 pio_write_8(port_addr + 4, 0x1f);
[49698fa]310 if ((pio_read_8(port_addr + 6) & 0xf0) != 0xf0)
[1f8657b]311 res = false;
312
313 pio_write_8(port_addr + 4, olddata);
314
[49698fa]315 if (!res)
[68414f4a]316 printf(NAME ": device %s is not present.\n", ns->dev->name);
[1f8657b]317
[49698fa]318 return res;
[1f8657b]319}
320
[cf8cc36]321/** Initialize serial port device.
[49698fa]322 *
[68414f4a]323 * @param ns Serial port device
324 * @return Zero on success, negative error number otherwise
[cf8cc36]325 */
[68414f4a]326static int ns8250_dev_initialize(ns8250_t *ns)
[1f8657b]327{
[68414f4a]328 printf(NAME ": ns8250_dev_initialize %s\n", ns->dev->name);
[1f8657b]329
[df747b9c]330 int ret = EOK;
[49698fa]331
[1f8657b]332 hw_resource_list_t hw_resources;
333 memset(&hw_resources, 0, sizeof(hw_resource_list_t));
334
[49698fa]335 /* Connect to the parent's driver. */
[68414f4a]336 ns->dev->parent_phone = devman_parent_device_connect(ns->dev->handle,
[49698fa]337 IPC_FLAG_BLOCKING);
[68414f4a]338 if (ns->dev->parent_phone < 0) {
[49698fa]339 printf(NAME ": failed to connect to the parent driver of the "
[68414f4a]340 "device %s.\n", ns->dev->name);
341 ret = ns->dev->parent_phone;
[1f8657b]342 goto failed;
343 }
344
[49698fa]345 /* Get hw resources. */
[68414f4a]346 ret = hw_res_get_resource_list(ns->dev->parent_phone, &hw_resources);
[be942bc]347 if (ret != EOK) {
[49698fa]348 printf(NAME ": failed to get hw resources for the device "
[68414f4a]349 "%s.\n", ns->dev->name);
[1f8657b]350 goto failed;
[49698fa]351 }
[1f8657b]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:
[68414f4a]362 ns->irq = res->res.interrupt.irq;
[1f8657b]363 irq = true;
[49698fa]364 printf(NAME ": the %s device was asigned irq = 0x%x.\n",
[68414f4a]365 ns->dev->name, ns->irq);
[1f8657b]366 break;
[49698fa]367
[1f8657b]368 case IO_RANGE:
[68414f4a]369 ns->io_addr = res->res.io_range.address;
[1f8657b]370 if (res->res.io_range.size < REG_COUNT) {
[49698fa]371 printf(NAME ": i/o range assigned to the device "
[68414f4a]372 "%s is too small.\n", ns->dev->name);
[be942bc]373 ret = ELIMIT;
[1f8657b]374 goto failed;
375 }
376 ioport = true;
[49698fa]377 printf(NAME ": the %s device was asigned i/o address = "
[68414f4a]378 "0x%x.\n", ns->dev->name, ns->io_addr);
[49698fa]379 break;
380
[dafe675]381 default:
382 break;
[1f8657b]383 }
384 }
385
386 if (!irq || !ioport) {
[49698fa]387 printf(NAME ": missing hw resource(s) for the device %s.\n",
[68414f4a]388 ns->dev->name);
[be942bc]389 ret = ENOENT;
[1f8657b]390 goto failed;
[49698fa]391 }
[1f8657b]392
[f724e82]393 hw_res_clean_resource_list(&hw_resources);
[df747b9c]394 return ret;
[1f8657b]395
396failed:
[68414f4a]397 ns8250_dev_cleanup(ns);
[f724e82]398 hw_res_clean_resource_list(&hw_resources);
[49698fa]399 return ret;
[1f8657b]400}
401
[cf8cc36]402/** Enable interrupts on the serial port device.
[49698fa]403 *
[68414f4a]404 * Interrupt when data is received
[49698fa]405 *
406 * @param port The base address of the serial port device's ports.
[cf8cc36]407 */
[04c7003f]408static inline void ns8250_port_interrupts_enable(ioport8_t *port)
[bab6388]409{
[49698fa]410 pio_write_8(port + 1, 0x1); /* Interrupt when data received. */
411 pio_write_8(port + 4, 0xB);
[f4ef3c2]412}
413
[cf8cc36]414/** Disable interrupts on the serial port device.
[49698fa]415 *
[68414f4a]416 * @param port The base address of the serial port device's ports
[cf8cc36]417 */
[04c7003f]418static inline void ns8250_port_interrupts_disable(ioport8_t *port)
[f4ef3c2]419{
[49698fa]420 pio_write_8(port + 1, 0x0); /* Disable all interrupts. */
[f4ef3c2]421}
422
[cf8cc36]423/** Enable interrupts for the serial port device.
[49698fa]424 *
[68414f4a]425 * @param ns Serial port device
426 * @return Zero on success, negative error number otherwise
[cf8cc36]427 */
[68414f4a]428static int ns8250_interrupt_enable(ns8250_t *ns)
[cfe7716]429{
[49698fa]430 /* Enable interrupt on the serial port. */
[68414f4a]431 ns8250_port_interrupts_enable(ns->port);
[cfe7716]432
433 return EOK;
434}
435
[cf8cc36]436/** Set Divisor Latch Access Bit.
[49698fa]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.
[cf8cc36]442 */
443static inline void enable_dlab(ioport8_t *port)
444{
445 uint8_t val = pio_read_8(port + 3);
[49698fa]446 pio_write_8(port + 3, val | DLAB_MASK);
[cf8cc36]447}
448
449/** Clear Divisor Latch Access Bit.
[49698fa]450 *
451 * @param port The base address of the serial port device's ports.
[cf8cc36]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.
[49698fa]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).
[cf8cc36]465 */
[04c7003f]466static int ns8250_port_set_baud_rate(ioport8_t *port, unsigned int baud_rate)
[f4ef3c2]467{
468 uint16_t divisor;
469 uint8_t div_low, div_high;
470
[33dbbd2]471 if (baud_rate < 50 || MAX_BAUD_RATE % baud_rate != 0) {
[49698fa]472 printf(NAME ": error - somebody tried to set invalid baud rate "
473 "%d\n", baud_rate);
474 return EINVAL;
[f4ef3c2]475 }
476
477 divisor = MAX_BAUD_RATE / baud_rate;
478 div_low = (uint8_t)divisor;
[49698fa]479 div_high = (uint8_t)(divisor >> 8);
[f4ef3c2]480
[49698fa]481 /* Enable DLAB to be able to access baud rate divisor. */
482 enable_dlab(port);
[f4ef3c2]483
[49698fa]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);
[f4ef3c2]488
[49698fa]489 clear_dlab(port);
[f4ef3c2]490
[49698fa]491 return EOK;
[f4ef3c2]492}
493
[cf8cc36]494/** Get baud rate used by the serial port device.
[49698fa]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.
[cf8cc36]498 */
499static unsigned int ns8250_port_get_baud_rate(ioport8_t *port)
[f4ef3c2]500{
[cf8cc36]501 uint16_t divisor;
502 uint8_t div_low, div_high;
[f4ef3c2]503
[49698fa]504 /* Enable DLAB to be able to access baud rate divisor. */
[cf8cc36]505 enable_dlab(port);
[f619943a]506
[49698fa]507 /* Get divisor low byte. */
[cf8cc36]508 div_low = pio_read_8(port + 0);
[49698fa]509 /* Get divisor high byte. */
510 div_high = pio_read_8(port + 1);
[f4ef3c2]511
[cf8cc36]512 clear_dlab(port);
513
514 divisor = (div_high << 8) | div_low;
515 return MAX_BAUD_RATE / divisor;
516}
517
[49698fa]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).
[cf8cc36]524 */
[33dbbd2]525static void ns8250_port_get_com_props(ioport8_t *port, unsigned int *parity,
[49698fa]526 unsigned int *word_length, unsigned int *stop_bits)
[cf8cc36]527{
528 uint8_t val;
529
530 val = pio_read_8(port + 3);
531 *parity = ((val >> 3) & 7);
532
533 switch (val & 3) {
[49698fa]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;
[cf8cc36]546 }
547
[49698fa]548 if ((val >> 2) & 1)
[cf8cc36]549 *stop_bits = 2;
[49698fa]550 else
[cf8cc36]551 *stop_bits = 1;
[f4ef3c2]552}
553
[cf8cc36]554/** Set the parameters of the serial communication on the serial port device.
[49698fa]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.
[cf8cc36]561 */
[33dbbd2]562static int ns8250_port_set_com_props(ioport8_t *port, unsigned int parity,
[49698fa]563 unsigned int word_length, unsigned int stop_bits)
[cf8cc36]564{
565 uint8_t val;
566
567 switch (word_length) {
[49698fa]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;
[cf8cc36]582 }
583
584 switch (stop_bits) {
[49698fa]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;
[cf8cc36]593 }
594
595 switch (parity) {
[49698fa]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;
[cf8cc36]605 }
606
[49698fa]607 pio_write_8(port + 3, val);
[cf8cc36]608
609 return EOK;
610}
611
612/** Initialize the serial port device.
[49698fa]613 *
[cf8cc36]614 * Set the default parameters of the serial communication.
[49698fa]615 *
[68414f4a]616 * @param ns Serial port device
[cf8cc36]617 */
[68414f4a]618static void ns8250_initialize_port(ns8250_t *ns)
[cfe7716]619{
[68414f4a]620 ioport8_t *port = ns->port;
[cfe7716]621
[49698fa]622 /* Disable interrupts. */
623 ns8250_port_interrupts_disable(port);
624 /* Set baud rate. */
[04c7003f]625 ns8250_port_set_baud_rate(port, 38400);
[49698fa]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 *
[68414f4a]640 * @param ns Serial port device
[cf8cc36]641 */
[68414f4a]642static void ns8250_read_from_device(ns8250_t *ns)
[7f8b581]643{
[68414f4a]644 ioport8_t *port = ns->port;
[dafe675]645 bool cont = true;
646
[49698fa]647 while (cont) {
[68414f4a]648 fibril_mutex_lock(&ns->mutex);
[ca97cad]649
[cf8cc36]650 cont = ns8250_received(port);
651 if (cont) {
[04c7003f]652 uint8_t val = ns8250_read_8(port);
[ca97cad]653
[68414f4a]654 if (ns->client_connected) {
655 if (!buf_push_back(&ns->input_buffer, val)) {
[49698fa]656 printf(NAME ": buffer overflow on "
[68414f4a]657 "%s.\n", ns->dev->name);
[dafe675]658 } else {
[49698fa]659 printf(NAME ": the character %c saved "
660 "to the buffer of %s.\n",
[68414f4a]661 val, ns->dev->name);
[dafe675]662 }
[49698fa]663 }
[dafe675]664 }
665
[68414f4a]666 fibril_mutex_unlock(&ns->mutex);
[49698fa]667 fibril_yield();
668 }
[2300b9d]669}
670
[cf8cc36]671/** The interrupt handler.
[49698fa]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.
[cf8cc36]677 */
[83a2f43]678static inline void ns8250_interrupt_handler(ddf_dev_t *dev, ipc_callid_t iid,
[33dbbd2]679 ipc_call_t *icall)
[2300b9d]680{
[68414f4a]681 ns8250_read_from_device(NS8250_FROM_DEV(dev));
[7f8b581]682}
683
[cf8cc36]684/** Register the interrupt handler for the device.
[49698fa]685 *
[68414f4a]686 * @param ns Serial port device
[cf8cc36]687 */
[68414f4a]688static inline int ns8250_register_interrupt_handler(ns8250_t *ns)
[7f8b581]689{
[68414f4a]690 return register_interrupt_handler(ns->dev, ns->irq,
[49698fa]691 ns8250_interrupt_handler, NULL);
[2300b9d]692}
693
[cf8cc36]694/** Unregister the interrupt handler for the device.
[49698fa]695 *
[68414f4a]696 * @param ns Serial port device
[cf8cc36]697 */
[68414f4a]698static inline int ns8250_unregister_interrupt_handler(ns8250_t *ns)
[2300b9d]699{
[68414f4a]700 return unregister_interrupt_handler(ns->dev, ns->irq);
[7f8b581]701}
702
[cf8cc36]703/** The add_device callback method of the serial port driver.
[49698fa]704 *
[cf8cc36]705 * Probe and initialize the newly added device.
[49698fa]706 *
707 * @param dev The serial port device.
[cf8cc36]708 */
[83a2f43]709static int ns8250_add_device(ddf_dev_t *dev)
[5fe1c32]710{
[68414f4a]711 ns8250_t *ns = NULL;
[83a2f43]712 ddf_fun_t *fun = NULL;
[68414f4a]713 bool need_cleanup = false;
714 int rc;
715
[49698fa]716 printf(NAME ": ns8250_add_device %s (handle = %d)\n",
[ab3a851]717 dev->name, (int) dev->handle);
[5fe1c32]718
[68414f4a]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;
[1f8657b]728
[68414f4a]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;
[49698fa]738 }
[df747b9c]739
[49698fa]740 /* Find out whether the device is present. */
[68414f4a]741 if (!ns8250_dev_probe(ns)) {
742 rc = ENOENT;
743 goto fail;
[49698fa]744 }
[1f8657b]745
[49698fa]746 /* Serial port initialization (baud rate etc.). */
[68414f4a]747 ns8250_initialize_port(ns);
[cfe7716]748
[49698fa]749 /* Register interrupt handler. */
[68414f4a]750 if (ns8250_register_interrupt_handler(ns) != EOK) {
[2300b9d]751 printf(NAME ": failed to register interrupt handler.\n");
[68414f4a]752 rc = EADDRNOTAVAIL;
753 goto fail;
[7f8b581]754 }
[cfe7716]755
[49698fa]756 /* Enable interrupt. */
[68414f4a]757 rc = ns8250_interrupt_enable(ns);
758 if (rc != EOK) {
[49698fa]759 printf(NAME ": failed to enable the interrupt. Error code = "
[68414f4a]760 "%d.\n", rc);
761 goto fail;
[49698fa]762 }
[68414f4a]763
[97a62fe]764 fun = ddf_fun_create(dev, fun_exposed, "a");
765 if (fun == NULL) {
766 printf(NAME ": error creating function.\n");
767 goto fail;
768 }
[f658458]769
[49698fa]770 /* Set device operations. */
[8b1e15ac]771 fun->ops = &ns8250_dev_ops;
[97a62fe]772 rc = ddf_fun_bind(fun);
773 if (rc != EOK) {
774 printf(NAME ": error binding function.\n");
775 goto fail;
776 }
777
[68414f4a]778 ns->fun = fun;
[5fe1c32]779
[83a2f43]780 ddf_fun_add_to_class(fun, "serial");
[692c40cb]781
[49698fa]782 printf(NAME ": the %s device has been successfully initialized.\n",
783 dev->name);
[2300b9d]784
[df747b9c]785 return EOK;
[68414f4a]786fail:
[97a62fe]787 if (fun != NULL)
788 ddf_fun_destroy(fun);
[68414f4a]789 if (need_cleanup)
790 ns8250_dev_cleanup(ns);
791 if (ns != NULL)
792 ns8250_delete(ns);
793 return rc;
[5fe1c32]794}
795
[25a7e11d]796/** Open the device.
[49698fa]797 *
798 * This is a callback function called when a client tries to connect to the
799 * device.
800 *
801 * @param dev The device.
[25a7e11d]802 */
[83a2f43]803static int ns8250_open(ddf_fun_t *fun)
[25a7e11d]804{
[68414f4a]805 ns8250_t *data = (ns8250_t *) fun->dev->driver_data;
[25a7e11d]806 int res;
807
[49698fa]808 fibril_mutex_lock(&data->mutex);
[25a7e11d]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);
[bab6388]816
[25a7e11d]817 return res;
818}
819
820/** Close the device.
[49698fa]821 *
822 * This is a callback function called when a client tries to disconnect from
823 * the device.
824 *
825 * @param dev The device.
[25a7e11d]826 */
[83a2f43]827static void ns8250_close(ddf_fun_t *fun)
[25a7e11d]828{
[68414f4a]829 ns8250_t *data = (ns8250_t *) fun->dev->driver_data;
[25a7e11d]830
831 fibril_mutex_lock(&data->mutex);
832
[49698fa]833 assert(data->client_connected);
[25a7e11d]834
835 data->client_connected = false;
836 buf_clear(&data->input_buffer);
837
[49698fa]838 fibril_mutex_unlock(&data->mutex);
[25a7e11d]839}
840
[49698fa]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.
[cf8cc36]849 */
[49698fa]850static void
[83a2f43]851ns8250_get_props(ddf_dev_t *dev, unsigned int *baud_rate, unsigned int *parity,
[49698fa]852 unsigned int *word_length, unsigned int* stop_bits)
853{
[68414f4a]854 ns8250_t *data = (ns8250_t *) dev->driver_data;
[cf8cc36]855 ioport8_t *port = data->port;
856
[49698fa]857 fibril_mutex_lock(&data->mutex);
858 ns8250_port_interrupts_disable(port);
[cf8cc36]859 *baud_rate = ns8250_port_get_baud_rate(port);
[49698fa]860 ns8250_port_get_com_props(port, parity, word_length, stop_bits);
[cf8cc36]861 ns8250_port_interrupts_enable(port);
[49698fa]862 fibril_mutex_unlock(&data->mutex);
[cf8cc36]863
[49698fa]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);
[cf8cc36]867}
868
[49698fa]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.
[cf8cc36]877 */
[83a2f43]878static int ns8250_set_props(ddf_dev_t *dev, unsigned int baud_rate,
[33dbbd2]879 unsigned int parity, unsigned int word_length, unsigned int stop_bits)
[cf8cc36]880{
[49698fa]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);
[cf8cc36]884
[68414f4a]885 ns8250_t *data = (ns8250_t *) dev->driver_data;
[cf8cc36]886 ioport8_t *port = data->port;
887 int ret;
888
[49698fa]889 fibril_mutex_lock(&data->mutex);
890 ns8250_port_interrupts_disable(port);
[cf8cc36]891 ret = ns8250_port_set_baud_rate(port, baud_rate);
[33dbbd2]892 if (ret == EOK)
[cf8cc36]893 ret = ns8250_port_set_com_props(port, parity, word_length, stop_bits);
894 ns8250_port_interrupts_enable(port);
[49698fa]895 fibril_mutex_unlock(&data->mutex);
[cf8cc36]896
[49698fa]897 return ret;
[cf8cc36]898}
899
[49698fa]900/** Default handler for client requests which are not handled by the standard
901 * interfaces.
902 *
[f4ef3c2]903 * Configure the parameters of the serial communication.
904 */
[83a2f43]905static void ns8250_default_handler(ddf_fun_t *fun, ipc_callid_t callid,
[33dbbd2]906 ipc_call_t *call)
[f4ef3c2]907{
[228e490]908 sysarg_t method = IPC_GET_IMETHOD(*call);
[f4ef3c2]909 int ret;
[49698fa]910 unsigned int baud_rate, parity, word_length, stop_bits;
911
912 switch (method) {
913 case SERIAL_GET_COM_PROPS:
[8b1e15ac]914 ns8250_get_props(fun->dev, &baud_rate, &parity, &word_length,
[49698fa]915 &stop_bits);
[ffa2c8ef]916 async_answer_4(callid, EOK, baud_rate, parity, word_length,
[49698fa]917 stop_bits);
918 break;
[cf8cc36]919
[49698fa]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);
[8b1e15ac]925 ret = ns8250_set_props(fun->dev, baud_rate, parity, word_length,
[49698fa]926 stop_bits);
[ffa2c8ef]927 async_answer_0(callid, ret);
[49698fa]928 break;
929
930 default:
[ffa2c8ef]931 async_answer_0(callid, ENOTSUP);
[49698fa]932 }
[f4ef3c2]933}
934
[25a7e11d]935/** Initialize the serial port driver.
[49698fa]936 *
937 * Initialize device operations structures with callback methods for handling
[25a7e11d]938 * client requests to the serial port devices.
939 */
[49698fa]940static void ns8250_init(void)
[5fe1c32]941{
[5159ae9]942 ns8250_dev_ops.open = &ns8250_open;
[49698fa]943 ns8250_dev_ops.close = &ns8250_close;
[f658458]944
[50c57df]945 ns8250_dev_ops.interfaces[CHAR_DEV_IFACE] = &ns8250_char_dev_ops;
[5159ae9]946 ns8250_dev_ops.default_handler = &ns8250_default_handler;
[5fe1c32]947}
948
949int main(int argc, char *argv[])
950{
[49698fa]951 printf(NAME ": HelenOS serial port driver\n");
[04c7003f]952 ns8250_init();
[83a2f43]953 return ddf_driver_main(&ns8250_driver);
[5fe1c32]954}
955
956/**
957 * @}
[49698fa]958 */
Note: See TracBrowser for help on using the repository browser.