diff options
Diffstat (limited to 'arch/sparc64/kernel/viohs.c')
-rw-r--r-- | arch/sparc64/kernel/viohs.c | 792 |
1 files changed, 792 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/viohs.c b/arch/sparc64/kernel/viohs.c new file mode 100644 index 000000000000..15613add45d1 --- /dev/null +++ b/arch/sparc64/kernel/viohs.c | |||
@@ -0,0 +1,792 @@ | |||
1 | /* viohs.c: LDOM Virtual I/O handshake helper layer. | ||
2 | * | ||
3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | ||
4 | */ | ||
5 | |||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/module.h> | ||
8 | #include <linux/string.h> | ||
9 | #include <linux/delay.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include <linux/slab.h> | ||
12 | |||
13 | #include <asm/ldc.h> | ||
14 | #include <asm/vio.h> | ||
15 | |||
16 | int vio_ldc_send(struct vio_driver_state *vio, void *data, int len) | ||
17 | { | ||
18 | int err, limit = 1000; | ||
19 | |||
20 | err = -EINVAL; | ||
21 | while (limit-- > 0) { | ||
22 | err = ldc_write(vio->lp, data, len); | ||
23 | if (!err || (err != -EAGAIN)) | ||
24 | break; | ||
25 | udelay(1); | ||
26 | } | ||
27 | |||
28 | return err; | ||
29 | } | ||
30 | EXPORT_SYMBOL(vio_ldc_send); | ||
31 | |||
32 | static int send_ctrl(struct vio_driver_state *vio, | ||
33 | struct vio_msg_tag *tag, int len) | ||
34 | { | ||
35 | tag->sid = vio_send_sid(vio); | ||
36 | return vio_ldc_send(vio, tag, len); | ||
37 | } | ||
38 | |||
39 | static void init_tag(struct vio_msg_tag *tag, u8 type, u8 stype, u16 stype_env) | ||
40 | { | ||
41 | tag->type = type; | ||
42 | tag->stype = stype; | ||
43 | tag->stype_env = stype_env; | ||
44 | } | ||
45 | |||
46 | static int send_version(struct vio_driver_state *vio, u16 major, u16 minor) | ||
47 | { | ||
48 | struct vio_ver_info pkt; | ||
49 | |||
50 | vio->_local_sid = (u32) sched_clock(); | ||
51 | |||
52 | memset(&pkt, 0, sizeof(pkt)); | ||
53 | init_tag(&pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_VER_INFO); | ||
54 | pkt.major = major; | ||
55 | pkt.minor = minor; | ||
56 | pkt.dev_class = vio->dev_class; | ||
57 | |||
58 | viodbg(HS, "SEND VERSION INFO maj[%u] min[%u] devclass[%u]\n", | ||
59 | major, minor, vio->dev_class); | ||
60 | |||
61 | return send_ctrl(vio, &pkt.tag, sizeof(pkt)); | ||
62 | } | ||
63 | |||
64 | static int start_handshake(struct vio_driver_state *vio) | ||
65 | { | ||
66 | int err; | ||
67 | |||
68 | viodbg(HS, "START HANDSHAKE\n"); | ||
69 | |||
70 | vio->hs_state = VIO_HS_INVALID; | ||
71 | |||
72 | err = send_version(vio, | ||
73 | vio->ver_table[0].major, | ||
74 | vio->ver_table[0].minor); | ||
75 | if (err < 0) | ||
76 | return err; | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | void vio_link_state_change(struct vio_driver_state *vio, int event) | ||
82 | { | ||
83 | if (event == LDC_EVENT_UP) { | ||
84 | vio->hs_state = VIO_HS_INVALID; | ||
85 | |||
86 | switch (vio->dev_class) { | ||
87 | case VDEV_NETWORK: | ||
88 | case VDEV_NETWORK_SWITCH: | ||
89 | vio->dr_state = (VIO_DR_STATE_TXREQ | | ||
90 | VIO_DR_STATE_RXREQ); | ||
91 | break; | ||
92 | |||
93 | case VDEV_DISK: | ||
94 | vio->dr_state = VIO_DR_STATE_TXREQ; | ||
95 | break; | ||
96 | case VDEV_DISK_SERVER: | ||
97 | vio->dr_state = VIO_DR_STATE_RXREQ; | ||
98 | break; | ||
99 | } | ||
100 | start_handshake(vio); | ||
101 | } | ||
102 | } | ||
103 | EXPORT_SYMBOL(vio_link_state_change); | ||
104 | |||
105 | static int handshake_failure(struct vio_driver_state *vio) | ||
106 | { | ||
107 | struct vio_dring_state *dr; | ||
108 | |||
109 | /* XXX Put policy here... Perhaps start a timer to fire | ||
110 | * XXX in 100 ms, which will bring the link up and retry | ||
111 | * XXX the handshake. | ||
112 | */ | ||
113 | |||
114 | viodbg(HS, "HANDSHAKE FAILURE\n"); | ||
115 | |||
116 | vio->dr_state &= ~(VIO_DR_STATE_TXREG | | ||
117 | VIO_DR_STATE_RXREG); | ||
118 | |||
119 | dr = &vio->drings[VIO_DRIVER_RX_RING]; | ||
120 | memset(dr, 0, sizeof(*dr)); | ||
121 | |||
122 | kfree(vio->desc_buf); | ||
123 | vio->desc_buf = NULL; | ||
124 | vio->desc_buf_len = 0; | ||
125 | |||
126 | vio->hs_state = VIO_HS_INVALID; | ||
127 | |||
128 | return -ECONNRESET; | ||
129 | } | ||
130 | |||
131 | static int process_unknown(struct vio_driver_state *vio, void *arg) | ||
132 | { | ||
133 | struct vio_msg_tag *pkt = arg; | ||
134 | |||
135 | viodbg(HS, "UNKNOWN CONTROL [%02x:%02x:%04x:%08x]\n", | ||
136 | pkt->type, pkt->stype, pkt->stype_env, pkt->sid); | ||
137 | |||
138 | printk(KERN_ERR "vio: ID[%lu] Resetting connection.\n", | ||
139 | vio->vdev->channel_id); | ||
140 | |||
141 | ldc_disconnect(vio->lp); | ||
142 | |||
143 | return -ECONNRESET; | ||
144 | } | ||
145 | |||
146 | static int send_dreg(struct vio_driver_state *vio) | ||
147 | { | ||
148 | struct vio_dring_state *dr = &vio->drings[VIO_DRIVER_TX_RING]; | ||
149 | union { | ||
150 | struct vio_dring_register pkt; | ||
151 | char all[sizeof(struct vio_dring_register) + | ||
152 | (sizeof(struct ldc_trans_cookie) * | ||
153 | dr->ncookies)]; | ||
154 | } u; | ||
155 | int i; | ||
156 | |||
157 | memset(&u, 0, sizeof(u)); | ||
158 | init_tag(&u.pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_DRING_REG); | ||
159 | u.pkt.dring_ident = 0; | ||
160 | u.pkt.num_descr = dr->num_entries; | ||
161 | u.pkt.descr_size = dr->entry_size; | ||
162 | u.pkt.options = VIO_TX_DRING; | ||
163 | u.pkt.num_cookies = dr->ncookies; | ||
164 | |||
165 | viodbg(HS, "SEND DRING_REG INFO ndesc[%u] dsz[%u] opt[0x%x] " | ||
166 | "ncookies[%u]\n", | ||
167 | u.pkt.num_descr, u.pkt.descr_size, u.pkt.options, | ||
168 | u.pkt.num_cookies); | ||
169 | |||
170 | for (i = 0; i < dr->ncookies; i++) { | ||
171 | u.pkt.cookies[i] = dr->cookies[i]; | ||
172 | |||
173 | viodbg(HS, "DRING COOKIE(%d) [%016llx:%016llx]\n", | ||
174 | i, | ||
175 | (unsigned long long) u.pkt.cookies[i].cookie_addr, | ||
176 | (unsigned long long) u.pkt.cookies[i].cookie_size); | ||
177 | } | ||
178 | |||
179 | return send_ctrl(vio, &u.pkt.tag, sizeof(u)); | ||
180 | } | ||
181 | |||
182 | static int send_rdx(struct vio_driver_state *vio) | ||
183 | { | ||
184 | struct vio_rdx pkt; | ||
185 | |||
186 | memset(&pkt, 0, sizeof(pkt)); | ||
187 | |||
188 | init_tag(&pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_RDX); | ||
189 | |||
190 | viodbg(HS, "SEND RDX INFO\n"); | ||
191 | |||
192 | return send_ctrl(vio, &pkt.tag, sizeof(pkt)); | ||
193 | } | ||
194 | |||
195 | static int send_attr(struct vio_driver_state *vio) | ||
196 | { | ||
197 | return vio->ops->send_attr(vio); | ||
198 | } | ||
199 | |||
200 | static struct vio_version *find_by_major(struct vio_driver_state *vio, | ||
201 | u16 major) | ||
202 | { | ||
203 | struct vio_version *ret = NULL; | ||
204 | int i; | ||
205 | |||
206 | for (i = 0; i < vio->ver_table_entries; i++) { | ||
207 | struct vio_version *v = &vio->ver_table[i]; | ||
208 | if (v->major <= major) { | ||
209 | ret = v; | ||
210 | break; | ||
211 | } | ||
212 | } | ||
213 | return ret; | ||
214 | } | ||
215 | |||
216 | static int process_ver_info(struct vio_driver_state *vio, | ||
217 | struct vio_ver_info *pkt) | ||
218 | { | ||
219 | struct vio_version *vap; | ||
220 | int err; | ||
221 | |||
222 | viodbg(HS, "GOT VERSION INFO maj[%u] min[%u] devclass[%u]\n", | ||
223 | pkt->major, pkt->minor, pkt->dev_class); | ||
224 | |||
225 | if (vio->hs_state != VIO_HS_INVALID) { | ||
226 | /* XXX Perhaps invoke start_handshake? XXX */ | ||
227 | memset(&vio->ver, 0, sizeof(vio->ver)); | ||
228 | vio->hs_state = VIO_HS_INVALID; | ||
229 | } | ||
230 | |||
231 | vap = find_by_major(vio, pkt->major); | ||
232 | |||
233 | vio->_peer_sid = pkt->tag.sid; | ||
234 | |||
235 | if (!vap) { | ||
236 | pkt->tag.stype = VIO_SUBTYPE_NACK; | ||
237 | pkt->major = 0; | ||
238 | pkt->minor = 0; | ||
239 | viodbg(HS, "SEND VERSION NACK maj[0] min[0]\n"); | ||
240 | err = send_ctrl(vio, &pkt->tag, sizeof(*pkt)); | ||
241 | } else if (vap->major != pkt->major) { | ||
242 | pkt->tag.stype = VIO_SUBTYPE_NACK; | ||
243 | pkt->major = vap->major; | ||
244 | pkt->minor = vap->minor; | ||
245 | viodbg(HS, "SEND VERSION NACK maj[%u] min[%u]\n", | ||
246 | pkt->major, pkt->minor); | ||
247 | err = send_ctrl(vio, &pkt->tag, sizeof(*pkt)); | ||
248 | } else { | ||
249 | struct vio_version ver = { | ||
250 | .major = pkt->major, | ||
251 | .minor = pkt->minor, | ||
252 | }; | ||
253 | if (ver.minor > vap->minor) | ||
254 | ver.minor = vap->minor; | ||
255 | pkt->minor = ver.minor; | ||
256 | pkt->tag.stype = VIO_SUBTYPE_ACK; | ||
257 | viodbg(HS, "SEND VERSION ACK maj[%u] min[%u]\n", | ||
258 | pkt->major, pkt->minor); | ||
259 | err = send_ctrl(vio, &pkt->tag, sizeof(*pkt)); | ||
260 | if (err > 0) { | ||
261 | vio->ver = ver; | ||
262 | vio->hs_state = VIO_HS_GOTVERS; | ||
263 | } | ||
264 | } | ||
265 | if (err < 0) | ||
266 | return handshake_failure(vio); | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int process_ver_ack(struct vio_driver_state *vio, | ||
272 | struct vio_ver_info *pkt) | ||
273 | { | ||
274 | viodbg(HS, "GOT VERSION ACK maj[%u] min[%u] devclass[%u]\n", | ||
275 | pkt->major, pkt->minor, pkt->dev_class); | ||
276 | |||
277 | if (vio->hs_state & VIO_HS_GOTVERS) { | ||
278 | if (vio->ver.major != pkt->major || | ||
279 | vio->ver.minor != pkt->minor) { | ||
280 | pkt->tag.stype = VIO_SUBTYPE_NACK; | ||
281 | (void) send_ctrl(vio, &pkt->tag, sizeof(*pkt)); | ||
282 | return handshake_failure(vio); | ||
283 | } | ||
284 | } else { | ||
285 | vio->ver.major = pkt->major; | ||
286 | vio->ver.minor = pkt->minor; | ||
287 | vio->hs_state = VIO_HS_GOTVERS; | ||
288 | } | ||
289 | |||
290 | switch (vio->dev_class) { | ||
291 | case VDEV_NETWORK: | ||
292 | case VDEV_DISK: | ||
293 | if (send_attr(vio) < 0) | ||
294 | return handshake_failure(vio); | ||
295 | break; | ||
296 | |||
297 | default: | ||
298 | break; | ||
299 | } | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static int process_ver_nack(struct vio_driver_state *vio, | ||
305 | struct vio_ver_info *pkt) | ||
306 | { | ||
307 | struct vio_version *nver; | ||
308 | |||
309 | viodbg(HS, "GOT VERSION NACK maj[%u] min[%u] devclass[%u]\n", | ||
310 | pkt->major, pkt->minor, pkt->dev_class); | ||
311 | |||
312 | if ((pkt->major == 0 && pkt->minor == 0) || | ||
313 | !(nver = find_by_major(vio, pkt->major))) | ||
314 | return handshake_failure(vio); | ||
315 | |||
316 | if (send_version(vio, nver->major, nver->minor) < 0) | ||
317 | return handshake_failure(vio); | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static int process_ver(struct vio_driver_state *vio, struct vio_ver_info *pkt) | ||
323 | { | ||
324 | switch (pkt->tag.stype) { | ||
325 | case VIO_SUBTYPE_INFO: | ||
326 | return process_ver_info(vio, pkt); | ||
327 | |||
328 | case VIO_SUBTYPE_ACK: | ||
329 | return process_ver_ack(vio, pkt); | ||
330 | |||
331 | case VIO_SUBTYPE_NACK: | ||
332 | return process_ver_nack(vio, pkt); | ||
333 | |||
334 | default: | ||
335 | return handshake_failure(vio); | ||
336 | }; | ||
337 | } | ||
338 | |||
339 | static int process_attr(struct vio_driver_state *vio, void *pkt) | ||
340 | { | ||
341 | int err; | ||
342 | |||
343 | if (!(vio->hs_state & VIO_HS_GOTVERS)) | ||
344 | return handshake_failure(vio); | ||
345 | |||
346 | err = vio->ops->handle_attr(vio, pkt); | ||
347 | if (err < 0) { | ||
348 | return handshake_failure(vio); | ||
349 | } else { | ||
350 | vio->hs_state |= VIO_HS_GOT_ATTR; | ||
351 | |||
352 | if ((vio->dr_state & VIO_DR_STATE_TXREQ) && | ||
353 | !(vio->hs_state & VIO_HS_SENT_DREG)) { | ||
354 | if (send_dreg(vio) < 0) | ||
355 | return handshake_failure(vio); | ||
356 | |||
357 | vio->hs_state |= VIO_HS_SENT_DREG; | ||
358 | } | ||
359 | } | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static int all_drings_registered(struct vio_driver_state *vio) | ||
364 | { | ||
365 | int need_rx, need_tx; | ||
366 | |||
367 | need_rx = (vio->dr_state & VIO_DR_STATE_RXREQ); | ||
368 | need_tx = (vio->dr_state & VIO_DR_STATE_TXREQ); | ||
369 | |||
370 | if (need_rx && | ||
371 | !(vio->dr_state & VIO_DR_STATE_RXREG)) | ||
372 | return 0; | ||
373 | |||
374 | if (need_tx && | ||
375 | !(vio->dr_state & VIO_DR_STATE_TXREG)) | ||
376 | return 0; | ||
377 | |||
378 | return 1; | ||
379 | } | ||
380 | |||
381 | static int process_dreg_info(struct vio_driver_state *vio, | ||
382 | struct vio_dring_register *pkt) | ||
383 | { | ||
384 | struct vio_dring_state *dr; | ||
385 | int i, len; | ||
386 | |||
387 | viodbg(HS, "GOT DRING_REG INFO ident[%llx] " | ||
388 | "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n", | ||
389 | (unsigned long long) pkt->dring_ident, | ||
390 | pkt->num_descr, pkt->descr_size, pkt->options, | ||
391 | pkt->num_cookies); | ||
392 | |||
393 | if (!(vio->dr_state & VIO_DR_STATE_RXREQ)) | ||
394 | goto send_nack; | ||
395 | |||
396 | if (vio->dr_state & VIO_DR_STATE_RXREG) | ||
397 | goto send_nack; | ||
398 | |||
399 | vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC); | ||
400 | if (!vio->desc_buf) | ||
401 | goto send_nack; | ||
402 | |||
403 | vio->desc_buf_len = pkt->descr_size; | ||
404 | |||
405 | dr = &vio->drings[VIO_DRIVER_RX_RING]; | ||
406 | |||
407 | dr->num_entries = pkt->num_descr; | ||
408 | dr->entry_size = pkt->descr_size; | ||
409 | dr->ncookies = pkt->num_cookies; | ||
410 | for (i = 0; i < dr->ncookies; i++) { | ||
411 | dr->cookies[i] = pkt->cookies[i]; | ||
412 | |||
413 | viodbg(HS, "DRING COOKIE(%d) [%016llx:%016llx]\n", | ||
414 | i, | ||
415 | (unsigned long long) | ||
416 | pkt->cookies[i].cookie_addr, | ||
417 | (unsigned long long) | ||
418 | pkt->cookies[i].cookie_size); | ||
419 | } | ||
420 | |||
421 | pkt->tag.stype = VIO_SUBTYPE_ACK; | ||
422 | pkt->dring_ident = ++dr->ident; | ||
423 | |||
424 | viodbg(HS, "SEND DRING_REG ACK ident[%llx]\n", | ||
425 | (unsigned long long) pkt->dring_ident); | ||
426 | |||
427 | len = (sizeof(*pkt) + | ||
428 | (dr->ncookies * sizeof(struct ldc_trans_cookie))); | ||
429 | if (send_ctrl(vio, &pkt->tag, len) < 0) | ||
430 | goto send_nack; | ||
431 | |||
432 | vio->dr_state |= VIO_DR_STATE_RXREG; | ||
433 | |||
434 | return 0; | ||
435 | |||
436 | send_nack: | ||
437 | pkt->tag.stype = VIO_SUBTYPE_NACK; | ||
438 | viodbg(HS, "SEND DRING_REG NACK\n"); | ||
439 | (void) send_ctrl(vio, &pkt->tag, sizeof(*pkt)); | ||
440 | |||
441 | return handshake_failure(vio); | ||
442 | } | ||
443 | |||
444 | static int process_dreg_ack(struct vio_driver_state *vio, | ||
445 | struct vio_dring_register *pkt) | ||
446 | { | ||
447 | struct vio_dring_state *dr; | ||
448 | |||
449 | viodbg(HS, "GOT DRING_REG ACK ident[%llx] " | ||
450 | "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n", | ||
451 | (unsigned long long) pkt->dring_ident, | ||
452 | pkt->num_descr, pkt->descr_size, pkt->options, | ||
453 | pkt->num_cookies); | ||
454 | |||
455 | dr = &vio->drings[VIO_DRIVER_TX_RING]; | ||
456 | |||
457 | if (!(vio->dr_state & VIO_DR_STATE_TXREQ)) | ||
458 | return handshake_failure(vio); | ||
459 | |||
460 | dr->ident = pkt->dring_ident; | ||
461 | vio->dr_state |= VIO_DR_STATE_TXREG; | ||
462 | |||
463 | if (all_drings_registered(vio)) { | ||
464 | if (send_rdx(vio) < 0) | ||
465 | return handshake_failure(vio); | ||
466 | vio->hs_state = VIO_HS_SENT_RDX; | ||
467 | } | ||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | static int process_dreg_nack(struct vio_driver_state *vio, | ||
472 | struct vio_dring_register *pkt) | ||
473 | { | ||
474 | viodbg(HS, "GOT DRING_REG NACK ident[%llx] " | ||
475 | "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n", | ||
476 | (unsigned long long) pkt->dring_ident, | ||
477 | pkt->num_descr, pkt->descr_size, pkt->options, | ||
478 | pkt->num_cookies); | ||
479 | |||
480 | return handshake_failure(vio); | ||
481 | } | ||
482 | |||
483 | static int process_dreg(struct vio_driver_state *vio, | ||
484 | struct vio_dring_register *pkt) | ||
485 | { | ||
486 | if (!(vio->hs_state & VIO_HS_GOTVERS)) | ||
487 | return handshake_failure(vio); | ||
488 | |||
489 | switch (pkt->tag.stype) { | ||
490 | case VIO_SUBTYPE_INFO: | ||
491 | return process_dreg_info(vio, pkt); | ||
492 | |||
493 | case VIO_SUBTYPE_ACK: | ||
494 | return process_dreg_ack(vio, pkt); | ||
495 | |||
496 | case VIO_SUBTYPE_NACK: | ||
497 | return process_dreg_nack(vio, pkt); | ||
498 | |||
499 | default: | ||
500 | return handshake_failure(vio); | ||
501 | } | ||
502 | } | ||
503 | |||
504 | static int process_dunreg(struct vio_driver_state *vio, | ||
505 | struct vio_dring_unregister *pkt) | ||
506 | { | ||
507 | struct vio_dring_state *dr = &vio->drings[VIO_DRIVER_RX_RING]; | ||
508 | |||
509 | viodbg(HS, "GOT DRING_UNREG\n"); | ||
510 | |||
511 | if (pkt->dring_ident != dr->ident) | ||
512 | return 0; | ||
513 | |||
514 | vio->dr_state &= ~VIO_DR_STATE_RXREG; | ||
515 | |||
516 | memset(dr, 0, sizeof(*dr)); | ||
517 | |||
518 | kfree(vio->desc_buf); | ||
519 | vio->desc_buf = NULL; | ||
520 | vio->desc_buf_len = 0; | ||
521 | |||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | static int process_rdx_info(struct vio_driver_state *vio, struct vio_rdx *pkt) | ||
526 | { | ||
527 | viodbg(HS, "GOT RDX INFO\n"); | ||
528 | |||
529 | pkt->tag.stype = VIO_SUBTYPE_ACK; | ||
530 | viodbg(HS, "SEND RDX ACK\n"); | ||
531 | if (send_ctrl(vio, &pkt->tag, sizeof(*pkt)) < 0) | ||
532 | return handshake_failure(vio); | ||
533 | |||
534 | vio->hs_state |= VIO_HS_SENT_RDX_ACK; | ||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | static int process_rdx_ack(struct vio_driver_state *vio, struct vio_rdx *pkt) | ||
539 | { | ||
540 | viodbg(HS, "GOT RDX ACK\n"); | ||
541 | |||
542 | if (!(vio->hs_state & VIO_HS_SENT_RDX)) | ||
543 | return handshake_failure(vio); | ||
544 | |||
545 | vio->hs_state |= VIO_HS_GOT_RDX_ACK; | ||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static int process_rdx_nack(struct vio_driver_state *vio, struct vio_rdx *pkt) | ||
550 | { | ||
551 | viodbg(HS, "GOT RDX NACK\n"); | ||
552 | |||
553 | return handshake_failure(vio); | ||
554 | } | ||
555 | |||
556 | static int process_rdx(struct vio_driver_state *vio, struct vio_rdx *pkt) | ||
557 | { | ||
558 | if (!all_drings_registered(vio)) | ||
559 | handshake_failure(vio); | ||
560 | |||
561 | switch (pkt->tag.stype) { | ||
562 | case VIO_SUBTYPE_INFO: | ||
563 | return process_rdx_info(vio, pkt); | ||
564 | |||
565 | case VIO_SUBTYPE_ACK: | ||
566 | return process_rdx_ack(vio, pkt); | ||
567 | |||
568 | case VIO_SUBTYPE_NACK: | ||
569 | return process_rdx_nack(vio, pkt); | ||
570 | |||
571 | default: | ||
572 | return handshake_failure(vio); | ||
573 | } | ||
574 | } | ||
575 | |||
576 | int vio_control_pkt_engine(struct vio_driver_state *vio, void *pkt) | ||
577 | { | ||
578 | struct vio_msg_tag *tag = pkt; | ||
579 | u8 prev_state = vio->hs_state; | ||
580 | int err; | ||
581 | |||
582 | switch (tag->stype_env) { | ||
583 | case VIO_VER_INFO: | ||
584 | err = process_ver(vio, pkt); | ||
585 | break; | ||
586 | |||
587 | case VIO_ATTR_INFO: | ||
588 | err = process_attr(vio, pkt); | ||
589 | break; | ||
590 | |||
591 | case VIO_DRING_REG: | ||
592 | err = process_dreg(vio, pkt); | ||
593 | break; | ||
594 | |||
595 | case VIO_DRING_UNREG: | ||
596 | err = process_dunreg(vio, pkt); | ||
597 | break; | ||
598 | |||
599 | case VIO_RDX: | ||
600 | err = process_rdx(vio, pkt); | ||
601 | break; | ||
602 | |||
603 | default: | ||
604 | err = process_unknown(vio, pkt); | ||
605 | break; | ||
606 | } | ||
607 | if (!err && | ||
608 | vio->hs_state != prev_state && | ||
609 | (vio->hs_state & VIO_HS_COMPLETE)) | ||
610 | vio->ops->handshake_complete(vio); | ||
611 | |||
612 | return err; | ||
613 | } | ||
614 | EXPORT_SYMBOL(vio_control_pkt_engine); | ||
615 | |||
616 | void vio_conn_reset(struct vio_driver_state *vio) | ||
617 | { | ||
618 | } | ||
619 | EXPORT_SYMBOL(vio_conn_reset); | ||
620 | |||
621 | /* The issue is that the Solaris virtual disk server just mirrors the | ||
622 | * SID values it gets from the client peer. So we work around that | ||
623 | * here in vio_{validate,send}_sid() so that the drivers don't need | ||
624 | * to be aware of this crap. | ||
625 | */ | ||
626 | int vio_validate_sid(struct vio_driver_state *vio, struct vio_msg_tag *tp) | ||
627 | { | ||
628 | u32 sid; | ||
629 | |||
630 | /* Always let VERSION+INFO packets through unchecked, they | ||
631 | * define the new SID. | ||
632 | */ | ||
633 | if (tp->type == VIO_TYPE_CTRL && | ||
634 | tp->stype == VIO_SUBTYPE_INFO && | ||
635 | tp->stype_env == VIO_VER_INFO) | ||
636 | return 0; | ||
637 | |||
638 | /* Ok, now figure out which SID to use. */ | ||
639 | switch (vio->dev_class) { | ||
640 | case VDEV_NETWORK: | ||
641 | case VDEV_NETWORK_SWITCH: | ||
642 | case VDEV_DISK_SERVER: | ||
643 | default: | ||
644 | sid = vio->_peer_sid; | ||
645 | break; | ||
646 | |||
647 | case VDEV_DISK: | ||
648 | sid = vio->_local_sid; | ||
649 | break; | ||
650 | } | ||
651 | |||
652 | if (sid == tp->sid) | ||
653 | return 0; | ||
654 | viodbg(DATA, "BAD SID tag->sid[%08x] peer_sid[%08x] local_sid[%08x]\n", | ||
655 | tp->sid, vio->_peer_sid, vio->_local_sid); | ||
656 | return -EINVAL; | ||
657 | } | ||
658 | EXPORT_SYMBOL(vio_validate_sid); | ||
659 | |||
660 | u32 vio_send_sid(struct vio_driver_state *vio) | ||
661 | { | ||
662 | switch (vio->dev_class) { | ||
663 | case VDEV_NETWORK: | ||
664 | case VDEV_NETWORK_SWITCH: | ||
665 | case VDEV_DISK: | ||
666 | default: | ||
667 | return vio->_local_sid; | ||
668 | |||
669 | case VDEV_DISK_SERVER: | ||
670 | return vio->_peer_sid; | ||
671 | } | ||
672 | } | ||
673 | EXPORT_SYMBOL(vio_send_sid); | ||
674 | |||
675 | extern int vio_ldc_alloc(struct vio_driver_state *vio, | ||
676 | struct ldc_channel_config *base_cfg, | ||
677 | void *event_arg) | ||
678 | { | ||
679 | struct ldc_channel_config cfg = *base_cfg; | ||
680 | struct ldc_channel *lp; | ||
681 | |||
682 | cfg.tx_irq = vio->vdev->tx_irq; | ||
683 | cfg.rx_irq = vio->vdev->rx_irq; | ||
684 | |||
685 | lp = ldc_alloc(vio->vdev->channel_id, &cfg, event_arg); | ||
686 | if (IS_ERR(lp)) | ||
687 | return PTR_ERR(lp); | ||
688 | |||
689 | vio->lp = lp; | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | EXPORT_SYMBOL(vio_ldc_alloc); | ||
694 | |||
695 | void vio_ldc_free(struct vio_driver_state *vio) | ||
696 | { | ||
697 | ldc_free(vio->lp); | ||
698 | vio->lp = NULL; | ||
699 | |||
700 | kfree(vio->desc_buf); | ||
701 | vio->desc_buf = NULL; | ||
702 | vio->desc_buf_len = 0; | ||
703 | } | ||
704 | EXPORT_SYMBOL(vio_ldc_free); | ||
705 | |||
706 | void vio_port_up(struct vio_driver_state *vio) | ||
707 | { | ||
708 | unsigned long flags; | ||
709 | int err, state; | ||
710 | |||
711 | spin_lock_irqsave(&vio->lock, flags); | ||
712 | |||
713 | state = ldc_state(vio->lp); | ||
714 | |||
715 | err = 0; | ||
716 | if (state == LDC_STATE_INIT) { | ||
717 | err = ldc_bind(vio->lp, vio->name); | ||
718 | if (err) | ||
719 | printk(KERN_WARNING "%s: Port %lu bind failed, " | ||
720 | "err=%d\n", | ||
721 | vio->name, vio->vdev->channel_id, err); | ||
722 | } | ||
723 | |||
724 | if (!err) { | ||
725 | err = ldc_connect(vio->lp); | ||
726 | if (err) | ||
727 | printk(KERN_WARNING "%s: Port %lu connect failed, " | ||
728 | "err=%d\n", | ||
729 | vio->name, vio->vdev->channel_id, err); | ||
730 | } | ||
731 | if (err) { | ||
732 | unsigned long expires = jiffies + HZ; | ||
733 | |||
734 | expires = round_jiffies(expires); | ||
735 | mod_timer(&vio->timer, expires); | ||
736 | } | ||
737 | |||
738 | spin_unlock_irqrestore(&vio->lock, flags); | ||
739 | } | ||
740 | EXPORT_SYMBOL(vio_port_up); | ||
741 | |||
742 | static void vio_port_timer(unsigned long _arg) | ||
743 | { | ||
744 | struct vio_driver_state *vio = (struct vio_driver_state *) _arg; | ||
745 | |||
746 | vio_port_up(vio); | ||
747 | } | ||
748 | |||
749 | int vio_driver_init(struct vio_driver_state *vio, struct vio_dev *vdev, | ||
750 | u8 dev_class, struct vio_version *ver_table, | ||
751 | int ver_table_size, struct vio_driver_ops *ops, | ||
752 | char *name) | ||
753 | { | ||
754 | switch (dev_class) { | ||
755 | case VDEV_NETWORK: | ||
756 | case VDEV_NETWORK_SWITCH: | ||
757 | case VDEV_DISK: | ||
758 | case VDEV_DISK_SERVER: | ||
759 | break; | ||
760 | |||
761 | default: | ||
762 | return -EINVAL; | ||
763 | } | ||
764 | |||
765 | if (!ops->send_attr || | ||
766 | !ops->handle_attr || | ||
767 | !ops->handshake_complete) | ||
768 | return -EINVAL; | ||
769 | |||
770 | if (!ver_table || ver_table_size < 0) | ||
771 | return -EINVAL; | ||
772 | |||
773 | if (!name) | ||
774 | return -EINVAL; | ||
775 | |||
776 | spin_lock_init(&vio->lock); | ||
777 | |||
778 | vio->name = name; | ||
779 | |||
780 | vio->dev_class = dev_class; | ||
781 | vio->vdev = vdev; | ||
782 | |||
783 | vio->ver_table = ver_table; | ||
784 | vio->ver_table_entries = ver_table_size; | ||
785 | |||
786 | vio->ops = ops; | ||
787 | |||
788 | setup_timer(&vio->timer, vio_port_timer, (unsigned long) vio); | ||
789 | |||
790 | return 0; | ||
791 | } | ||
792 | EXPORT_SYMBOL(vio_driver_init); | ||