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

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

backup

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