Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/drivers/char/serial.c
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * serial.c
3
 * 
4
 * Framework for serial device drivers.
5
 * 
6
 * Copyright (c) 2003-2008, K A Fraser
7
 */
8
9
#include <xen/delay.h>
10
#include <xen/init.h>
11
#include <xen/mm.h>
12
#include <xen/serial.h>
13
#include <xen/cache.h>
14
15
/* Never drop characters, even if the async transmit buffer fills. */
16
/* #define SERIAL_NEVER_DROP_CHARS 1 */
17
18
unsigned int __read_mostly serial_txbufsz = 16384;
19
size_param("serial_tx_buffer", serial_txbufsz);
20
21
0
#define mask_serial_rxbuf_idx(_i) ((_i)&(serial_rxbufsz-1))
22
0
#define mask_serial_txbuf_idx(_i) ((_i)&(serial_txbufsz-1))
23
24
static struct serial_port com[SERHND_IDX + 1] = {
25
    [0 ... SERHND_IDX] = {
26
        .rx_lock = SPIN_LOCK_UNLOCKED,
27
        .tx_lock = SPIN_LOCK_UNLOCKED
28
    }
29
};
30
31
static bool_t __read_mostly post_irq;
32
33
static inline void serial_start_tx(struct serial_port *port)
34
141k
{
35
141k
    if ( port->driver->start_tx != NULL )
36
141k
        port->driver->start_tx(port);
37
141k
}
38
39
static inline void serial_stop_tx(struct serial_port *port)
40
5.32k
{
41
5.32k
    if ( port->driver->stop_tx != NULL )
42
5.32k
        port->driver->stop_tx(port);
43
5.32k
}
44
45
void serial_rx_interrupt(struct serial_port *port, struct cpu_user_regs *regs)
46
0
{
47
0
    char c;
48
0
    serial_rx_fn fn = NULL;
49
0
    unsigned long flags;
50
0
51
0
    spin_lock_irqsave(&port->rx_lock, flags);
52
0
53
0
    if ( port->driver->getc(port, &c) )
54
0
    {
55
0
        if ( port->rx != NULL )
56
0
            fn = port->rx;
57
0
        else if ( (c & 0x80) && (port->rx_hi != NULL) )
58
0
            fn = port->rx_hi;
59
0
        else if ( !(c & 0x80) && (port->rx_lo != NULL) )
60
0
            fn = port->rx_lo;
61
0
        else if ( (port->rxbufp - port->rxbufc) != serial_rxbufsz )
62
0
            port->rxbuf[mask_serial_rxbuf_idx(port->rxbufp++)] = c;            
63
0
    }
64
0
65
0
    spin_unlock_irqrestore(&port->rx_lock, flags);
66
0
67
0
    if ( fn != NULL )
68
0
        (*fn)(c & 0x7f, regs);
69
0
}
70
71
void serial_tx_interrupt(struct serial_port *port, struct cpu_user_regs *regs)
72
5.32k
{
73
5.32k
    int i, n;
74
5.32k
    unsigned long flags;
75
5.32k
76
5.32k
    local_irq_save(flags);
77
5.32k
78
5.32k
    /*
79
5.32k
     * Avoid spinning for a long time: if there is a long-term lock holder
80
5.32k
     * then we know that they'll be stuffing bytes into the transmitter which
81
5.32k
     * will therefore not be empty for long.
82
5.32k
     */
83
5.33k
    while ( !spin_trylock(&port->tx_lock) )
84
10
    {
85
10
        if ( port->driver->tx_ready(port) <= 0 )
86
5
            goto out;
87
5
        cpu_relax();
88
5
    }
89
5.32k
90
5.32k
    if ( port->txbufc == port->txbufp )
91
5.32k
    {
92
5.32k
        /* Disable TX. nothing to send */
93
5.32k
        serial_stop_tx(port);
94
5.32k
        spin_unlock(&port->tx_lock);
95
5.32k
        goto out;
96
5.32k
    }
97
5.32k
    else
98
0
    {
99
0
        if ( port->driver->tx_ready(port) )
100
0
            serial_start_tx(port);
101
0
    }
102
0
    for ( i = 0, n = port->driver->tx_ready(port); i < n; i++ )
103
0
    {
104
0
        if ( port->txbufc == port->txbufp )
105
0
            break;
106
0
        port->driver->putc(
107
0
            port, port->txbuf[mask_serial_txbuf_idx(port->txbufc++)]);
108
0
    }
109
0
    if ( i && port->driver->flush )
110
0
        port->driver->flush(port);
111
0
112
0
    spin_unlock(&port->tx_lock);
113
0
114
5.32k
 out:
115
5.32k
    local_irq_restore(flags);
116
5.32k
}
117
118
static void __serial_putc(struct serial_port *port, char c)
119
141k
{
120
141k
    if ( (port->txbuf != NULL) && !port->sync )
121
0
    {
122
0
        /* Interrupt-driven (asynchronous) transmitter. */
123
0
124
0
        if ( port->tx_quench )
125
0
        {
126
0
            /* Buffer filled and we are dropping characters. */
127
0
            if ( (port->txbufp - port->txbufc) > (serial_txbufsz / 2) )
128
0
                return;
129
0
            port->tx_quench = 0;
130
0
        }
131
0
132
0
        if ( (port->txbufp - port->txbufc) == serial_txbufsz )
133
0
        {
134
0
            if ( port->tx_log_everything )
135
0
            {
136
0
                /* Buffer is full: we spin waiting for space to appear. */
137
0
                int n;
138
0
139
0
                while ( (n = port->driver->tx_ready(port)) == 0 )
140
0
                    cpu_relax();
141
0
                if ( n > 0 )
142
0
                {
143
0
                    /* Enable TX before sending chars */
144
0
                    serial_start_tx(port);
145
0
                    while ( n-- )
146
0
                        port->driver->putc(
147
0
                            port,
148
0
                            port->txbuf[mask_serial_txbuf_idx(port->txbufc++)]);
149
0
                    port->txbuf[mask_serial_txbuf_idx(port->txbufp++)] = c;
150
0
                }
151
0
            }
152
0
            else
153
0
            {
154
0
                /* Buffer is full: drop chars until buffer is half empty. */
155
0
                port->tx_quench = 1;
156
0
            }
157
0
            return;
158
0
        }
159
0
160
0
        if ( ((port->txbufp - port->txbufc) == 0) &&
161
0
             port->driver->tx_ready(port) > 0 )
162
0
        {
163
0
            /* Enable TX before sending chars */
164
0
            serial_start_tx(port);
165
0
            /* Buffer and UART FIFO are both empty, and port is available. */
166
0
            port->driver->putc(port, c);
167
0
        }
168
0
        else
169
0
        {
170
0
            /* Normal case: buffer the character. */
171
0
            port->txbuf[mask_serial_txbuf_idx(port->txbufp++)] = c;
172
0
        }
173
0
    }
174
141k
    else if ( port->driver->tx_ready )
175
141k
    {
176
141k
        int n;
177
141k
178
141k
        /* Synchronous finite-capacity transmitter. */
179
3.41M
        while ( !(n = port->driver->tx_ready(port)) )
180
3.27M
            cpu_relax();
181
141k
        if ( n > 0 )
182
141k
        {
183
141k
            /* Enable TX before sending chars */
184
141k
            serial_start_tx(port);
185
141k
            port->driver->putc(port, c);
186
141k
        }
187
141k
    }
188
141k
    else
189
0
    {
190
0
        /* Simple synchronous transmitter. */
191
0
        serial_start_tx(port);
192
0
        port->driver->putc(port, c);
193
0
    }
194
141k
}
195
196
void serial_putc(int handle, char c)
197
0
{
198
0
    struct serial_port *port;
199
0
    unsigned long flags;
200
0
201
0
    if ( handle == -1 )
202
0
        return;
203
0
204
0
    port = &com[handle & SERHND_IDX];
205
0
    if ( !port->driver || !port->driver->putc )
206
0
        return;
207
0
208
0
    spin_lock_irqsave(&port->tx_lock, flags);
209
0
210
0
    if ( (c == '\n') && (handle & SERHND_COOKED) )
211
0
        __serial_putc(port, '\r' | ((handle & SERHND_HI) ? 0x80 : 0x00));
212
0
213
0
    if ( handle & SERHND_HI )
214
0
        c |= 0x80;
215
0
    else if ( handle & SERHND_LO )
216
0
        c &= 0x7f;
217
0
218
0
    __serial_putc(port, c);
219
0
220
0
    if ( port->driver->flush )
221
0
        port->driver->flush(port);
222
0
223
0
    spin_unlock_irqrestore(&port->tx_lock, flags);
224
0
}
225
226
void serial_puts(int handle, const char *s)
227
119k
{
228
119k
    struct serial_port *port;
229
119k
    unsigned long flags;
230
119k
    char c;
231
119k
232
119k
    if ( handle == -1 )
233
0
        return;
234
119k
235
119k
    port = &com[handle & SERHND_IDX];
236
119k
    if ( !port->driver || !port->driver->putc )
237
0
        return;
238
119k
239
119k
    spin_lock_irqsave(&port->tx_lock, flags);
240
119k
241
258k
    while ( (c = *s++) != '\0' )
242
138k
    {
243
138k
        if ( (c == '\n') && (handle & SERHND_COOKED) )
244
2.84k
            __serial_putc(port, '\r' | ((handle & SERHND_HI) ? 0x80 : 0x00));
245
138k
246
138k
        if ( handle & SERHND_HI )
247
0
            c |= 0x80;
248
138k
        else if ( handle & SERHND_LO )
249
0
            c &= 0x7f;
250
138k
251
138k
        __serial_putc(port, c);
252
138k
    }
253
119k
254
119k
    if ( port->driver->flush )
255
0
        port->driver->flush(port);
256
119k
257
119k
    spin_unlock_irqrestore(&port->tx_lock, flags);
258
119k
}
259
260
char serial_getc(int handle)
261
0
{
262
0
    struct serial_port *port;
263
0
    char c;
264
0
    unsigned long flags;
265
0
266
0
    if ( handle == -1 )
267
0
        return '\0';
268
0
269
0
    port = &com[handle & SERHND_IDX];
270
0
    if ( !port->driver || !port->driver->getc )
271
0
        return '\0';
272
0
273
0
    do {
274
0
        for ( ; ; )
275
0
        {
276
0
            spin_lock_irqsave(&port->rx_lock, flags);
277
0
            
278
0
            if ( port->rxbufp != port->rxbufc )
279
0
            {
280
0
                c = port->rxbuf[mask_serial_rxbuf_idx(port->rxbufc++)];
281
0
                spin_unlock_irqrestore(&port->rx_lock, flags);
282
0
                break;
283
0
            }
284
0
            
285
0
            if ( port->driver->getc(port, &c) )
286
0
            {
287
0
                spin_unlock_irqrestore(&port->rx_lock, flags);
288
0
                break;
289
0
            }
290
0
291
0
            spin_unlock_irqrestore(&port->rx_lock, flags);
292
0
293
0
            cpu_relax();
294
0
            udelay(100);
295
0
        }
296
0
    } while ( ((handle & SERHND_LO) &&  (c & 0x80)) ||
297
0
              ((handle & SERHND_HI) && !(c & 0x80)) );
298
0
    
299
0
    return c & 0x7f;
300
0
}
301
302
int __init serial_parse_handle(char *conf)
303
1
{
304
1
    int handle, flags = 0;
305
1
306
1
    if ( !strncmp(conf, "dbgp", 4) && (!conf[4] || conf[4] == ',') )
307
0
    {
308
0
        handle = SERHND_DBGP;
309
0
        goto common;
310
0
    }
311
1
312
1
    if ( !strncmp(conf, "dtuart", 6) )
313
0
    {
314
0
        handle = SERHND_DTUART;
315
0
        goto common;
316
0
    }
317
1
318
1
    if ( strncmp(conf, "com", 3) )
319
0
        goto fail;
320
1
321
1
    switch ( conf[3] )
322
1
    {
323
1
    case '1':
324
1
        handle = SERHND_COM1;
325
1
        break;
326
0
    case '2':
327
0
        handle = SERHND_COM2;
328
0
        break;
329
0
    default:
330
0
        goto fail;
331
1
    }
332
1
333
1
    if ( conf[4] == 'H' )
334
0
        flags |= SERHND_HI;
335
1
    else if ( conf[4] == 'L' )
336
0
        flags |= SERHND_LO;
337
1
338
1
 common:
339
1
    if ( !com[handle].driver )
340
0
        goto fail;
341
1
342
1
    if ( !post_irq )
343
1
        com[handle].state = serial_parsed;
344
0
    else if ( com[handle].state != serial_initialized )
345
0
    {
346
0
        if ( com[handle].driver->init_postirq )
347
0
            com[handle].driver->init_postirq(&com[handle]);
348
0
        com[handle].state = serial_initialized;
349
0
    }
350
1
351
1
    return handle | flags | SERHND_COOKED;
352
1
353
0
 fail:
354
0
    return -1;
355
1
}
356
357
void __init serial_set_rx_handler(int handle, serial_rx_fn fn)
358
1
{
359
1
    struct serial_port *port;
360
1
    unsigned long flags;
361
1
362
1
    if ( handle == -1 )
363
0
        return;
364
1
365
1
    port = &com[handle & SERHND_IDX];
366
1
367
1
    spin_lock_irqsave(&port->rx_lock, flags);
368
1
369
1
    if ( port->rx != NULL )
370
0
        goto fail;
371
1
372
1
    if ( handle & SERHND_LO )
373
0
    {
374
0
        if ( port->rx_lo != NULL )
375
0
            goto fail;
376
0
        port->rx_lo = fn;        
377
0
    }
378
1
    else if ( handle & SERHND_HI )
379
0
    {
380
0
        if ( port->rx_hi != NULL )
381
0
            goto fail;
382
0
        port->rx_hi = fn;
383
0
    }
384
1
    else
385
1
    {
386
1
        if ( (port->rx_hi != NULL) || (port->rx_lo != NULL) )
387
0
            goto fail;
388
1
        port->rx = fn;
389
1
    }
390
1
391
1
    spin_unlock_irqrestore(&port->rx_lock, flags);
392
1
    return;
393
1
394
0
 fail:
395
0
    spin_unlock_irqrestore(&port->rx_lock, flags);
396
0
    printk("ERROR: Conflicting receive handlers for COM%d\n", 
397
0
           handle & SERHND_IDX);
398
0
}
399
400
void serial_force_unlock(int handle)
401
0
{
402
0
    struct serial_port *port;
403
0
404
0
    if ( handle == -1 )
405
0
        return;
406
0
407
0
    port = &com[handle & SERHND_IDX];
408
0
409
0
    spin_lock_init(&port->rx_lock);
410
0
    spin_lock_init(&port->tx_lock);
411
0
412
0
    serial_start_sync(handle);
413
0
}
414
415
void serial_start_sync(int handle)
416
1
{
417
1
    struct serial_port *port;
418
1
    unsigned long flags;
419
1
420
1
    if ( handle == -1 )
421
0
        return;
422
1
    
423
1
    port = &com[handle & SERHND_IDX];
424
1
425
1
    spin_lock_irqsave(&port->tx_lock, flags);
426
1
427
1
    if ( port->sync++ == 0 )
428
1
    {
429
1
        while ( (port->txbufp - port->txbufc) != 0 )
430
0
        {
431
0
            int n;
432
0
433
0
            while ( !(n = port->driver->tx_ready(port)) )
434
0
                cpu_relax();
435
0
            if ( n < 0 )
436
0
                /* port is unavailable and might not come up until reenabled by
437
0
                   dom0, we can't really do proper sync */
438
0
                break;
439
0
            serial_start_tx(port);
440
0
            port->driver->putc(
441
0
                port, port->txbuf[mask_serial_txbuf_idx(port->txbufc++)]);
442
0
        }
443
1
        if ( port->driver->flush )
444
0
            port->driver->flush(port);
445
1
    }
446
1
447
1
    spin_unlock_irqrestore(&port->tx_lock, flags);
448
1
}
449
450
void serial_end_sync(int handle)
451
0
{
452
0
    struct serial_port *port;
453
0
    unsigned long flags;
454
0
455
0
    if ( handle == -1 )
456
0
        return;
457
0
    
458
0
    port = &com[handle & SERHND_IDX];
459
0
460
0
    spin_lock_irqsave(&port->tx_lock, flags);
461
0
462
0
    port->sync--;
463
0
464
0
    spin_unlock_irqrestore(&port->tx_lock, flags);
465
0
}
466
467
void serial_start_log_everything(int handle)
468
0
{
469
0
    struct serial_port *port;
470
0
    unsigned long flags;
471
0
472
0
    if ( handle == -1 )
473
0
        return;
474
0
    
475
0
    port = &com[handle & SERHND_IDX];
476
0
477
0
    spin_lock_irqsave(&port->tx_lock, flags);
478
0
    port->tx_log_everything++;
479
0
    port->tx_quench = 0;
480
0
    spin_unlock_irqrestore(&port->tx_lock, flags);
481
0
}
482
483
void serial_end_log_everything(int handle)
484
0
{
485
0
    struct serial_port *port;
486
0
    unsigned long flags;
487
0
488
0
    if ( handle == -1 )
489
0
        return;
490
0
    
491
0
    port = &com[handle & SERHND_IDX];
492
0
493
0
    spin_lock_irqsave(&port->tx_lock, flags);
494
0
    port->tx_log_everything--;
495
0
    spin_unlock_irqrestore(&port->tx_lock, flags);
496
0
}
497
498
void __init serial_init_preirq(void)
499
1
{
500
1
    int i;
501
5
    for ( i = 0; i < ARRAY_SIZE(com); i++ )
502
4
        if ( com[i].driver && com[i].driver->init_preirq )
503
1
            com[i].driver->init_preirq(&com[i]);
504
1
}
505
506
void __init serial_init_postirq(void)
507
1
{
508
1
    int i;
509
5
    for ( i = 0; i < ARRAY_SIZE(com); i++ )
510
4
        if ( com[i].state == serial_parsed )
511
1
        {
512
1
            if ( com[i].driver->init_postirq )
513
1
                com[i].driver->init_postirq(&com[i]);
514
1
            com[i].state = serial_initialized;
515
1
        }
516
1
    post_irq = 1;
517
1
}
518
519
void __init serial_endboot(void)
520
1
{
521
1
    int i;
522
5
    for ( i = 0; i < ARRAY_SIZE(com); i++ )
523
4
        if ( com[i].driver && com[i].driver->endboot )
524
1
            com[i].driver->endboot(&com[i]);
525
1
}
526
527
int __init serial_irq(int idx)
528
4
{
529
4
    if ( (idx >= 0) && (idx < ARRAY_SIZE(com)) &&
530
4
         com[idx].driver && com[idx].driver->irq )
531
1
        return com[idx].driver->irq(&com[idx]);
532
4
533
3
    return -1;
534
4
}
535
536
const struct vuart_info *serial_vuart_info(int idx)
537
0
{
538
0
    if ( (idx >= 0) && (idx < ARRAY_SIZE(com)) &&
539
0
         com[idx].driver && com[idx].driver->vuart_info )
540
0
        return com[idx].driver->vuart_info(&com[idx]);
541
0
542
0
    return NULL;
543
0
}
544
545
void serial_suspend(void)
546
0
{
547
0
    int i;
548
0
    for ( i = 0; i < ARRAY_SIZE(com); i++ )
549
0
        if ( com[i].state == serial_initialized && com[i].driver->suspend )
550
0
            com[i].driver->suspend(&com[i]);
551
0
}
552
553
void serial_resume(void)
554
0
{
555
0
    int i;
556
0
    for ( i = 0; i < ARRAY_SIZE(com); i++ )
557
0
        if ( com[i].state == serial_initialized && com[i].driver->resume )
558
0
            com[i].driver->resume(&com[i]);
559
0
}
560
561
void __init serial_register_uart(int idx, struct uart_driver *driver,
562
                                 void *uart)
563
1
{
564
1
    /* Store UART-specific info. */
565
1
    com[idx].driver = driver;
566
1
    com[idx].uart   = uart;
567
1
}
568
569
void __init serial_async_transmit(struct serial_port *port)
570
1
{
571
1
    BUG_ON(!port->driver->tx_ready);
572
1
    if ( port->txbuf != NULL )
573
0
        return;
574
1
    if ( serial_txbufsz < PAGE_SIZE )
575
0
        serial_txbufsz = PAGE_SIZE;
576
1
    while ( serial_txbufsz & (serial_txbufsz - 1) )
577
0
        serial_txbufsz &= serial_txbufsz - 1;
578
1
    port->txbuf = alloc_xenheap_pages(
579
1
        get_order_from_bytes(serial_txbufsz), 0);
580
1
}
581
582
/*
583
 * Local variables:
584
 * mode: C
585
 * c-file-style: "BSD"
586
 * c-basic-offset: 4
587
 * tab-width: 4
588
 * indent-tabs-mode: nil
589
 * End:
590
 */