Subversion Repositories shark

Rev

Rev 715 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
538 mauro 1
/*
2
 * Project: S.Ha.R.K.
3
 *
4
 * Coordinators:
5
 *   Giorgio Buttazzo    <giorgio@sssup.it>
6
 *   Paolo Gai           <pj@gandalf.sssup.it>
7
 *
8
 * Authors     :
9
 *   Paolo Gai           <pj@gandalf.sssup.it>
10
 *   Massimiliano Giorgi <massy@gandalf.sssup.it>
11
 *   Luca Abeni          <luca@gandalf.sssup.it>
12
 *   (see the web pages for full authors list)
13
 *
14
 * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
15
 *
16
 * http://www.sssup.it
17
 * http://retis.sssup.it
18
 * http://shark.sssup.it
19
 */
20
 
21
/**
22
 ------------
23
 CVS :        $Id: scom.c,v 1.1 2004-03-29 18:31:42 mauro Exp $
24
 
25
 File:        $File$
26
 Revision:    $Revision: 1.1 $
27
 Last update: $Date: 2004-03-29 18:31:42 $
28
 ------------
29
 
30
 Author:      Massimiliano Giorgi
31
 
32
 Author:        Gerardo Lamastra
33
 Date:  9/5/96
34
 
35
 File:  SCOM.C
36
 Revision:      1.0g
37
 
38
**/
39
 
40
/*
41
 * Copyright (C) 2000 Paolo Gai
42
 *
43
 * This program is free software; you can redistribute it and/or modify
44
 * it under the terms of the GNU General Public License as published by
45
 * the Free Software Foundation; either version 2 of the License, or
46
 * (at your option) any later version.
47
 *
48
 * This program is distributed in the hope that it will be useful,
49
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
50
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
51
 * GNU General Public License for more details.
52
 *
53
 * You should have received a copy of the GNU General Public License
54
 * along with this program; if not, write to the Free Software
55
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
56
 *
57
 */
58
 
59
/* Serial communication device */
60
/* This implementation is capable of handling 4 distinct COM ports */
61
/* The COM port settings are the standard PC settings:             */
62
/* PORT         ADDRESS         IRQ                                */
63
/*   1           0x3F8           4                                 */
64
/*   2           0x2F8           3                                 */
65
/*   3           0x3E8           4                                 */
66
/*   4           0x2E8           2                                 */
67
 
68
/* By Massy
69
 * I have modified the fast handler routines to support serial
70
 * mouse better (see below)
71
 */
72
 
73
//#include <string.h>
74
//#include <stdlib.h>
75
//#include <cons.h>
76
 
77
#include <kernel/kern.h>
78
//#include "exc.h"
79
 
80
#include <modules/sem.h>
81
#include <drivers/scom.h>
82
 
83
 
84
/* #define __DEBUG_SERIAL__ */
85
/* #define __STUB__ */
86
 
87
/* Base address for each standard COM link */
88
static unsigned com_base[] = {0x03F8,0x02F8,0x03E8,0x02E8};
89
/* Irq linked to each serial channel */
90
static unsigned com_irq[]  = {COM1_IRQ,COM2_IRQ,COM3_IRQ,COM4_IRQ};
91
/* COM port which shares interrupt with indexed one             */
92
/* I assume standard AT assignement where each irq line can     */
93
/* hold up to 2 different COM port                              */
94
static const int com_share[] = {COM3,COM4,COM1,COM2};
95
 
96
/* Used for decoding the IIR status */
97
const int IIRbits[] = {MS_CHANGED,TX_EMPTY,RX_FULL,LS_CHANGED};
98
 
99
/* The descriptor of a serial link                               */
100
/* Each array entry is associated to a COM port                  */
101
/* The control field is used to specify which kind of interrupts */
102
/* are going to be served; the status field tracks if the entry  */
103
/* is curretly used & if the shared fast handler is linked       */
104
/* The semaphores are opened if you use an asyncronous server    */
105
/* with the link                                                 */
106
 
107
struct COM_LINK com_link[COM_LINKS];
108
 
109
/* Register level access functions */
110
 
111
unsigned com_read(unsigned port,unsigned reg)
112
{
113
    unsigned b;
114
    if (port > 3 || reg > 7) return(0);
115
    b = ll_in(com_base[port]+reg);
116
    return(b);
117
}
118
 
119
void com_write(unsigned port,unsigned reg,unsigned value)
120
{
121
    if (port > 3 || reg > 7) return;
122
    ll_out(com_base[port]+reg,value);
123
}
124
 
125
/* Polled send/receive */
126
 
