source: mainline/uspace/srv/drivers/serial/serial.c@ f4ef3c2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f4ef3c2 was f4ef3c2, checked in by Lenka Trochtova <trochtova.lenka@…>, 16 years ago

add default client connection handler to serial port driver (for setting the parameters of the serial communication)

  • Property mode set to 100644
File size: 14.0 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 serial 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 <string.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 "serial"
66
67#define REG_COUNT 7
68#define MAX_BAUD_RATE 115200
69
70typedef struct serial_dev_data {
71 bool client_connected;
72 int irq;
73 uint32_t io_addr;
74 ioport8_t *port;
75 cyclic_buffer_t input_buffer;
76 fibril_mutex_t mutex;
77} serial_dev_data_t;
78
79static serial_dev_data_t * create_serial_dev_data()
80{
81 serial_dev_data_t *data = (serial_dev_data_t *)malloc(sizeof(serial_dev_data_t));
82 if (NULL != data) {
83 memset(data, 0, sizeof(serial_dev_data_t));
84 fibril_mutex_initialize(&data->mutex);
85 }
86 return data;
87}
88
89static void delete_serial_dev_data(serial_dev_data_t *data)
90{
91 if (NULL != data) {
92 free(data);
93 }
94}
95
96static bool serial_received(ioport8_t *port)
97{
98 return (pio_read_8(port + 5) & 1) != 0;
99}
100
101static uint8_t serial_read_8(ioport8_t *port)
102{
103 return pio_read_8(port);
104}
105
106static bool is_transmit_empty(ioport8_t *port)
107{
108 return (pio_read_8(port + 5) & 0x20) != 0;
109}
110
111static void serial_write_8(ioport8_t *port, uint8_t c)
112{
113 while (!is_transmit_empty(port))
114 ;
115
116 pio_write_8(port, c);
117}
118
119static int serial_read(device_t *dev, char *buf, size_t count)
120{
121 printf(NAME ": serial_read %s\n", dev->name);
122
123 int ret = 0;
124
125 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
126 fibril_mutex_lock(&data->mutex);
127
128 while (!buf_is_empty(&data->input_buffer) && ret < count) {
129 buf[ret] = (char)buf_pop_front(&data->input_buffer);
130 ret++;
131 }
132
133 fibril_mutex_unlock(&data->mutex);
134
135 return ret;
136}
137
138static inline void serial_putchar(serial_dev_data_t *data, uint8_t c)
139{
140 fibril_mutex_lock(&data->mutex);
141 serial_write_8(data->port, c);
142 fibril_mutex_unlock(&data->mutex);
143}
144
145static int serial_write(device_t *dev, char *buf, size_t count)
146{
147 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
148
149 size_t idx;
150 for (idx = 0; idx < count; idx++) {
151 serial_putchar(data, (uint8_t)buf[idx]);
152 }
153
154 return 0;
155}
156
157static device_class_t serial_dev_class;
158
159static char_iface_t serial_char_iface = {
160 .read = &serial_read,
161 .write = &serial_write
162};
163
164static int serial_add_device(device_t *dev);
165
166/** The serial port device driver's standard operations.
167 */
168static driver_ops_t serial_ops = {
169 .add_device = &serial_add_device
170};
171
172/** The serial port device driver structure.
173 */
174static driver_t serial_driver = {
175 .name = NAME,
176 .driver_ops = &serial_ops
177};
178
179static void serial_dev_cleanup(device_t *dev)
180{
181 if (NULL != dev->driver_data) {
182 delete_serial_dev_data((serial_dev_data_t*)dev->driver_data);
183 dev->driver_data = NULL;
184 }
185
186 if (dev->parent_phone > 0) {
187 ipc_hangup(dev->parent_phone);
188 dev->parent_phone = 0;
189 }
190}
191
192static bool serial_pio_enable(device_t *dev)
193{
194 printf(NAME ": serial_pio_enable %s\n", dev->name);
195
196 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
197
198 // Gain control over port's registers.
199 if (pio_enable((void *)data->io_addr, REG_COUNT, (void **)(&data->port))) {
200 printf(NAME ": error - cannot gain the port %lx for device %s.\n", data->io_addr, dev->name);
201 return false;
202 }
203
204 return true;
205}
206
207static bool serial_dev_probe(device_t *dev)
208{
209 printf(NAME ": serial_dev_probe %s\n", dev->name);
210
211 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
212 ioport8_t *port_addr = data->port;
213 bool res = true;
214 uint8_t olddata;
215
216 olddata = pio_read_8(port_addr + 4);
217
218 pio_write_8(port_addr + 4, 0x10);
219 if (pio_read_8(port_addr + 6) & 0xf0) {
220 res = false;
221 }
222
223 pio_write_8(port_addr + 4, 0x1f);
224 if ((pio_read_8(port_addr + 6) & 0xf0) != 0xf0) {
225 res = false;
226 }
227
228 pio_write_8(port_addr + 4, olddata);
229
230 if (!res) {
231 printf(NAME ": device %s is not present.\n", dev->name);
232 }
233
234 return res;
235}
236
237static int serial_dev_initialize(device_t *dev)
238{
239 printf(NAME ": serial_dev_initialize %s\n", dev->name);
240
241 int ret = EOK;
242 hw_resource_list_t hw_resources;
243 memset(&hw_resources, 0, sizeof(hw_resource_list_t));
244
245 // allocate driver data for the device
246 serial_dev_data_t *data = create_serial_dev_data();
247 if (NULL == data) {
248 return ENOMEM;
249 }
250 dev->driver_data = data;
251
252 // connect to the parent's driver
253 dev->parent_phone = devman_parent_device_connect(dev->handle, IPC_FLAG_BLOCKING);
254 if (dev->parent_phone <= 0) {
255 printf(NAME ": failed to connect to the parent driver of the device %s.\n", dev->name);
256 ret = EPARTY;
257 goto failed;
258 }
259
260 // get hw resources
261
262 if (!get_hw_resources(dev->parent_phone, &hw_resources)) {
263 printf(NAME ": failed to get hw resources for the device %s.\n", dev->name);
264 ret = EPARTY;
265 goto failed;
266 }
267
268 size_t i;
269 hw_resource_t *res;
270 bool irq = false;
271 bool ioport = false;
272
273 for (i = 0; i < hw_resources.count; i++) {
274 res = &hw_resources.resources[i];
275 switch (res->type) {
276 case INTERRUPT:
277 data->irq = res->res.interrupt.irq;
278 irq = true;
279 printf(NAME ": the %s device was asigned irq = 0x%x.\n", dev->name, data->irq);
280 break;
281 case IO_RANGE:
282 data->io_addr = res->res.io_range.address;
283 if (res->res.io_range.size < REG_COUNT) {
284 printf(NAME ": i/o range assigned to the device %s is too small.\n", dev->name);
285 ret = EPARTY;
286 goto failed;
287 }
288 ioport = true;
289 printf(NAME ": the %s device was asigned i/o address = 0x%x.\n", dev->name, data->io_addr);
290 break;
291 default:
292 break;
293 }
294 }
295
296 if (!irq || !ioport) {
297 printf(NAME ": missing hw resource(s) for the device %s.\n", dev->name);
298 ret = EPARTY;
299 goto failed;
300 }
301
302 clean_hw_resource_list(&hw_resources);
303 return ret;
304
305failed:
306 serial_dev_cleanup(dev);
307 clean_hw_resource_list(&hw_resources);
308 return ret;
309}
310
311static inline void serial_port_interrupts_enable(ioport8_t *port)
312{
313 pio_write_8(port + 1 , 0x01); // Interrupt when data received
314 pio_write_8(port + 4, 0x0B);
315}
316
317static inline void serial_port_interrupts_disable(ioport8_t *port)
318{
319 pio_write_8(port + 1, 0x00); // Disable all interrupts
320}
321
322static int serial_interrupt_enable(device_t *dev)
323{
324 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
325
326 int res;
327 // enable interrupt globally
328 if (EOK != (res = interrupt_enable(data->irq))) {
329 return res;
330 }
331
332 // enable interrupt on the serial port
333 serial_port_interrupts_enable(data->port);
334
335 return EOK;
336}
337
338static int serial_port_set_baud_rate(ioport8_t *port, unsigned int baud_rate)
339{
340 uint16_t divisor;
341 uint8_t div_low, div_high;
342
343 if (50 > baud_rate || 0 != MAX_BAUD_RATE % baud_rate) {
344 return EINVAL;
345 }
346
347 divisor = MAX_BAUD_RATE / baud_rate;
348 div_low = (uint8_t)divisor;
349 div_high = (uint8_t)(divisor >> 8);
350
351 pio_write_8(port + 3, 0x80); // Enable DLAB (set baud rate divisor)
352
353 pio_write_8(port + 0, div_low); // Set divisor low byte
354 pio_write_8(port + 1, div_high); // Set divisor high byte
355
356 pio_write_8(port + 3, pio_read_8(port + 3) & (~0x80)); // Clear DLAB
357
358 return EOK;
359}
360
361static int serial_set_baud_rate(device_t *dev, unsigned int baud_rate)
362{
363 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
364 ioport8_t *port = data->port;
365 int ret;
366
367 fibril_mutex_lock(&data->mutex);
368 serial_port_interrupts_disable(port); // Disable all interrupts
369 ret = serial_port_set_baud_rate(port, baud_rate);
370 serial_port_interrupts_enable(port);
371 fibril_mutex_unlock(&data->mutex);
372
373 return ret;
374}
375
376static void serial_initialize_port(device_t *dev)
377{
378 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
379 ioport8_t *port = data->port;
380
381 serial_port_interrupts_disable(port); // Disable all interrupts
382 serial_port_set_baud_rate(port, 1200);
383 pio_write_8(port + 3, 0x07); // 8 bits, no parity, two stop bits
384 pio_write_8(port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
385 pio_write_8(port + 4, 0x0B); // RTS/DSR set (Request to Send and Data Terminal Ready lines enabled),
386 // Aux Output2 set - needed for interrupts
387}
388
389static void serial_read_from_device(device_t *dev)
390{
391 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
392 ioport8_t *port = data->port;
393 bool cont = true;
394
395 while (cont) {
396 fibril_mutex_lock(&data->mutex);
397
398 if (cont = serial_received(port)) {
399 uint8_t val = serial_read_8(port);
400 printf(NAME ": character %c read from %s.\n", val, dev->name);
401
402
403 if (data->client_connected) {
404 if (!buf_push_back(&(data->input_buffer), val)) {
405 printf(NAME ": buffer overflow on %s.\n", dev->name);
406 } else {
407 printf(NAME ": the character %c saved to the buffer of %s.\n", val, dev->name);
408 }
409 } else {
410 printf(NAME ": no client is connected to %s, discarding the character which was read.\n", dev->name);
411 }
412 }
413
414 fibril_mutex_unlock(&data->mutex);
415
416 fibril_yield();
417 }
418}
419
420static inline void serial_interrupt_handler(device_t *dev, ipc_callid_t iid, ipc_call_t *icall)
421{
422 serial_read_from_device(dev);
423}
424
425static inline int serial_register_interrupt_handler(device_t *dev)
426{
427 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
428
429 return register_interrupt_handler(dev, data->irq, serial_interrupt_handler, NULL);
430}
431
432static inline int serial_unregister_interrupt_handler(device_t *dev)
433{
434 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
435
436 return unregister_interrupt_handler(dev, data->irq);
437}
438
439static int serial_add_device(device_t *dev)
440{
441 printf(NAME ": serial_add_device %s (handle = %d)\n", dev->name, dev->handle);
442
443 int res = serial_dev_initialize(dev);
444 if (EOK != res) {
445 return res;
446 }
447
448 if (!serial_pio_enable(dev)) {
449 serial_dev_cleanup(dev);
450 return EADDRNOTAVAIL;
451 }
452
453 // find out whether the device is present
454 if (!serial_dev_probe(dev)) {
455 serial_dev_cleanup(dev);
456 return ENOENT;
457 }
458
459 // serial port initialization (baud rate etc.)
460 serial_initialize_port(dev);
461
462 // register interrupt handler
463 if (EOK != serial_register_interrupt_handler(dev)) {
464 printf(NAME ": failed to register interrupt handler.\n");
465 serial_dev_cleanup(dev);
466 return res;
467 }
468
469 // enable interrupt
470 if (EOK != (res = serial_interrupt_enable(dev))) {
471 printf(NAME ": failed to enable the interrupt. Error code = %d.\n", res);
472 serial_dev_cleanup(dev);
473 serial_unregister_interrupt_handler(dev);
474 return res;
475 }
476
477 dev->class = &serial_dev_class;
478
479 printf(NAME ": the %s device has been successfully initialized.\n", dev->name);
480
481 return EOK;
482}
483
484/** Open the device.
485 *
486 * This is a callback function called when a client tries to connect to the device.
487 *
488 * @param dev the device.
489 */
490static int serial_open(device_t *dev)
491{
492 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
493 int res;
494
495 fibril_mutex_lock(&data->mutex);
496
497 if (data->client_connected) {
498 res = ELIMIT;
499 } else {
500 res = EOK;
501 data->client_connected = true;
502 }
503
504 fibril_mutex_unlock(&data->mutex);
505
506 return res;
507}
508
509/** Close the device.
510 *
511 * This is a callback function called when a client tries to disconnect from the device.
512 *
513 * @param dev the device.
514 */
515static void serial_close(device_t *dev)
516{
517 serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
518
519 fibril_mutex_lock(&data->mutex);
520
521 assert(data->client_connected);
522
523 data->client_connected = false;
524 buf_clear(&data->input_buffer);
525
526 fibril_mutex_unlock(&data->mutex);
527}
528
529/** Default handler for client requests which are not handled by the standard interfaces.
530 *
531 * Configure the parameters of the serial communication.
532 */
533static void serial_default_handler(device_t *dev, ipc_callid_t callid, ipc_call_t *call)
534{
535 ipcarg_t method = IPC_GET_METHOD(*call);
536 int ret;
537
538 switch(method) {
539 case SERIAL_SET_BAUD_RATE:
540 ret = serial_set_baud_rate(dev, IPC_GET_ARG1(*call));
541 ipc_answer_0(callid, ret);
542 break;
543 case SERIAL_SET_PARITY:
544 // TODO
545 break;
546 case SERIAL_SET_STOP_BITS:
547 // TODO
548 break;
549 default:
550 ipc_answer_0(callid, ENOTSUP);
551 }
552}
553
554/** Initialize the serial port driver.
555 *
556 * Initialize class structures with callback methods for handling
557 * client requests to the serial port devices.
558 */
559static void serial_init()
560{
561 // TODO
562 serial_dev_class.id = 0;
563 serial_dev_class.open = &serial_open;
564 serial_dev_class.close = &serial_close;
565
566 serial_dev_class.interfaces[CHAR_DEV_IFACE] = &serial_char_iface;
567 serial_dev_class.default_handler = &serial_default_handler;
568}
569
570int main(int argc, char *argv[])
571{
572 printf(NAME ": HelenOS serial port driver\n");
573 serial_init();
574 return driver_main(&serial_driver);
575}
576
577/**
578 * @}
579 */
Note: See TracBrowser for help on using the repository browser.