source: mainline/uspace/srv/dd/serial.c@ 5e598e0

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

the name of the hierarchical driver was changed II

  • Property mode set to 100644
File size: 13.4 KB
Line 
1#include <unistd.h>
2#include <ddi.h>
3#include <libarch/ddi.h>
4#include <ipc/ipc.h>
5#include <ipc/services.h>
6#include <ipc/serial.h>
7#include <ipc/devmap.h>
8#include <ipc/ns.h>
9#include <bool.h>
10#include <errno.h>
11#include <async.h>
12#include <stdio.h>
13#include <futex.h>
14#include <assert.h>
15#include <adt/list.h>
16#include <string.h>
17
18#include "isa.h"
19#include "serial.h"
20#include "pic.h"
21
22#define NAME "serial"
23
24#define REG_COUNT 7
25
26#define MAX_NAME_LEN 8
27#define BUF_LEN 256 // the length of the input buffer
28
29struct cyclic_buffer {
30 uint8_t buf[BUF_LEN]; // cyclic buffer
31 int start;
32 int cnt;
33};
34
35typedef struct cyclic_buffer cyclic_buffer_t;
36
37// returns false if the buffer is full
38static bool buf_push_back(cyclic_buffer_t *buf, uint8_t item)
39{
40 if (buf->cnt >= BUF_LEN) {
41 return false;
42 }
43
44 int pos = (buf->start + buf->cnt) % BUF_LEN;
45 buf->buf[pos] = item;
46 buf->cnt++;
47 return true;
48}
49
50static bool buf_is_empty(cyclic_buffer_t *buf)
51{
52 return buf->cnt == 0;
53}
54
55// call it on non empty buffer!
56static uint8_t buf_pop_front(cyclic_buffer_t *buf)
57{
58 assert(!buf_is_empty(buf));
59
60 uint8_t res = buf->buf[buf->start];
61 buf->start = (buf->start + 1) % BUF_LEN;
62 buf->cnt--;
63 return res;
64}
65
66static void buf_clear(cyclic_buffer_t *buf)
67{
68 buf->cnt = 0;
69}
70
71struct serial_dev {
72 link_t link;
73 char name[MAX_NAME_LEN];
74 int handle; // devmapper device handle
75 int devno; // unique device number; used in irq registration (cannot be handle used instead of it?)
76 bool client_connected;
77 ioport8_t *port;
78 void *phys_addr;
79 int irq;
80 bridge_to_isa_t *parent;
81 cyclic_buffer_t input_buffer;
82 futex_t futex;
83};
84
85typedef struct serial_dev serial_dev_t;
86
87static irq_cmd_t serial_cmds[] = {
88 {
89 .cmd = CMD_ACCEPT
90 }
91};
92
93static irq_code_t serial_pseudocode = {
94 sizeof(serial_cmds) / sizeof(irq_cmd_t),
95 serial_cmds
96};
97
98static void * serial_phys_addresses[] = { (void *)0x3F8, (void *)0x2F8 };
99static int serial_irqs[] = { 4, 3 }; // TODO - what about if there were more than two serial ports?
100static int serial_phys_addr_cnt = sizeof(serial_phys_addresses)/sizeof(void *);
101static int serial_irq_cnt = sizeof(serial_irqs)/sizeof(int);
102
103// number, which should be assigned to a newly found serial device - increment first, then assign to the device
104static int serial_idx = 0;
105
106static int serial_driver_phone = -1;
107
108LIST_INITIALIZE(serial_devices_list);
109
110static atomic_t serial_futex = FUTEX_INITIALIZER;
111
112
113static void serial_init_port(ioport8_t *port);
114static void serial_write_8(ioport8_t *port, uint8_t c);
115static bool is_transmit_empty(ioport8_t *port);
116static uint8_t serial_read_8(ioport8_t *port);
117static bool serial_received(ioport8_t *port);
118static void serial_probe(bridge_to_isa_t *parent);
119static ioport8_t * serial_probe_port(void *phys_addr);
120static int serial_device_register(int driver_phone, char *name, int *handle);
121static int serial_driver_register(char *name);
122static void serial_putchar(serial_dev_t *dev, ipc_callid_t rid, ipc_call_t *request);
123static void serial_getchar(serial_dev_t *dev, ipc_callid_t rid);
124static void serial_client_conn(ipc_callid_t iid, ipc_call_t *icall);
125static void serial_irq_handler(ipc_callid_t iid, ipc_call_t *icall);
126static serial_dev_t * serial_devno_to_dev(int devno);
127static serial_dev_t * serial_handle_to_dev(int handle);
128static void serial_enable_interrupt(serial_dev_t *dev);
129
130static isa_drv_ops_t serial_isa_ops = {
131 .probe = serial_probe
132};
133
134static isa_drv_t serial_isa_drv = {
135 .name = NAME,
136 .ops = &serial_isa_ops
137};
138
139int serial_init()
140{
141 // register driver by devmapper
142 serial_driver_phone = serial_driver_register(NAME);
143 if (serial_driver_phone < 0) {
144 printf(NAME ": Unable to register driver\n");
145 return 0;
146 }
147
148 // register irq handler
149 printf(NAME ": Registering interrup notification callback function.\n");
150 async_set_interrupt_received(serial_irq_handler);
151
152 // register this driver by generic isa bus driver
153 isa_register_driver(&serial_isa_drv);
154 return 1;
155}
156
157static bool serial_received(ioport8_t *port)
158{
159 return (pio_read_8(port + 5) & 1) != 0;
160}
161
162static uint8_t serial_read_8(ioport8_t *port)
163{
164 return pio_read_8(port);
165}
166
167static bool is_transmit_empty(ioport8_t *port)
168{
169 return (pio_read_8(port + 5) & 0x20) != 0;
170}
171
172static void serial_write_8(ioport8_t *port, uint8_t c)
173{
174 while (!is_transmit_empty(port))
175 ;
176
177 pio_write_8(port, c);
178}
179
180static void serial_init_port(ioport8_t *port)
181{
182 pio_write_8(port + 1, 0x00); // Disable all interrupts
183 pio_write_8(port + 3, 0x80); // Enable DLAB (set baud rate divisor)
184 pio_write_8(port + 0, 0x60); // Set divisor to 96 (lo byte) 1200 baud
185 pio_write_8(port + 1, 0x00); // (hi byte)
186 pio_write_8(port + 3, 0x07); // 8 bits, no parity, two stop bits
187 pio_write_8(port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
188 pio_write_8(port + 4, 0x0B); // RTS/DSR set (Request to Send and Data Terminal Ready lines enabled),
189 // Aux Output2 set - needed for interrupts
190
191}
192
193static void serial_enable_interrupt(serial_dev_t *dev)
194{
195 futex_down(&(dev->futex));
196 // TODO do not call pic directly, do it more generally
197 pic_enable_interrupt(dev->irq);
198 pio_write_8(dev->port + 1 , 0x01); // Interrupt when data received
199 pio_write_8(dev->port + 4, 0x0B);
200 futex_up(&(dev->futex));
201}
202
203static serial_dev_t * serial_alloc_dev()
204{
205 serial_dev_t *dev = (serial_dev_t *)malloc(sizeof(serial_dev_t));
206 memset(dev, 0, sizeof(serial_dev_t));
207 return dev;
208}
209
210static void serial_init_dev(serial_dev_t *dev, bridge_to_isa_t *parent, int idx)
211{
212 assert(dev != NULL);
213
214 memset(dev, 0, sizeof(serial_dev_t));
215 dev->parent = parent;
216 dev->phys_addr = dev->parent->ops->absolutize(serial_phys_addresses[idx % serial_phys_addr_cnt]);
217 dev->irq = serial_irqs[idx % serial_irq_cnt];
218 snprintf(dev->name, MAX_NAME_LEN, "com%d", idx + 1);
219 dev->devno = -1;
220 dev->client_connected = false;
221 futex_initialize(&(dev->futex), 1);
222}
223
224static bool serial_probe_dev(serial_dev_t *dev)
225{
226 assert(dev != NULL);
227
228 return (dev->port = (ioport8_t *)serial_probe_port(dev->phys_addr)) != NULL;
229}
230
231static void serial_delete_dev(serial_dev_t *dev) {
232 free(dev);
233}
234
235static void serial_probe(bridge_to_isa_t *parent)
236{
237 printf(NAME " driver: probe()\n");
238
239 serial_dev_t *dev = serial_alloc_dev();
240
241 int i;
242 for (i = 0; i < serial_phys_addr_cnt; i++) {
243 serial_init_dev(dev, parent, serial_idx);
244 printf(NAME ": probing %s. \n", dev->name);
245 if (serial_probe_dev(dev)) {
246 printf(NAME " driver: initializing %s.\n", dev->name);
247 serial_init_port(dev->port);
248 if (EOK != serial_device_register(serial_driver_phone, dev->name, &(dev->handle))) {
249 printf(NAME ": unable to register device %s\n", dev->name);
250 } else {
251 dev->devno = device_assign_devno();
252
253 // 3rd argument called method is equal devno,this enables us to identify the device
254 // which caused the interrupt in the irq notification callback function
255 printf(NAME ": registering irq = %d for %s.\n", dev->irq, dev->name);
256 ipc_register_irq(dev->irq, dev->devno, dev->devno, &serial_pseudocode);
257 list_append(&(dev->link), &serial_devices_list);
258 printf(NAME ": enabling irq = %d for %s.\n", dev->irq, dev->name);
259 serial_enable_interrupt(dev);
260 dev = serial_alloc_dev();
261 }
262 } else {
263 printf(NAME " driver: %s is not present \n", dev->name);
264 }
265 serial_idx++;
266 }
267
268 serial_delete_dev(dev);
269}
270
271// returns virtual address of the serial port, if the serial port is present at this physical address, NULL otherwise
272static ioport8_t * serial_probe_port(void *phys_addr)
273{
274 ioport8_t *port_addr = NULL;
275
276 if (pio_enable(phys_addr, REG_COUNT, (void **)(&port_addr))) { // Gain control over port's registers.
277 printf(NAME ": Error - cannot gain the port %lx.\n", phys_addr);
278 return NULL;
279 }
280
281 uint8_t olddata;
282
283 olddata = pio_read_8(port_addr + 4);
284 pio_write_8(port_addr + 4, 0x10);
285 if ((pio_read_8(port_addr + 6) & 0xf0)) {
286 return NULL;
287 }
288
289 pio_write_8(port_addr + 4, 0x1f);
290 if ((pio_read_8(port_addr + 6) & 0xf0) != 0xf0) {
291 return NULL;
292 }
293 pio_write_8(port_addr + 4, olddata);
294
295 return port_addr;
296}
297
298
299static void serial_putchar(serial_dev_t *dev, ipc_callid_t rid, ipc_call_t *request)
300{
301 int c = IPC_GET_ARG1(*request);
302 futex_down(&(dev->futex));
303 serial_write_8(dev->port, (uint8_t)c);
304 futex_up(&(dev->futex));
305 ipc_answer_0(rid, EOK);
306}
307
308static void serial_getchar(serial_dev_t *dev, ipc_callid_t rid)
309{
310 uint8_t c;
311 printf(NAME ": trying to read from serial port %s\n", dev->name);
312 while (true) { // TODO: fix it - the queue of requests to read ?
313 futex_down(&(dev->futex));
314 if (!buf_is_empty(&(dev->input_buffer))) {
315 c = buf_pop_front(&(dev->input_buffer));
316 futex_up(&(dev->futex));
317 break;
318 }
319 //printf(NAME "no data ready, trying to read again after some while.\n", dev->name);
320 futex_up(&(dev->futex));
321 async_usleep(10000);
322 }
323 printf(NAME ": serial_getchar: sending characer %c read from %s to client.\n", c, dev->name);
324 ipc_answer_1(rid, EOK, c);
325}
326
327static serial_dev_t * serial_handle_to_dev(int handle)
328{
329 futex_down(&serial_futex);
330
331 link_t *item = serial_devices_list.next;
332 serial_dev_t *dev = NULL;
333
334 while (item != &serial_devices_list) {
335 dev = list_get_instance(item, serial_dev_t, link);
336 if (dev->handle == handle) {
337 futex_up(&serial_futex);
338 return dev;
339 }
340 item = item->next;
341 }
342
343 futex_up(&serial_futex);
344 return NULL;
345}
346
347static serial_dev_t * serial_devno_to_dev(int devno)
348{
349 futex_down(&serial_futex);
350
351 link_t *item = serial_devices_list.next;
352 serial_dev_t *dev = NULL;
353
354 while (item != &serial_devices_list) {
355 dev = list_get_instance(item, serial_dev_t, link);
356 if (dev->devno == devno) {
357 futex_up(&serial_futex);
358 return dev;
359 }
360 item = item->next;
361 }
362
363 futex_up(&serial_futex);
364 return NULL;
365}
366
367
368/** Handle one connection to the driver.
369 *
370 * @param iid Hash of the request that opened the connection.
371 * @param icall Call data of the request that opened the connection.
372 */
373static void serial_client_conn(ipc_callid_t iid, ipc_call_t *icall)
374{
375 /*
376 * Answer the first IPC_M_CONNECT_ME_TO call and remember the handle of the device to which the client connected.
377 */
378 int handle = IPC_GET_ARG1(*icall);
379 serial_dev_t *dev = serial_handle_to_dev(handle);
380
381 if (dev == NULL) {
382 ipc_answer_0(iid, ENOENT);
383 return;
384 }
385 if (dev->client_connected) {
386 ipc_answer_0(iid, ELIMIT);
387 return;
388 }
389
390 buf_clear(&(dev->input_buffer)); // synchronization with interrupt service routine ?
391 dev->client_connected = true;
392 ipc_answer_0(iid, EOK);
393
394 while (1) {
395 ipc_callid_t callid;
396 ipc_call_t call;
397
398 callid = async_get_call(&call);
399 switch (IPC_GET_METHOD(call)) {
400 case IPC_M_PHONE_HUNGUP:
401 /*
402 * The other side has hung up.
403 * Answer the message and exit the fibril.
404 */
405 ipc_answer_0(callid, EOK);
406 dev->client_connected = false;
407 return;
408 break;
409 case SERIAL_GETCHAR:
410 serial_getchar(dev, callid);
411 break;
412 case SERIAL_PUTCHAR:
413 serial_putchar(dev, callid, &call);
414 break;
415 default:
416 ipc_answer_0(callid, ENOTSUP);
417 break;
418 }
419 }
420}
421
422
423/**
424 * Register the driver with the given name and return its newly created phone.
425 */
426static int serial_driver_register(char *name)
427{
428 ipcarg_t retval;
429 aid_t req;
430 ipc_call_t answer;
431 int phone;
432 ipcarg_t callback_phonehash;
433
434 phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP, DEVMAP_DRIVER, 0);
435
436 while (phone < 0) {
437 usleep(10000);
438 phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP, DEVMAP_DRIVER, 0);
439 }
440
441 req = async_send_2(phone, DEVMAP_DRIVER_REGISTER, 0, 0, &answer);
442
443 retval = ipc_data_write_start(phone, (char *) name, str_length(name) + 1);
444
445 if (retval != EOK) {
446 async_wait_for(req, NULL);
447 return -1;
448 }
449
450 async_set_client_connection(serial_client_conn); // set callback function which will serve client connections
451
452 ipc_connect_to_me(phone, 0, 0, 0, &callback_phonehash);
453 async_wait_for(req, &retval);
454
455 return phone;
456}
457
458static int serial_device_register(int driver_phone, char *name, int *handle)
459{
460 ipcarg_t retval;
461 aid_t req;
462 ipc_call_t answer;
463
464 if (handle != NULL) {
465 *handle = -1;
466 }
467
468 req = async_send_2(driver_phone, DEVMAP_DEVICE_REGISTER, 0, 0, &answer);
469
470 retval = ipc_data_write_start(driver_phone, (char *) name, str_length(name) + 1);
471
472 if (retval != EOK) {
473 async_wait_for(req, NULL);
474 return retval;
475 }
476
477 async_wait_for(req, &retval);
478
479 if (EOK == retval) {
480 if (NULL != handle)
481 *handle = (int) IPC_GET_ARG1(answer);
482 }
483
484 return retval;
485}
486
487static void serial_read_from_device(serial_dev_t *dev)
488{
489 bool cont = true;
490
491 while (cont) {
492 futex_down(&(dev->futex));
493 if (cont = serial_received(dev->port)) {
494 uint8_t val = serial_read_8(dev->port);
495 printf(NAME ": character %c read from %s.\n", val, dev->name);
496 if (dev->client_connected) {
497 if (!buf_push_back(&(dev->input_buffer), val)) {
498 printf(NAME ": buffer overflow on %s.\n", dev->name);
499 } else {
500 printf(NAME ": the character %c saved to the buffer of %s.\n", val, dev->name);
501 }
502 } else {
503 printf(NAME ": no client is connected to %s, discarding the character which was read.\n", dev->name);
504 }
505 }
506 futex_up(&(dev->futex));
507 usleep(10000);
508 }
509}
510
511// TODO - this is the only irq handling function registered by this task.
512// (A task can register only one such function.)
513// If more drivers within the task needed to handle interrupts,
514// there will have to be one generic handler redistributing
515// irq notification messages among them.
516// Or the change of ansync framework will be needed.
517static void serial_irq_handler(ipc_callid_t iid, ipc_call_t *icall)
518{
519 printf(NAME ": irq handler\n");
520 int devno = IPC_GET_METHOD(*icall);
521 serial_dev_t *dev = serial_devno_to_dev(devno);
522 serial_read_from_device(dev);
523}
524
Note: See TracBrowser for help on using the repository browser.