127
void com_send(unsigned port,BYTE b)
128
{
129
    while ((com_read(port,LSR) & 32) == 0);
130
    com_write(port,THR,b);
131
}
132
 
133
unsigned com_receive(unsigned port)
134
{
135
    while ((com_read(port,LSR) & 1) == 0);
136
    return(com_read(port,RBR));
137
}
138
 
139
/* Fast routines for cascaded irq */
140
 
141
/* By massy */
142
/* com_fast must be called if there isn't a server
143
 * and an interrupt is detected (so sermouse.c module can
144
 * activate mouse task only if a packet not a byte is received).
145
 */
146
static void dummy(int x) {}
147
void (*com_fast)(int)=dummy;
148
 
149
#ifdef __DEBUG_SERIAL__
150
    int fast_times1 = 0,fast_times3 = 0;
151
#endif
152
 
153
static void com_1_3_fast(int n)
154
{
155
  unsigned iir1,iir3;
156
  iir1 = DECODE(com_read(COM1,IIR));
157
  iir3 = DECODE(com_read(COM3,IIR));
158
  if ((iir1 & com_link[COM1].control)) {
159
#ifdef __DEBUG_SERIAL__
160
    fast_times1++;
161
#endif
162
    com_write(COM1,IER,0);
163
    com_link[COM1].request = iir1;
164
    if (com_link[COM1].server==NIL)
165
      com_fast(COM1);
166
    else
167
      task_activate(com_link[COM1].server);
168
  }
169
  else if ((iir3 & com_link[COM3].control)) {
170
#ifdef __DEBUG_SERIAL__
171
    fast_times3++;
172
#endif
173
    com_write(COM3,IER,0);
174
    com_link[COM3].request = iir3;
175
    if (com_link[COM3].server==NIL)
176
      com_fast(COM3);
177
    else
178
    task_activate(com_link[COM3].server);
179
  }
180
}
181
 
182
#ifdef __DEBUG_SERIAL__
183
    int fast_times2 = 0,fast_times4 = 0;
184
#endif
185
 
186
static void com_2_4_fast(int n)
187
{
188
  unsigned iir2,iir4;
189
  iir2 = DECODE(com_read(COM2,IIR));
190
  iir4 = DECODE(com_read(COM4,IIR));
191
  if ((iir2 & com_link[COM2].control)) {
192
#ifdef __DEBUG_SERIAL__
193
    fast_times2++;
194
#endif
195
    com_write(COM2,IER,0);
196
    com_link[COM2].request = iir2;
197
    if (com_link[COM2].server==NIL)
198
      com_fast(COM2);
199
    else    
200
      task_activate(com_link[COM2].server);
201
  }
202
  else if ((iir4 & com_link[COM4].control)) {
203
#ifdef __DEBUG_SERIAL__
204
    fast_times4++;
205
#endif
206
    com_write(COM4,IER,0);
207
    com_link[COM4].request = iir4;
208
    if (com_link[COM4].server==NIL)
209
      com_fast(COM4);
210
    else    
211
      task_activate(com_link[COM4].server);
212
  }
213
}
214
 
215
/* Initialize a serial channel */
216
 
217
int com_open(unsigned port,DWORD speed,BYTE parity,BYTE len,BYTE stop)
218
{
219
    unsigned long div,b_mask;
220
 
221
    /* Check if link is already open */
222
    cli();
223
    if (com_link[port].status & LINK_BUSY) {
224
        sti();
225
        return(-1);
226
    } else com_link[port].status |= LINK_BUSY;
227
    sti();
228
 
229
    /* Now set up the serial link */
230
    b_mask = (parity & 3) * 8 + (stop & 1) * 4 + ((len - 5) & 3);
231
    div = 115200L / speed;
232
    /* Clear serial interrupt enable register */
233
    com_write(port,IER,0);
234
    /* Empty input buffer */
235
    com_read(port,RBR);
236
    /* Activate DLAB bit for speed setting */
237
    com_write(port,LCR,0x80);
238
    /* Load baud divisor */
239
    com_write(port,0,div & 0x00FF);
240
    div >>= 8;
241
    com_write(port,1,div & 0x00FF);
242
    /* Load control word (parity,stop bit,bit len) */
243
    com_write(port,LCR,b_mask);
244
    /* Attiva OUT1 & OUT2 */
245
    com_write(port,MCR,0x0C);
246
    return(1);
247
}
248
 
249
/* Link a particular server to a serial channel                 */
250
/* The semaphores are opened to syncronize the server and the   */
251
/* application task which use serial communication              */
252
 
