diff options
Diffstat (limited to 'arch/ia64/hp/sim/simserial.c')
-rw-r--r-- | arch/ia64/hp/sim/simserial.c | 1032 |
1 files changed, 1032 insertions, 0 deletions
diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c new file mode 100644 index 000000000000..786e70718ce4 --- /dev/null +++ b/arch/ia64/hp/sim/simserial.c | |||
@@ -0,0 +1,1032 @@ | |||
1 | /* | ||
2 | * Simulated Serial Driver (fake serial) | ||
3 | * | ||
4 | * This driver is mostly used for bringup purposes and will go away. | ||
5 | * It has a strong dependency on the system console. All outputs | ||
6 | * are rerouted to the same facility as the one used by printk which, in our | ||
7 | * case means sys_sim.c console (goes via the simulator). The code hereafter | ||
8 | * is completely leveraged from the serial.c driver. | ||
9 | * | ||
10 | * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co | ||
11 | * Stephane Eranian <eranian@hpl.hp.com> | ||
12 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
13 | * | ||
14 | * 02/04/00 D. Mosberger Merged in serial.c bug fixes in rs_close(). | ||
15 | * 02/25/00 D. Mosberger Synced up with 2.3.99pre-5 version of serial.c. | ||
16 | * 07/30/02 D. Mosberger Replace sti()/cli() with explicit spinlocks & local irq masking | ||
17 | */ | ||
18 | |||
19 | #include <linux/config.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/tty.h> | ||
24 | #include <linux/tty_flip.h> | ||
25 | #include <linux/major.h> | ||
26 | #include <linux/fcntl.h> | ||
27 | #include <linux/mm.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/console.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/serial.h> | ||
32 | #include <linux/serialP.h> | ||
33 | |||
34 | #include <asm/irq.h> | ||
35 | #include <asm/hw_irq.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | |||
38 | #ifdef CONFIG_KDB | ||
39 | # include <linux/kdb.h> | ||
40 | #endif | ||
41 | |||
42 | #undef SIMSERIAL_DEBUG /* define this to get some debug information */ | ||
43 | |||
44 | #define KEYBOARD_INTR 3 /* must match with simulator! */ | ||
45 | |||
46 | #define NR_PORTS 1 /* only one port for now */ | ||
47 | #define SERIAL_INLINE 1 | ||
48 | |||
49 | #ifdef SERIAL_INLINE | ||
50 | #define _INLINE_ inline | ||
51 | #endif | ||
52 | |||
53 | #define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) | ||
54 | |||
55 | #define SSC_GETCHAR 21 | ||
56 | |||
57 | extern long ia64_ssc (long, long, long, long, int); | ||
58 | extern void ia64_ssc_connect_irq (long intr, long irq); | ||
59 | |||
60 | static char *serial_name = "SimSerial driver"; | ||
61 | static char *serial_version = "0.6"; | ||
62 | |||
63 | /* | ||
64 | * This has been extracted from asm/serial.h. We need one eventually but | ||
65 | * I don't know exactly what we're going to put in it so just fake one | ||
66 | * for now. | ||
67 | */ | ||
68 | #define BASE_BAUD ( 1843200 / 16 ) | ||
69 | |||
70 | #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) | ||
71 | |||
72 | /* | ||
73 | * Most of the values here are meaningless to this particular driver. | ||
74 | * However some values must be preserved for the code (leveraged from serial.c | ||
75 | * to work correctly). | ||
76 | * port must not be 0 | ||
77 | * type must not be UNKNOWN | ||
78 | * So I picked arbitrary (guess from where?) values instead | ||
79 | */ | ||
80 | static struct serial_state rs_table[NR_PORTS]={ | ||
81 | /* UART CLK PORT IRQ FLAGS */ | ||
82 | { 0, BASE_BAUD, 0x3F8, 0, STD_COM_FLAGS,0,PORT_16550 } /* ttyS0 */ | ||
83 | }; | ||
84 | |||
85 | /* | ||
86 | * Just for the fun of it ! | ||
87 | */ | ||
88 | static struct serial_uart_config uart_config[] = { | ||
89 | { "unknown", 1, 0 }, | ||
90 | { "8250", 1, 0 }, | ||
91 | { "16450", 1, 0 }, | ||
92 | { "16550", 1, 0 }, | ||
93 | { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, | ||
94 | { "cirrus", 1, 0 }, | ||
95 | { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, | ||
96 | { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | | ||
97 | UART_STARTECH }, | ||
98 | { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, | ||
99 | { 0, 0} | ||
100 | }; | ||
101 | |||
102 | struct tty_driver *hp_simserial_driver; | ||
103 | |||
104 | static struct async_struct *IRQ_ports[NR_IRQS]; | ||
105 | |||
106 | static struct console *console; | ||
107 | |||
108 | static unsigned char *tmp_buf; | ||
109 | static DECLARE_MUTEX(tmp_buf_sem); | ||
110 | |||
111 | extern struct console *console_drivers; /* from kernel/printk.c */ | ||
112 | |||
113 | /* | ||
114 | * ------------------------------------------------------------ | ||
115 | * rs_stop() and rs_start() | ||
116 | * | ||
117 | * This routines are called before setting or resetting tty->stopped. | ||
118 | * They enable or disable transmitter interrupts, as necessary. | ||
119 | * ------------------------------------------------------------ | ||
120 | */ | ||
121 | static void rs_stop(struct tty_struct *tty) | ||
122 | { | ||
123 | #ifdef SIMSERIAL_DEBUG | ||
124 | printk("rs_stop: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n", | ||
125 | tty->stopped, tty->hw_stopped, tty->flow_stopped); | ||
126 | #endif | ||
127 | |||
128 | } | ||
129 | |||
130 | static void rs_start(struct tty_struct *tty) | ||
131 | { | ||
132 | #if SIMSERIAL_DEBUG | ||
133 | printk("rs_start: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n", | ||
134 | tty->stopped, tty->hw_stopped, tty->flow_stopped); | ||
135 | #endif | ||
136 | } | ||
137 | |||
138 | static void receive_chars(struct tty_struct *tty, struct pt_regs *regs) | ||
139 | { | ||
140 | unsigned char ch; | ||
141 | static unsigned char seen_esc = 0; | ||
142 | |||
143 | while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) { | ||
144 | if ( ch == 27 && seen_esc == 0 ) { | ||
145 | seen_esc = 1; | ||
146 | continue; | ||
147 | } else { | ||
148 | if ( seen_esc==1 && ch == 'O' ) { | ||
149 | seen_esc = 2; | ||
150 | continue; | ||
151 | } else if ( seen_esc == 2 ) { | ||
152 | if ( ch == 'P' ) show_state(); /* F1 key */ | ||
153 | #ifdef CONFIG_KDB | ||
154 | if ( ch == 'S' ) | ||
155 | kdb(KDB_REASON_KEYBOARD, 0, (kdb_eframe_t) regs); | ||
156 | #endif | ||
157 | |||
158 | seen_esc = 0; | ||
159 | continue; | ||
160 | } | ||
161 | } | ||
162 | seen_esc = 0; | ||
163 | if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; | ||
164 | |||
165 | *tty->flip.char_buf_ptr = ch; | ||
166 | |||
167 | *tty->flip.flag_buf_ptr = 0; | ||
168 | |||
169 | tty->flip.flag_buf_ptr++; | ||
170 | tty->flip.char_buf_ptr++; | ||
171 | tty->flip.count++; | ||
172 | } | ||
173 | tty_flip_buffer_push(tty); | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * This is the serial driver's interrupt routine for a single port | ||
178 | */ | ||
179 | static irqreturn_t rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) | ||
180 | { | ||
181 | struct async_struct * info; | ||
182 | |||
183 | /* | ||
184 | * I don't know exactly why they don't use the dev_id opaque data | ||
185 | * pointer instead of this extra lookup table | ||
186 | */ | ||
187 | info = IRQ_ports[irq]; | ||
188 | if (!info || !info->tty) { | ||
189 | printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info); | ||
190 | return IRQ_NONE; | ||
191 | } | ||
192 | /* | ||
193 | * pretty simple in our case, because we only get interrupts | ||
194 | * on inbound traffic | ||
195 | */ | ||
196 | receive_chars(info->tty, regs); | ||
197 | return IRQ_HANDLED; | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * ------------------------------------------------------------------- | ||
202 | * Here ends the serial interrupt routines. | ||
203 | * ------------------------------------------------------------------- | ||
204 | */ | ||
205 | |||
206 | #if 0 | ||
207 | /* | ||
208 | * not really used in our situation so keep them commented out for now | ||
209 | */ | ||
210 | static DECLARE_TASK_QUEUE(tq_serial); /* used to be at the top of the file */ | ||
211 | static void do_serial_bh(void) | ||
212 | { | ||
213 | run_task_queue(&tq_serial); | ||
214 | printk(KERN_ERR "do_serial_bh: called\n"); | ||
215 | } | ||
216 | #endif | ||
217 | |||
218 | static void do_softint(void *private_) | ||
219 | { | ||
220 | printk(KERN_ERR "simserial: do_softint called\n"); | ||
221 | } | ||
222 | |||
223 | static void rs_put_char(struct tty_struct *tty, unsigned char ch) | ||
224 | { | ||
225 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
226 | unsigned long flags; | ||
227 | |||
228 | if (!tty || !info->xmit.buf) return; | ||
229 | |||
230 | local_irq_save(flags); | ||
231 | if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) { | ||
232 | local_irq_restore(flags); | ||
233 | return; | ||
234 | } | ||
235 | info->xmit.buf[info->xmit.head] = ch; | ||
236 | info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); | ||
237 | local_irq_restore(flags); | ||
238 | } | ||
239 | |||
240 | static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) | ||
241 | { | ||
242 | int count; | ||
243 | unsigned long flags; | ||
244 | |||
245 | |||
246 | local_irq_save(flags); | ||
247 | |||
248 | if (info->x_char) { | ||
249 | char c = info->x_char; | ||
250 | |||
251 | console->write(console, &c, 1); | ||
252 | |||
253 | info->state->icount.tx++; | ||
254 | info->x_char = 0; | ||
255 | |||
256 | goto out; | ||
257 | } | ||
258 | |||
259 | if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) { | ||
260 | #ifdef SIMSERIAL_DEBUG | ||
261 | printk("transmit_chars: head=%d, tail=%d, stopped=%d\n", | ||
262 | info->xmit.head, info->xmit.tail, info->tty->stopped); | ||
263 | #endif | ||
264 | goto out; | ||
265 | } | ||
266 | /* | ||
267 | * We removed the loop and try to do it in to chunks. We need | ||
268 | * 2 operations maximum because it's a ring buffer. | ||
269 | * | ||
270 | * First from current to tail if possible. | ||
271 | * Then from the beginning of the buffer until necessary | ||
272 | */ | ||
273 | |||
274 | count = min(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE), | ||
275 | SERIAL_XMIT_SIZE - info->xmit.tail); | ||
276 | console->write(console, info->xmit.buf+info->xmit.tail, count); | ||
277 | |||
278 | info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1); | ||
279 | |||
280 | /* | ||
281 | * We have more at the beginning of the buffer | ||
282 | */ | ||
283 | count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); | ||
284 | if (count) { | ||
285 | console->write(console, info->xmit.buf, count); | ||
286 | info->xmit.tail += count; | ||
287 | } | ||
288 | out: | ||
289 | local_irq_restore(flags); | ||
290 | } | ||
291 | |||
292 | static void rs_flush_chars(struct tty_struct *tty) | ||
293 | { | ||
294 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
295 | |||
296 | if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped || | ||
297 | !info->xmit.buf) | ||
298 | return; | ||
299 | |||
300 | transmit_chars(info, NULL); | ||
301 | } | ||
302 | |||
303 | |||
304 | static int rs_write(struct tty_struct * tty, | ||
305 | const unsigned char *buf, int count) | ||
306 | { | ||
307 | int c, ret = 0; | ||
308 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
309 | unsigned long flags; | ||
310 | |||
311 | if (!tty || !info->xmit.buf || !tmp_buf) return 0; | ||
312 | |||
313 | local_irq_save(flags); | ||
314 | while (1) { | ||
315 | c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); | ||
316 | if (count < c) | ||
317 | c = count; | ||
318 | if (c <= 0) { | ||
319 | break; | ||
320 | } | ||
321 | memcpy(info->xmit.buf + info->xmit.head, buf, c); | ||
322 | info->xmit.head = ((info->xmit.head + c) & | ||
323 | (SERIAL_XMIT_SIZE-1)); | ||
324 | buf += c; | ||
325 | count -= c; | ||
326 | ret += c; | ||
327 | } | ||
328 | local_irq_restore(flags); | ||
329 | /* | ||
330 | * Hey, we transmit directly from here in our case | ||
331 | */ | ||
332 | if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) | ||
333 | && !tty->stopped && !tty->hw_stopped) { | ||
334 | transmit_chars(info, NULL); | ||
335 | } | ||
336 | return ret; | ||
337 | } | ||
338 | |||
339 | static int rs_write_room(struct tty_struct *tty) | ||
340 | { | ||
341 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
342 | |||
343 | return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); | ||
344 | } | ||
345 | |||
346 | static int rs_chars_in_buffer(struct tty_struct *tty) | ||
347 | { | ||
348 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
349 | |||
350 | return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); | ||
351 | } | ||
352 | |||
353 | static void rs_flush_buffer(struct tty_struct *tty) | ||
354 | { | ||
355 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
356 | unsigned long flags; | ||
357 | |||
358 | local_irq_save(flags); | ||
359 | info->xmit.head = info->xmit.tail = 0; | ||
360 | local_irq_restore(flags); | ||
361 | |||
362 | wake_up_interruptible(&tty->write_wait); | ||
363 | |||
364 | if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && | ||
365 | tty->ldisc.write_wakeup) | ||
366 | (tty->ldisc.write_wakeup)(tty); | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * This function is used to send a high-priority XON/XOFF character to | ||
371 | * the device | ||
372 | */ | ||
373 | static void rs_send_xchar(struct tty_struct *tty, char ch) | ||
374 | { | ||
375 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
376 | |||
377 | info->x_char = ch; | ||
378 | if (ch) { | ||
379 | /* | ||
380 | * I guess we could call console->write() directly but | ||
381 | * let's do that for now. | ||
382 | */ | ||
383 | transmit_chars(info, NULL); | ||
384 | } | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * ------------------------------------------------------------ | ||
389 | * rs_throttle() | ||
390 | * | ||
391 | * This routine is called by the upper-layer tty layer to signal that | ||
392 | * incoming characters should be throttled. | ||
393 | * ------------------------------------------------------------ | ||
394 | */ | ||
395 | static void rs_throttle(struct tty_struct * tty) | ||
396 | { | ||
397 | if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); | ||
398 | |||
399 | printk(KERN_INFO "simrs_throttle called\n"); | ||
400 | } | ||
401 | |||
402 | static void rs_unthrottle(struct tty_struct * tty) | ||
403 | { | ||
404 | struct async_struct *info = (struct async_struct *)tty->driver_data; | ||
405 | |||
406 | if (I_IXOFF(tty)) { | ||
407 | if (info->x_char) | ||
408 | info->x_char = 0; | ||
409 | else | ||
410 | rs_send_xchar(tty, START_CHAR(tty)); | ||
411 | } | ||
412 | printk(KERN_INFO "simrs_unthrottle called\n"); | ||
413 | } | ||
414 | |||
415 | /* | ||
416 | * rs_break() --- routine which turns the break handling on or off | ||
417 | */ | ||
418 | static void rs_break(struct tty_struct *tty, int break_state) | ||
419 | { | ||
420 | } | ||
421 | |||
422 | static int rs_ioctl(struct tty_struct *tty, struct file * file, | ||
423 | unsigned int cmd, unsigned long arg) | ||
424 | { | ||
425 | if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && | ||
426 | (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && | ||
427 | (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { | ||
428 | if (tty->flags & (1 << TTY_IO_ERROR)) | ||
429 | return -EIO; | ||
430 | } | ||
431 | |||
432 | switch (cmd) { | ||
433 | case TIOCMGET: | ||
434 | printk(KERN_INFO "rs_ioctl: TIOCMGET called\n"); | ||
435 | return -EINVAL; | ||
436 | case TIOCMBIS: | ||
437 | case TIOCMBIC: | ||
438 | case TIOCMSET: | ||
439 | printk(KERN_INFO "rs_ioctl: TIOCMBIS/BIC/SET called\n"); | ||
440 | return -EINVAL; | ||
441 | case TIOCGSERIAL: | ||
442 | printk(KERN_INFO "simrs_ioctl TIOCGSERIAL called\n"); | ||
443 | return 0; | ||
444 | case TIOCSSERIAL: | ||
445 | printk(KERN_INFO "simrs_ioctl TIOCSSERIAL called\n"); | ||
446 | return 0; | ||
447 | case TIOCSERCONFIG: | ||
448 | printk(KERN_INFO "rs_ioctl: TIOCSERCONFIG called\n"); | ||
449 | return -EINVAL; | ||
450 | |||
451 | case TIOCSERGETLSR: /* Get line status register */ | ||
452 | printk(KERN_INFO "rs_ioctl: TIOCSERGETLSR called\n"); | ||
453 | return -EINVAL; | ||
454 | |||
455 | case TIOCSERGSTRUCT: | ||
456 | printk(KERN_INFO "rs_ioctl: TIOCSERGSTRUCT called\n"); | ||
457 | #if 0 | ||
458 | if (copy_to_user((struct async_struct *) arg, | ||
459 | info, sizeof(struct async_struct))) | ||
460 | return -EFAULT; | ||
461 | #endif | ||
462 | return 0; | ||
463 | |||
464 | /* | ||
465 | * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change | ||
466 | * - mask passed in arg for lines of interest | ||
467 | * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) | ||
468 | * Caller should use TIOCGICOUNT to see which one it was | ||
469 | */ | ||
470 | case TIOCMIWAIT: | ||
471 | printk(KERN_INFO "rs_ioctl: TIOCMIWAIT: called\n"); | ||
472 | return 0; | ||
473 | /* | ||
474 | * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) | ||
475 | * Return: write counters to the user passed counter struct | ||
476 | * NB: both 1->0 and 0->1 transitions are counted except for | ||
477 | * RI where only 0->1 is counted. | ||
478 | */ | ||
479 | case TIOCGICOUNT: | ||
480 | printk(KERN_INFO "rs_ioctl: TIOCGICOUNT called\n"); | ||
481 | return 0; | ||
482 | |||
483 | case TIOCSERGWILD: | ||
484 | case TIOCSERSWILD: | ||
485 | /* "setserial -W" is called in Debian boot */ | ||
486 | printk (KERN_INFO "TIOCSER?WILD ioctl obsolete, ignored.\n"); | ||
487 | return 0; | ||
488 | |||
489 | default: | ||
490 | return -ENOIOCTLCMD; | ||
491 | } | ||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) | ||
496 | |||
497 | static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) | ||
498 | { | ||
499 | unsigned int cflag = tty->termios->c_cflag; | ||
500 | |||
501 | if ( (cflag == old_termios->c_cflag) | ||
502 | && ( RELEVANT_IFLAG(tty->termios->c_iflag) | ||
503 | == RELEVANT_IFLAG(old_termios->c_iflag))) | ||
504 | return; | ||
505 | |||
506 | |||
507 | /* Handle turning off CRTSCTS */ | ||
508 | if ((old_termios->c_cflag & CRTSCTS) && | ||
509 | !(tty->termios->c_cflag & CRTSCTS)) { | ||
510 | tty->hw_stopped = 0; | ||
511 | rs_start(tty); | ||
512 | } | ||
513 | } | ||
514 | /* | ||
515 | * This routine will shutdown a serial port; interrupts are disabled, and | ||
516 | * DTR is dropped if the hangup on close termio flag is on. | ||
517 | */ | ||
518 | static void shutdown(struct async_struct * info) | ||
519 | { | ||
520 | unsigned long flags; | ||
521 | struct serial_state *state; | ||
522 | int retval; | ||
523 | |||
524 | if (!(info->flags & ASYNC_INITIALIZED)) return; | ||
525 | |||
526 | state = info->state; | ||
527 | |||
528 | #ifdef SIMSERIAL_DEBUG | ||
529 | printk("Shutting down serial port %d (irq %d)....", info->line, | ||
530 | state->irq); | ||
531 | #endif | ||
532 | |||
533 | local_irq_save(flags); | ||
534 | { | ||
535 | /* | ||
536 | * First unlink the serial port from the IRQ chain... | ||
537 | */ | ||
538 | if (info->next_port) | ||
539 | info->next_port->prev_port = info->prev_port; | ||
540 | if (info->prev_port) | ||
541 | info->prev_port->next_port = info->next_port; | ||
542 | else | ||
543 | IRQ_ports[state->irq] = info->next_port; | ||
544 | |||
545 | /* | ||
546 | * Free the IRQ, if necessary | ||
547 | */ | ||
548 | if (state->irq && (!IRQ_ports[state->irq] || | ||
549 | !IRQ_ports[state->irq]->next_port)) { | ||
550 | if (IRQ_ports[state->irq]) { | ||
551 | free_irq(state->irq, NULL); | ||
552 | retval = request_irq(state->irq, rs_interrupt_single, | ||
553 | IRQ_T(info), "serial", NULL); | ||
554 | |||
555 | if (retval) | ||
556 | printk(KERN_ERR "serial shutdown: request_irq: error %d" | ||
557 | " Couldn't reacquire IRQ.\n", retval); | ||
558 | } else | ||
559 | free_irq(state->irq, NULL); | ||
560 | } | ||
561 | |||
562 | if (info->xmit.buf) { | ||
563 | free_page((unsigned long) info->xmit.buf); | ||
564 | info->xmit.buf = 0; | ||
565 | } | ||
566 | |||
567 | if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); | ||
568 | |||
569 | info->flags &= ~ASYNC_INITIALIZED; | ||
570 | } | ||
571 | local_irq_restore(flags); | ||
572 | } | ||
573 | |||
574 | /* | ||
575 | * ------------------------------------------------------------ | ||
576 | * rs_close() | ||
577 | * | ||
578 | * This routine is called when the serial port gets closed. First, we | ||
579 | * wait for the last remaining data to be sent. Then, we unlink its | ||
580 | * async structure from the interrupt chain if necessary, and we free | ||
581 | * that IRQ if nothing is left in the chain. | ||
582 | * ------------------------------------------------------------ | ||
583 | */ | ||
584 | static void rs_close(struct tty_struct *tty, struct file * filp) | ||
585 | { | ||
586 | struct async_struct * info = (struct async_struct *)tty->driver_data; | ||
587 | struct serial_state *state; | ||
588 | unsigned long flags; | ||
589 | |||
590 | if (!info ) return; | ||
591 | |||
592 | state = info->state; | ||
593 | |||
594 | local_irq_save(flags); | ||
595 | if (tty_hung_up_p(filp)) { | ||
596 | #ifdef SIMSERIAL_DEBUG | ||
597 | printk("rs_close: hung_up\n"); | ||
598 | #endif | ||
599 | local_irq_restore(flags); | ||
600 | return; | ||
601 | } | ||
602 | #ifdef SIMSERIAL_DEBUG | ||
603 | printk("rs_close ttys%d, count = %d\n", info->line, state->count); | ||
604 | #endif | ||
605 | if ((tty->count == 1) && (state->count != 1)) { | ||
606 | /* | ||
607 | * Uh, oh. tty->count is 1, which means that the tty | ||
608 | * structure will be freed. state->count should always | ||
609 | * be one in these conditions. If it's greater than | ||
610 | * one, we've got real problems, since it means the | ||
611 | * serial port won't be shutdown. | ||
612 | */ | ||
613 | printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, " | ||
614 | "state->count is %d\n", state->count); | ||
615 | state->count = 1; | ||
616 | } | ||
617 | if (--state->count < 0) { | ||
618 | printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n", | ||
619 | info->line, state->count); | ||
620 | state->count = 0; | ||
621 | } | ||
622 | if (state->count) { | ||
623 | local_irq_restore(flags); | ||
624 | return; | ||
625 | } | ||
626 | info->flags |= ASYNC_CLOSING; | ||
627 | local_irq_restore(flags); | ||
628 | |||
629 | /* | ||
630 | * Now we wait for the transmit buffer to clear; and we notify | ||
631 | * the line discipline to only process XON/XOFF characters. | ||
632 | */ | ||
633 | shutdown(info); | ||
634 | if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); | ||
635 | if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); | ||
636 | info->event = 0; | ||
637 | info->tty = 0; | ||
638 | if (info->blocked_open) { | ||
639 | if (info->close_delay) { | ||
640 | current->state = TASK_INTERRUPTIBLE; | ||
641 | schedule_timeout(info->close_delay); | ||
642 | } | ||
643 | wake_up_interruptible(&info->open_wait); | ||
644 | } | ||
645 | info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); | ||
646 | wake_up_interruptible(&info->close_wait); | ||
647 | } | ||
648 | |||
649 | /* | ||
650 | * rs_wait_until_sent() --- wait until the transmitter is empty | ||
651 | */ | ||
652 | static void rs_wait_until_sent(struct tty_struct *tty, int timeout) | ||
653 | { | ||
654 | } | ||
655 | |||
656 | |||
657 | /* | ||
658 | * rs_hangup() --- called by tty_hangup() when a hangup is signaled. | ||
659 | */ | ||
660 | static void rs_hangup(struct tty_struct *tty) | ||
661 | { | ||
662 | struct async_struct * info = (struct async_struct *)tty->driver_data; | ||
663 | struct serial_state *state = info->state; | ||
664 | |||
665 | #ifdef SIMSERIAL_DEBUG | ||
666 | printk("rs_hangup: called\n"); | ||
667 | #endif | ||
668 | |||
669 | state = info->state; | ||
670 | |||
671 | rs_flush_buffer(tty); | ||
672 | if (info->flags & ASYNC_CLOSING) | ||
673 | return; | ||
674 | shutdown(info); | ||
675 | |||
676 | info->event = 0; | ||
677 | state->count = 0; | ||
678 | info->flags &= ~ASYNC_NORMAL_ACTIVE; | ||
679 | info->tty = 0; | ||
680 | wake_up_interruptible(&info->open_wait); | ||
681 | } | ||
682 | |||
683 | |||
684 | static int get_async_struct(int line, struct async_struct **ret_info) | ||
685 | { | ||
686 | struct async_struct *info; | ||
687 | struct serial_state *sstate; | ||
688 | |||
689 | sstate = rs_table + line; | ||
690 | sstate->count++; | ||
691 | if (sstate->info) { | ||
692 | *ret_info = sstate->info; | ||
693 | return 0; | ||
694 | } | ||
695 | info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); | ||
696 | if (!info) { | ||
697 | sstate->count--; | ||
698 | return -ENOMEM; | ||
699 | } | ||
700 | memset(info, 0, sizeof(struct async_struct)); | ||
701 | init_waitqueue_head(&info->open_wait); | ||
702 | init_waitqueue_head(&info->close_wait); | ||
703 | init_waitqueue_head(&info->delta_msr_wait); | ||
704 | info->magic = SERIAL_MAGIC; | ||
705 | info->port = sstate->port; | ||
706 | info->flags = sstate->flags; | ||
707 | info->xmit_fifo_size = sstate->xmit_fifo_size; | ||
708 | info->line = line; | ||
709 | INIT_WORK(&info->work, do_softint, info); | ||
710 | info->state = sstate; | ||
711 | if (sstate->info) { | ||
712 | kfree(info); | ||
713 | *ret_info = sstate->info; | ||
714 | return 0; | ||
715 | } | ||
716 | *ret_info = sstate->info = info; | ||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | static int | ||
721 | startup(struct async_struct *info) | ||
722 | { | ||
723 | unsigned long flags; | ||
724 | int retval=0; | ||
725 | irqreturn_t (*handler)(int, void *, struct pt_regs *); | ||
726 | struct serial_state *state= info->state; | ||
727 | unsigned long page; | ||
728 | |||
729 | page = get_zeroed_page(GFP_KERNEL); | ||
730 | if (!page) | ||
731 | return -ENOMEM; | ||
732 | |||
733 | local_irq_save(flags); | ||
734 | |||
735 | if (info->flags & ASYNC_INITIALIZED) { | ||
736 | free_page(page); | ||
737 | goto errout; | ||
738 | } | ||
739 | |||
740 | if (!state->port || !state->type) { | ||
741 | if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); | ||
742 | free_page(page); | ||
743 | goto errout; | ||
744 | } | ||
745 | if (info->xmit.buf) | ||
746 | free_page(page); | ||
747 | else | ||
748 | info->xmit.buf = (unsigned char *) page; | ||
749 | |||
750 | #ifdef SIMSERIAL_DEBUG | ||
751 | printk("startup: ttys%d (irq %d)...", info->line, state->irq); | ||
752 | #endif | ||
753 | |||
754 | /* | ||
755 | * Allocate the IRQ if necessary | ||
756 | */ | ||
757 | if (state->irq && (!IRQ_ports[state->irq] || | ||
758 | !IRQ_ports[state->irq]->next_port)) { | ||
759 | if (IRQ_ports[state->irq]) { | ||
760 | retval = -EBUSY; | ||
761 | goto errout; | ||
762 | } else | ||
763 | handler = rs_interrupt_single; | ||
764 | |||
765 | retval = request_irq(state->irq, handler, IRQ_T(info), "simserial", NULL); | ||
766 | if (retval) { | ||
767 | if (capable(CAP_SYS_ADMIN)) { | ||
768 | if (info->tty) | ||
769 | set_bit(TTY_IO_ERROR, | ||
770 | &info->tty->flags); | ||
771 | retval = 0; | ||
772 | } | ||
773 | goto errout; | ||
774 | } | ||
775 | } | ||
776 | |||
777 | /* | ||
778 | * Insert serial port into IRQ chain. | ||
779 | */ | ||
780 | info->prev_port = 0; | ||
781 | info->next_port = IRQ_ports[state->irq]; | ||
782 | if (info->next_port) | ||
783 | info->next_port->prev_port = info; | ||
784 | IRQ_ports[state->irq] = info; | ||
785 | |||
786 | if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); | ||
787 | |||
788 | info->xmit.head = info->xmit.tail = 0; | ||
789 | |||
790 | #if 0 | ||
791 | /* | ||
792 | * Set up serial timers... | ||
793 | */ | ||
794 | timer_table[RS_TIMER].expires = jiffies + 2*HZ/100; | ||
795 | timer_active |= 1 << RS_TIMER; | ||
796 | #endif | ||
797 | |||
798 | /* | ||
799 | * Set up the tty->alt_speed kludge | ||
800 | */ | ||
801 | if (info->tty) { | ||
802 | if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) | ||
803 | info->tty->alt_speed = 57600; | ||
804 | if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) | ||
805 | info->tty->alt_speed = 115200; | ||
806 | if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) | ||
807 | info->tty->alt_speed = 230400; | ||
808 | if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) | ||
809 | info->tty->alt_speed = 460800; | ||
810 | } | ||
811 | |||
812 | info->flags |= ASYNC_INITIALIZED; | ||
813 | local_irq_restore(flags); | ||
814 | return 0; | ||
815 | |||
816 | errout: | ||
817 | local_irq_restore(flags); | ||
818 | return retval; | ||
819 | } | ||
820 | |||
821 | |||
822 | /* | ||
823 | * This routine is called whenever a serial port is opened. It | ||
824 | * enables interrupts for a serial port, linking in its async structure into | ||
825 | * the IRQ chain. It also performs the serial-specific | ||
826 | * initialization for the tty structure. | ||
827 | */ | ||
828 | static int rs_open(struct tty_struct *tty, struct file * filp) | ||
829 | { | ||
830 | struct async_struct *info; | ||
831 | int retval, line; | ||
832 | unsigned long page; | ||
833 | |||
834 | line = tty->index; | ||
835 | if ((line < 0) || (line >= NR_PORTS)) | ||
836 | return -ENODEV; | ||
837 | retval = get_async_struct(line, &info); | ||
838 | if (retval) | ||
839 | return retval; | ||
840 | tty->driver_data = info; | ||
841 | info->tty = tty; | ||
842 | |||
843 | #ifdef SIMSERIAL_DEBUG | ||
844 | printk("rs_open %s, count = %d\n", tty->name, info->state->count); | ||
845 | #endif | ||
846 | info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; | ||
847 | |||
848 | if (!tmp_buf) { | ||
849 | page = get_zeroed_page(GFP_KERNEL); | ||
850 | if (!page) | ||
851 | return -ENOMEM; | ||
852 | if (tmp_buf) | ||
853 | free_page(page); | ||
854 | else | ||
855 | tmp_buf = (unsigned char *) page; | ||
856 | } | ||
857 | |||
858 | /* | ||
859 | * If the port is the middle of closing, bail out now | ||
860 | */ | ||
861 | if (tty_hung_up_p(filp) || | ||
862 | (info->flags & ASYNC_CLOSING)) { | ||
863 | if (info->flags & ASYNC_CLOSING) | ||
864 | interruptible_sleep_on(&info->close_wait); | ||
865 | #ifdef SERIAL_DO_RESTART | ||
866 | return ((info->flags & ASYNC_HUP_NOTIFY) ? | ||
867 | -EAGAIN : -ERESTARTSYS); | ||
868 | #else | ||
869 | return -EAGAIN; | ||
870 | #endif | ||
871 | } | ||
872 | |||
873 | /* | ||
874 | * Start up serial port | ||
875 | */ | ||
876 | retval = startup(info); | ||
877 | if (retval) { | ||
878 | return retval; | ||
879 | } | ||
880 | |||
881 | /* | ||
882 | * figure out which console to use (should be one already) | ||
883 | */ | ||
884 | console = console_drivers; | ||
885 | while (console) { | ||
886 | if ((console->flags & CON_ENABLED) && console->write) break; | ||
887 | console = console->next; | ||
888 | } | ||
889 | |||
890 | #ifdef SIMSERIAL_DEBUG | ||
891 | printk("rs_open ttys%d successful\n", info->line); | ||
892 | #endif | ||
893 | return 0; | ||
894 | } | ||
895 | |||
896 | /* | ||
897 | * /proc fs routines.... | ||
898 | */ | ||
899 | |||
900 | static inline int line_info(char *buf, struct serial_state *state) | ||
901 | { | ||
902 | return sprintf(buf, "%d: uart:%s port:%lX irq:%d\n", | ||
903 | state->line, uart_config[state->type].name, | ||
904 | state->port, state->irq); | ||
905 | } | ||
906 | |||
907 | static int rs_read_proc(char *page, char **start, off_t off, int count, | ||
908 | int *eof, void *data) | ||
909 | { | ||
910 | int i, len = 0, l; | ||
911 | off_t begin = 0; | ||
912 | |||
913 | len += sprintf(page, "simserinfo:1.0 driver:%s\n", serial_version); | ||
914 | for (i = 0; i < NR_PORTS && len < 4000; i++) { | ||
915 | l = line_info(page + len, &rs_table[i]); | ||
916 | len += l; | ||
917 | if (len+begin > off+count) | ||
918 | goto done; | ||
919 | if (len+begin < off) { | ||
920 | begin += len; | ||
921 | len = 0; | ||
922 | } | ||
923 | } | ||
924 | *eof = 1; | ||
925 | done: | ||
926 | if (off >= len+begin) | ||
927 | return 0; | ||
928 | *start = page + (begin-off); | ||
929 | return ((count < begin+len-off) ? count : begin+len-off); | ||
930 | } | ||
931 | |||
932 | /* | ||
933 | * --------------------------------------------------------------------- | ||
934 | * rs_init() and friends | ||
935 | * | ||
936 | * rs_init() is called at boot-time to initialize the serial driver. | ||
937 | * --------------------------------------------------------------------- | ||
938 | */ | ||
939 | |||
940 | /* | ||
941 | * This routine prints out the appropriate serial driver version | ||
942 | * number, and identifies which options were configured into this | ||
943 | * driver. | ||
944 | */ | ||
945 | static inline void show_serial_version(void) | ||
946 | { | ||
947 | printk(KERN_INFO "%s version %s with", serial_name, serial_version); | ||
948 | printk(KERN_INFO " no serial options enabled\n"); | ||
949 | } | ||
950 | |||
951 | static struct tty_operations hp_ops = { | ||
952 | .open = rs_open, | ||
953 | .close = rs_close, | ||
954 | .write = rs_write, | ||
955 | .put_char = rs_put_char, | ||
956 | .flush_chars = rs_flush_chars, | ||
957 | .write_room = rs_write_room, | ||
958 | .chars_in_buffer = rs_chars_in_buffer, | ||
959 | .flush_buffer = rs_flush_buffer, | ||
960 | .ioctl = rs_ioctl, | ||
961 | .throttle = rs_throttle, | ||
962 | .unthrottle = rs_unthrottle, | ||
963 | .send_xchar = rs_send_xchar, | ||
964 | .set_termios = rs_set_termios, | ||
965 | .stop = rs_stop, | ||
966 | .start = rs_start, | ||
967 | .hangup = rs_hangup, | ||
968 | .break_ctl = rs_break, | ||
969 | .wait_until_sent = rs_wait_until_sent, | ||
970 | .read_proc = rs_read_proc, | ||
971 | }; | ||
972 | |||
973 | /* | ||
974 | * The serial driver boot-time initialization code! | ||
975 | */ | ||
976 | static int __init | ||
977 | simrs_init (void) | ||
978 | { | ||
979 | int i; | ||
980 | struct serial_state *state; | ||
981 | |||
982 | if (!ia64_platform_is("hpsim")) | ||
983 | return -ENODEV; | ||
984 | |||
985 | hp_simserial_driver = alloc_tty_driver(1); | ||
986 | if (!hp_simserial_driver) | ||
987 | return -ENOMEM; | ||
988 | |||
989 | show_serial_version(); | ||
990 | |||
991 | /* Initialize the tty_driver structure */ | ||
992 | |||
993 | hp_simserial_driver->owner = THIS_MODULE; | ||
994 | hp_simserial_driver->driver_name = "simserial"; | ||
995 | hp_simserial_driver->name = "ttyS"; | ||
996 | hp_simserial_driver->major = TTY_MAJOR; | ||
997 | hp_simserial_driver->minor_start = 64; | ||
998 | hp_simserial_driver->type = TTY_DRIVER_TYPE_SERIAL; | ||
999 | hp_simserial_driver->subtype = SERIAL_TYPE_NORMAL; | ||
1000 | hp_simserial_driver->init_termios = tty_std_termios; | ||
1001 | hp_simserial_driver->init_termios.c_cflag = | ||
1002 | B9600 | CS8 | CREAD | HUPCL | CLOCAL; | ||
1003 | hp_simserial_driver->flags = TTY_DRIVER_REAL_RAW; | ||
1004 | tty_set_operations(hp_simserial_driver, &hp_ops); | ||
1005 | |||
1006 | /* | ||
1007 | * Let's have a little bit of fun ! | ||
1008 | */ | ||
1009 | for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { | ||
1010 | |||
1011 | if (state->type == PORT_UNKNOWN) continue; | ||
1012 | |||
1013 | if (!state->irq) { | ||
1014 | state->irq = assign_irq_vector(AUTO_ASSIGN); | ||
1015 | ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq); | ||
1016 | } | ||
1017 | |||
1018 | printk(KERN_INFO "ttyS%d at 0x%04lx (irq = %d) is a %s\n", | ||
1019 | state->line, | ||
1020 | state->port, state->irq, | ||
1021 | uart_config[state->type].name); | ||
1022 | } | ||
1023 | |||
1024 | if (tty_register_driver(hp_simserial_driver)) | ||
1025 | panic("Couldn't register simserial driver\n"); | ||
1026 | |||
1027 | return 0; | ||
1028 | } | ||
1029 | |||
1030 | #ifndef MODULE | ||
1031 | __initcall(simrs_init); | ||
1032 | #endif | ||