source: mainline/uspace/drv/ns8250/ns8250.c@ 5e6e50b

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

Logging functions should append newline automatically. Since one has no
choice but to end log message with a newline, there is no need to do it
manually in every invocation.

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