source: mainline/uspace/drv/ns8250/ns8250.c@ 032e0bb

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 032e0bb was a79d88d, checked in by Jakub Jermar <jakub@…>, 15 years ago

Move the device driver framework drivers under "uspace/drv" in the source tree and
"/drv" on the RAM disk.

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