source: mainline/uspace/drv/ns8250/ns8250.c@ 64d2b10

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 64d2b10 was 77429d3, checked in by Martin Decky <martin@…>, 14 years ago

remove the obsoleted SYS_INTERRUPT_ENABLE syscall

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