Contents of /alx-src/tags/kernel26-2.6.12-alx-r9/drivers/char/hvc_console.c
Parent Directory
|
Revision Log
Revision 630 -
(show annotations)
(download)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 20235 byte(s)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 20235 byte(s)
Tag kernel26-2.6.12-alx-r9
1 | /* |
2 | * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM |
3 | * Copyright (C) 2001 Paul Mackerras <paulus@au.ibm.com>, IBM |
4 | * Copyright (C) 2004 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. |
5 | * Copyright (C) 2004 IBM Corporation |
6 | * |
7 | * Additional Author(s): |
8 | * Ryan S. Arnold <rsa@us.ibm.com> |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License as published by |
12 | * the Free Software Foundation; either version 2 of the License, or |
13 | * (at your option) any later version. |
14 | * |
15 | * This program is distributed in the hope that it will be useful, |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | * GNU General Public License for more details. |
19 | * |
20 | * You should have received a copy of the GNU General Public License |
21 | * along with this program; if not, write to the Free Software |
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 | */ |
24 | |
25 | #include <linux/console.h> |
26 | #include <linux/cpumask.h> |
27 | #include <linux/init.h> |
28 | #include <linux/kbd_kern.h> |
29 | #include <linux/kernel.h> |
30 | #include <linux/kobject.h> |
31 | #include <linux/kthread.h> |
32 | #include <linux/list.h> |
33 | #include <linux/module.h> |
34 | #include <linux/major.h> |
35 | #include <linux/sysrq.h> |
36 | #include <linux/tty.h> |
37 | #include <linux/tty_flip.h> |
38 | #include <linux/sched.h> |
39 | #include <linux/spinlock.h> |
40 | #include <linux/delay.h> |
41 | #include <asm/uaccess.h> |
42 | #include <asm/hvconsole.h> |
43 | #include <asm/vio.h> |
44 | |
45 | #define HVC_MAJOR 229 |
46 | #define HVC_MINOR 0 |
47 | |
48 | #define TIMEOUT (10) |
49 | |
50 | /* |
51 | * Wait this long per iteration while trying to push buffered data to the |
52 | * hypervisor before allowing the tty to complete a close operation. |
53 | */ |
54 | #define HVC_CLOSE_WAIT (HZ/100) /* 1/10 of a second */ |
55 | |
56 | /* |
57 | * The Linux TTY code does not support dynamic addition of tty derived devices |
58 | * so we need to know how many tty devices we might need when space is allocated |
59 | * for the tty device. Since this driver supports hotplug of vty adapters we |
60 | * need to make sure we have enough allocated. |
61 | */ |
62 | #define HVC_ALLOC_TTY_ADAPTERS 8 |
63 | |
64 | static struct tty_driver *hvc_driver; |
65 | #ifdef CONFIG_MAGIC_SYSRQ |
66 | static int sysrq_pressed; |
67 | #endif |
68 | |
69 | #define N_OUTBUF 16 |
70 | #define N_INBUF 16 |
71 | |
72 | #define __ALIGNED__ __attribute__((__aligned__(8))) |
73 | |
74 | struct hvc_struct { |
75 | spinlock_t lock; |
76 | int index; |
77 | struct tty_struct *tty; |
78 | unsigned int count; |
79 | int do_wakeup; |
80 | char outbuf[N_OUTBUF] __ALIGNED__; |
81 | int n_outbuf; |
82 | uint32_t vtermno; |
83 | int irq_requested; |
84 | int irq; |
85 | struct list_head next; |
86 | struct kobject kobj; /* ref count & hvc_struct lifetime */ |
87 | struct vio_dev *vdev; |
88 | }; |
89 | |
90 | /* dynamic list of hvc_struct instances */ |
91 | static struct list_head hvc_structs = LIST_HEAD_INIT(hvc_structs); |
92 | |
93 | /* |
94 | * Protect the list of hvc_struct instances from inserts and removals during |
95 | * list traversal. |
96 | */ |
97 | static DEFINE_SPINLOCK(hvc_structs_lock); |
98 | |
99 | /* |
100 | * Initial console vtermnos for console API usage prior to full console |
101 | * initialization. Any vty adapter outside this range will not have usable |
102 | * console interfaces but can still be used as a tty device. This has to be |
103 | * static because kmalloc will not work during early console init. |
104 | */ |
105 | static uint32_t vtermnos[MAX_NR_HVC_CONSOLES]; |
106 | |
107 | /* Used for accounting purposes */ |
108 | static int num_vterms = 0; |
109 | |
110 | static struct task_struct *hvc_task; |
111 | |
112 | /* |
113 | * This value is used to associate a tty->index value to a hvc_struct based |
114 | * upon order of exposure via hvc_probe(). |
115 | */ |
116 | static int hvc_count = -1; |
117 | |
118 | /* Picks up late kicks after list walk but before schedule() */ |
119 | static int hvc_kicked; |
120 | |
121 | /* Wake the sleeping khvcd */ |
122 | static void hvc_kick(void) |
123 | { |
124 | hvc_kicked = 1; |
125 | wake_up_process(hvc_task); |
126 | } |
127 | |
128 | /* |
129 | * NOTE: This API isn't used if the console adapter doesn't support interrupts. |
130 | * In this case the console is poll driven. |
131 | */ |
132 | static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance, struct pt_regs *regs) |
133 | { |
134 | hvc_kick(); |
135 | return IRQ_HANDLED; |
136 | } |
137 | |
138 | static void hvc_unthrottle(struct tty_struct *tty) |
139 | { |
140 | hvc_kick(); |
141 | } |
142 | |
143 | /* |
144 | * Do not call this function with either the hvc_strucst_lock or the hvc_struct |
145 | * lock held. If successful, this function increments the kobject reference |
146 | * count against the target hvc_struct so it should be released when finished. |
147 | */ |
148 | struct hvc_struct *hvc_get_by_index(int index) |
149 | { |
150 | struct hvc_struct *hp; |
151 | unsigned long flags; |
152 | |
153 | spin_lock(&hvc_structs_lock); |
154 | |
155 | list_for_each_entry(hp, &hvc_structs, next) { |
156 | spin_lock_irqsave(&hp->lock, flags); |
157 | if (hp->index == index) { |
158 | kobject_get(&hp->kobj); |
159 | spin_unlock_irqrestore(&hp->lock, flags); |
160 | spin_unlock(&hvc_structs_lock); |
161 | return hp; |
162 | } |
163 | spin_unlock_irqrestore(&hp->lock, flags); |
164 | } |
165 | hp = NULL; |
166 | |
167 | spin_unlock(&hvc_structs_lock); |
168 | return hp; |
169 | } |
170 | |
171 | /* |
172 | * The TTY interface won't be used until after the vio layer has exposed the vty |
173 | * adapter to the kernel. |
174 | */ |
175 | static int hvc_open(struct tty_struct *tty, struct file * filp) |
176 | { |
177 | struct hvc_struct *hp; |
178 | unsigned long flags; |
179 | int irq = NO_IRQ; |
180 | int rc = 0; |
181 | struct kobject *kobjp; |
182 | |
183 | /* Auto increments kobject reference if found. */ |
184 | if (!(hp = hvc_get_by_index(tty->index))) { |
185 | printk(KERN_WARNING "hvc_console: tty open failed, no vty associated with tty.\n"); |
186 | return -ENODEV; |
187 | } |
188 | |
189 | spin_lock_irqsave(&hp->lock, flags); |
190 | /* Check and then increment for fast path open. */ |
191 | if (hp->count++ > 0) { |
192 | spin_unlock_irqrestore(&hp->lock, flags); |
193 | hvc_kick(); |
194 | return 0; |
195 | } /* else count == 0 */ |
196 | |
197 | tty->driver_data = hp; |
198 | hp->tty = tty; |
199 | /* Save for request_irq outside of spin_lock. */ |
200 | irq = hp->irq; |
201 | if (irq != NO_IRQ) |
202 | hp->irq_requested = 1; |
203 | |
204 | kobjp = &hp->kobj; |
205 | |
206 | spin_unlock_irqrestore(&hp->lock, flags); |
207 | /* check error, fallback to non-irq */ |
208 | if (irq != NO_IRQ) |
209 | rc = request_irq(irq, hvc_handle_interrupt, SA_INTERRUPT, "hvc_console", hp); |
210 | |
211 | /* |
212 | * If the request_irq() fails and we return an error. The tty layer |
213 | * will call hvc_close() after a failed open but we don't want to clean |
214 | * up there so we'll clean up here and clear out the previously set |
215 | * tty fields and return the kobject reference. |
216 | */ |
217 | if (rc) { |
218 | spin_lock_irqsave(&hp->lock, flags); |
219 | hp->tty = NULL; |
220 | hp->irq_requested = 0; |
221 | spin_unlock_irqrestore(&hp->lock, flags); |
222 | tty->driver_data = NULL; |
223 | kobject_put(kobjp); |
224 | printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc); |
225 | } |
226 | /* Force wakeup of the polling thread */ |
227 | hvc_kick(); |
228 | |
229 | return rc; |
230 | } |
231 | |
232 | static void hvc_close(struct tty_struct *tty, struct file * filp) |
233 | { |
234 | struct hvc_struct *hp; |
235 | struct kobject *kobjp; |
236 | int irq = NO_IRQ; |
237 | unsigned long flags; |
238 | |
239 | if (tty_hung_up_p(filp)) |
240 | return; |
241 | |
242 | /* |
243 | * No driver_data means that this close was issued after a failed |
244 | * hvc_open by the tty layer's release_dev() function and we can just |
245 | * exit cleanly because the kobject reference wasn't made. |
246 | */ |
247 | if (!tty->driver_data) |
248 | return; |
249 | |
250 | hp = tty->driver_data; |
251 | spin_lock_irqsave(&hp->lock, flags); |
252 | |
253 | kobjp = &hp->kobj; |
254 | if (--hp->count == 0) { |
255 | if (hp->irq_requested) |
256 | irq = hp->irq; |
257 | hp->irq_requested = 0; |
258 | |
259 | /* We are done with the tty pointer now. */ |
260 | hp->tty = NULL; |
261 | spin_unlock_irqrestore(&hp->lock, flags); |
262 | |
263 | /* |
264 | * Chain calls chars_in_buffer() and returns immediately if |
265 | * there is no buffered data otherwise sleeps on a wait queue |
266 | * waking periodically to check chars_in_buffer(). |
267 | */ |
268 | tty_wait_until_sent(tty, HVC_CLOSE_WAIT); |
269 | |
270 | if (irq != NO_IRQ) |
271 | free_irq(irq, hp); |
272 | |
273 | } else { |
274 | if (hp->count < 0) |
275 | printk(KERN_ERR "hvc_close %X: oops, count is %d\n", |
276 | hp->vtermno, hp->count); |
277 | spin_unlock_irqrestore(&hp->lock, flags); |
278 | } |
279 | |
280 | kobject_put(kobjp); |
281 | } |
282 | |
283 | static void hvc_hangup(struct tty_struct *tty) |
284 | { |
285 | struct hvc_struct *hp = tty->driver_data; |
286 | unsigned long flags; |
287 | int irq = NO_IRQ; |
288 | int temp_open_count; |
289 | struct kobject *kobjp; |
290 | |
291 | if (!hp) |
292 | return; |
293 | |
294 | spin_lock_irqsave(&hp->lock, flags); |
295 | |
296 | /* |
297 | * The N_TTY line discipline has problems such that in a close vs |
298 | * open->hangup case this can be called after the final close so prevent |
299 | * that from happening for now. |
300 | */ |
301 | if (hp->count <= 0) { |
302 | spin_unlock_irqrestore(&hp->lock, flags); |
303 | return; |
304 | } |
305 | |
306 | kobjp = &hp->kobj; |
307 | temp_open_count = hp->count; |
308 | hp->count = 0; |
309 | hp->n_outbuf = 0; |
310 | hp->tty = NULL; |
311 | if (hp->irq_requested) |
312 | /* Saved for use outside of spin_lock. */ |
313 | irq = hp->irq; |
314 | hp->irq_requested = 0; |
315 | spin_unlock_irqrestore(&hp->lock, flags); |
316 | if (irq != NO_IRQ) |
317 | free_irq(irq, hp); |
318 | while(temp_open_count) { |
319 | --temp_open_count; |
320 | kobject_put(kobjp); |
321 | } |
322 | } |
323 | |
324 | /* |
325 | * Push buffered characters whether they were just recently buffered or waiting |
326 | * on a blocked hypervisor. Call this function with hp->lock held. |
327 | */ |
328 | static void hvc_push(struct hvc_struct *hp) |
329 | { |
330 | int n; |
331 | |
332 | n = hvc_put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf); |
333 | if (n <= 0) { |
334 | if (n == 0) |
335 | return; |
336 | /* throw away output on error; this happens when |
337 | there is no session connected to the vterm. */ |
338 | hp->n_outbuf = 0; |
339 | } else |
340 | hp->n_outbuf -= n; |
341 | if (hp->n_outbuf > 0) |
342 | memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf); |
343 | else |
344 | hp->do_wakeup = 1; |
345 | } |
346 | |
347 | static inline int __hvc_write_kernel(struct hvc_struct *hp, |
348 | const unsigned char *buf, int count) |
349 | { |
350 | unsigned long flags; |
351 | int rsize, written = 0; |
352 | |
353 | spin_lock_irqsave(&hp->lock, flags); |
354 | |
355 | /* Push pending writes */ |
356 | if (hp->n_outbuf > 0) |
357 | hvc_push(hp); |
358 | |
359 | while (count > 0 && (rsize = N_OUTBUF - hp->n_outbuf) > 0) { |
360 | if (rsize > count) |
361 | rsize = count; |
362 | memcpy(hp->outbuf + hp->n_outbuf, buf, rsize); |
363 | count -= rsize; |
364 | buf += rsize; |
365 | hp->n_outbuf += rsize; |
366 | written += rsize; |
367 | hvc_push(hp); |
368 | } |
369 | spin_unlock_irqrestore(&hp->lock, flags); |
370 | |
371 | return written; |
372 | } |
373 | static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count) |
374 | { |
375 | struct hvc_struct *hp = tty->driver_data; |
376 | int written; |
377 | |
378 | /* This write was probably executed during a tty close. */ |
379 | if (!hp) |
380 | return -EPIPE; |
381 | |
382 | if (hp->count <= 0) |
383 | return -EIO; |
384 | |
385 | written = __hvc_write_kernel(hp, buf, count); |
386 | |
387 | /* |
388 | * Racy, but harmless, kick thread if there is still pending data. |
389 | * There really is nothing wrong with kicking the thread, even if there |
390 | * is no buffered data. |
391 | */ |
392 | if (hp->n_outbuf) |
393 | hvc_kick(); |
394 | |
395 | return written; |
396 | } |
397 | |
398 | /* |
399 | * This is actually a contract between the driver and the tty layer outlining |
400 | * how much write room the driver can guarentee will be sent OR BUFFERED. This |
401 | * driver MUST honor the return value. |
402 | */ |
403 | static int hvc_write_room(struct tty_struct *tty) |
404 | { |
405 | struct hvc_struct *hp = tty->driver_data; |
406 | |
407 | if (!hp) |
408 | return -1; |
409 | |
410 | return N_OUTBUF - hp->n_outbuf; |
411 | } |
412 | |
413 | static int hvc_chars_in_buffer(struct tty_struct *tty) |
414 | { |
415 | struct hvc_struct *hp = tty->driver_data; |
416 | |
417 | if (!hp) |
418 | return -1; |
419 | return hp->n_outbuf; |
420 | } |
421 | |
422 | #define HVC_POLL_READ 0x00000001 |
423 | #define HVC_POLL_WRITE 0x00000002 |
424 | #define HVC_POLL_QUICK 0x00000004 |
425 | |
426 | static int hvc_poll(struct hvc_struct *hp) |
427 | { |
428 | struct tty_struct *tty; |
429 | int i, n, poll_mask = 0; |
430 | char buf[N_INBUF] __ALIGNED__; |
431 | unsigned long flags; |
432 | int read_total = 0; |
433 | |
434 | spin_lock_irqsave(&hp->lock, flags); |
435 | |
436 | /* Push pending writes */ |
437 | if (hp->n_outbuf > 0) |
438 | hvc_push(hp); |
439 | /* Reschedule us if still some write pending */ |
440 | if (hp->n_outbuf > 0) |
441 | poll_mask |= HVC_POLL_WRITE; |
442 | |
443 | /* No tty attached, just skip */ |
444 | tty = hp->tty; |
445 | if (tty == NULL) |
446 | goto bail; |
447 | |
448 | /* Now check if we can get data (are we throttled ?) */ |
449 | if (test_bit(TTY_THROTTLED, &tty->flags)) |
450 | goto throttled; |
451 | |
452 | /* If we aren't interrupt driven and aren't throttled, we always |
453 | * request a reschedule |
454 | */ |
455 | if (hp->irq == NO_IRQ) |
456 | poll_mask |= HVC_POLL_READ; |
457 | |
458 | /* Read data if any */ |
459 | for (;;) { |
460 | int count = N_INBUF; |
461 | if (count > (TTY_FLIPBUF_SIZE - tty->flip.count)) |
462 | count = TTY_FLIPBUF_SIZE - tty->flip.count; |
463 | |
464 | /* If flip is full, just reschedule a later read */ |
465 | if (count == 0) { |
466 | poll_mask |= HVC_POLL_READ; |
467 | break; |
468 | } |
469 | |
470 | n = hvc_get_chars(hp->vtermno, buf, count); |
471 | if (n <= 0) { |
472 | /* Hangup the tty when disconnected from host */ |
473 | if (n == -EPIPE) { |
474 | spin_unlock_irqrestore(&hp->lock, flags); |
475 | tty_hangup(tty); |
476 | spin_lock_irqsave(&hp->lock, flags); |
477 | } |
478 | break; |
479 | } |
480 | for (i = 0; i < n; ++i) { |
481 | #ifdef CONFIG_MAGIC_SYSRQ |
482 | /* Handle the SysRq Hack */ |
483 | if (buf[i] == '\x0f') { /* ^O -- should support a sequence */ |
484 | sysrq_pressed = 1; |
485 | continue; |
486 | } else if (sysrq_pressed) { |
487 | handle_sysrq(buf[i], NULL, tty); |
488 | sysrq_pressed = 0; |
489 | continue; |
490 | } |
491 | #endif /* CONFIG_MAGIC_SYSRQ */ |
492 | tty_insert_flip_char(tty, buf[i], 0); |
493 | } |
494 | |
495 | if (tty->flip.count) |
496 | tty_schedule_flip(tty); |
497 | |
498 | /* |
499 | * Account for the total amount read in one loop, and if above |
500 | * 64 bytes, we do a quick schedule loop to let the tty grok the |
501 | * data and eventually throttle us. |
502 | */ |
503 | read_total += n; |
504 | if (read_total >= 64) { |
505 | poll_mask |= HVC_POLL_QUICK; |
506 | break; |
507 | } |
508 | } |
509 | throttled: |
510 | /* Wakeup write queue if necessary */ |
511 | if (hp->do_wakeup) { |
512 | hp->do_wakeup = 0; |
513 | tty_wakeup(tty); |
514 | } |
515 | bail: |
516 | spin_unlock_irqrestore(&hp->lock, flags); |
517 | |
518 | return poll_mask; |
519 | } |
520 | |
521 | #if defined(CONFIG_XMON) && defined(CONFIG_SMP) |
522 | extern cpumask_t cpus_in_xmon; |
523 | #else |
524 | static const cpumask_t cpus_in_xmon = CPU_MASK_NONE; |
525 | #endif |
526 | |
527 | /* |
528 | * This kthread is either polling or interrupt driven. This is determined by |
529 | * calling hvc_poll() who determines whether a console adapter support |
530 | * interrupts. |
531 | */ |
532 | int khvcd(void *unused) |
533 | { |
534 | int poll_mask; |
535 | struct hvc_struct *hp; |
536 | |
537 | __set_current_state(TASK_RUNNING); |
538 | do { |
539 | poll_mask = 0; |
540 | hvc_kicked = 0; |
541 | wmb(); |
542 | if (cpus_empty(cpus_in_xmon)) { |
543 | spin_lock(&hvc_structs_lock); |
544 | list_for_each_entry(hp, &hvc_structs, next) { |
545 | /*hp = list_entry(node, struct hvc_struct, * next); */ |
546 | poll_mask |= hvc_poll(hp); |
547 | } |
548 | spin_unlock(&hvc_structs_lock); |
549 | } else |
550 | poll_mask |= HVC_POLL_READ; |
551 | if (hvc_kicked) |
552 | continue; |
553 | if (poll_mask & HVC_POLL_QUICK) { |
554 | yield(); |
555 | continue; |
556 | } |
557 | set_current_state(TASK_INTERRUPTIBLE); |
558 | if (!hvc_kicked) { |
559 | if (poll_mask == 0) |
560 | schedule(); |
561 | else |
562 | msleep_interruptible(TIMEOUT); |
563 | } |
564 | __set_current_state(TASK_RUNNING); |
565 | } while (!kthread_should_stop()); |
566 | |
567 | return 0; |
568 | } |
569 | |
570 | static struct tty_operations hvc_ops = { |
571 | .open = hvc_open, |
572 | .close = hvc_close, |
573 | .write = hvc_write, |
574 | .hangup = hvc_hangup, |
575 | .unthrottle = hvc_unthrottle, |
576 | .write_room = hvc_write_room, |
577 | .chars_in_buffer = hvc_chars_in_buffer, |
578 | }; |
579 | |
580 | char hvc_driver_name[] = "hvc_console"; |
581 | |
582 | static struct vio_device_id hvc_driver_table[] __devinitdata= { |
583 | {"serial", "hvterm1"}, |
584 | { NULL, } |
585 | }; |
586 | MODULE_DEVICE_TABLE(vio, hvc_driver_table); |
587 | |
588 | /* callback when the kboject ref count reaches zero. */ |
589 | static void destroy_hvc_struct(struct kobject *kobj) |
590 | { |
591 | struct hvc_struct *hp = container_of(kobj, struct hvc_struct, kobj); |
592 | unsigned long flags; |
593 | |
594 | spin_lock(&hvc_structs_lock); |
595 | |
596 | spin_lock_irqsave(&hp->lock, flags); |
597 | list_del(&(hp->next)); |
598 | spin_unlock_irqrestore(&hp->lock, flags); |
599 | |
600 | spin_unlock(&hvc_structs_lock); |
601 | |
602 | kfree(hp); |
603 | } |
604 | |
605 | static struct kobj_type hvc_kobj_type = { |
606 | .release = destroy_hvc_struct, |
607 | }; |
608 | |
609 | static int __devinit hvc_probe( |
610 | struct vio_dev *dev, |
611 | const struct vio_device_id *id) |
612 | { |
613 | struct hvc_struct *hp; |
614 | |
615 | /* probed with invalid parameters. */ |
616 | if (!dev || !id) |
617 | return -EPERM; |
618 | |
619 | hp = kmalloc(sizeof(*hp), GFP_KERNEL); |
620 | if (!hp) |
621 | return -ENOMEM; |
622 | |
623 | memset(hp, 0x00, sizeof(*hp)); |
624 | hp->vtermno = dev->unit_address; |
625 | hp->vdev = dev; |
626 | hp->vdev->dev.driver_data = hp; |
627 | hp->irq = dev->irq; |
628 | |
629 | kobject_init(&hp->kobj); |
630 | hp->kobj.ktype = &hvc_kobj_type; |
631 | |
632 | spin_lock_init(&hp->lock); |
633 | spin_lock(&hvc_structs_lock); |
634 | hp->index = ++hvc_count; |
635 | list_add_tail(&(hp->next), &hvc_structs); |
636 | spin_unlock(&hvc_structs_lock); |
637 | |
638 | return 0; |
639 | } |
640 | |
641 | static int __devexit hvc_remove(struct vio_dev *dev) |
642 | { |
643 | struct hvc_struct *hp = dev->dev.driver_data; |
644 | unsigned long flags; |
645 | struct kobject *kobjp; |
646 | struct tty_struct *tty; |
647 | |
648 | spin_lock_irqsave(&hp->lock, flags); |
649 | tty = hp->tty; |
650 | kobjp = &hp->kobj; |
651 | |
652 | if (hp->index < MAX_NR_HVC_CONSOLES) |
653 | vtermnos[hp->index] = -1; |
654 | |
655 | /* Don't whack hp->irq because tty_hangup() will need to free the irq. */ |
656 | |
657 | spin_unlock_irqrestore(&hp->lock, flags); |
658 | |
659 | /* |
660 | * We 'put' the instance that was grabbed when the kobject instance |
661 | * was intialized using kobject_init(). Let the last holder of this |
662 | * kobject cause it to be removed, which will probably be the tty_hangup |
663 | * below. |
664 | */ |
665 | kobject_put(kobjp); |
666 | |
667 | /* |
668 | * This function call will auto chain call hvc_hangup. The tty should |
669 | * always be valid at this time unless a simultaneous tty close already |
670 | * cleaned up the hvc_struct. |
671 | */ |
672 | if (tty) |
673 | tty_hangup(tty); |
674 | return 0; |
675 | } |
676 | |
677 | static struct vio_driver hvc_vio_driver = { |
678 | .name = hvc_driver_name, |
679 | .id_table = hvc_driver_table, |
680 | .probe = hvc_probe, |
681 | .remove = hvc_remove, |
682 | }; |
683 | |
684 | /* Driver initialization. Follow console initialization. This is where the TTY |
685 | * interfaces start to become available. */ |
686 | int __init hvc_init(void) |
687 | { |
688 | int rc; |
689 | |
690 | /* We need more than num_vterms adapters due to hotplug additions. */ |
691 | hvc_driver = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS); |
692 | /* hvc_driver = alloc_tty_driver(num_vterms); */ |
693 | if (!hvc_driver) |
694 | return -ENOMEM; |
695 | |
696 | hvc_driver->owner = THIS_MODULE; |
697 | hvc_driver->devfs_name = "hvc/"; |
698 | hvc_driver->driver_name = "hvc"; |
699 | hvc_driver->name = "hvc"; |
700 | hvc_driver->major = HVC_MAJOR; |
701 | hvc_driver->minor_start = HVC_MINOR; |
702 | hvc_driver->type = TTY_DRIVER_TYPE_SYSTEM; |
703 | hvc_driver->init_termios = tty_std_termios; |
704 | hvc_driver->flags = TTY_DRIVER_REAL_RAW; |
705 | tty_set_operations(hvc_driver, &hvc_ops); |
706 | |
707 | if (tty_register_driver(hvc_driver)) |
708 | panic("Couldn't register hvc console driver\n"); |
709 | |
710 | /* Always start the kthread because there can be hotplug vty adapters |
711 | * added later. */ |
712 | hvc_task = kthread_run(khvcd, NULL, "khvcd"); |
713 | if (IS_ERR(hvc_task)) { |
714 | panic("Couldn't create kthread for console.\n"); |
715 | put_tty_driver(hvc_driver); |
716 | return -EIO; |
717 | } |
718 | |
719 | /* Register as a vio device to receive callbacks */ |
720 | rc = vio_register_driver(&hvc_vio_driver); |
721 | |
722 | return rc; |
723 | } |
724 | |
725 | /* This isn't particularily necessary due to this being a console driver but it |
726 | * is nice to be thorough */ |
727 | static void __exit hvc_exit(void) |
728 | { |
729 | kthread_stop(hvc_task); |
730 | |
731 | vio_unregister_driver(&hvc_vio_driver); |
732 | tty_unregister_driver(hvc_driver); |
733 | /* return tty_struct instances allocated in hvc_init(). */ |
734 | put_tty_driver(hvc_driver); |
735 | } |
736 | |
737 | /* |
738 | * Console APIs, NOT TTY. These APIs are available immediately when |
739 | * hvc_console_setup() finds adapters. |
740 | */ |
741 | |
742 | /* |
743 | * hvc_instantiate() is an early console discovery method which locates consoles |
744 | * prior to the vio subsystem discovering them. Hotplugged vty adapters do NOT |
745 | * get an hvc_instantiate() callback since the appear after early console init. |
746 | */ |
747 | int hvc_instantiate(uint32_t vtermno, int index) |
748 | { |
749 | if (index < 0 || index >= MAX_NR_HVC_CONSOLES) |
750 | return -1; |
751 | |
752 | if (vtermnos[index] != -1) |
753 | return -1; |
754 | |
755 | vtermnos[index] = vtermno; |
756 | return 0; |
757 | } |
758 | |
759 | void hvc_console_print(struct console *co, const char *b, unsigned count) |
760 | { |
761 | char c[16] __ALIGNED__; |
762 | unsigned i = 0, n = 0; |
763 | int r, donecr = 0; |
764 | |
765 | /* Console access attempt outside of acceptable console range. */ |
766 | if (co->index >= MAX_NR_HVC_CONSOLES) |
767 | return; |
768 | |
769 | /* This console adapter was removed so it is not useable. */ |
770 | if (vtermnos[co->index] < 0) |
771 | return; |
772 | |
773 | while (count > 0 || i > 0) { |
774 | if (count > 0 && i < sizeof(c)) { |
775 | if (b[n] == '\n' && !donecr) { |
776 | c[i++] = '\r'; |
777 | donecr = 1; |
778 | } else { |
779 | c[i++] = b[n++]; |
780 | donecr = 0; |
781 | --count; |
782 | } |
783 | } else { |
784 | r = hvc_put_chars(vtermnos[co->index], c, i); |
785 | if (r < 0) { |
786 | /* throw away chars on error */ |
787 | i = 0; |
788 | } else if (r > 0) { |
789 | i -= r; |
790 | if (i > 0) |
791 | memmove(c, c+r, i); |
792 | } |
793 | } |
794 | } |
795 | } |
796 | |
797 | static struct tty_driver *hvc_console_device(struct console *c, int *index) |
798 | { |
799 | *index = c->index; |
800 | return hvc_driver; |
801 | } |
802 | |
803 | static int __init hvc_console_setup(struct console *co, char *options) |
804 | { |
805 | return 0; |
806 | } |
807 | |
808 | struct console hvc_con_driver = { |
809 | .name = "hvc", |
810 | .write = hvc_console_print, |
811 | .device = hvc_console_device, |
812 | .setup = hvc_console_setup, |
813 | .flags = CON_PRINTBUFFER, |
814 | .index = -1, |
815 | }; |
816 | |
817 | /* Early console initialization. Preceeds driver initialization. */ |
818 | static int __init hvc_console_init(void) |
819 | { |
820 | int i; |
821 | |
822 | for (i=0; i<MAX_NR_HVC_CONSOLES; i++) |
823 | vtermnos[i] = -1; |
824 | num_vterms = hvc_find_vtys(); |
825 | register_console(&hvc_con_driver); |
826 | return 0; |
827 | } |
828 | console_initcall(hvc_console_init); |
829 | |
830 | module_init(hvc_init); |
831 | module_exit(hvc_exit); |