diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-26 01:59:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-26 01:59:39 -0400 |
commit | 184475029a724b6b900d88fc3a5f462a6107d5af (patch) | |
tree | 408320b46df221a2424bf94282b1b8e5b7aff7a1 /drivers/tty | |
parent | 3b76eefe0f970c2e19f165d4a1650abc523d10bc (diff) | |
parent | f1f4ee01c0d3dce0e3aa7d04e4332677db7af478 (diff) |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (99 commits)
drivers/virt: add missing linux/interrupt.h to fsl_hypervisor.c
powerpc/85xx: fix mpic configuration in CAMP mode
powerpc: Copy back TIF flags on return from softirq stack
powerpc/64: Make server perfmon only built on ppc64 server devices
powerpc/pseries: Fix hvc_vio.c build due to recent changes
powerpc: Exporting boot_cpuid_phys
powerpc: Add CFAR to oops output
hvc_console: Add kdb support
powerpc/pseries: Fix hvterm_raw_get_chars to accept < 16 chars, fixing xmon
powerpc/irq: Quieten irq mapping printks
powerpc: Enable lockup and hung task detectors in pseries and ppc64 defeconfigs
powerpc: Add mpt2sas driver to pseries and ppc64 defconfig
powerpc: Disable IRQs off tracer in ppc64 defconfig
powerpc: Sync pseries and ppc64 defconfigs
powerpc/pseries/hvconsole: Fix dropped console output
hvc_console: Improve tty/console put_chars handling
powerpc/kdump: Fix timeout in crash_kexec_wait_realmode
powerpc/mm: Fix output of total_ram.
powerpc/cpufreq: Add cpufreq driver for Momentum Maple boards
powerpc: Correct annotations of pmu registration functions
...
Fix up trivial Kconfig/Makefile conflicts in arch/powerpc, drivers, and
drivers/cpufreq
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/hvc/Kconfig | 5 | ||||
-rw-r--r-- | drivers/tty/hvc/Makefile | 3 | ||||
-rw-r--r-- | drivers/tty/hvc/hvc_console.c | 70 | ||||
-rw-r--r-- | drivers/tty/hvc/hvc_console.h | 4 | ||||
-rw-r--r-- | drivers/tty/hvc/hvc_vio.c | 408 | ||||
-rw-r--r-- | drivers/tty/hvc/hvsi.c | 129 | ||||
-rw-r--r-- | drivers/tty/hvc/hvsi_lib.c | 426 |
7 files changed, 897 insertions, 148 deletions
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 6f2c9809f1fb..e371753ba921 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig | |||
@@ -19,6 +19,11 @@ config HVC_CONSOLE | |||
19 | console. This driver allows each pSeries partition to have a console | 19 | console. This driver allows each pSeries partition to have a console |
20 | which is accessed via the HMC. | 20 | which is accessed via the HMC. |
21 | 21 | ||
22 | config HVC_OLD_HVSI | ||
23 | bool "Old driver for pSeries serial port (/dev/hvsi*)" | ||
24 | depends on HVC_CONSOLE | ||
25 | default n | ||
26 | |||
22 | config HVC_ISERIES | 27 | config HVC_ISERIES |
23 | bool "iSeries Hypervisor Virtual Console support" | 28 | bool "iSeries Hypervisor Virtual Console support" |
24 | depends on PPC_ISERIES | 29 | depends on PPC_ISERIES |
diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile index 40a25d93fe52..e29205316376 100644 --- a/drivers/tty/hvc/Makefile +++ b/drivers/tty/hvc/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o | 1 | obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o |
2 | obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o | ||
2 | obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o | 3 | obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o |
3 | obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o | 4 | obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o |
4 | obj-$(CONFIG_HVC_TILE) += hvc_tile.o | 5 | obj-$(CONFIG_HVC_TILE) += hvc_tile.o |
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index e9cba13ee800..e1aaf4f309b3 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <linux/delay.h> | 39 | #include <linux/delay.h> |
40 | #include <linux/freezer.h> | 40 | #include <linux/freezer.h> |
41 | #include <linux/slab.h> | 41 | #include <linux/slab.h> |
42 | #include <linux/serial_core.h> | ||
42 | 43 | ||
43 | #include <asm/uaccess.h> | 44 | #include <asm/uaccess.h> |
44 | 45 | ||
@@ -163,8 +164,10 @@ static void hvc_console_print(struct console *co, const char *b, | |||
163 | } else { | 164 | } else { |
164 | r = cons_ops[index]->put_chars(vtermnos[index], c, i); | 165 | r = cons_ops[index]->put_chars(vtermnos[index], c, i); |
165 | if (r <= 0) { | 166 | if (r <= 0) { |
166 | /* throw away chars on error */ | 167 | /* throw away characters on error |
167 | i = 0; | 168 | * but spin in case of -EAGAIN */ |
169 | if (r != -EAGAIN) | ||
170 | i = 0; | ||
168 | } else if (r > 0) { | 171 | } else if (r > 0) { |
169 | i -= r; | 172 | i -= r; |
170 | if (i > 0) | 173 | if (i > 0) |
@@ -184,7 +187,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index) | |||
184 | } | 187 | } |
185 | 188 | ||
186 | static int __init hvc_console_setup(struct console *co, char *options) | 189 | static int __init hvc_console_setup(struct console *co, char *options) |
187 | { | 190 | { |
188 | if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) | 191 | if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) |
189 | return -ENODEV; | 192 | return -ENODEV; |
190 | 193 | ||
@@ -448,7 +451,7 @@ static int hvc_push(struct hvc_struct *hp) | |||
448 | 451 | ||
449 | n = hp->ops->put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf); | 452 | n = hp->ops->put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf); |
450 | if (n <= 0) { | 453 | if (n <= 0) { |
451 | if (n == 0) { | 454 | if (n == 0 || n == -EAGAIN) { |
452 | hp->do_wakeup = 1; | 455 | hp->do_wakeup = 1; |
453 | return 0; | 456 | return 0; |
454 | } | 457 | } |
@@ -745,6 +748,58 @@ static int khvcd(void *unused) | |||
745 | return 0; | 748 | return 0; |
746 | } | 749 | } |
747 | 750 | ||
751 | static int hvc_tiocmget(struct tty_struct *tty) | ||
752 | { | ||
753 | struct hvc_struct *hp = tty->driver_data; | ||
754 | |||
755 | if (!hp || !hp->ops->tiocmget) | ||
756 | return -EINVAL; | ||
757 | return hp->ops->tiocmget(hp); | ||
758 | } | ||
759 | |||
760 | static int hvc_tiocmset(struct tty_struct *tty, | ||
761 | unsigned int set, unsigned int clear) | ||
762 | { | ||
763 | struct hvc_struct *hp = tty->driver_data; | ||
764 | |||
765 | if (!hp || !hp->ops->tiocmset) | ||
766 | return -EINVAL; | ||
767 | return hp->ops->tiocmset(hp, set, clear); | ||
768 | } | ||
769 | |||
770 | #ifdef CONFIG_CONSOLE_POLL | ||
771 | int hvc_poll_init(struct tty_driver *driver, int line, char *options) | ||
772 | { | ||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | static int hvc_poll_get_char(struct tty_driver *driver, int line) | ||
777 | { | ||
778 | struct tty_struct *tty = driver->ttys[0]; | ||
779 | struct hvc_struct *hp = tty->driver_data; | ||
780 | int n; | ||
781 | char ch; | ||
782 | |||
783 | n = hp->ops->get_chars(hp->vtermno, &ch, 1); | ||
784 | |||
785 | if (n == 0) | ||
786 | return NO_POLL_CHAR; | ||
787 | |||
788 | return ch; | ||
789 | } | ||
790 | |||
791 | static void hvc_poll_put_char(struct tty_driver *driver, int line, char ch) | ||
792 | { | ||
793 | struct tty_struct *tty = driver->ttys[0]; | ||
794 | struct hvc_struct *hp = tty->driver_data; | ||
795 | int n; | ||
796 | |||
797 | do { | ||
798 | n = hp->ops->put_chars(hp->vtermno, &ch, 1); | ||
799 | } while (n <= 0); | ||
800 | } | ||
801 | #endif | ||
802 | |||
748 | static const struct tty_operations hvc_ops = { | 803 | static const struct tty_operations hvc_ops = { |
749 | .open = hvc_open, | 804 | .open = hvc_open, |
750 | .close = hvc_close, | 805 | .close = hvc_close, |
@@ -753,6 +808,13 @@ static const struct tty_operations hvc_ops = { | |||
753 | .unthrottle = hvc_unthrottle, | 808 | .unthrottle = hvc_unthrottle, |
754 | .write_room = hvc_write_room, | 809 | .write_room = hvc_write_room, |
755 | .chars_in_buffer = hvc_chars_in_buffer, | 810 | .chars_in_buffer = hvc_chars_in_buffer, |
811 | .tiocmget = hvc_tiocmget, | ||
812 | .tiocmset = hvc_tiocmset, | ||
813 | #ifdef CONFIG_CONSOLE_POLL | ||
814 | .poll_init = hvc_poll_init, | ||
815 | .poll_get_char = hvc_poll_get_char, | ||
816 | .poll_put_char = hvc_poll_put_char, | ||
817 | #endif | ||
756 | }; | 818 | }; |
757 | 819 | ||
758 | struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, | 820 | struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, |
diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h index 54381eba4e4a..c335a1492a54 100644 --- a/drivers/tty/hvc/hvc_console.h +++ b/drivers/tty/hvc/hvc_console.h | |||
@@ -73,6 +73,10 @@ struct hv_ops { | |||
73 | int (*notifier_add)(struct hvc_struct *hp, int irq); | 73 | int (*notifier_add)(struct hvc_struct *hp, int irq); |
74 | void (*notifier_del)(struct hvc_struct *hp, int irq); | 74 | void (*notifier_del)(struct hvc_struct *hp, int irq); |
75 | void (*notifier_hangup)(struct hvc_struct *hp, int irq); | 75 | void (*notifier_hangup)(struct hvc_struct *hp, int irq); |
76 | |||
77 | /* tiocmget/set implementation */ | ||
78 | int (*tiocmget)(struct hvc_struct *hp); | ||
79 | int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear); | ||
76 | }; | 80 | }; |
77 | 81 | ||
78 | /* Register a vterm and a slot index for use as a console (console_init) */ | 82 | /* Register a vterm and a slot index for use as a console (console_init) */ |
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index e6eea1485244..130aace67f31 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c | |||
@@ -27,15 +27,27 @@ | |||
27 | * You should have received a copy of the GNU General Public License | 27 | * You should have received a copy of the GNU General Public License |
28 | * along with this program; if not, write to the Free Software | 28 | * along with this program; if not, write to the Free Software |
29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
30 | * | ||
31 | * TODO: | ||
32 | * | ||
33 | * - handle error in sending hvsi protocol packets | ||
34 | * - retry nego on subsequent sends ? | ||
30 | */ | 35 | */ |
31 | 36 | ||
37 | #undef DEBUG | ||
38 | |||
32 | #include <linux/types.h> | 39 | #include <linux/types.h> |
33 | #include <linux/init.h> | 40 | #include <linux/init.h> |
41 | #include <linux/delay.h> | ||
42 | #include <linux/slab.h> | ||
43 | #include <linux/console.h> | ||
34 | 44 | ||
35 | #include <asm/hvconsole.h> | 45 | #include <asm/hvconsole.h> |
36 | #include <asm/vio.h> | 46 | #include <asm/vio.h> |
37 | #include <asm/prom.h> | 47 | #include <asm/prom.h> |
38 | #include <asm/firmware.h> | 48 | #include <asm/firmware.h> |
49 | #include <asm/hvsi.h> | ||
50 | #include <asm/udbg.h> | ||
39 | 51 | ||
40 | #include "hvc_console.h" | 52 | #include "hvc_console.h" |
41 | 53 | ||
@@ -43,59 +55,236 @@ static const char hvc_driver_name[] = "hvc_console"; | |||
43 | 55 | ||
44 | static struct vio_device_id hvc_driver_table[] __devinitdata = { | 56 | static struct vio_device_id hvc_driver_table[] __devinitdata = { |
45 | {"serial", "hvterm1"}, | 57 | {"serial", "hvterm1"}, |
58 | #ifndef HVC_OLD_HVSI | ||
59 | {"serial", "hvterm-protocol"}, | ||
60 | #endif | ||
46 | { "", "" } | 61 | { "", "" } |
47 | }; | 62 | }; |
48 | MODULE_DEVICE_TABLE(vio, hvc_driver_table); | 63 | MODULE_DEVICE_TABLE(vio, hvc_driver_table); |
49 | 64 | ||
50 | static int filtered_get_chars(uint32_t vtermno, char *buf, int count) | 65 | typedef enum hv_protocol { |
66 | HV_PROTOCOL_RAW, | ||
67 | HV_PROTOCOL_HVSI | ||
68 | } hv_protocol_t; | ||
69 | |||
70 | struct hvterm_priv { | ||
71 | u32 termno; /* HV term number */ | ||
72 | hv_protocol_t proto; /* Raw data or HVSI packets */ | ||
73 | struct hvsi_priv hvsi; /* HVSI specific data */ | ||
74 | spinlock_t buf_lock; | ||
75 | char buf[SIZE_VIO_GET_CHARS]; | ||
76 | int left; | ||
77 | int offset; | ||
78 | }; | ||
79 | static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES]; | ||
80 | /* For early boot console */ | ||
81 | static struct hvterm_priv hvterm_priv0; | ||
82 | |||
83 | static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count) | ||
51 | { | 84 | { |
52 | unsigned long got; | 85 | struct hvterm_priv *pv = hvterm_privs[vtermno]; |
53 | int i; | 86 | unsigned long i; |
87 | unsigned long flags; | ||
88 | int got; | ||
54 | 89 | ||
55 | /* | 90 | if (WARN_ON(!pv)) |
56 | * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion | 91 | return 0; |
57 | * so we play safe and avoid the situation where got > count which could | ||
58 | * overload the flip buffer. | ||
59 | */ | ||
60 | if (count < SIZE_VIO_GET_CHARS) | ||
61 | return -EAGAIN; | ||
62 | 92 | ||
63 | got = hvc_get_chars(vtermno, buf, count); | 93 | spin_lock_irqsave(&pv->buf_lock, flags); |
64 | 94 | ||
65 | /* | 95 | if (pv->left == 0) { |
66 | * Work around a HV bug where it gives us a null | 96 | pv->offset = 0; |
67 | * after every \r. -- paulus | 97 | pv->left = hvc_get_chars(pv->termno, pv->buf, count); |
68 | */ | 98 | |
69 | for (i = 1; i < got; ++i) { | 99 | /* |
70 | if (buf[i] == 0 && buf[i-1] == '\r') { | 100 | * Work around a HV bug where it gives us a null |
71 | --got; | 101 | * after every \r. -- paulus |
72 | if (i < got) | 102 | */ |
73 | memmove(&buf[i], &buf[i+1], | 103 | for (i = 1; i < pv->left; ++i) { |
74 | got - i); | 104 | if (pv->buf[i] == 0 && pv->buf[i-1] == '\r') { |
105 | --pv->left; | ||
106 | if (i < pv->left) { | ||
107 | memmove(&pv->buf[i], &pv->buf[i+1], | ||
108 | pv->left - i); | ||
109 | } | ||
110 | } | ||
75 | } | 111 | } |
76 | } | 112 | } |
113 | |||
114 | got = min(count, pv->left); | ||
115 | memcpy(buf, &pv->buf[pv->offset], got); | ||
116 | pv->offset += got; | ||
117 | pv->left -= got; | ||
118 | |||
119 | spin_unlock_irqrestore(&pv->buf_lock, flags); | ||
120 | |||
77 | return got; | 121 | return got; |
78 | } | 122 | } |
79 | 123 | ||
80 | static const struct hv_ops hvc_get_put_ops = { | 124 | static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count) |
81 | .get_chars = filtered_get_chars, | 125 | { |
82 | .put_chars = hvc_put_chars, | 126 | struct hvterm_priv *pv = hvterm_privs[vtermno]; |
127 | |||
128 | if (WARN_ON(!pv)) | ||
129 | return 0; | ||
130 | |||
131 | return hvc_put_chars(pv->termno, buf, count); | ||
132 | } | ||
133 | |||
134 | static const struct hv_ops hvterm_raw_ops = { | ||
135 | .get_chars = hvterm_raw_get_chars, | ||
136 | .put_chars = hvterm_raw_put_chars, | ||
83 | .notifier_add = notifier_add_irq, | 137 | .notifier_add = notifier_add_irq, |
84 | .notifier_del = notifier_del_irq, | 138 | .notifier_del = notifier_del_irq, |
85 | .notifier_hangup = notifier_hangup_irq, | 139 | .notifier_hangup = notifier_hangup_irq, |
86 | }; | 140 | }; |
87 | 141 | ||
142 | static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count) | ||
143 | { | ||
144 | struct hvterm_priv *pv = hvterm_privs[vtermno]; | ||
145 | |||
146 | if (WARN_ON(!pv)) | ||
147 | return 0; | ||
148 | |||
149 | return hvsilib_get_chars(&pv->hvsi, buf, count); | ||
150 | } | ||
151 | |||
152 | static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count) | ||
153 | { | ||
154 | struct hvterm_priv *pv = hvterm_privs[vtermno]; | ||
155 | |||
156 | if (WARN_ON(!pv)) | ||
157 | return 0; | ||
158 | |||
159 | return hvsilib_put_chars(&pv->hvsi, buf, count); | ||
160 | } | ||
161 | |||
162 | static int hvterm_hvsi_open(struct hvc_struct *hp, int data) | ||
163 | { | ||
164 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
165 | int rc; | ||
166 | |||
167 | pr_devel("HVSI@%x: open !\n", pv->termno); | ||
168 | |||
169 | rc = notifier_add_irq(hp, data); | ||
170 | if (rc) | ||
171 | return rc; | ||
172 | |||
173 | return hvsilib_open(&pv->hvsi, hp); | ||
174 | } | ||
175 | |||
176 | static void hvterm_hvsi_close(struct hvc_struct *hp, int data) | ||
177 | { | ||
178 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
179 | |||
180 | pr_devel("HVSI@%x: do close !\n", pv->termno); | ||
181 | |||
182 | hvsilib_close(&pv->hvsi, hp); | ||
183 | |||
184 | notifier_del_irq(hp, data); | ||
185 | } | ||
186 | |||
187 | void hvterm_hvsi_hangup(struct hvc_struct *hp, int data) | ||
188 | { | ||
189 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
190 | |||
191 | pr_devel("HVSI@%x: do hangup !\n", pv->termno); | ||
192 | |||
193 | hvsilib_close(&pv->hvsi, hp); | ||
194 | |||
195 | notifier_hangup_irq(hp, data); | ||
196 | } | ||
197 | |||
198 | static int hvterm_hvsi_tiocmget(struct hvc_struct *hp) | ||
199 | { | ||
200 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
201 | |||
202 | if (!pv) | ||
203 | return -EINVAL; | ||
204 | return pv->hvsi.mctrl; | ||
205 | } | ||
206 | |||
207 | static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set, | ||
208 | unsigned int clear) | ||
209 | { | ||
210 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
211 | |||
212 | pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n", | ||
213 | pv->termno, set, clear); | ||
214 | |||
215 | if (set & TIOCM_DTR) | ||
216 | hvsilib_write_mctrl(&pv->hvsi, 1); | ||
217 | else if (clear & TIOCM_DTR) | ||
218 | hvsilib_write_mctrl(&pv->hvsi, 0); | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static const struct hv_ops hvterm_hvsi_ops = { | ||
224 | .get_chars = hvterm_hvsi_get_chars, | ||
225 | .put_chars = hvterm_hvsi_put_chars, | ||
226 | .notifier_add = hvterm_hvsi_open, | ||
227 | .notifier_del = hvterm_hvsi_close, | ||
228 | .notifier_hangup = hvterm_hvsi_hangup, | ||
229 | .tiocmget = hvterm_hvsi_tiocmget, | ||
230 | .tiocmset = hvterm_hvsi_tiocmset, | ||
231 | }; | ||
232 | |||
88 | static int __devinit hvc_vio_probe(struct vio_dev *vdev, | 233 | static int __devinit hvc_vio_probe(struct vio_dev *vdev, |
89 | const struct vio_device_id *id) | 234 | const struct vio_device_id *id) |
90 | { | 235 | { |
236 | const struct hv_ops *ops; | ||
91 | struct hvc_struct *hp; | 237 | struct hvc_struct *hp; |
238 | struct hvterm_priv *pv; | ||
239 | hv_protocol_t proto; | ||
240 | int i, termno = -1; | ||
92 | 241 | ||
93 | /* probed with invalid parameters. */ | 242 | /* probed with invalid parameters. */ |
94 | if (!vdev || !id) | 243 | if (!vdev || !id) |
95 | return -EPERM; | 244 | return -EPERM; |
96 | 245 | ||
97 | hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, | 246 | if (of_device_is_compatible(vdev->dev.of_node, "hvterm1")) { |
98 | MAX_VIO_PUT_CHARS); | 247 | proto = HV_PROTOCOL_RAW; |
248 | ops = &hvterm_raw_ops; | ||
249 | } else if (of_device_is_compatible(vdev->dev.of_node, "hvterm-protocol")) { | ||
250 | proto = HV_PROTOCOL_HVSI; | ||
251 | ops = &hvterm_hvsi_ops; | ||
252 | } else { | ||
253 | pr_err("hvc_vio: Unkown protocol for %s\n", vdev->dev.of_node->full_name); | ||
254 | return -ENXIO; | ||
255 | } | ||
256 | |||
257 | pr_devel("hvc_vio_probe() device %s, using %s protocol\n", | ||
258 | vdev->dev.of_node->full_name, | ||
259 | proto == HV_PROTOCOL_RAW ? "raw" : "hvsi"); | ||
260 | |||
261 | /* Is it our boot one ? */ | ||
262 | if (hvterm_privs[0] == &hvterm_priv0 && | ||
263 | vdev->unit_address == hvterm_priv0.termno) { | ||
264 | pv = hvterm_privs[0]; | ||
265 | termno = 0; | ||
266 | pr_devel("->boot console, using termno 0\n"); | ||
267 | } | ||
268 | /* nope, allocate a new one */ | ||
269 | else { | ||
270 | for (i = 0; i < MAX_NR_HVC_CONSOLES && termno < 0; i++) | ||
271 | if (!hvterm_privs[i]) | ||
272 | termno = i; | ||
273 | pr_devel("->non-boot console, using termno %d\n", termno); | ||
274 | if (termno < 0) | ||
275 | return -ENODEV; | ||
276 | pv = kzalloc(sizeof(struct hvterm_priv), GFP_KERNEL); | ||
277 | if (!pv) | ||
278 | return -ENOMEM; | ||
279 | pv->termno = vdev->unit_address; | ||
280 | pv->proto = proto; | ||
281 | spin_lock_init(&pv->buf_lock); | ||
282 | hvterm_privs[termno] = pv; | ||
283 | hvsilib_init(&pv->hvsi, hvc_get_chars, hvc_put_chars, | ||
284 | pv->termno, 0); | ||
285 | } | ||
286 | |||
287 | hp = hvc_alloc(termno, vdev->irq, ops, MAX_VIO_PUT_CHARS); | ||
99 | if (IS_ERR(hp)) | 288 | if (IS_ERR(hp)) |
100 | return PTR_ERR(hp); | 289 | return PTR_ERR(hp); |
101 | dev_set_drvdata(&vdev->dev, hp); | 290 | dev_set_drvdata(&vdev->dev, hp); |
@@ -106,8 +295,16 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev, | |||
106 | static int __devexit hvc_vio_remove(struct vio_dev *vdev) | 295 | static int __devexit hvc_vio_remove(struct vio_dev *vdev) |
107 | { | 296 | { |
108 | struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); | 297 | struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); |
298 | int rc, termno; | ||
109 | 299 | ||
110 | return hvc_remove(hp); | 300 | termno = hp->vtermno; |
301 | rc = hvc_remove(hp); | ||
302 | if (rc == 0) { | ||
303 | if (hvterm_privs[termno] != &hvterm_priv0) | ||
304 | kfree(hvterm_privs[termno]); | ||
305 | hvterm_privs[termno] = NULL; | ||
306 | } | ||
307 | return rc; | ||
111 | } | 308 | } |
112 | 309 | ||
113 | static struct vio_driver hvc_vio_driver = { | 310 | static struct vio_driver hvc_vio_driver = { |
@@ -140,34 +337,149 @@ static void __exit hvc_vio_exit(void) | |||
140 | } | 337 | } |
141 | module_exit(hvc_vio_exit); | 338 | module_exit(hvc_vio_exit); |
142 | 339 | ||
143 | /* the device tree order defines our numbering */ | 340 | static void udbg_hvc_putc(char c) |
144 | static int hvc_find_vtys(void) | ||
145 | { | 341 | { |
146 | struct device_node *vty; | 342 | int count = -1; |
147 | int num_found = 0; | ||
148 | 343 | ||
149 | for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; | 344 | if (c == '\n') |
150 | vty = of_find_node_by_name(vty, "vty")) { | 345 | udbg_hvc_putc('\r'); |
151 | const uint32_t *vtermno; | ||
152 | 346 | ||
153 | /* We have statically defined space for only a certain number | 347 | do { |
154 | * of console adapters. | 348 | switch(hvterm_priv0.proto) { |
155 | */ | 349 | case HV_PROTOCOL_RAW: |
156 | if (num_found >= MAX_NR_HVC_CONSOLES) { | 350 | count = hvterm_raw_put_chars(0, &c, 1); |
157 | of_node_put(vty); | 351 | break; |
352 | case HV_PROTOCOL_HVSI: | ||
353 | count = hvterm_hvsi_put_chars(0, &c, 1); | ||
158 | break; | 354 | break; |
159 | } | 355 | } |
356 | } while(count == 0); | ||
357 | } | ||
358 | |||
359 | static int udbg_hvc_getc_poll(void) | ||
360 | { | ||
361 | int rc = 0; | ||
362 | char c; | ||
160 | 363 | ||
161 | vtermno = of_get_property(vty, "reg", NULL); | 364 | switch(hvterm_priv0.proto) { |
162 | if (!vtermno) | 365 | case HV_PROTOCOL_RAW: |
163 | continue; | 366 | rc = hvterm_raw_get_chars(0, &c, 1); |
367 | break; | ||
368 | case HV_PROTOCOL_HVSI: | ||
369 | rc = hvterm_hvsi_get_chars(0, &c, 1); | ||
370 | break; | ||
371 | } | ||
372 | if (!rc) | ||
373 | return -1; | ||
374 | return c; | ||
375 | } | ||
164 | 376 | ||
165 | if (of_device_is_compatible(vty, "hvterm1")) { | 377 | static int udbg_hvc_getc(void) |
166 | hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); | 378 | { |
167 | ++num_found; | 379 | int ch; |
380 | for (;;) { | ||
381 | ch = udbg_hvc_getc_poll(); | ||
382 | if (ch == -1) { | ||
383 | /* This shouldn't be needed...but... */ | ||
384 | volatile unsigned long delay; | ||
385 | for (delay=0; delay < 2000000; delay++) | ||
386 | ; | ||
387 | } else { | ||
388 | return ch; | ||
168 | } | 389 | } |
169 | } | 390 | } |
391 | } | ||
392 | |||
393 | void __init hvc_vio_init_early(void) | ||
394 | { | ||
395 | struct device_node *stdout_node; | ||
396 | const u32 *termno; | ||
397 | const char *name; | ||
398 | const struct hv_ops *ops; | ||
399 | |||
400 | /* find the boot console from /chosen/stdout */ | ||
401 | if (!of_chosen) | ||
402 | return; | ||
403 | name = of_get_property(of_chosen, "linux,stdout-path", NULL); | ||
404 | if (name == NULL) | ||
405 | return; | ||
406 | stdout_node = of_find_node_by_path(name); | ||
407 | if (!stdout_node) | ||
408 | return; | ||
409 | name = of_get_property(stdout_node, "name", NULL); | ||
410 | if (!name) { | ||
411 | printk(KERN_WARNING "stdout node missing 'name' property!\n"); | ||
412 | goto out; | ||
413 | } | ||
414 | |||
415 | /* Check if it's a virtual terminal */ | ||
416 | if (strncmp(name, "vty", 3) != 0) | ||
417 | goto out; | ||
418 | termno = of_get_property(stdout_node, "reg", NULL); | ||
419 | if (termno == NULL) | ||
420 | goto out; | ||
421 | hvterm_priv0.termno = *termno; | ||
422 | spin_lock_init(&hvterm_priv0.buf_lock); | ||
423 | hvterm_privs[0] = &hvterm_priv0; | ||
424 | |||
425 | /* Check the protocol */ | ||
426 | if (of_device_is_compatible(stdout_node, "hvterm1")) { | ||
427 | hvterm_priv0.proto = HV_PROTOCOL_RAW; | ||
428 | ops = &hvterm_raw_ops; | ||
429 | } | ||
430 | else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { | ||
431 | hvterm_priv0.proto = HV_PROTOCOL_HVSI; | ||
432 | ops = &hvterm_hvsi_ops; | ||
433 | hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars, | ||
434 | hvterm_priv0.termno, 1); | ||
435 | /* HVSI, perform the handshake now */ | ||
436 | hvsilib_establish(&hvterm_priv0.hvsi); | ||
437 | } else | ||
438 | goto out; | ||
439 | udbg_putc = udbg_hvc_putc; | ||
440 | udbg_getc = udbg_hvc_getc; | ||
441 | udbg_getc_poll = udbg_hvc_getc_poll; | ||
442 | #ifdef HVC_OLD_HVSI | ||
443 | /* When using the old HVSI driver don't register the HVC | ||
444 | * backend for HVSI, only do udbg | ||
445 | */ | ||
446 | if (hvterm_priv0.proto == HV_PROTOCOL_HVSI) | ||
447 | goto out; | ||
448 | #endif | ||
449 | add_preferred_console("hvc", 0, NULL); | ||
450 | hvc_instantiate(0, 0, ops); | ||
451 | out: | ||
452 | of_node_put(stdout_node); | ||
453 | } | ||
170 | 454 | ||
171 | return num_found; | 455 | /* call this from early_init() for a working debug console on |
456 | * vterm capable LPAR machines | ||
457 | */ | ||
458 | #ifdef CONFIG_PPC_EARLY_DEBUG_LPAR | ||
459 | void __init udbg_init_debug_lpar(void) | ||
460 | { | ||
461 | hvterm_privs[0] = &hvterm_priv0; | ||
462 | hvterm_priv0.termno = 0; | ||
463 | hvterm_priv0.proto = HV_PROTOCOL_RAW; | ||
464 | spin_lock_init(&hvterm_priv0.buf_lock); | ||
465 | udbg_putc = udbg_hvc_putc; | ||
466 | udbg_getc = udbg_hvc_getc; | ||
467 | udbg_getc_poll = udbg_hvc_getc_poll; | ||
468 | } | ||
469 | #endif /* CONFIG_PPC_EARLY_DEBUG_LPAR */ | ||
470 | |||
471 | #ifdef CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI | ||
472 | void __init udbg_init_debug_lpar_hvsi(void) | ||
473 | { | ||
474 | hvterm_privs[0] = &hvterm_priv0; | ||
475 | hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO; | ||
476 | hvterm_priv0.proto = HV_PROTOCOL_HVSI; | ||
477 | spin_lock_init(&hvterm_priv0.buf_lock); | ||
478 | udbg_putc = udbg_hvc_putc; | ||
479 | udbg_getc = udbg_hvc_getc; | ||
480 | udbg_getc_poll = udbg_hvc_getc_poll; | ||
481 | hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars, | ||
482 | hvterm_priv0.termno, 1); | ||
483 | hvsilib_establish(&hvterm_priv0.hvsi); | ||
172 | } | 484 | } |
173 | console_initcall(hvc_find_vtys); | 485 | #endif /* CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI */ |
diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 8a8d6373f164..c94e2f5853d8 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #include <asm/uaccess.h> | 49 | #include <asm/uaccess.h> |
50 | #include <asm/vio.h> | 50 | #include <asm/vio.h> |
51 | #include <asm/param.h> | 51 | #include <asm/param.h> |
52 | #include <asm/hvsi.h> | ||
52 | 53 | ||
53 | #define HVSI_MAJOR 229 | 54 | #define HVSI_MAJOR 229 |
54 | #define HVSI_MINOR 128 | 55 | #define HVSI_MINOR 128 |
@@ -109,68 +110,6 @@ enum HVSI_PROTOCOL_STATE { | |||
109 | }; | 110 | }; |
110 | #define HVSI_CONSOLE 0x1 | 111 | #define HVSI_CONSOLE 0x1 |
111 | 112 | ||
112 | #define VS_DATA_PACKET_HEADER 0xff | ||
113 | #define VS_CONTROL_PACKET_HEADER 0xfe | ||
114 | #define VS_QUERY_PACKET_HEADER 0xfd | ||
115 | #define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc | ||
116 | |||
117 | /* control verbs */ | ||
118 | #define VSV_SET_MODEM_CTL 1 /* to service processor only */ | ||
119 | #define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */ | ||
120 | #define VSV_CLOSE_PROTOCOL 3 | ||
121 | |||
122 | /* query verbs */ | ||
123 | #define VSV_SEND_VERSION_NUMBER 1 | ||
124 | #define VSV_SEND_MODEM_CTL_STATUS 2 | ||
125 | |||
126 | /* yes, these masks are not consecutive. */ | ||
127 | #define HVSI_TSDTR 0x01 | ||
128 | #define HVSI_TSCD 0x20 | ||
129 | |||
130 | struct hvsi_header { | ||
131 | uint8_t type; | ||
132 | uint8_t len; | ||
133 | uint16_t seqno; | ||
134 | } __attribute__((packed)); | ||
135 | |||
136 | struct hvsi_data { | ||
137 | uint8_t type; | ||
138 | uint8_t len; | ||
139 | uint16_t seqno; | ||
140 | uint8_t data[HVSI_MAX_OUTGOING_DATA]; | ||
141 | } __attribute__((packed)); | ||
142 | |||
143 | struct hvsi_control { | ||
144 | uint8_t type; | ||
145 | uint8_t len; | ||
146 | uint16_t seqno; | ||
147 | uint16_t verb; | ||
148 | /* optional depending on verb: */ | ||
149 | uint32_t word; | ||
150 | uint32_t mask; | ||
151 | } __attribute__((packed)); | ||
152 | |||
153 | struct hvsi_query { | ||
154 | uint8_t type; | ||
155 | uint8_t len; | ||
156 | uint16_t seqno; | ||
157 | uint16_t verb; | ||
158 | } __attribute__((packed)); | ||
159 | |||
160 | struct hvsi_query_response { | ||
161 | uint8_t type; | ||
162 | uint8_t len; | ||
163 | uint16_t seqno; | ||
164 | uint16_t verb; | ||
165 | uint16_t query_seqno; | ||
166 | union { | ||
167 | uint8_t version; | ||
168 | uint32_t mctrl_word; | ||
169 | } u; | ||
170 | } __attribute__((packed)); | ||
171 | |||
172 | |||
173 | |||
174 | static inline int is_console(struct hvsi_struct *hp) | 113 | static inline int is_console(struct hvsi_struct *hp) |
175 | { | 114 | { |
176 | return hp->flags & HVSI_CONSOLE; | 115 | return hp->flags & HVSI_CONSOLE; |
@@ -356,18 +295,18 @@ static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) | |||
356 | struct hvsi_query_response packet __ALIGNED__; | 295 | struct hvsi_query_response packet __ALIGNED__; |
357 | int wrote; | 296 | int wrote; |
358 | 297 | ||
359 | packet.type = VS_QUERY_RESPONSE_PACKET_HEADER; | 298 | packet.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; |
360 | packet.len = sizeof(struct hvsi_query_response); | 299 | packet.hdr.len = sizeof(struct hvsi_query_response); |
361 | packet.seqno = atomic_inc_return(&hp->seqno); | 300 | packet.hdr.seqno = atomic_inc_return(&hp->seqno); |
362 | packet.verb = VSV_SEND_VERSION_NUMBER; | 301 | packet.verb = VSV_SEND_VERSION_NUMBER; |
363 | packet.u.version = HVSI_VERSION; | 302 | packet.u.version = HVSI_VERSION; |
364 | packet.query_seqno = query_seqno+1; | 303 | packet.query_seqno = query_seqno+1; |
365 | 304 | ||
366 | pr_debug("%s: sending %i bytes\n", __func__, packet.len); | 305 | pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); |
367 | dbg_dump_hex((uint8_t*)&packet, packet.len); | 306 | dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); |
368 | 307 | ||
369 | wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); | 308 | wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); |
370 | if (wrote != packet.len) { | 309 | if (wrote != packet.hdr.len) { |
371 | printk(KERN_ERR "hvsi%i: couldn't send query response!\n", | 310 | printk(KERN_ERR "hvsi%i: couldn't send query response!\n", |
372 | hp->index); | 311 | hp->index); |
373 | return -EIO; | 312 | return -EIO; |
@@ -382,7 +321,7 @@ static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) | |||
382 | 321 | ||
383 | switch (hp->state) { | 322 | switch (hp->state) { |
384 | case HVSI_WAIT_FOR_VER_QUERY: | 323 | case HVSI_WAIT_FOR_VER_QUERY: |
385 | hvsi_version_respond(hp, query->seqno); | 324 | hvsi_version_respond(hp, query->hdr.seqno); |
386 | __set_state(hp, HVSI_OPEN); | 325 | __set_state(hp, HVSI_OPEN); |
387 | break; | 326 | break; |
388 | default: | 327 | default: |
@@ -640,16 +579,16 @@ static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) | |||
640 | struct hvsi_query packet __ALIGNED__; | 579 | struct hvsi_query packet __ALIGNED__; |
641 | int wrote; | 580 | int wrote; |
642 | 581 | ||
643 | packet.type = VS_QUERY_PACKET_HEADER; | 582 | packet.hdr.type = VS_QUERY_PACKET_HEADER; |
644 | packet.len = sizeof(struct hvsi_query); | 583 | packet.hdr.len = sizeof(struct hvsi_query); |
645 | packet.seqno = atomic_inc_return(&hp->seqno); | 584 | packet.hdr.seqno = atomic_inc_return(&hp->seqno); |
646 | packet.verb = verb; | 585 | packet.verb = verb; |
647 | 586 | ||
648 | pr_debug("%s: sending %i bytes\n", __func__, packet.len); | 587 | pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); |
649 | dbg_dump_hex((uint8_t*)&packet, packet.len); | 588 | dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); |
650 | 589 | ||
651 | wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); | 590 | wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); |
652 | if (wrote != packet.len) { | 591 | if (wrote != packet.hdr.len) { |
653 | printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, | 592 | printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, |
654 | wrote); | 593 | wrote); |
655 | return -EIO; | 594 | return -EIO; |
@@ -683,20 +622,20 @@ static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) | |||
683 | struct hvsi_control packet __ALIGNED__; | 622 | struct hvsi_control packet __ALIGNED__; |
684 | int wrote; | 623 | int wrote; |
685 | 624 | ||
686 | packet.type = VS_CONTROL_PACKET_HEADER, | 625 | packet.hdr.type = VS_CONTROL_PACKET_HEADER, |
687 | packet.seqno = atomic_inc_return(&hp->seqno); | 626 | packet.hdr.seqno = atomic_inc_return(&hp->seqno); |
688 | packet.len = sizeof(struct hvsi_control); | 627 | packet.hdr.len = sizeof(struct hvsi_control); |
689 | packet.verb = VSV_SET_MODEM_CTL; | 628 | packet.verb = VSV_SET_MODEM_CTL; |
690 | packet.mask = HVSI_TSDTR; | 629 | packet.mask = HVSI_TSDTR; |
691 | 630 | ||
692 | if (mctrl & TIOCM_DTR) | 631 | if (mctrl & TIOCM_DTR) |
693 | packet.word = HVSI_TSDTR; | 632 | packet.word = HVSI_TSDTR; |
694 | 633 | ||
695 | pr_debug("%s: sending %i bytes\n", __func__, packet.len); | 634 | pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); |
696 | dbg_dump_hex((uint8_t*)&packet, packet.len); | 635 | dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); |
697 | 636 | ||
698 | wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); | 637 | wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); |
699 | if (wrote != packet.len) { | 638 | if (wrote != packet.hdr.len) { |
700 | printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); | 639 | printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); |
701 | return -EIO; | 640 | return -EIO; |
702 | } | 641 | } |
@@ -766,13 +705,13 @@ static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) | |||
766 | 705 | ||
767 | BUG_ON(count > HVSI_MAX_OUTGOING_DATA); | 706 | BUG_ON(count > HVSI_MAX_OUTGOING_DATA); |
768 | 707 | ||
769 | packet.type = VS_DATA_PACKET_HEADER; | 708 | packet.hdr.type = VS_DATA_PACKET_HEADER; |
770 | packet.seqno = atomic_inc_return(&hp->seqno); | 709 | packet.hdr.seqno = atomic_inc_return(&hp->seqno); |
771 | packet.len = count + sizeof(struct hvsi_header); | 710 | packet.hdr.len = count + sizeof(struct hvsi_header); |
772 | memcpy(&packet.data, buf, count); | 711 | memcpy(&packet.data, buf, count); |
773 | 712 | ||
774 | ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); | 713 | ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); |
775 | if (ret == packet.len) { | 714 | if (ret == packet.hdr.len) { |
776 | /* return the number of chars written, not the packet length */ | 715 | /* return the number of chars written, not the packet length */ |
777 | return count; | 716 | return count; |
778 | } | 717 | } |
@@ -783,15 +722,15 @@ static void hvsi_close_protocol(struct hvsi_struct *hp) | |||
783 | { | 722 | { |
784 | struct hvsi_control packet __ALIGNED__; | 723 | struct hvsi_control packet __ALIGNED__; |
785 | 724 | ||
786 | packet.type = VS_CONTROL_PACKET_HEADER; | 725 | packet.hdr.type = VS_CONTROL_PACKET_HEADER; |
787 | packet.seqno = atomic_inc_return(&hp->seqno); | 726 | packet.hdr.seqno = atomic_inc_return(&hp->seqno); |
788 | packet.len = 6; | 727 | packet.hdr.len = 6; |
789 | packet.verb = VSV_CLOSE_PROTOCOL; | 728 | packet.verb = VSV_CLOSE_PROTOCOL; |
790 | 729 | ||
791 | pr_debug("%s: sending %i bytes\n", __func__, packet.len); | 730 | pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); |
792 | dbg_dump_hex((uint8_t*)&packet, packet.len); | 731 | dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); |
793 | 732 | ||
794 | hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); | 733 | hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); |
795 | } | 734 | } |
796 | 735 | ||
797 | static int hvsi_open(struct tty_struct *tty, struct file *filp) | 736 | static int hvsi_open(struct tty_struct *tty, struct file *filp) |
diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c new file mode 100644 index 000000000000..bd9b09827b24 --- /dev/null +++ b/drivers/tty/hvc/hvsi_lib.c | |||
@@ -0,0 +1,426 @@ | |||
1 | #include <linux/types.h> | ||
2 | #include <linux/init.h> | ||
3 | #include <linux/delay.h> | ||
4 | #include <linux/slab.h> | ||
5 | #include <linux/console.h> | ||
6 | #include <asm/hvsi.h> | ||
7 | |||
8 | #include "hvc_console.h" | ||
9 | |||
10 | static int hvsi_send_packet(struct hvsi_priv *pv, struct hvsi_header *packet) | ||
11 | { | ||
12 | packet->seqno = atomic_inc_return(&pv->seqno); | ||
13 | |||
14 | /* Assumes that always succeeds, works in practice */ | ||
15 | return pv->put_chars(pv->termno, (char *)packet, packet->len); | ||
16 | } | ||
17 | |||
18 | static void hvsi_start_handshake(struct hvsi_priv *pv) | ||
19 | { | ||
20 | struct hvsi_query q; | ||
21 | |||
22 | /* Reset state */ | ||
23 | pv->established = 0; | ||
24 | atomic_set(&pv->seqno, 0); | ||
25 | |||
26 | pr_devel("HVSI@%x: Handshaking started\n", pv->termno); | ||
27 | |||
28 | /* Send version query */ | ||
29 | q.hdr.type = VS_QUERY_PACKET_HEADER; | ||
30 | q.hdr.len = sizeof(struct hvsi_query); | ||
31 | q.verb = VSV_SEND_VERSION_NUMBER; | ||
32 | hvsi_send_packet(pv, &q.hdr); | ||
33 | } | ||
34 | |||
35 | static int hvsi_send_close(struct hvsi_priv *pv) | ||
36 | { | ||
37 | struct hvsi_control ctrl; | ||
38 | |||
39 | pv->established = 0; | ||
40 | |||
41 | ctrl.hdr.type = VS_CONTROL_PACKET_HEADER; | ||
42 | ctrl.hdr.len = sizeof(struct hvsi_control); | ||
43 | ctrl.verb = VSV_CLOSE_PROTOCOL; | ||
44 | return hvsi_send_packet(pv, &ctrl.hdr); | ||
45 | } | ||
46 | |||
47 | static void hvsi_cd_change(struct hvsi_priv *pv, int cd) | ||
48 | { | ||
49 | if (cd) | ||
50 | pv->mctrl |= TIOCM_CD; | ||
51 | else { | ||
52 | pv->mctrl &= ~TIOCM_CD; | ||
53 | |||
54 | /* We copy the existing hvsi driver semantics | ||
55 | * here which are to trigger a hangup when | ||
56 | * we get a carrier loss. | ||
57 | * Closing our connection to the server will | ||
58 | * do just that. | ||
59 | */ | ||
60 | if (!pv->is_console && pv->opened) { | ||
61 | pr_devel("HVSI@%x Carrier lost, hanging up !\n", | ||
62 | pv->termno); | ||
63 | hvsi_send_close(pv); | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
68 | static void hvsi_got_control(struct hvsi_priv *pv) | ||
69 | { | ||
70 | struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf; | ||
71 | |||
72 | switch (pkt->verb) { | ||
73 | case VSV_CLOSE_PROTOCOL: | ||
74 | /* We restart the handshaking */ | ||
75 | hvsi_start_handshake(pv); | ||
76 | break; | ||
77 | case VSV_MODEM_CTL_UPDATE: | ||
78 | /* Transition of carrier detect */ | ||
79 | hvsi_cd_change(pv, pkt->word & HVSI_TSCD); | ||
80 | break; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | static void hvsi_got_query(struct hvsi_priv *pv) | ||
85 | { | ||
86 | struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf; | ||
87 | struct hvsi_query_response r; | ||
88 | |||
89 | /* We only handle version queries */ | ||
90 | if (pkt->verb != VSV_SEND_VERSION_NUMBER) | ||
91 | return; | ||
92 | |||
93 | pr_devel("HVSI@%x: Got version query, sending response...\n", | ||
94 | pv->termno); | ||
95 | |||
96 | /* Send version response */ | ||
97 | r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; | ||
98 | r.hdr.len = sizeof(struct hvsi_query_response); | ||
99 | r.verb = VSV_SEND_VERSION_NUMBER; | ||
100 | r.u.version = HVSI_VERSION; | ||
101 | r.query_seqno = pkt->hdr.seqno; | ||
102 | hvsi_send_packet(pv, &r.hdr); | ||
103 | |||
104 | /* Assume protocol is open now */ | ||
105 | pv->established = 1; | ||
106 | } | ||
107 | |||
108 | static void hvsi_got_response(struct hvsi_priv *pv) | ||
109 | { | ||
110 | struct hvsi_query_response *r = | ||
111 | (struct hvsi_query_response *)pv->inbuf; | ||
112 | |||
113 | switch(r->verb) { | ||
114 | case VSV_SEND_MODEM_CTL_STATUS: | ||
115 | hvsi_cd_change(pv, r->u.mctrl_word & HVSI_TSCD); | ||
116 | pv->mctrl_update = 1; | ||
117 | break; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static int hvsi_check_packet(struct hvsi_priv *pv) | ||
122 | { | ||
123 | u8 len, type; | ||
124 | |||
125 | /* Check header validity. If it's invalid, we ditch | ||
126 | * the whole buffer and hope we eventually resync | ||
127 | */ | ||
128 | if (pv->inbuf[0] < 0xfc) { | ||
129 | pv->inbuf_len = pv->inbuf_pktlen = 0; | ||
130 | return 0; | ||
131 | } | ||
132 | type = pv->inbuf[0]; | ||
133 | len = pv->inbuf[1]; | ||
134 | |||
135 | /* Packet incomplete ? */ | ||
136 | if (pv->inbuf_len < len) | ||
137 | return 0; | ||
138 | |||
139 | pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n", | ||
140 | pv->termno, type, len); | ||
141 | |||
142 | /* We have a packet, yay ! Handle it */ | ||
143 | switch(type) { | ||
144 | case VS_DATA_PACKET_HEADER: | ||
145 | pv->inbuf_pktlen = len - 4; | ||
146 | pv->inbuf_cur = 4; | ||
147 | return 1; | ||
148 | case VS_CONTROL_PACKET_HEADER: | ||
149 | hvsi_got_control(pv); | ||
150 | break; | ||
151 | case VS_QUERY_PACKET_HEADER: | ||
152 | hvsi_got_query(pv); | ||
153 | break; | ||
154 | case VS_QUERY_RESPONSE_PACKET_HEADER: | ||
155 | hvsi_got_response(pv); | ||
156 | break; | ||
157 | } | ||
158 | |||
159 | /* Swallow packet and retry */ | ||
160 | pv->inbuf_len -= len; | ||
161 | memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len); | ||
162 | return 1; | ||
163 | } | ||
164 | |||
165 | static int hvsi_get_packet(struct hvsi_priv *pv) | ||
166 | { | ||
167 | /* If we have room in the buffer, ask HV for more */ | ||
168 | if (pv->inbuf_len < HVSI_INBUF_SIZE) | ||
169 | pv->inbuf_len += pv->get_chars(pv->termno, | ||
170 | &pv->inbuf[pv->inbuf_len], | ||
171 | HVSI_INBUF_SIZE - pv->inbuf_len); | ||
172 | /* | ||
173 | * If we have at least 4 bytes in the buffer, check for | ||
174 | * a full packet and retry | ||
175 | */ | ||
176 | if (pv->inbuf_len >= 4) | ||
177 | return hvsi_check_packet(pv); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count) | ||
182 | { | ||
183 | unsigned int tries, read = 0; | ||
184 | |||
185 | if (WARN_ON(!pv)) | ||
186 | return 0; | ||
187 | |||
188 | /* If we aren't open, don't do anything in order to avoid races | ||
189 | * with connection establishment. The hvc core will call this | ||
190 | * before we have returned from notifier_add(), and we need to | ||
191 | * avoid multiple users playing with the receive buffer | ||
192 | */ | ||
193 | if (!pv->opened) | ||
194 | return 0; | ||
195 | |||
196 | /* We try twice, once with what data we have and once more | ||
197 | * after we try to fetch some more from the hypervisor | ||
198 | */ | ||
199 | for (tries = 1; count && tries < 2; tries++) { | ||
200 | /* Consume existing data packet */ | ||
201 | if (pv->inbuf_pktlen) { | ||
202 | unsigned int l = min(count, (int)pv->inbuf_pktlen); | ||
203 | memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l); | ||
204 | pv->inbuf_cur += l; | ||
205 | pv->inbuf_pktlen -= l; | ||
206 | count -= l; | ||
207 | read += l; | ||
208 | } | ||
209 | if (count == 0) | ||
210 | break; | ||
211 | |||
212 | /* Data packet fully consumed, move down remaning data */ | ||
213 | if (pv->inbuf_cur) { | ||
214 | pv->inbuf_len -= pv->inbuf_cur; | ||
215 | memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur], | ||
216 | pv->inbuf_len); | ||
217 | pv->inbuf_cur = 0; | ||
218 | } | ||
219 | |||
220 | /* Try to get another packet */ | ||
221 | if (hvsi_get_packet(pv)) | ||
222 | tries--; | ||
223 | } | ||
224 | if (!pv->established) { | ||
225 | pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno); | ||
226 | return -EPIPE; | ||
227 | } | ||
228 | return read; | ||
229 | } | ||
230 | |||
231 | int hvsilib_put_chars(struct hvsi_priv *pv, const char *buf, int count) | ||
232 | { | ||
233 | struct hvsi_data dp; | ||
234 | int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA); | ||
235 | |||
236 | if (WARN_ON(!pv)) | ||
237 | return 0; | ||
238 | |||
239 | dp.hdr.type = VS_DATA_PACKET_HEADER; | ||
240 | dp.hdr.len = adjcount + sizeof(struct hvsi_header); | ||
241 | memcpy(dp.data, buf, adjcount); | ||
242 | rc = hvsi_send_packet(pv, &dp.hdr); | ||
243 | if (rc <= 0) | ||
244 | return rc; | ||
245 | return adjcount; | ||
246 | } | ||
247 | |||
248 | static void maybe_msleep(unsigned long ms) | ||
249 | { | ||
250 | /* During early boot, IRQs are disabled, use mdelay */ | ||
251 | if (irqs_disabled()) | ||
252 | mdelay(ms); | ||
253 | else | ||
254 | msleep(ms); | ||
255 | } | ||
256 | |||
257 | int hvsilib_read_mctrl(struct hvsi_priv *pv) | ||
258 | { | ||
259 | struct hvsi_query q; | ||
260 | int rc, timeout; | ||
261 | |||
262 | pr_devel("HVSI@%x: Querying modem control status...\n", | ||
263 | pv->termno); | ||
264 | |||
265 | pv->mctrl_update = 0; | ||
266 | q.hdr.type = VS_QUERY_PACKET_HEADER; | ||
267 | q.hdr.len = sizeof(struct hvsi_query); | ||
268 | q.hdr.seqno = atomic_inc_return(&pv->seqno); | ||
269 | q.verb = VSV_SEND_MODEM_CTL_STATUS; | ||
270 | rc = hvsi_send_packet(pv, &q.hdr); | ||
271 | if (rc <= 0) { | ||
272 | pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc); | ||
273 | return rc; | ||
274 | } | ||
275 | |||
276 | /* Try for up to 200ms */ | ||
277 | for (timeout = 0; timeout < 20; timeout++) { | ||
278 | if (!pv->established) | ||
279 | return -ENXIO; | ||
280 | if (pv->mctrl_update) | ||
281 | return 0; | ||
282 | if (!hvsi_get_packet(pv)) | ||
283 | maybe_msleep(10); | ||
284 | } | ||
285 | return -EIO; | ||
286 | } | ||
287 | |||
288 | int hvsilib_write_mctrl(struct hvsi_priv *pv, int dtr) | ||
289 | { | ||
290 | struct hvsi_control ctrl; | ||
291 | unsigned short mctrl; | ||
292 | |||
293 | mctrl = pv->mctrl; | ||
294 | if (dtr) | ||
295 | mctrl |= TIOCM_DTR; | ||
296 | else | ||
297 | mctrl &= ~TIOCM_DTR; | ||
298 | if (mctrl == pv->mctrl) | ||
299 | return 0; | ||
300 | pv->mctrl = mctrl; | ||
301 | |||
302 | pr_devel("HVSI@%x: %s DTR...\n", pv->termno, | ||
303 | dtr ? "Setting" : "Clearing"); | ||
304 | |||
305 | ctrl.hdr.type = VS_CONTROL_PACKET_HEADER, | ||
306 | ctrl.hdr.len = sizeof(struct hvsi_control); | ||
307 | ctrl.verb = VSV_SET_MODEM_CTL; | ||
308 | ctrl.mask = HVSI_TSDTR; | ||
309 | ctrl.word = dtr ? HVSI_TSDTR : 0; | ||
310 | return hvsi_send_packet(pv, &ctrl.hdr); | ||
311 | } | ||
312 | |||
313 | void hvsilib_establish(struct hvsi_priv *pv) | ||
314 | { | ||
315 | int timeout; | ||
316 | |||
317 | pr_devel("HVSI@%x: Establishing...\n", pv->termno); | ||
318 | |||
319 | /* Try for up to 200ms, there can be a packet to | ||
320 | * start the process waiting for us... | ||
321 | */ | ||
322 | for (timeout = 0; timeout < 20; timeout++) { | ||
323 | if (pv->established) | ||
324 | goto established; | ||
325 | if (!hvsi_get_packet(pv)) | ||
326 | maybe_msleep(10); | ||
327 | } | ||
328 | |||
329 | /* Failed, send a close connection packet just | ||
330 | * in case | ||
331 | */ | ||
332 | pr_devel("HVSI@%x: ... sending close\n", pv->termno); | ||
333 | |||
334 | hvsi_send_close(pv); | ||
335 | |||
336 | /* Then restart handshake */ | ||
337 | |||
338 | pr_devel("HVSI@%x: ... restarting handshake\n", pv->termno); | ||
339 | |||
340 | hvsi_start_handshake(pv); | ||
341 | |||
342 | pr_devel("HVSI@%x: ... waiting handshake\n", pv->termno); | ||
343 | |||
344 | /* Try for up to 200s */ | ||
345 | for (timeout = 0; timeout < 20; timeout++) { | ||
346 | if (pv->established) | ||
347 | goto established; | ||
348 | if (!hvsi_get_packet(pv)) | ||
349 | maybe_msleep(10); | ||
350 | } | ||
351 | |||
352 | if (!pv->established) { | ||
353 | pr_devel("HVSI@%x: Timeout handshaking, giving up !\n", | ||
354 | pv->termno); | ||
355 | return; | ||
356 | } | ||
357 | established: | ||
358 | /* Query modem control lines */ | ||
359 | |||
360 | pr_devel("HVSI@%x: ... established, reading mctrl\n", pv->termno); | ||
361 | |||
362 | hvsilib_read_mctrl(pv); | ||
363 | |||
364 | /* Set our own DTR */ | ||
365 | |||
366 | pr_devel("HVSI@%x: ... setting mctrl\n", pv->termno); | ||
367 | |||
368 | hvsilib_write_mctrl(pv, 1); | ||
369 | |||
370 | /* Set the opened flag so reads are allowed */ | ||
371 | wmb(); | ||
372 | pv->opened = 1; | ||
373 | } | ||
374 | |||
375 | int hvsilib_open(struct hvsi_priv *pv, struct hvc_struct *hp) | ||
376 | { | ||
377 | pr_devel("HVSI@%x: open !\n", pv->termno); | ||
378 | |||
379 | /* Keep track of the tty data structure */ | ||
380 | pv->tty = tty_kref_get(hp->tty); | ||
381 | |||
382 | hvsilib_establish(pv); | ||
383 | |||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | void hvsilib_close(struct hvsi_priv *pv, struct hvc_struct *hp) | ||
388 | { | ||
389 | unsigned long flags; | ||
390 | |||
391 | pr_devel("HVSI@%x: close !\n", pv->termno); | ||
392 | |||
393 | if (!pv->is_console) { | ||
394 | pr_devel("HVSI@%x: Not a console, tearing down\n", | ||
395 | pv->termno); | ||
396 | |||
397 | /* Clear opened, synchronize with khvcd */ | ||
398 | spin_lock_irqsave(&hp->lock, flags); | ||
399 | pv->opened = 0; | ||
400 | spin_unlock_irqrestore(&hp->lock, flags); | ||
401 | |||
402 | /* Clear our own DTR */ | ||
403 | if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL)) | ||
404 | hvsilib_write_mctrl(pv, 0); | ||
405 | |||
406 | /* Tear down the connection */ | ||
407 | hvsi_send_close(pv); | ||
408 | } | ||
409 | |||
410 | if (pv->tty) | ||
411 | tty_kref_put(pv->tty); | ||
412 | pv->tty = NULL; | ||
413 | } | ||
414 | |||
415 | void hvsilib_init(struct hvsi_priv *pv, | ||
416 | int (*get_chars)(uint32_t termno, char *buf, int count), | ||
417 | int (*put_chars)(uint32_t termno, const char *buf, | ||
418 | int count), | ||
419 | int termno, int is_console) | ||
420 | { | ||
421 | memset(pv, 0, sizeof(*pv)); | ||
422 | pv->get_chars = get_chars; | ||
423 | pv->put_chars = put_chars; | ||
424 | pv->termno = termno; | ||
425 | pv->is_console = is_console; | ||
426 | } | ||