debuggers.hg

view xen/drivers/char/serial.c @ 17546:a7ddd6bcd564

New boot parameter 'serial_tx_buffer=<size>' to change serial
transmit buffer size from default of 16kB.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Apr 23 14:06:51 2008 +0100 (2008-04-23)
parents b0c85bc56f9e
children 64c0014ae757
line source
1 /******************************************************************************
2 * serial.c
3 *
4 * Framework for serial device drivers.
5 *
6 * Copyright (c) 2003-2005, K A Fraser
7 */
9 #include <xen/config.h>
10 #include <xen/delay.h>
11 #include <xen/init.h>
12 #include <xen/irq.h>
13 #include <xen/keyhandler.h>
14 #include <xen/sched.h>
15 #include <xen/mm.h>
16 #include <xen/serial.h>
18 unsigned int serial_txbufsz = 16384;
19 static void __init parse_serial_tx_buffer(const char *s)
20 {
21 serial_txbufsz = max((unsigned int)parse_size_and_unit(s, NULL), 512u);
22 }
23 custom_param("serial_tx_buffer", parse_serial_tx_buffer);
25 #define mask_serial_rxbuf_idx(_i) ((_i)&(serial_rxbufsz-1))
26 #define mask_serial_txbuf_idx(_i) ((_i)&(serial_txbufsz-1))
28 static struct serial_port com[2] = {
29 { .rx_lock = SPIN_LOCK_UNLOCKED, .tx_lock = SPIN_LOCK_UNLOCKED },
30 { .rx_lock = SPIN_LOCK_UNLOCKED, .tx_lock = SPIN_LOCK_UNLOCKED }
31 };
33 void serial_rx_interrupt(struct serial_port *port, struct cpu_user_regs *regs)
34 {
35 char c;
36 serial_rx_fn fn = NULL;
37 unsigned long flags;
39 spin_lock_irqsave(&port->rx_lock, flags);
41 if ( port->driver->getc(port, &c) )
42 {
43 if ( port->rx != NULL )
44 fn = port->rx;
45 else if ( (c & 0x80) && (port->rx_hi != NULL) )
46 fn = port->rx_hi;
47 else if ( !(c & 0x80) && (port->rx_lo != NULL) )
48 fn = port->rx_lo;
49 else if ( (port->rxbufp - port->rxbufc) != serial_rxbufsz )
50 port->rxbuf[mask_serial_rxbuf_idx(port->rxbufp++)] = c;
51 }
53 spin_unlock_irqrestore(&port->rx_lock, flags);
55 if ( fn != NULL )
56 (*fn)(c & 0x7f, regs);
57 }
59 void serial_tx_interrupt(struct serial_port *port, struct cpu_user_regs *regs)
60 {
61 int i;
62 unsigned long flags;
64 local_irq_save(flags);
66 /*
67 * Avoid spinning for a long time: if there is a long-term lock holder
68 * then we know that they'll be stuffing bytes into the transmitter which
69 * will therefore not be empty for long.
70 */
71 while ( !spin_trylock(&port->tx_lock) )
72 {
73 if ( !port->driver->tx_empty(port) )
74 return;
75 cpu_relax();
76 }
78 if ( port->driver->tx_empty(port) )
79 {
80 for ( i = 0; i < port->tx_fifo_size; i++ )
81 {
82 if ( port->txbufc == port->txbufp )
83 break;
84 port->driver->putc(
85 port, port->txbuf[mask_serial_txbuf_idx(port->txbufc++)]);
86 }
87 }
89 spin_unlock_irqrestore(&port->tx_lock, flags);
90 }
92 static void __serial_putc(struct serial_port *port, char c)
93 {
94 int i;
96 if ( (port->txbuf != NULL) && !port->sync )
97 {
98 /* Interrupt-driven (asynchronous) transmitter. */
99 if ( (port->txbufp - port->txbufc) == serial_txbufsz )
100 {
101 /* Buffer is full: we spin, but could alternatively drop chars. */
102 while ( !port->driver->tx_empty(port) )
103 cpu_relax();
104 for ( i = 0; i < port->tx_fifo_size; i++ )
105 port->driver->putc(
106 port, port->txbuf[mask_serial_txbuf_idx(port->txbufc++)]);
107 port->txbuf[mask_serial_txbuf_idx(port->txbufp++)] = c;
108 }
109 else if ( ((port->txbufp - port->txbufc) == 0) &&
110 port->driver->tx_empty(port) )
111 {
112 /* Buffer and UART FIFO are both empty. */
113 port->driver->putc(port, c);
114 }
115 else
116 {
117 /* Normal case: buffer the character. */
118 port->txbuf[mask_serial_txbuf_idx(port->txbufp++)] = c;
119 }
120 }
121 else if ( port->driver->tx_empty )
122 {
123 /* Synchronous finite-capacity transmitter. */
124 while ( !port->driver->tx_empty(port) )
125 cpu_relax();
126 port->driver->putc(port, c);
127 }
128 else
129 {
130 /* Simple synchronous transmitter. */
131 port->driver->putc(port, c);
132 }
133 }
135 void serial_putc(int handle, char c)
136 {
137 struct serial_port *port;
138 unsigned long flags;
140 if ( handle == -1 )
141 return;
143 port = &com[handle & SERHND_IDX];
144 if ( !port->driver || !port->driver->putc )
145 return;
147 spin_lock_irqsave(&port->tx_lock, flags);
149 if ( (c == '\n') && (handle & SERHND_COOKED) )
150 __serial_putc(port, '\r' | ((handle & SERHND_HI) ? 0x80 : 0x00));
152 if ( handle & SERHND_HI )
153 c |= 0x80;
154 else if ( handle & SERHND_LO )
155 c &= 0x7f;
157 __serial_putc(port, c);
159 spin_unlock_irqrestore(&port->tx_lock, flags);
160 }
162 void serial_puts(int handle, const char *s)
163 {
164 struct serial_port *port;
165 unsigned long flags;
166 char c;
168 if ( handle == -1 )
169 return;
171 port = &com[handle & SERHND_IDX];
172 if ( !port->driver || !port->driver->putc )
173 return;
175 spin_lock_irqsave(&port->tx_lock, flags);
177 while ( (c = *s++) != '\0' )
178 {
179 if ( (c == '\n') && (handle & SERHND_COOKED) )
180 __serial_putc(port, '\r' | ((handle & SERHND_HI) ? 0x80 : 0x00));
182 if ( handle & SERHND_HI )
183 c |= 0x80;
184 else if ( handle & SERHND_LO )
185 c &= 0x7f;
187 __serial_putc(port, c);
188 }
190 spin_unlock_irqrestore(&port->tx_lock, flags);
191 }
193 char serial_getc(int handle)
194 {
195 struct serial_port *port;
196 char c;
197 unsigned long flags;
199 if ( handle == -1 )
200 return '\0';
202 port = &com[handle & SERHND_IDX];
203 if ( !port->driver || !port->driver->getc )
204 return '\0';
206 do {
207 for ( ; ; )
208 {
209 spin_lock_irqsave(&port->rx_lock, flags);
211 if ( port->rxbufp != port->rxbufc )
212 {
213 c = port->rxbuf[mask_serial_rxbuf_idx(port->rxbufc++)];
214 spin_unlock_irqrestore(&port->rx_lock, flags);
215 break;
216 }
218 if ( port->driver->getc(port, &c) )
219 {
220 spin_unlock_irqrestore(&port->rx_lock, flags);
221 break;
222 }
224 spin_unlock_irqrestore(&port->rx_lock, flags);
226 cpu_relax();
227 udelay(100);
228 }
229 } while ( ((handle & SERHND_LO) && (c & 0x80)) ||
230 ((handle & SERHND_HI) && !(c & 0x80)) );
232 return c & 0x7f;
233 }
235 int serial_parse_handle(char *conf)
236 {
237 int handle;
239 /* Silently fail if user has explicitly requested no serial I/O. */
240 if ( strcmp(conf, "none") == 0 )
241 return -1;
243 if ( strncmp(conf, "com", 3) != 0 )
244 goto fail;
246 switch ( conf[3] )
247 {
248 case '1':
249 handle = 0;
250 break;
251 case '2':
252 handle = 1;
253 break;
254 default:
255 goto fail;
256 }
258 if ( conf[4] == 'H' )
259 handle |= SERHND_HI;
260 else if ( conf[4] == 'L' )
261 handle |= SERHND_LO;
263 handle |= SERHND_COOKED;
265 return handle;
267 fail:
268 printk("ERROR: bad serial-interface specification '%s'\n", conf);
269 return -1;
270 }
272 void serial_set_rx_handler(int handle, serial_rx_fn fn)
273 {
274 struct serial_port *port;
275 unsigned long flags;
277 if ( handle == -1 )
278 return;
280 port = &com[handle & SERHND_IDX];
282 spin_lock_irqsave(&port->rx_lock, flags);
284 if ( port->rx != NULL )
285 goto fail;
287 if ( handle & SERHND_LO )
288 {
289 if ( port->rx_lo != NULL )
290 goto fail;
291 port->rx_lo = fn;
292 }
293 else if ( handle & SERHND_HI )
294 {
295 if ( port->rx_hi != NULL )
296 goto fail;
297 port->rx_hi = fn;
298 }
299 else
300 {
301 if ( (port->rx_hi != NULL) || (port->rx_lo != NULL) )
302 goto fail;
303 port->rx = fn;
304 }
306 spin_unlock_irqrestore(&port->rx_lock, flags);
307 return;
309 fail:
310 spin_unlock_irqrestore(&port->rx_lock, flags);
311 printk("ERROR: Conflicting receive handlers for COM%d\n",
312 handle & SERHND_IDX);
313 }
315 void serial_force_unlock(int handle)
316 {
317 struct serial_port *port;
319 if ( handle == -1 )
320 return;
322 port = &com[handle & SERHND_IDX];
324 spin_lock_init(&port->rx_lock);
325 spin_lock_init(&port->tx_lock);
327 serial_start_sync(handle);
328 }
330 void serial_start_sync(int handle)
331 {
332 struct serial_port *port;
333 unsigned long flags;
335 if ( handle == -1 )
336 return;
338 port = &com[handle & SERHND_IDX];
340 spin_lock_irqsave(&port->tx_lock, flags);
342 if ( port->sync++ == 0 )
343 {
344 while ( (port->txbufp - port->txbufc) != 0 )
345 {
346 while ( !port->driver->tx_empty(port) )
347 cpu_relax();
348 port->driver->putc(
349 port, port->txbuf[mask_serial_txbuf_idx(port->txbufc++)]);
350 }
351 }
353 spin_unlock_irqrestore(&port->tx_lock, flags);
354 }
356 void serial_end_sync(int handle)
357 {
358 struct serial_port *port;
359 unsigned long flags;
361 if ( handle == -1 )
362 return;
364 port = &com[handle & SERHND_IDX];
366 spin_lock_irqsave(&port->tx_lock, flags);
368 port->sync--;
370 spin_unlock_irqrestore(&port->tx_lock, flags);
371 }
373 int serial_tx_space(int handle)
374 {
375 struct serial_port *port;
376 if ( handle == -1 )
377 return serial_txbufsz;
378 port = &com[handle & SERHND_IDX];
379 return serial_txbufsz - (port->txbufp - port->txbufc);
380 }
382 void __devinit serial_init_preirq(void)
383 {
384 int i;
385 for ( i = 0; i < ARRAY_SIZE(com); i++ )
386 if ( com[i].driver && com[i].driver->init_preirq )
387 com[i].driver->init_preirq(&com[i]);
388 }
390 void __devinit serial_init_postirq(void)
391 {
392 int i;
393 for ( i = 0; i < ARRAY_SIZE(com); i++ )
394 if ( com[i].driver && com[i].driver->init_postirq )
395 com[i].driver->init_postirq(&com[i]);
396 }
398 void __init serial_endboot(void)
399 {
400 int i;
401 for ( i = 0; i < ARRAY_SIZE(com); i++ )
402 if ( com[i].driver && com[i].driver->endboot )
403 com[i].driver->endboot(&com[i]);
404 }
406 int serial_irq(int idx)
407 {
408 if ( (idx >= 0) && (idx < ARRAY_SIZE(com)) &&
409 com[idx].driver && com[idx].driver->irq )
410 return com[idx].driver->irq(&com[idx]);
412 return -1;
413 }
415 void serial_suspend(void)
416 {
417 int i, irq;
418 for ( i = 0; i < ARRAY_SIZE(com); i++ )
419 if ( (irq = serial_irq(i)) >= 0 )
420 free_irq(irq);
421 }
423 void serial_resume(void)
424 {
425 serial_init_preirq();
426 serial_init_postirq();
427 }
429 void serial_register_uart(int idx, struct uart_driver *driver, void *uart)
430 {
431 /* Store UART-specific info. */
432 com[idx].driver = driver;
433 com[idx].uart = uart;
435 /* Default is no transmit FIFO. */
436 com[idx].tx_fifo_size = 1;
437 }
439 void serial_async_transmit(struct serial_port *port)
440 {
441 BUG_ON(!port->driver->tx_empty);
442 if ( port->txbuf == NULL )
443 port->txbuf = alloc_xenheap_pages(
444 get_order_from_bytes(serial_txbufsz));
445 }
447 /*
448 * Local variables:
449 * mode: C
450 * c-set-style: "BSD"
451 * c-basic-offset: 4
452 * tab-width: 4
453 * indent-tabs-mode: nil
454 * End:
455 */