Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
hvsi_lib.c
Go to the documentation of this file.
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);
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;
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 */
91  return;
92 
93  pr_devel("HVSI@%x: Got version query, sending response...\n",
94  pv->termno);
95 
96  /* Send version response */
98  r.hdr.len = sizeof(struct hvsi_query_response);
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) {
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) {
145  pv->inbuf_pktlen = len - 4;
146  pv->inbuf_cur = 4;
147  return 1;
149  hvsi_got_control(pv);
150  break;
152  hvsi_got_query(pv);
153  break;
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 
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);
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 }