253
int com_server(unsigned port,unsigned control,PID server)
254
{
255
    unsigned hndl = com_irq[port];
256
    unsigned shared = com_share[port];
257
    void (*com_fast)(int n);
258
    /* Select appropriate fast routine */
259
    if (port == COM1 || port == COM3) com_fast = com_1_3_fast;
260
    else if (port == COM2 || port == COM4) com_fast = com_2_4_fast;
261
    else return(-1);
262
    if ((control & (RX_FULL|TX_EMPTY)) == 0) return(-1);
263
    /* If the fast routine is not already installed, install it! */
264
    cli();
265
    if (!(com_link[port].status & FAST_INSTALLED)) {
266
        bit_on(com_link[port].status,FAST_INSTALLED);
267
        bit_on(com_link[shared].status,FAST_INSTALLED);
268
        handler_set(hndl,com_fast,NIL, TRUE);
269
        #ifdef __DEBUG_SERIAL__
270
            cputs("Handler OK\n");
271
        #endif
272
    }
273
    sti();
274
    /* Set com link tasks & flags */
275
    com_link[port].control = control;
276
    com_link[port].server = server;
277
    com_link[port].msk = 0;
278
    if (control & RX_FULL) sem_init(&com_link[port].rx_sem,0,0);
279
    if (control & TX_EMPTY) sem_init(&com_link[port].tx_sem,0,0);
280
    sem_init(&com_link[port].mutex,0,1);
281
    return(1);
282
}
283
 
284
/* Close port channel & release the server */
285
 
286
int com_close(unsigned port)
287
{    
288
    unsigned hndl = com_irq[port];
289
    unsigned shared = com_share[port];
290
 
291
    /* Check if fast is already installed */
292
    cli();
293
    if (!(com_link[port].status & LINK_BUSY)) {
294
        sti();
295
        return(-1);
296
    } else {
297
        if (com_link[port].control & RX_FULL) sem_destroy(&com_link[port].rx_sem);
298
        if (com_link[port].control & TX_EMPTY) sem_destroy(&com_link[port].tx_sem);
299
        com_write(port,IER,0);
300
        com_read(port,IIR);
301
        com_read(port,RBR);
302
        com_link[port].status = 0;
303
        com_link[port].control = 0;
304
        com_link[port].msk = 0;
305
        sti();
306
        sem_destroy(&com_link[port].mutex);
307
        if (com_link[port].server != NIL) {
308
            task_kill(com_link[port].server);
309
            com_link[port].server = NIL;
310
        }
311
    }
312
    /* If the fast routine is no more necessary, remove it */
313
    if (!(com_link[shared].status & FAST_INSTALLED))
314
       handler_remove(hndl);
315
    /* If the other link still uses it, we must remember this */
316
    else com_link[port].status = FAST_INSTALLED;
317
    return(1);
318
}
319
 
320
#ifdef __DEBUG_SERIAL__
321
    int rx_time = 0;
322
    int tx_time = 0;
323
#endif
324
 
325
/* This is the full duplex server; used for bidirectional data  */
326
/* transmission.                                                */
327
/* As intr is masked, the server can only be activated once     */
328
/* and operates in mutex with the fast handler                  */
329
/* This server operates in conjunction with the com_Async...    */
330
/* procedures!                                                  */
331
 
332
TASK duplexServer(int port)
333
{
334
    char data;
335
    for(;;) {
336
        if (com_link[port].request & RX_FULL) {
337
            #ifdef __DEBUG_SERIAL__
338
                putc_xy(78,0,RED,'R');
339
                rx_time++;
340
            #endif
341
            data = com_read(port,RBR);
342
            *(com_link[port].rx_buf + com_link[port].rx_cnt) = data;
343
            com_link[port].rx_cnt++;
344
            if (com_link[port].rx_cnt == com_link[port].rx_len) {
345
               bit_off(com_link[port].msk,RX_FULL);
346
               sem_post(&com_link[port].rx_sem);
347
            }
348
        }
349
        if (com_link[port].request & TX_EMPTY) {
350
            #ifdef __DEBUG_SERIAL__
351
                putc_xy(79,0,GREEN,'T');
352
                tx_time++;
353
            #endif
354
            data = *(com_link[port].tx_buf + com_link[port].tx_cnt);
355
            com_link[port].tx_cnt++;
356
            com_write(port,THR,data);
357
            if (com_link[port].tx_cnt == com_link[port].tx_len) {
358
               bit_off(com_link[port].msk,TX_EMPTY);
359
               sem_post(&com_link[port].tx_sem);
360
            }
361
        }
362
        cli();
363
        com_write(port,IER,com_link[port].msk);
364
        task_endcycle();
365
        sti();
366
    }
367
}
368
 
