Root/
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 -ENXIO; |
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 -ENODEV; |
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_port_tty_get(&hp->port); |
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 | } |
427 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9