diff options
Diffstat (limited to 'drivers/net/wireless/hostap/hostap_cs.c')
-rw-r--r-- | drivers/net/wireless/hostap/hostap_cs.c | 120 |
1 files changed, 81 insertions, 39 deletions
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index 3210c99694e6..70242459d57a 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c | |||
@@ -40,6 +40,14 @@ module_param(ignore_cis_vcc, int, 0444); | |||
40 | MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); | 40 | MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); |
41 | 41 | ||
42 | 42 | ||
43 | /* struct local_info::hw_priv */ | ||
44 | struct hostap_cs_priv { | ||
45 | dev_node_t node; | ||
46 | dev_link_t *link; | ||
47 | int sandisk_connectplus; | ||
48 | }; | ||
49 | |||
50 | |||
43 | #ifdef PRISM2_IO_DEBUG | 51 | #ifdef PRISM2_IO_DEBUG |
44 | 52 | ||
45 | static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) | 53 | static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) |
@@ -203,8 +211,9 @@ static int prism2_event(event_t event, int priority, | |||
203 | 211 | ||
204 | static int prism2_pccard_card_present(local_info_t *local) | 212 | static int prism2_pccard_card_present(local_info_t *local) |
205 | { | 213 | { |
206 | if (local->link != NULL && | 214 | struct hostap_cs_priv *hw_priv = local->hw_priv; |
207 | ((local->link->state & (DEV_PRESENT | DEV_CONFIG)) == | 215 | if (hw_priv->link != NULL && |
216 | ((hw_priv->link->state & (DEV_PRESENT | DEV_CONFIG)) == | ||
208 | (DEV_PRESENT | DEV_CONFIG))) | 217 | (DEV_PRESENT | DEV_CONFIG))) |
209 | return 1; | 218 | return 1; |
210 | return 0; | 219 | return 0; |
@@ -224,12 +233,14 @@ static void sandisk_set_iobase(local_info_t *local) | |||
224 | { | 233 | { |
225 | int res; | 234 | int res; |
226 | conf_reg_t reg; | 235 | conf_reg_t reg; |
236 | struct hostap_cs_priv *hw_priv = local->hw_priv; | ||
227 | 237 | ||
228 | reg.Function = 0; | 238 | reg.Function = 0; |
229 | reg.Action = CS_WRITE; | 239 | reg.Action = CS_WRITE; |
230 | reg.Offset = 0x10; /* 0x3f0 IO base 1 */ | 240 | reg.Offset = 0x10; /* 0x3f0 IO base 1 */ |
231 | reg.Value = local->link->io.BasePort1 & 0x00ff; | 241 | reg.Value = hw_priv->link->io.BasePort1 & 0x00ff; |
232 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 242 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
243 | ®); | ||
233 | if (res != CS_SUCCESS) { | 244 | if (res != CS_SUCCESS) { |
234 | printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -" | 245 | printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -" |
235 | " res=%d\n", res); | 246 | " res=%d\n", res); |
@@ -239,8 +250,9 @@ static void sandisk_set_iobase(local_info_t *local) | |||
239 | reg.Function = 0; | 250 | reg.Function = 0; |
240 | reg.Action = CS_WRITE; | 251 | reg.Action = CS_WRITE; |
241 | reg.Offset = 0x12; /* 0x3f2 IO base 2 */ | 252 | reg.Offset = 0x12; /* 0x3f2 IO base 2 */ |
242 | reg.Value = (local->link->io.BasePort1 & 0xff00) >> 8; | 253 | reg.Value = (hw_priv->link->io.BasePort1 & 0xff00) >> 8; |
243 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 254 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
255 | ®); | ||
244 | if (res != CS_SUCCESS) { | 256 | if (res != CS_SUCCESS) { |
245 | printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -" | 257 | printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -" |
246 | " res=%d\n", res); | 258 | " res=%d\n", res); |
@@ -272,8 +284,9 @@ static int sandisk_enable_wireless(struct net_device *dev) | |||
272 | tuple_t tuple; | 284 | tuple_t tuple; |
273 | cisparse_t *parse = NULL; | 285 | cisparse_t *parse = NULL; |
274 | u_char buf[64]; | 286 | u_char buf[64]; |
287 | struct hostap_cs_priv *hw_priv = local->hw_priv; | ||
275 | 288 | ||
276 | if (local->link->io.NumPorts1 < 0x42) { | 289 | if (hw_priv->link->io.NumPorts1 < 0x42) { |
277 | /* Not enough ports to be SanDisk multi-function card */ | 290 | /* Not enough ports to be SanDisk multi-function card */ |
278 | ret = -ENODEV; | 291 | ret = -ENODEV; |
279 | goto done; | 292 | goto done; |
@@ -290,9 +303,9 @@ static int sandisk_enable_wireless(struct net_device *dev) | |||
290 | tuple.TupleData = buf; | 303 | tuple.TupleData = buf; |
291 | tuple.TupleDataMax = sizeof(buf); | 304 | tuple.TupleDataMax = sizeof(buf); |
292 | tuple.TupleOffset = 0; | 305 | tuple.TupleOffset = 0; |
293 | if (pcmcia_get_first_tuple(local->link->handle, &tuple) || | 306 | if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) || |
294 | pcmcia_get_tuple_data(local->link->handle, &tuple) || | 307 | pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) || |
295 | pcmcia_parse_tuple(local->link->handle, &tuple, parse) || | 308 | pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) || |
296 | parse->manfid.manf != 0xd601 || parse->manfid.card != 0x0101) { | 309 | parse->manfid.manf != 0xd601 || parse->manfid.card != 0x0101) { |
297 | /* No SanDisk manfid found */ | 310 | /* No SanDisk manfid found */ |
298 | ret = -ENODEV; | 311 | ret = -ENODEV; |
@@ -300,9 +313,9 @@ static int sandisk_enable_wireless(struct net_device *dev) | |||
300 | } | 313 | } |
301 | 314 | ||
302 | tuple.DesiredTuple = CISTPL_LONGLINK_MFC; | 315 | tuple.DesiredTuple = CISTPL_LONGLINK_MFC; |
303 | if (pcmcia_get_first_tuple(local->link->handle, &tuple) || | 316 | if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) || |
304 | pcmcia_get_tuple_data(local->link->handle, &tuple) || | 317 | pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) || |
305 | pcmcia_parse_tuple(local->link->handle, &tuple, parse) || | 318 | pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) || |
306 | parse->longlink_mfc.nfn < 2) { | 319 | parse->longlink_mfc.nfn < 2) { |
307 | /* No multi-function links found */ | 320 | /* No multi-function links found */ |
308 | ret = -ENODEV; | 321 | ret = -ENODEV; |
@@ -311,13 +324,14 @@ static int sandisk_enable_wireless(struct net_device *dev) | |||
311 | 324 | ||
312 | printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected" | 325 | printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected" |
313 | " - using vendor-specific initialization\n", dev->name); | 326 | " - using vendor-specific initialization\n", dev->name); |
314 | local->sandisk_connectplus = 1; | 327 | hw_priv->sandisk_connectplus = 1; |
315 | 328 | ||
316 | reg.Function = 0; | 329 | reg.Function = 0; |
317 | reg.Action = CS_WRITE; | 330 | reg.Action = CS_WRITE; |
318 | reg.Offset = CISREG_COR; | 331 | reg.Offset = CISREG_COR; |
319 | reg.Value = COR_SOFT_RESET; | 332 | reg.Value = COR_SOFT_RESET; |
320 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 333 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
334 | ®); | ||
321 | if (res != CS_SUCCESS) { | 335 | if (res != CS_SUCCESS) { |
322 | printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", | 336 | printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", |
323 | dev->name, res); | 337 | dev->name, res); |
@@ -333,7 +347,8 @@ static int sandisk_enable_wireless(struct net_device *dev) | |||
333 | * will be enabled during the first cor_sreset call. | 347 | * will be enabled during the first cor_sreset call. |
334 | */ | 348 | */ |
335 | reg.Value = COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | COR_FUNC_ENA; | 349 | reg.Value = COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | COR_FUNC_ENA; |
336 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 350 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
351 | ®); | ||
337 | if (res != CS_SUCCESS) { | 352 | if (res != CS_SUCCESS) { |
338 | printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", | 353 | printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", |
339 | dev->name, res); | 354 | dev->name, res); |
@@ -358,6 +373,7 @@ static void prism2_pccard_cor_sreset(local_info_t *local) | |||
358 | { | 373 | { |
359 | int res; | 374 | int res; |
360 | conf_reg_t reg; | 375 | conf_reg_t reg; |
376 | struct hostap_cs_priv *hw_priv = local->hw_priv; | ||
361 | 377 | ||
362 | if (!prism2_pccard_card_present(local)) | 378 | if (!prism2_pccard_card_present(local)) |
363 | return; | 379 | return; |
@@ -366,7 +382,8 @@ static void prism2_pccard_cor_sreset(local_info_t *local) | |||
366 | reg.Action = CS_READ; | 382 | reg.Action = CS_READ; |
367 | reg.Offset = CISREG_COR; | 383 | reg.Offset = CISREG_COR; |
368 | reg.Value = 0; | 384 | reg.Value = 0; |
369 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 385 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
386 | ®); | ||
370 | if (res != CS_SUCCESS) { | 387 | if (res != CS_SUCCESS) { |
371 | printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n", | 388 | printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n", |
372 | res); | 389 | res); |
@@ -377,28 +394,30 @@ static void prism2_pccard_cor_sreset(local_info_t *local) | |||
377 | 394 | ||
378 | reg.Action = CS_WRITE; | 395 | reg.Action = CS_WRITE; |
379 | reg.Value |= COR_SOFT_RESET; | 396 | reg.Value |= COR_SOFT_RESET; |
380 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 397 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
398 | ®); | ||
381 | if (res != CS_SUCCESS) { | 399 | if (res != CS_SUCCESS) { |
382 | printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n", | 400 | printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n", |
383 | res); | 401 | res); |
384 | return; | 402 | return; |
385 | } | 403 | } |
386 | 404 | ||
387 | mdelay(local->sandisk_connectplus ? 5 : 2); | 405 | mdelay(hw_priv->sandisk_connectplus ? 5 : 2); |
388 | 406 | ||
389 | reg.Value &= ~COR_SOFT_RESET; | 407 | reg.Value &= ~COR_SOFT_RESET; |
390 | if (local->sandisk_connectplus) | 408 | if (hw_priv->sandisk_connectplus) |
391 | reg.Value |= COR_IREQ_ENA; | 409 | reg.Value |= COR_IREQ_ENA; |
392 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 410 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
411 | ®); | ||
393 | if (res != CS_SUCCESS) { | 412 | if (res != CS_SUCCESS) { |
394 | printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n", | 413 | printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n", |
395 | res); | 414 | res); |
396 | return; | 415 | return; |
397 | } | 416 | } |
398 | 417 | ||
399 | mdelay(local->sandisk_connectplus ? 5 : 2); | 418 | mdelay(hw_priv->sandisk_connectplus ? 5 : 2); |
400 | 419 | ||
401 | if (local->sandisk_connectplus) | 420 | if (hw_priv->sandisk_connectplus) |
402 | sandisk_set_iobase(local); | 421 | sandisk_set_iobase(local); |
403 | } | 422 | } |
404 | 423 | ||
@@ -408,11 +427,12 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) | |||
408 | int res; | 427 | int res; |
409 | conf_reg_t reg; | 428 | conf_reg_t reg; |
410 | int old_cor; | 429 | int old_cor; |
430 | struct hostap_cs_priv *hw_priv = local->hw_priv; | ||
411 | 431 | ||
412 | if (!prism2_pccard_card_present(local)) | 432 | if (!prism2_pccard_card_present(local)) |
413 | return; | 433 | return; |
414 | 434 | ||
415 | if (local->sandisk_connectplus) { | 435 | if (hw_priv->sandisk_connectplus) { |
416 | sandisk_write_hcr(local, hcr); | 436 | sandisk_write_hcr(local, hcr); |
417 | return; | 437 | return; |
418 | } | 438 | } |
@@ -421,7 +441,8 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) | |||
421 | reg.Action = CS_READ; | 441 | reg.Action = CS_READ; |
422 | reg.Offset = CISREG_COR; | 442 | reg.Offset = CISREG_COR; |
423 | reg.Value = 0; | 443 | reg.Value = 0; |
424 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 444 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
445 | ®); | ||
425 | if (res != CS_SUCCESS) { | 446 | if (res != CS_SUCCESS) { |
426 | printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 " | 447 | printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 " |
427 | "(%d)\n", res); | 448 | "(%d)\n", res); |
@@ -433,7 +454,8 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) | |||
433 | 454 | ||
434 | reg.Action = CS_WRITE; | 455 | reg.Action = CS_WRITE; |
435 | reg.Value |= COR_SOFT_RESET; | 456 | reg.Value |= COR_SOFT_RESET; |
436 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 457 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
458 | ®); | ||
437 | if (res != CS_SUCCESS) { | 459 | if (res != CS_SUCCESS) { |
438 | printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 " | 460 | printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 " |
439 | "(%d)\n", res); | 461 | "(%d)\n", res); |
@@ -446,7 +468,8 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) | |||
446 | reg.Action = CS_WRITE; | 468 | reg.Action = CS_WRITE; |
447 | reg.Value = hcr; | 469 | reg.Value = hcr; |
448 | reg.Offset = CISREG_CCSR; | 470 | reg.Offset = CISREG_CCSR; |
449 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 471 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
472 | ®); | ||
450 | if (res != CS_SUCCESS) { | 473 | if (res != CS_SUCCESS) { |
451 | printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 " | 474 | printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 " |
452 | "(%d)\n", res); | 475 | "(%d)\n", res); |
@@ -457,7 +480,8 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) | |||
457 | reg.Action = CS_WRITE; | 480 | reg.Action = CS_WRITE; |
458 | reg.Offset = CISREG_COR; | 481 | reg.Offset = CISREG_COR; |
459 | reg.Value = old_cor & ~COR_SOFT_RESET; | 482 | reg.Value = old_cor & ~COR_SOFT_RESET; |
460 | res = pcmcia_access_configuration_register(local->link->handle, ®); | 483 | res = pcmcia_access_configuration_register(hw_priv->link->handle, |
484 | ®); | ||
461 | if (res != CS_SUCCESS) { | 485 | if (res != CS_SUCCESS) { |
462 | printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 " | 486 | printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 " |
463 | "(%d)\n", res); | 487 | "(%d)\n", res); |
@@ -470,23 +494,29 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) | |||
470 | 494 | ||
471 | static int prism2_pccard_dev_open(local_info_t *local) | 495 | static int prism2_pccard_dev_open(local_info_t *local) |
472 | { | 496 | { |
473 | local->link->open++; | 497 | struct hostap_cs_priv *hw_priv = local->hw_priv; |
498 | hw_priv->link->open++; | ||
474 | return 0; | 499 | return 0; |
475 | } | 500 | } |
476 | 501 | ||
477 | 502 | ||
478 | static int prism2_pccard_dev_close(local_info_t *local) | 503 | static int prism2_pccard_dev_close(local_info_t *local) |
479 | { | 504 | { |
480 | if (local == NULL || local->link == NULL) | 505 | struct hostap_cs_priv *hw_priv; |
506 | |||
507 | if (local == NULL || local->hw_priv == NULL) | ||
508 | return 1; | ||
509 | hw_priv = local->hw_priv; | ||
510 | if (hw_priv->link == NULL) | ||
481 | return 1; | 511 | return 1; |
482 | 512 | ||
483 | if (!local->link->open) { | 513 | if (!hw_priv->link->open) { |
484 | printk(KERN_WARNING "%s: prism2_pccard_dev_close(): " | 514 | printk(KERN_WARNING "%s: prism2_pccard_dev_close(): " |
485 | "link not open?!\n", local->dev->name); | 515 | "link not open?!\n", local->dev->name); |
486 | return 1; | 516 | return 1; |
487 | } | 517 | } |
488 | 518 | ||
489 | local->link->open--; | 519 | hw_priv->link->open--; |
490 | 520 | ||
491 | return 0; | 521 | return 0; |
492 | } | 522 | } |
@@ -567,8 +597,13 @@ static void prism2_detach(dev_link_t *link) | |||
567 | *linkp = link->next; | 597 | *linkp = link->next; |
568 | /* release net devices */ | 598 | /* release net devices */ |
569 | if (link->priv) { | 599 | if (link->priv) { |
570 | prism2_free_local_data((struct net_device *) link->priv); | 600 | struct net_device *dev; |
571 | 601 | struct hostap_interface *iface; | |
602 | dev = link->priv; | ||
603 | iface = netdev_priv(dev); | ||
604 | kfree(iface->local->hw_priv); | ||
605 | iface->local->hw_priv = NULL; | ||
606 | prism2_free_local_data(dev); | ||
572 | } | 607 | } |
573 | kfree(link); | 608 | kfree(link); |
574 | } | 609 | } |
@@ -601,14 +636,19 @@ static int prism2_config(dev_link_t *link) | |||
601 | u_char buf[64]; | 636 | u_char buf[64]; |
602 | config_info_t conf; | 637 | config_info_t conf; |
603 | cistpl_cftable_entry_t dflt = { 0 }; | 638 | cistpl_cftable_entry_t dflt = { 0 }; |
639 | struct hostap_cs_priv *hw_priv; | ||
604 | 640 | ||
605 | PDEBUG(DEBUG_FLOW, "prism2_config()\n"); | 641 | PDEBUG(DEBUG_FLOW, "prism2_config()\n"); |
606 | 642 | ||
607 | parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL); | 643 | parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL); |
608 | if (parse == NULL) { | 644 | hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL); |
645 | if (parse == NULL || hw_priv == NULL) { | ||
646 | kfree(parse); | ||
647 | kfree(hw_priv); | ||
609 | ret = -ENOMEM; | 648 | ret = -ENOMEM; |
610 | goto failed; | 649 | goto failed; |
611 | } | 650 | } |
651 | memset(hw_priv, 0, sizeof(*hw_priv)); | ||
612 | 652 | ||
613 | tuple.DesiredTuple = CISTPL_CONFIG; | 653 | tuple.DesiredTuple = CISTPL_CONFIG; |
614 | tuple.Attributes = 0; | 654 | tuple.Attributes = 0; |
@@ -779,9 +819,10 @@ static int prism2_config(dev_link_t *link) | |||
779 | 819 | ||
780 | iface = netdev_priv(dev); | 820 | iface = netdev_priv(dev); |
781 | local = iface->local; | 821 | local = iface->local; |
782 | local->link = link; | 822 | local->hw_priv = hw_priv; |
783 | strcpy(local->node.dev_name, dev->name); | 823 | hw_priv->link = link; |
784 | link->dev = &local->node; | 824 | strcpy(hw_priv->node.dev_name, dev->name); |
825 | link->dev = &hw_priv->node; | ||
785 | 826 | ||
786 | local->shutdown = 0; | 827 | local->shutdown = 0; |
787 | 828 | ||
@@ -791,7 +832,7 @@ static int prism2_config(dev_link_t *link) | |||
791 | if (!ret) { | 832 | if (!ret) { |
792 | ret = hostap_hw_ready(dev); | 833 | ret = hostap_hw_ready(dev); |
793 | if (ret == 0 && local->ddev) | 834 | if (ret == 0 && local->ddev) |
794 | strcpy(local->node.dev_name, local->ddev->name); | 835 | strcpy(hw_priv->node.dev_name, local->ddev->name); |
795 | } | 836 | } |
796 | kfree(parse); | 837 | kfree(parse); |
797 | return ret; | 838 | return ret; |
@@ -801,6 +842,7 @@ static int prism2_config(dev_link_t *link) | |||
801 | 842 | ||
802 | failed: | 843 | failed: |
803 | kfree(parse); | 844 | kfree(parse); |
845 | kfree(hw_priv); | ||
804 | prism2_release((u_long)link); | 846 | prism2_release((u_long)link); |
805 | return ret; | 847 | return ret; |
806 | } | 848 | } |