369
/* This routines provides asyncronous decoupling between the server */
370
/* and the tasks which produce/consume serial data                  */
371
 
372
void com_AsyncSend(int port,void *buf,unsigned len)
373
{
374
    sem_wait(&com_link[port].mutex);
375
    com_link[port].tx_buf = buf;
376
    com_link[port].tx_cnt = 0;
377
    com_link[port].tx_len = len;
378
    bit_on(com_link[port].msk,TX_EMPTY);
379
    sem_post(&com_link[port].mutex);
380
    com_write(port,IER,com_link[port].msk);
381
    sem_wait(&com_link[port].tx_sem);
382
}
383
 
384
void com_AsyncReceive(int port,void *buf,unsigned len)
385
{    
386
    sem_wait(&com_link[port].mutex);
387
    com_link[port].rx_buf = buf;
388
    com_link[port].rx_cnt = 0;
389
    com_link[port].rx_len = len;    
390
    bit_on(com_link[port].msk,RX_FULL);
391
    sem_post(&com_link[port].mutex);
392
    com_write(port,IER,com_link[port].msk);
393
    sem_wait(&com_link[port].rx_sem);
394
}
395
 
396
/* Receive Only Server                                          */
397
/* This server is used for passive devices which cannot receive */
398
/* data, but only transmit. I assume that only one byte is pro- */
399
/* cessed each time and the byte is got to the control process  */
400
 
401
TASK rxServer(int port)
402
{
403
    static char data;
404
    com_link[port].rx_buf = &data;
405
    for(;;) {
406
        #ifdef __DEBUG_SERIAL__
407
            putc_xy(76,0,YELLOW,'R');
408
        #endif
409
        data = com_read(port,RBR);
410
        sem_post(&com_link[port].rx_sem);
411
        cli();
412
        com_write(port,IER,com_link[port].msk);
413
        task_endcycle();
414
        sti();
415
    }
416
}
417
 
418
/* Debug Stub */
419
 
420
#ifdef __STUB__
421
 
422
#include "keyb.h"
423
 
424
TASK Visualize(void)
425
{
426
    char str[40];
427
    char buf[80];
428
    unsigned long times = 0;
429
 
430
    for (;;) {
431
        com_AsyncReceive(COM2,str,10);
432
        sprintf(buf,"Str : %s (Times : %lu)",str,times++);
433
        puts_xy(0,1,WHITE,buf);
434
    }
435
}
436
 
437
TASK Sender(void)
438
{
439
    char buf[80];
440
    unsigned long times = 0;
441
    cputs("Sender has started...\n");
442
    for (;;) {
443
        buf[0] = keyb_getchar();
444
        if (buf[0] == 'x') task_activate(MAIN_INDEX);
445
        if ((times % 10) == 9) buf[0] = 0;
446
        com_AsyncSend(COM2,buf,1);
447
        sprintf(buf,"Sender (Times : %lu)",times++);
448
        puts_xy(0,3,WHITE,buf);
449
    }
450
}        0
451
 
452
void main()
453
{
454
    PID p1,p2,p3;
455
    BYTE m1 = TX_EMPTY|RX_FULL;
456
    BYTE m2 = RX_FULL;
457
    BYTE m3 = TX_EMPTY;
458
    MODEL m = BASE_MODEL;
459
    MODEL mm = BASE_MODEL;
460
 
461
    sys_init(1000,uSec,0);
462
    keyb_init(HARD,50);
463
    clear();
464
    /* This is the sequence of operations needed to setup */
465
    /* a serial asyncronous bidirectional link            */
466
    task_def_arg(m,COM2);
467
    task_def_wcet(m,500);
468
    p1 = task_create("ComDuplex",duplexServer,HARD,APERIODIC,100,&m);
469
    if (p1 == -1) {
470
        ll_printf("Error creating comduplex task\n");
471
        sys_end();
472
        exit(-1);
473
    }
474
    com_open(COM2,9600,NONE,8,1);
475
    com_server(COM2,m1,p1);
476
 
477
    /* Well, that is some other stuff ... */
478
    task_def_wcet(mm,500);
479
    p2 = task_create("Visor",Visualize,NRT,APERIODIC,11,&mm);
480
    p3 = task_create("Sender",Sender,NRT,APERIODIC,11,&mm);
481
    task_activate(p2);
482
    task_activate(p3);
483
    task_endcycle();
484
    sys_end();
485
    #ifdef __DEBUG_SERIAL__
486
        cprintf("RxServer was activated %d times\n",rx_time);
487
        cprintf("TxServer was activated %d times\n",tx_time);
488
        cprintf("Fast : %d\n",fast_times1);
489
    #endif
490
}
491
 
492
#endif