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

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

parts of generic char interface, fixed some bugs

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