source: mainline/uspace/drv/ns8250/ns8250.c@ 7a252ec8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7a252ec8 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
Line 
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/**
30 * @defgroup ns8250 Serial port driver.
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>
44#include <str.h>
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>
51#include <ddi.h>
52#include <libarch/ddi.h>
53
54#include <driver.h>
55#include <char.h>
56#include <resource.h>
57
58#include <devman.h>
59#include <ipc/devman.h>
60#include <device/hw_res.h>
61#include <ipc/serial_ctl.h>
62
63#include "cyclic_buffer.h"
64
65#define NAME "ns8250"
66
67#define REG_COUNT 7
68#define MAX_BAUD_RATE 115200
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 */
89typedef struct ns8250_dev_data {
90 /** Is there any client conntected to the device? */
91 bool client_connected;
92 /** The irq assigned to this device. */
93 int irq;
94 /** The base i/o address of the devices registers. */
95 uint32_t io_addr;
96 /** The i/o port used to access the serial ports registers. */
97 ioport8_t *port;
98 /** The buffer for incomming data.*/
99 cyclic_buffer_t input_buffer;
100 /** The fibril mutex for synchronizing the access to the device.*/
101 fibril_mutex_t mutex;
102} ns8250_dev_data_t;
103
104/** Create driver data for a device.
105 *
106 * @return the driver data.
107 */
108static ns8250_dev_data_t * create_ns8250_dev_data()
109{
110 ns8250_dev_data_t *data = (ns8250_dev_data_t *)malloc(sizeof(ns8250_dev_data_t));
111 if (NULL != data) {
112 memset(data, 0, sizeof(ns8250_dev_data_t));
113 fibril_mutex_initialize(&data->mutex);
114 }
115 return data;
116}
117
118/** Delete driver data.
119 *
120 * @param data the driver data structure.
121 */
122static void delete_ns8250_dev_data(ns8250_dev_data_t *data)
123{
124 if (NULL != data) {
125 free(data);
126 }
127}
128
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 */
134static bool ns8250_received(ioport8_t *port)
135{
136 return (pio_read_8(port + 5) & 1) != 0;
137}
138
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 */
144static uint8_t ns8250_read_8(ioport8_t *port)
145{
146 return pio_read_8(port);
147}
148
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 */
153static bool is_transmit_empty(ioport8_t *port)
154{
155 return (pio_read_8(port + 5) & 0x20) != 0;
156}
157
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 */
163static void ns8250_write_8(ioport8_t *port, uint8_t c)
164{
165 while (!is_transmit_empty(port))
166 ;
167
168 pio_write_8(port, c);
169}
170
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 */
179static int ns8250_read(device_t *dev, char *buf, size_t count)
180{
181 // printf(NAME ": ns8250_read %s\n", dev->name);
182
183 int ret = EOK;
184
185 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
186 fibril_mutex_lock(&data->mutex);
187
188 while (!buf_is_empty(&data->input_buffer) && (size_t)ret < count) {
189 buf[ret] = (char)buf_pop_front(&data->input_buffer);
190 ret++;
191 }
192
193 fibril_mutex_unlock(&data->mutex);
194
195 return ret;
196}
197
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 */
203static inline void ns8250_putchar(ns8250_dev_data_t *data, uint8_t c)
204{
205 fibril_mutex_lock(&data->mutex);
206 ns8250_write_8(data->port, c);
207 fibril_mutex_unlock(&data->mutex);
208}
209
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 */
218static int ns8250_write(device_t *dev, char *buf, size_t count)
219{
220 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
221
222 size_t idx;
223 for (idx = 0; idx < count; idx++) {
224 ns8250_putchar(data, (uint8_t)buf[idx]);
225 }
226
227 return 0;
228}
229
230static device_ops_t ns8250_dev_ops;
231
232/** The character interface's callbacks.
233 */
234static char_iface_t ns8250_char_iface = {
235 .read = &ns8250_read,
236 .write = &ns8250_write
237};
238
239static int ns8250_add_device(device_t *dev);
240
241/** The serial port device driver's standard operations.
242 */
243static driver_ops_t ns8250_ops = {
244 .add_device = &ns8250_add_device
245};
246
247/** The serial port device driver structure.
248 */
249static driver_t ns8250_driver = {
250 .name = NAME,
251 .driver_ops = &ns8250_ops
252};
253
254/** Clean up the serial port device structure.
255 *
256 * @param dev the device structure.
257 */
258static void ns8250_dev_cleanup(device_t *dev)
259{
260 if (NULL != dev->driver_data) {
261 delete_ns8250_dev_data((ns8250_dev_data_t*)dev->driver_data);
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
271/** Enable the i/o ports of the device.
272 *
273 * @param the serial port device.
274 * @return true on success, false otherwise.
275 */
276static bool ns8250_pio_enable(device_t *dev)
277{
278 printf(NAME ": ns8250_pio_enable %s\n", dev->name);
279
280 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
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
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 */
296static bool ns8250_dev_probe(device_t *dev)
297{
298 printf(NAME ": ns8250_dev_probe %s\n", dev->name);
299
300 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
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
326/** Initialize serial port device.
327 *
328 * @param dev the serial port device.
329 * @return 0 on success, negative error number otherwise.
330 */
331static int ns8250_dev_initialize(device_t *dev)
332{
333 printf(NAME ": ns8250_dev_initialize %s\n", dev->name);
334
335 int ret = EOK;
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
340 ns8250_dev_data_t *data = create_ns8250_dev_data();
341 if (NULL == data) {
342 return ENOMEM;
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);
350 ret = EPARTY;
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);
358 ret = EPARTY;
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);
379 ret = EPARTY;
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);
384 break;
385 default:
386 break;
387 }
388 }
389
390 if (!irq || !ioport) {
391 printf(NAME ": missing hw resource(s) for the device %s.\n", dev->name);
392 ret = EPARTY;
393 goto failed;
394 }
395
396 clean_hw_resource_list(&hw_resources);
397 return ret;
398
399failed:
400 ns8250_dev_cleanup(dev);
401 clean_hw_resource_list(&hw_resources);
402 return ret;
403}
404
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 */
411static inline void ns8250_port_interrupts_enable(ioport8_t *port)
412{
413 pio_write_8(port + 1 , 0x01); // Interrupt when data received
414 pio_write_8(port + 4, 0x0B);
415}
416
417/** Disable interrupts on the serial port device.
418 *
419 * @param port the base address of the serial port device's ports.
420 */
421static inline void ns8250_port_interrupts_disable(ioport8_t *port)
422{
423 pio_write_8(port + 1, 0x00); // Disable all interrupts
424}
425
426/** Enable interrupts for the serial port device.
427 *
428 * @param dev the device.
429 * @return 0 on success, negative error number otherwise.
430 */
431static int ns8250_interrupt_enable(device_t *dev)
432{
433 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
434
435 int res;
436 // enable interrupt globally
437 if (EOK != (res = interrupt_enable(data->irq))) {
438 return res;
439 }
440
441 // enable interrupt on the serial port
442 ns8250_port_interrupts_enable(data->port);
443
444 return EOK;
445}
446
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 */
477static int ns8250_port_set_baud_rate(ioport8_t *port, unsigned int baud_rate)
478{
479 uint16_t divisor;
480 uint8_t div_low, div_high;
481
482 if (50 > baud_rate || 0 != MAX_BAUD_RATE % baud_rate) {
483 printf(NAME ": error - somebody tried to set invalid baud rate %d\n", baud_rate);
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
491 // enable DLAB to be able to access baud rate divisor
492 enable_dlab(port);
493
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);
498
499 clear_dlab(port);
500
501 return EOK;
502}
503
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)
510{
511 uint16_t divisor;
512 uint8_t div_low, div_high;
513
514 // enable DLAB to be able to access baud rate divisor
515 enable_dlab(port);
516
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);
521
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 }
563}
564
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 */
629static void ns8250_initialize_port(device_t *dev)
630{
631 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
632 ioport8_t *port = data->port;
633
634 // disable interrupts
635 ns8250_port_interrupts_disable(port);
636 // set baud rate
637 ns8250_port_set_baud_rate(port, 38400);
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);
645}
646
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 */
651static void ns8250_read_from_device(device_t *dev)
652{
653 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
654 ioport8_t *port = data->port;
655 bool cont = true;
656
657 while (cont) {
658 fibril_mutex_lock(&data->mutex);
659
660 cont = ns8250_received(port);
661 if (cont) {
662 uint8_t val = ns8250_read_8(port);
663 // printf(NAME ": character %c read from %s.\n", val, dev->name);
664
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 {
672 // printf(NAME ": no client is connected to %s, discarding the character which was read.\n", dev->name);
673 }
674 }
675
676 fibril_mutex_unlock(&data->mutex);
677
678 fibril_yield();
679 }
680}
681
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 */
689static inline void ns8250_interrupt_handler(device_t *dev, ipc_callid_t iid, ipc_call_t *icall)
690{
691 ns8250_read_from_device(dev);
692}
693
694/** Register the interrupt handler for the device.
695 *
696 * @param dev the serial port device.
697 */
698static inline int ns8250_register_interrupt_handler(device_t *dev)
699{
700 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
701
702 return register_interrupt_handler(dev, data->irq, ns8250_interrupt_handler, NULL);
703}
704
705/** Unregister the interrupt handler for the device.
706 *
707 * @param dev the serial port device.
708 */
709static inline int ns8250_unregister_interrupt_handler(device_t *dev)
710{
711 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
712
713 return unregister_interrupt_handler(dev, data->irq);
714}
715
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 */
722static int ns8250_add_device(device_t *dev)
723{
724 printf(NAME ": ns8250_add_device %s (handle = %d)\n", dev->name, dev->handle);
725
726 int res = ns8250_dev_initialize(dev);
727 if (EOK != res) {
728 return res;
729 }
730
731 if (!ns8250_pio_enable(dev)) {
732 ns8250_dev_cleanup(dev);
733 return EADDRNOTAVAIL;
734 }
735
736 // find out whether the device is present
737 if (!ns8250_dev_probe(dev)) {
738 ns8250_dev_cleanup(dev);
739 return ENOENT;
740 }
741
742 // serial port initialization (baud rate etc.)
743 ns8250_initialize_port(dev);
744
745 // register interrupt handler
746 if (EOK != ns8250_register_interrupt_handler(dev)) {
747 printf(NAME ": failed to register interrupt handler.\n");
748 ns8250_dev_cleanup(dev);
749 return res;
750 }
751
752 // enable interrupt
753 if (EOK != (res = ns8250_interrupt_enable(dev))) {
754 printf(NAME ": failed to enable the interrupt. Error code = %d.\n", res);
755 ns8250_dev_cleanup(dev);
756 ns8250_unregister_interrupt_handler(dev);
757 return res;
758 }
759
760 // set device operations
761 dev->ops = &ns8250_dev_ops;
762
763 add_device_to_class(dev, "serial");
764
765 printf(NAME ": the %s device has been successfully initialized.\n", dev->name);
766
767 return EOK;
768}
769
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 */
776static int ns8250_open(device_t *dev)
777{
778 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
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 */
801static void ns8250_close(device_t *dev)
802{
803 ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
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
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
871/** Default handler for client requests which are not handled by the standard interfaces.
872 *
873 * Configure the parameters of the serial communication.
874 */
875static void ns8250_default_handler(device_t *dev, ipc_callid_t callid, ipc_call_t *call)
876{
877 ipcarg_t method = IPC_GET_METHOD(*call);
878 int ret;
879 unsigned int baud_rate, parity, word_length, stop_bits;
880
881 switch(method) {
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);
885 break;
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);
894 break;
895
896 default:
897 ipc_answer_0(callid, ENOTSUP);
898 }
899}
900
901/** Initialize the serial port driver.
902 *
903 * Initialize device operations structures with callback methods for handling
904 * client requests to the serial port devices.
905 */
906static void ns8250_init()
907{
908 ns8250_dev_ops.open = &ns8250_open;
909 ns8250_dev_ops.close = &ns8250_close;
910
911 ns8250_dev_ops.interfaces[CHAR_DEV_IFACE] = &ns8250_char_iface;
912 ns8250_dev_ops.default_handler = &ns8250_default_handler;
913}
914
915int main(int argc, char *argv[])
916{
917 printf(NAME ": HelenOS serial port driver\n");
918 ns8250_init();
919 return driver_main(&ns8250_driver);
920}
921
922/**
923 * @}
924 */
Note: See TracBrowser for help on using the repository browser.