source: mainline/uspace/drv/char/ns8250/ns8250.c@ 612af1a0

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 612af1a0 was 128c78b, checked in by Martin Sucha <sucha14@…>, 14 years ago

Add checks and documentation to interrupt-enabling code in the ns8250 driver.

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