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/hvc/hvsi_lib.c | |
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/hvc/hvsi_lib.c')
-rw-r--r-- | drivers/tty/hvc/hvsi_lib.c | 426 |
1 files changed, 426 insertions, 0 deletions
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 | } | ||