[5fe1c32] | 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 | /**
|
---|
[04c7003f] | 30 | * @defgroup ns8250 Serial port driver.
|
---|
[5fe1c32] | 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>
|
---|
[c47e1a8] | 44 | #include <str.h>
|
---|
[5fe1c32] | 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>
|
---|
[1f8657b] | 51 | #include <ddi.h>
|
---|
| 52 | #include <libarch/ddi.h>
|
---|
[5fe1c32] | 53 |
|
---|
| 54 | #include <driver.h>
|
---|
[f658458] | 55 | #include <char.h>
|
---|
[5fe1c32] | 56 | #include <resource.h>
|
---|
| 57 |
|
---|
| 58 | #include <devman.h>
|
---|
| 59 | #include <ipc/devman.h>
|
---|
| 60 | #include <device/hw_res.h>
|
---|
[f4ef3c2] | 61 | #include <ipc/serial_ctl.h>
|
---|
[5fe1c32] | 62 |
|
---|
[1f8657b] | 63 | #include "cyclic_buffer.h"
|
---|
| 64 |
|
---|
[04c7003f] | 65 | #define NAME "ns8250"
|
---|
[5fe1c32] | 66 |
|
---|
[1f8657b] | 67 | #define REG_COUNT 7
|
---|
[f4ef3c2] | 68 | #define MAX_BAUD_RATE 115200
|
---|
[cf8cc36] | 69 | #define DLAB_MASK (1 << 7)
|
---|
| 70 |
|
---|
[49698fa] | 71 | /** The number of bits of one data unit send by the serial port. */
|
---|
[cf8cc36] | 72 | typedef enum {
|
---|
| 73 | WORD_LENGTH_5,
|
---|
| 74 | WORD_LENGTH_6,
|
---|
| 75 | WORD_LENGTH_7,
|
---|
[49698fa] | 76 | WORD_LENGTH_8
|
---|
[cf8cc36] | 77 | } word_length_t;
|
---|
| 78 |
|
---|
| 79 | /** The number of stop bits used by the serial port. */
|
---|
| 80 | typedef enum {
|
---|
| 81 | /** Use one stop bit. */
|
---|
| 82 | ONE_STOP_BIT,
|
---|
| 83 | /** 1.5 stop bits for word length 5, 2 stop bits otherwise. */
|
---|
[49698fa] | 84 | TWO_STOP_BITS
|
---|
[cf8cc36] | 85 | } stop_bit_t;
|
---|
| 86 |
|
---|
[49698fa] | 87 | /** The driver data for the serial port devices. */
|
---|
[04c7003f] | 88 | typedef struct ns8250_dev_data {
|
---|
[cf8cc36] | 89 | /** Is there any client conntected to the device? */
|
---|
[1f8657b] | 90 | bool client_connected;
|
---|
[cf8cc36] | 91 | /** The irq assigned to this device. */
|
---|
[1f8657b] | 92 | int irq;
|
---|
[cf8cc36] | 93 | /** The base i/o address of the devices registers. */
|
---|
[1f8657b] | 94 | uint32_t io_addr;
|
---|
[cf8cc36] | 95 | /** The i/o port used to access the serial ports registers. */
|
---|
[1f8657b] | 96 | ioport8_t *port;
|
---|
[49698fa] | 97 | /** The buffer for incomming data. */
|
---|
[1f8657b] | 98 | cyclic_buffer_t input_buffer;
|
---|
[49698fa] | 99 | /** The fibril mutex for synchronizing the access to the device. */
|
---|
| 100 | fibril_mutex_t mutex;
|
---|
[04c7003f] | 101 | } ns8250_dev_data_t;
|
---|
[5fe1c32] | 102 |
|
---|
[cf8cc36] | 103 | /** Create driver data for a device.
|
---|
[49698fa] | 104 | *
|
---|
| 105 | * @return The driver data.
|
---|
[cf8cc36] | 106 | */
|
---|
[49698fa] | 107 | static ns8250_dev_data_t *create_ns8250_dev_data(void)
|
---|
[1f8657b] | 108 | {
|
---|
[49698fa] | 109 | ns8250_dev_data_t *data;
|
---|
| 110 |
|
---|
| 111 | data = (ns8250_dev_data_t *) malloc(sizeof(ns8250_dev_data_t));
|
---|
[1f8657b] | 112 | if (NULL != data) {
|
---|
[04c7003f] | 113 | memset(data, 0, sizeof(ns8250_dev_data_t));
|
---|
[1f8657b] | 114 | fibril_mutex_initialize(&data->mutex);
|
---|
| 115 | }
|
---|
[49698fa] | 116 | return data;
|
---|
[1f8657b] | 117 | }
|
---|
| 118 |
|
---|
[cf8cc36] | 119 | /** Delete driver data.
|
---|
[49698fa] | 120 | *
|
---|
| 121 | * @param data The driver data structure.
|
---|
[cf8cc36] | 122 | */
|
---|
[49698fa] | 123 | static void delete_ns8250_dev_data(ns8250_dev_data_t *data)
|
---|
[1f8657b] | 124 | {
|
---|
[33dbbd2] | 125 | if (data != NULL)
|
---|
[1f8657b] | 126 | free(data);
|
---|
| 127 | }
|
---|
[5fe1c32] | 128 |
|
---|
[cf8cc36] | 129 | /** Find out if there is some incomming data available on the serial port.
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 134 | */
|
---|
[49698fa] | 135 | static bool ns8250_received(ioport8_t *port)
|
---|
[ca97cad] | 136 | {
|
---|
[49698fa] | 137 | return (pio_read_8(port + 5) & 1) != 0;
|
---|
[ca97cad] | 138 | }
|
---|
| 139 |
|
---|
[cf8cc36] | 140 | /** Read one byte from the serial port.
|
---|
[49698fa] | 141 | *
|
---|
| 142 | * @param port The base address of the serial port device's ports.
|
---|
| 143 | * @return The data read.
|
---|
[cf8cc36] | 144 | */
|
---|
[49698fa] | 145 | static uint8_t ns8250_read_8(ioport8_t *port)
|
---|
[ca97cad] | 146 | {
|
---|
| 147 | return pio_read_8(port);
|
---|
| 148 | }
|
---|
| 149 |
|
---|
[cf8cc36] | 150 | /** Find out wheter it is possible to send data.
|
---|
[49698fa] | 151 | *
|
---|
| 152 | * @param port The base address of the serial port device's ports.
|
---|
[cf8cc36] | 153 | */
|
---|
[49698fa] | 154 | static bool is_transmit_empty(ioport8_t *port)
|
---|
[ca97cad] | 155 | {
|
---|
[49698fa] | 156 | return (pio_read_8(port + 5) & 0x20) != 0;
|
---|
[ca97cad] | 157 | }
|
---|
| 158 |
|
---|
[cf8cc36] | 159 | /** Write one character on the serial port.
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 163 | */
|
---|
[49698fa] | 164 | static void ns8250_write_8(ioport8_t *port, uint8_t c)
|
---|
[ca97cad] | 165 | {
|
---|
[49698fa] | 166 | while (!is_transmit_empty(port))
|
---|
[ca97cad] | 167 | ;
|
---|
| 168 |
|
---|
| 169 | pio_write_8(port, c);
|
---|
| 170 | }
|
---|
| 171 |
|
---|
[cf8cc36] | 172 | /** Read data from the serial port device.
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 180 | */
|
---|
[49698fa] | 181 | static int ns8250_read(device_t *dev, char *buf, size_t count)
|
---|
[f658458] | 182 | {
|
---|
[cf8cc36] | 183 | int ret = EOK;
|
---|
[49698fa] | 184 | ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
|
---|
[bb864a0] | 185 |
|
---|
| 186 | fibril_mutex_lock(&data->mutex);
|
---|
[cf8cc36] | 187 | while (!buf_is_empty(&data->input_buffer) && (size_t)ret < count) {
|
---|
[bb864a0] | 188 | buf[ret] = (char)buf_pop_front(&data->input_buffer);
|
---|
| 189 | ret++;
|
---|
| 190 | }
|
---|
| 191 | fibril_mutex_unlock(&data->mutex);
|
---|
| 192 |
|
---|
| 193 | return ret;
|
---|
[f658458] | 194 | }
|
---|
| 195 |
|
---|
[cf8cc36] | 196 | /** Write a character to the serial port.
|
---|
[49698fa] | 197 | *
|
---|
| 198 | * @param data The serial port device's driver data.
|
---|
| 199 | * @param c The character to be written.
|
---|
[cf8cc36] | 200 | */
|
---|
[04c7003f] | 201 | static inline void ns8250_putchar(ns8250_dev_data_t *data, uint8_t c)
|
---|
[49698fa] | 202 | {
|
---|
[ca97cad] | 203 | fibril_mutex_lock(&data->mutex);
|
---|
[49698fa] | 204 | ns8250_write_8(data->port, c);
|
---|
[ca97cad] | 205 | fibril_mutex_unlock(&data->mutex);
|
---|
| 206 | }
|
---|
| 207 |
|
---|
[cf8cc36] | 208 | /** Write data to the serial port.
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 214 | */
|
---|
[04c7003f] | 215 | static int ns8250_write(device_t *dev, char *buf, size_t count)
|
---|
[f658458] | 216 | {
|
---|
[49698fa] | 217 | ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
|
---|
[ca97cad] | 218 | size_t idx;
|
---|
[49698fa] | 219 |
|
---|
| 220 | for (idx = 0; idx < count; idx++)
|
---|
| 221 | ns8250_putchar(data, (uint8_t) buf[idx]);
|
---|
[ca97cad] | 222 |
|
---|
[f658458] | 223 | return 0;
|
---|
| 224 | }
|
---|
| 225 |
|
---|
[5159ae9] | 226 | static device_ops_t ns8250_dev_ops;
|
---|
[5fe1c32] | 227 |
|
---|
[49698fa] | 228 | /** The character interface's callbacks. */
|
---|
[04c7003f] | 229 | static char_iface_t ns8250_char_iface = {
|
---|
| 230 | .read = &ns8250_read,
|
---|
| 231 | .write = &ns8250_write
|
---|
[f658458] | 232 | };
|
---|
| 233 |
|
---|
[04c7003f] | 234 | static int ns8250_add_device(device_t *dev);
|
---|
[5fe1c32] | 235 |
|
---|
[49698fa] | 236 | /** The serial port device driver's standard operations. */
|
---|
[04c7003f] | 237 | static driver_ops_t ns8250_ops = {
|
---|
| 238 | .add_device = &ns8250_add_device
|
---|
[5fe1c32] | 239 | };
|
---|
| 240 |
|
---|
[49698fa] | 241 | /** The serial port device driver structure. */
|
---|
[04c7003f] | 242 | static driver_t ns8250_driver = {
|
---|
[5fe1c32] | 243 | .name = NAME,
|
---|
[04c7003f] | 244 | .driver_ops = &ns8250_ops
|
---|
[5fe1c32] | 245 | };
|
---|
| 246 |
|
---|
[cf8cc36] | 247 | /** Clean up the serial port device structure.
|
---|
[49698fa] | 248 | *
|
---|
| 249 | * @param dev The device structure.
|
---|
[cf8cc36] | 250 | */
|
---|
[04c7003f] | 251 | static void ns8250_dev_cleanup(device_t *dev)
|
---|
[1f8657b] | 252 | {
|
---|
[33dbbd2] | 253 | if (dev->driver_data != NULL) {
|
---|
[49698fa] | 254 | delete_ns8250_dev_data((ns8250_dev_data_t*) dev->driver_data);
|
---|
[1f8657b] | 255 | dev->driver_data = NULL;
|
---|
| 256 | }
|
---|
| 257 |
|
---|
| 258 | if (dev->parent_phone > 0) {
|
---|
| 259 | ipc_hangup(dev->parent_phone);
|
---|
| 260 | dev->parent_phone = 0;
|
---|
[49698fa] | 261 | }
|
---|
[1f8657b] | 262 | }
|
---|
| 263 |
|
---|
[cf8cc36] | 264 | /** Enable the i/o ports of the device.
|
---|
[49698fa] | 265 | *
|
---|
| 266 | * @param dev The serial port device.
|
---|
| 267 | * @return True on success, false otherwise.
|
---|
[cf8cc36] | 268 | */
|
---|
[04c7003f] | 269 | static bool ns8250_pio_enable(device_t *dev)
|
---|
[1f8657b] | 270 | {
|
---|
[04c7003f] | 271 | printf(NAME ": ns8250_pio_enable %s\n", dev->name);
|
---|
[1f8657b] | 272 |
|
---|
[04c7003f] | 273 | ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
|
---|
[1f8657b] | 274 |
|
---|
[49698fa] | 275 | /* Gain control over port's registers. */
|
---|
| 276 | if (pio_enable((void *) data->io_addr, REG_COUNT,
|
---|
| 277 | (void **) &data->port)) {
|
---|
[7e752b2] | 278 | printf(NAME ": error - cannot gain the port %#" PRIx32 " for device "
|
---|
[49698fa] | 279 | "%s.\n", data->io_addr, dev->name);
|
---|
[1f8657b] | 280 | return false;
|
---|
| 281 | }
|
---|
| 282 |
|
---|
| 283 | return true;
|
---|
| 284 | }
|
---|
| 285 |
|
---|
[cf8cc36] | 286 | /** Probe the serial port device for its presence.
|
---|
[49698fa] | 287 | *
|
---|
| 288 | * @param dev The serial port device.
|
---|
| 289 | * @return True if the device is present, false otherwise.
|
---|
[cf8cc36] | 290 | */
|
---|
[04c7003f] | 291 | static bool ns8250_dev_probe(device_t *dev)
|
---|
[1f8657b] | 292 | {
|
---|
[04c7003f] | 293 | printf(NAME ": ns8250_dev_probe %s\n", dev->name);
|
---|
[1f8657b] | 294 |
|
---|
[49698fa] | 295 | ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
|
---|
| 296 | ioport8_t *port_addr = data->port;
|
---|
[1f8657b] | 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);
|
---|
[49698fa] | 303 | if (pio_read_8(port_addr + 6) & 0xf0)
|
---|
[1f8657b] | 304 | res = false;
|
---|
| 305 |
|
---|
| 306 | pio_write_8(port_addr + 4, 0x1f);
|
---|
[49698fa] | 307 | if ((pio_read_8(port_addr + 6) & 0xf0) != 0xf0)
|
---|
[1f8657b] | 308 | res = false;
|
---|
| 309 |
|
---|
| 310 | pio_write_8(port_addr + 4, olddata);
|
---|
| 311 |
|
---|
[49698fa] | 312 | if (!res)
|
---|
[1f8657b] | 313 | printf(NAME ": device %s is not present.\n", dev->name);
|
---|
| 314 |
|
---|
[49698fa] | 315 | return res;
|
---|
[1f8657b] | 316 | }
|
---|
| 317 |
|
---|
[cf8cc36] | 318 | /** Initialize serial port device.
|
---|
[49698fa] | 319 | *
|
---|
| 320 | * @param dev The serial port device.
|
---|
| 321 | * @return Zero on success, negative error number otherwise.
|
---|
[cf8cc36] | 322 | */
|
---|
[04c7003f] | 323 | static int ns8250_dev_initialize(device_t *dev)
|
---|
[1f8657b] | 324 | {
|
---|
[04c7003f] | 325 | printf(NAME ": ns8250_dev_initialize %s\n", dev->name);
|
---|
[1f8657b] | 326 |
|
---|
[df747b9c] | 327 | int ret = EOK;
|
---|
[49698fa] | 328 |
|
---|
[1f8657b] | 329 | hw_resource_list_t hw_resources;
|
---|
| 330 | memset(&hw_resources, 0, sizeof(hw_resource_list_t));
|
---|
| 331 |
|
---|
[49698fa] | 332 | /* Allocate driver data for the device. */
|
---|
| 333 | ns8250_dev_data_t *data = create_ns8250_dev_data();
|
---|
[33dbbd2] | 334 | if (data == NULL)
|
---|
[df747b9c] | 335 | return ENOMEM;
|
---|
[1f8657b] | 336 | dev->driver_data = data;
|
---|
| 337 |
|
---|
[49698fa] | 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 */
|
---|
[1f8657b] | 345 | goto failed;
|
---|
| 346 | }
|
---|
| 347 |
|
---|
[49698fa] | 348 | /* Get hw resources. */
|
---|
[1f8657b] | 349 | if (!get_hw_resources(dev->parent_phone, &hw_resources)) {
|
---|
[49698fa] | 350 | printf(NAME ": failed to get hw resources for the device "
|
---|
| 351 | "%s.\n", dev->name);
|
---|
| 352 | ret = EPARTY; /* FIXME: use another EC */
|
---|
[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:
|
---|
| 365 | data->irq = res->res.interrupt.irq;
|
---|
| 366 | irq = true;
|
---|
[49698fa] | 367 | printf(NAME ": the %s device was asigned irq = 0x%x.\n",
|
---|
| 368 | dev->name, data->irq);
|
---|
[1f8657b] | 369 | break;
|
---|
[49698fa] | 370 |
|
---|
[1f8657b] | 371 | case IO_RANGE:
|
---|
| 372 | data->io_addr = res->res.io_range.address;
|
---|
| 373 | if (res->res.io_range.size < REG_COUNT) {
|
---|
[49698fa] | 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 */
|
---|
[1f8657b] | 377 | goto failed;
|
---|
| 378 | }
|
---|
| 379 | ioport = true;
|
---|
[49698fa] | 380 | printf(NAME ": the %s device was asigned i/o address = "
|
---|
| 381 | "0x%x.\n", dev->name, data->io_addr);
|
---|
| 382 | break;
|
---|
| 383 |
|
---|
[dafe675] | 384 | default:
|
---|
| 385 | break;
|
---|
[1f8657b] | 386 | }
|
---|
| 387 | }
|
---|
| 388 |
|
---|
| 389 | if (!irq || !ioport) {
|
---|
[49698fa] | 390 | printf(NAME ": missing hw resource(s) for the device %s.\n",
|
---|
| 391 | dev->name);
|
---|
| 392 | ret = EPARTY; /* FIXME: use another EC */
|
---|
[1f8657b] | 393 | goto failed;
|
---|
[49698fa] | 394 | }
|
---|
[1f8657b] | 395 |
|
---|
| 396 | clean_hw_resource_list(&hw_resources);
|
---|
[df747b9c] | 397 | return ret;
|
---|
[1f8657b] | 398 |
|
---|
| 399 | failed:
|
---|
[49698fa] | 400 | ns8250_dev_cleanup(dev);
|
---|
| 401 | clean_hw_resource_list(&hw_resources);
|
---|
| 402 | return ret;
|
---|
[1f8657b] | 403 | }
|
---|
| 404 |
|
---|
[cf8cc36] | 405 | /** Enable interrupts on the serial port device.
|
---|
[49698fa] | 406 | *
|
---|
[cf8cc36] | 407 | * Interrupt when data is received.
|
---|
[49698fa] | 408 | *
|
---|
| 409 | * @param port The base address of the serial port device's ports.
|
---|
[cf8cc36] | 410 | */
|
---|
[04c7003f] | 411 | static inline void ns8250_port_interrupts_enable(ioport8_t *port)
|
---|
[f4ef3c2] | 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 | *
|
---|
| 419 | * @param port The base address of the serial port device's ports.
|
---|
[cf8cc36] | 420 | */
|
---|
[04c7003f] | 421 | static 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 | *
|
---|
| 428 | * @param dev The device.
|
---|
| 429 | * @return Zero on success, negative error number otherwise.
|
---|
[cf8cc36] | 430 | */
|
---|
[04c7003f] | 431 | static int ns8250_interrupt_enable(device_t *dev)
|
---|
[cfe7716] | 432 | {
|
---|
[49698fa] | 433 | ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
|
---|
[2300b9d] | 434 | int res;
|
---|
[49698fa] | 435 |
|
---|
| 436 | /* Enable interrupt globally. */
|
---|
| 437 | res = interrupt_enable(data->irq);
|
---|
[33dbbd2] | 438 | if (res != EOK)
|
---|
[2300b9d] | 439 | return res;
|
---|
[cfe7716] | 440 |
|
---|
[49698fa] | 441 | /* Enable interrupt on the serial port. */
|
---|
[04c7003f] | 442 | ns8250_port_interrupts_enable(data->port);
|
---|
[cfe7716] | 443 |
|
---|
| 444 | return EOK;
|
---|
| 445 | }
|
---|
| 446 |
|
---|
[cf8cc36] | 447 | /** Set Divisor Latch Access Bit.
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 453 | */
|
---|
| 454 | static inline void enable_dlab(ioport8_t *port)
|
---|
| 455 | {
|
---|
| 456 | uint8_t val = pio_read_8(port + 3);
|
---|
[49698fa] | 457 | pio_write_8(port + 3, val | DLAB_MASK);
|
---|
[cf8cc36] | 458 | }
|
---|
| 459 |
|
---|
| 460 | /** Clear Divisor Latch Access Bit.
|
---|
[49698fa] | 461 | *
|
---|
| 462 | * @param port The base address of the serial port device's ports.
|
---|
[cf8cc36] | 463 | */
|
---|
| 464 | static 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.
|
---|
[49698fa] | 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).
|
---|
[cf8cc36] | 476 | */
|
---|
[04c7003f] | 477 | static int ns8250_port_set_baud_rate(ioport8_t *port, unsigned int baud_rate)
|
---|
[f4ef3c2] | 478 | {
|
---|
| 479 | uint16_t divisor;
|
---|
| 480 | uint8_t div_low, div_high;
|
---|
| 481 |
|
---|
[33dbbd2] | 482 | if (baud_rate < 50 || MAX_BAUD_RATE % baud_rate != 0) {
|
---|
[49698fa] | 483 | printf(NAME ": error - somebody tried to set invalid baud rate "
|
---|
| 484 | "%d\n", baud_rate);
|
---|
| 485 | return EINVAL;
|
---|
[f4ef3c2] | 486 | }
|
---|
| 487 |
|
---|
| 488 | divisor = MAX_BAUD_RATE / baud_rate;
|
---|
| 489 | div_low = (uint8_t)divisor;
|
---|
[49698fa] | 490 | div_high = (uint8_t)(divisor >> 8);
|
---|
[f4ef3c2] | 491 |
|
---|
[49698fa] | 492 | /* Enable DLAB to be able to access baud rate divisor. */
|
---|
| 493 | enable_dlab(port);
|
---|
[f4ef3c2] | 494 |
|
---|
[49698fa] | 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);
|
---|
[f4ef3c2] | 499 |
|
---|
[49698fa] | 500 | clear_dlab(port);
|
---|
[f4ef3c2] | 501 |
|
---|
[49698fa] | 502 | return EOK;
|
---|
[f4ef3c2] | 503 | }
|
---|
| 504 |
|
---|
[cf8cc36] | 505 | /** Get baud rate used by the serial port device.
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 509 | */
|
---|
| 510 | static unsigned int ns8250_port_get_baud_rate(ioport8_t *port)
|
---|
[f4ef3c2] | 511 | {
|
---|
[cf8cc36] | 512 | uint16_t divisor;
|
---|
| 513 | uint8_t div_low, div_high;
|
---|
[f4ef3c2] | 514 |
|
---|
[49698fa] | 515 | /* Enable DLAB to be able to access baud rate divisor. */
|
---|
[cf8cc36] | 516 | enable_dlab(port);
|
---|
[f619943a] | 517 |
|
---|
[49698fa] | 518 | /* Get divisor low byte. */
|
---|
[cf8cc36] | 519 | div_low = pio_read_8(port + 0);
|
---|
[49698fa] | 520 | /* Get divisor high byte. */
|
---|
| 521 | div_high = pio_read_8(port + 1);
|
---|
[f4ef3c2] | 522 |
|
---|
[cf8cc36] | 523 | clear_dlab(port);
|
---|
| 524 |
|
---|
| 525 | divisor = (div_high << 8) | div_low;
|
---|
| 526 | return MAX_BAUD_RATE / divisor;
|
---|
| 527 | }
|
---|
| 528 |
|
---|
[49698fa] | 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).
|
---|
[cf8cc36] | 535 | */
|
---|
[33dbbd2] | 536 | static void ns8250_port_get_com_props(ioport8_t *port, unsigned int *parity,
|
---|
[49698fa] | 537 | unsigned int *word_length, unsigned int *stop_bits)
|
---|
[cf8cc36] | 538 | {
|
---|
| 539 | uint8_t val;
|
---|
| 540 |
|
---|
| 541 | val = pio_read_8(port + 3);
|
---|
| 542 | *parity = ((val >> 3) & 7);
|
---|
| 543 |
|
---|
| 544 | switch (val & 3) {
|
---|
[49698fa] | 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;
|
---|
[cf8cc36] | 557 | }
|
---|
| 558 |
|
---|
[49698fa] | 559 | if ((val >> 2) & 1)
|
---|
[cf8cc36] | 560 | *stop_bits = 2;
|
---|
[49698fa] | 561 | else
|
---|
[cf8cc36] | 562 | *stop_bits = 1;
|
---|
[f4ef3c2] | 563 | }
|
---|
| 564 |
|
---|
[cf8cc36] | 565 | /** Set the parameters of the serial communication on the serial port device.
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 572 | */
|
---|
[33dbbd2] | 573 | static int ns8250_port_set_com_props(ioport8_t *port, unsigned int parity,
|
---|
[49698fa] | 574 | unsigned int word_length, unsigned int stop_bits)
|
---|
[cf8cc36] | 575 | {
|
---|
| 576 | uint8_t val;
|
---|
| 577 |
|
---|
| 578 | switch (word_length) {
|
---|
[49698fa] | 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;
|
---|
[cf8cc36] | 593 | }
|
---|
| 594 |
|
---|
| 595 | switch (stop_bits) {
|
---|
[49698fa] | 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;
|
---|
[cf8cc36] | 604 | }
|
---|
| 605 |
|
---|
| 606 | switch (parity) {
|
---|
[49698fa] | 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;
|
---|
[cf8cc36] | 616 | }
|
---|
| 617 |
|
---|
[49698fa] | 618 | pio_write_8(port + 3, val);
|
---|
[cf8cc36] | 619 |
|
---|
| 620 | return EOK;
|
---|
| 621 | }
|
---|
| 622 |
|
---|
| 623 | /** Initialize the serial port device.
|
---|
[49698fa] | 624 | *
|
---|
[cf8cc36] | 625 | * Set the default parameters of the serial communication.
|
---|
[49698fa] | 626 | *
|
---|
| 627 | * @param dev The serial port device.
|
---|
[cf8cc36] | 628 | */
|
---|
[04c7003f] | 629 | static void ns8250_initialize_port(device_t *dev)
|
---|
[cfe7716] | 630 | {
|
---|
[04c7003f] | 631 | ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
|
---|
[cfe7716] | 632 | ioport8_t *port = data->port;
|
---|
| 633 |
|
---|
[49698fa] | 634 | /* Disable interrupts. */
|
---|
| 635 | ns8250_port_interrupts_disable(port);
|
---|
| 636 | /* Set baud rate. */
|
---|
[04c7003f] | 637 | ns8250_port_set_baud_rate(port, 38400);
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 653 | */
|
---|
[04c7003f] | 654 | static void ns8250_read_from_device(device_t *dev)
|
---|
[7f8b581] | 655 | {
|
---|
[49698fa] | 656 | ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
|
---|
[dafe675] | 657 | ioport8_t *port = data->port;
|
---|
| 658 | bool cont = true;
|
---|
| 659 |
|
---|
[49698fa] | 660 | while (cont) {
|
---|
[ca97cad] | 661 | fibril_mutex_lock(&data->mutex);
|
---|
| 662 |
|
---|
[cf8cc36] | 663 | cont = ns8250_received(port);
|
---|
| 664 | if (cont) {
|
---|
[04c7003f] | 665 | uint8_t val = ns8250_read_8(port);
|
---|
[ca97cad] | 666 |
|
---|
[dafe675] | 667 | if (data->client_connected) {
|
---|
[49698fa] | 668 | if (!buf_push_back(&data->input_buffer, val)) {
|
---|
| 669 | printf(NAME ": buffer overflow on "
|
---|
| 670 | "%s.\n", dev->name);
|
---|
[dafe675] | 671 | } else {
|
---|
[49698fa] | 672 | printf(NAME ": the character %c saved "
|
---|
| 673 | "to the buffer of %s.\n",
|
---|
| 674 | val, dev->name);
|
---|
[dafe675] | 675 | }
|
---|
[49698fa] | 676 | }
|
---|
[dafe675] | 677 | }
|
---|
| 678 |
|
---|
[49698fa] | 679 | fibril_mutex_unlock(&data->mutex);
|
---|
| 680 | fibril_yield();
|
---|
| 681 | }
|
---|
[2300b9d] | 682 | }
|
---|
| 683 |
|
---|
[cf8cc36] | 684 | /** The interrupt handler.
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 690 | */
|
---|
[33dbbd2] | 691 | static inline void ns8250_interrupt_handler(device_t *dev, ipc_callid_t iid,
|
---|
| 692 | ipc_call_t *icall)
|
---|
[2300b9d] | 693 | {
|
---|
[04c7003f] | 694 | ns8250_read_from_device(dev);
|
---|
[7f8b581] | 695 | }
|
---|
| 696 |
|
---|
[cf8cc36] | 697 | /** Register the interrupt handler for the device.
|
---|
[49698fa] | 698 | *
|
---|
| 699 | * @param dev The serial port device.
|
---|
[cf8cc36] | 700 | */
|
---|
[04c7003f] | 701 | static inline int ns8250_register_interrupt_handler(device_t *dev)
|
---|
[7f8b581] | 702 | {
|
---|
[49698fa] | 703 | ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
|
---|
[7f8b581] | 704 |
|
---|
[49698fa] | 705 | return register_interrupt_handler(dev, data->irq,
|
---|
| 706 | ns8250_interrupt_handler, NULL);
|
---|
[2300b9d] | 707 | }
|
---|
| 708 |
|
---|
[cf8cc36] | 709 | /** Unregister the interrupt handler for the device.
|
---|
[49698fa] | 710 | *
|
---|
| 711 | * @param dev The serial port device.
|
---|
[cf8cc36] | 712 | */
|
---|
[04c7003f] | 713 | static inline int ns8250_unregister_interrupt_handler(device_t *dev)
|
---|
[2300b9d] | 714 | {
|
---|
[49698fa] | 715 | ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
|
---|
[7f8b581] | 716 |
|
---|
[49698fa] | 717 | return unregister_interrupt_handler(dev, data->irq);
|
---|
[7f8b581] | 718 | }
|
---|
| 719 |
|
---|
[cf8cc36] | 720 | /** The add_device callback method of the serial port driver.
|
---|
[49698fa] | 721 | *
|
---|
[cf8cc36] | 722 | * Probe and initialize the newly added device.
|
---|
[49698fa] | 723 | *
|
---|
| 724 | * @param dev The serial port device.
|
---|
[cf8cc36] | 725 | */
|
---|
[33dbbd2] | 726 | static int ns8250_add_device(device_t *dev)
|
---|
[5fe1c32] | 727 | {
|
---|
[49698fa] | 728 | printf(NAME ": ns8250_add_device %s (handle = %d)\n",
|
---|
| 729 | dev->name, dev->handle);
|
---|
[5fe1c32] | 730 |
|
---|
[04c7003f] | 731 | int res = ns8250_dev_initialize(dev);
|
---|
[33dbbd2] | 732 | if (res != EOK)
|
---|
[df747b9c] | 733 | return res;
|
---|
[1f8657b] | 734 |
|
---|
[04c7003f] | 735 | if (!ns8250_pio_enable(dev)) {
|
---|
| 736 | ns8250_dev_cleanup(dev);
|
---|
[df747b9c] | 737 | return EADDRNOTAVAIL;
|
---|
[49698fa] | 738 | }
|
---|
[df747b9c] | 739 |
|
---|
[49698fa] | 740 | /* Find out whether the device is present. */
|
---|
[04c7003f] | 741 | if (!ns8250_dev_probe(dev)) {
|
---|
| 742 | ns8250_dev_cleanup(dev);
|
---|
[df747b9c] | 743 | return ENOENT;
|
---|
[49698fa] | 744 | }
|
---|
[1f8657b] | 745 |
|
---|
[49698fa] | 746 | /* Serial port initialization (baud rate etc.). */
|
---|
[04c7003f] | 747 | ns8250_initialize_port(dev);
|
---|
[cfe7716] | 748 |
|
---|
[49698fa] | 749 | /* Register interrupt handler. */
|
---|
[33dbbd2] | 750 | if (ns8250_register_interrupt_handler(dev) != EOK) {
|
---|
[2300b9d] | 751 | printf(NAME ": failed to register interrupt handler.\n");
|
---|
[04c7003f] | 752 | ns8250_dev_cleanup(dev);
|
---|
[2300b9d] | 753 | return res;
|
---|
[7f8b581] | 754 | }
|
---|
[cfe7716] | 755 |
|
---|
[49698fa] | 756 | /* Enable interrupt. */
|
---|
| 757 | res = ns8250_interrupt_enable(dev);
|
---|
[33dbbd2] | 758 | if (res != EOK) {
|
---|
[49698fa] | 759 | printf(NAME ": failed to enable the interrupt. Error code = "
|
---|
| 760 | "%d.\n", res);
|
---|
[04c7003f] | 761 | ns8250_dev_cleanup(dev);
|
---|
| 762 | ns8250_unregister_interrupt_handler(dev);
|
---|
[cfe7716] | 763 | return res;
|
---|
[49698fa] | 764 | }
|
---|
[f658458] | 765 |
|
---|
[49698fa] | 766 | /* Set device operations. */
|
---|
[5159ae9] | 767 | dev->ops = &ns8250_dev_ops;
|
---|
[5fe1c32] | 768 |
|
---|
[692c40cb] | 769 | add_device_to_class(dev, "serial");
|
---|
| 770 |
|
---|
[49698fa] | 771 | printf(NAME ": the %s device has been successfully initialized.\n",
|
---|
| 772 | dev->name);
|
---|
[2300b9d] | 773 |
|
---|
[df747b9c] | 774 | return EOK;
|
---|
[5fe1c32] | 775 | }
|
---|
| 776 |
|
---|
[25a7e11d] | 777 | /** Open the device.
|
---|
[49698fa] | 778 | *
|
---|
| 779 | * This is a callback function called when a client tries to connect to the
|
---|
| 780 | * device.
|
---|
| 781 | *
|
---|
| 782 | * @param dev The device.
|
---|
[25a7e11d] | 783 | */
|
---|
[04c7003f] | 784 | static int ns8250_open(device_t *dev)
|
---|
[25a7e11d] | 785 | {
|
---|
[49698fa] | 786 | ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
|
---|
[25a7e11d] | 787 | int res;
|
---|
| 788 |
|
---|
[49698fa] | 789 | fibril_mutex_lock(&data->mutex);
|
---|
[25a7e11d] | 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.
|
---|
[49698fa] | 802 | *
|
---|
| 803 | * This is a callback function called when a client tries to disconnect from
|
---|
| 804 | * the device.
|
---|
| 805 | *
|
---|
| 806 | * @param dev The device.
|
---|
[25a7e11d] | 807 | */
|
---|
[04c7003f] | 808 | static void ns8250_close(device_t *dev)
|
---|
[25a7e11d] | 809 | {
|
---|
[49698fa] | 810 | ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
|
---|
[25a7e11d] | 811 |
|
---|
| 812 | fibril_mutex_lock(&data->mutex);
|
---|
| 813 |
|
---|
[49698fa] | 814 | assert(data->client_connected);
|
---|
[25a7e11d] | 815 |
|
---|
| 816 | data->client_connected = false;
|
---|
| 817 | buf_clear(&data->input_buffer);
|
---|
| 818 |
|
---|
[49698fa] | 819 | fibril_mutex_unlock(&data->mutex);
|
---|
[25a7e11d] | 820 | }
|
---|
| 821 |
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 830 | */
|
---|
[49698fa] | 831 | static void
|
---|
| 832 | ns8250_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;
|
---|
[cf8cc36] | 836 | ioport8_t *port = data->port;
|
---|
| 837 |
|
---|
[49698fa] | 838 | fibril_mutex_lock(&data->mutex);
|
---|
| 839 | ns8250_port_interrupts_disable(port);
|
---|
[cf8cc36] | 840 | *baud_rate = ns8250_port_get_baud_rate(port);
|
---|
[49698fa] | 841 | ns8250_port_get_com_props(port, parity, word_length, stop_bits);
|
---|
[cf8cc36] | 842 | ns8250_port_interrupts_enable(port);
|
---|
[49698fa] | 843 | fibril_mutex_unlock(&data->mutex);
|
---|
[cf8cc36] | 844 |
|
---|
[49698fa] | 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);
|
---|
[cf8cc36] | 848 | }
|
---|
| 849 |
|
---|
[49698fa] | 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.
|
---|
[cf8cc36] | 858 | */
|
---|
[33dbbd2] | 859 | static int ns8250_set_props(device_t *dev, unsigned int baud_rate,
|
---|
| 860 | unsigned int parity, unsigned int word_length, unsigned int stop_bits)
|
---|
[cf8cc36] | 861 | {
|
---|
[49698fa] | 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);
|
---|
[cf8cc36] | 865 |
|
---|
[49698fa] | 866 | ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
|
---|
[cf8cc36] | 867 | ioport8_t *port = data->port;
|
---|
| 868 | int ret;
|
---|
| 869 |
|
---|
[49698fa] | 870 | fibril_mutex_lock(&data->mutex);
|
---|
| 871 | ns8250_port_interrupts_disable(port);
|
---|
[cf8cc36] | 872 | ret = ns8250_port_set_baud_rate(port, baud_rate);
|
---|
[33dbbd2] | 873 | if (ret == EOK)
|
---|
[cf8cc36] | 874 | ret = ns8250_port_set_com_props(port, parity, word_length, stop_bits);
|
---|
| 875 | ns8250_port_interrupts_enable(port);
|
---|
[49698fa] | 876 | fibril_mutex_unlock(&data->mutex);
|
---|
[cf8cc36] | 877 |
|
---|
[49698fa] | 878 | return ret;
|
---|
[cf8cc36] | 879 | }
|
---|
| 880 |
|
---|
[49698fa] | 881 | /** Default handler for client requests which are not handled by the standard
|
---|
| 882 | * interfaces.
|
---|
| 883 | *
|
---|
[f4ef3c2] | 884 | * Configure the parameters of the serial communication.
|
---|
| 885 | */
|
---|
[33dbbd2] | 886 | static void ns8250_default_handler(device_t *dev, ipc_callid_t callid,
|
---|
| 887 | ipc_call_t *call)
|
---|
[f4ef3c2] | 888 | {
|
---|
| 889 | ipcarg_t method = IPC_GET_METHOD(*call);
|
---|
| 890 | int ret;
|
---|
[49698fa] | 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;
|
---|
[cf8cc36] | 900 |
|
---|
[49698fa] | 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 | }
|
---|
[f4ef3c2] | 914 | }
|
---|
| 915 |
|
---|
[25a7e11d] | 916 | /** Initialize the serial port driver.
|
---|
[49698fa] | 917 | *
|
---|
| 918 | * Initialize device operations structures with callback methods for handling
|
---|
[25a7e11d] | 919 | * client requests to the serial port devices.
|
---|
| 920 | */
|
---|
[49698fa] | 921 | static void ns8250_init(void)
|
---|
[5fe1c32] | 922 | {
|
---|
[5159ae9] | 923 | ns8250_dev_ops.open = &ns8250_open;
|
---|
[49698fa] | 924 | ns8250_dev_ops.close = &ns8250_close;
|
---|
[f658458] | 925 |
|
---|
[5159ae9] | 926 | ns8250_dev_ops.interfaces[CHAR_DEV_IFACE] = &ns8250_char_iface;
|
---|
| 927 | ns8250_dev_ops.default_handler = &ns8250_default_handler;
|
---|
[5fe1c32] | 928 | }
|
---|
| 929 |
|
---|
| 930 | int main(int argc, char *argv[])
|
---|
| 931 | {
|
---|
[49698fa] | 932 | printf(NAME ": HelenOS serial port driver\n");
|
---|
[04c7003f] | 933 | ns8250_init();
|
---|
| 934 | return driver_main(&ns8250_driver);
|
---|
[5fe1c32] | 935 | }
|
---|
| 936 |
|
---|
| 937 | /**
|
---|
| 938 | * @}
|
---|
[49698fa] | 939 | */
|
---|