linux-old/drivers/ieee1394/highlevel.c
<<
>>
Prefs
   1/*
   2 * IEEE 1394 for Linux
   3 *
   4 * Copyright (C) 1999 Andreas E. Bombe
   5 *
   6 * This code is licensed under the GPL.  See the file COPYING in the root
   7 * directory of the kernel sources for details.
   8 *
   9 *
  10 * Contributions:
  11 *
  12 * Christian Toegel <christian.toegel@gmx.at>
  13 *        unregister address space
  14 *
  15 * Manfred Weihs <weihs@ict.tuwien.ac.at>
  16 *        unregister address space
  17 *
  18 */
  19
  20#include <linux/config.h>
  21#include <linux/slab.h>
  22#include <linux/list.h>
  23
  24#include "ieee1394.h"
  25#include "ieee1394_types.h"
  26#include "hosts.h"
  27#include "ieee1394_core.h"
  28#include "highlevel.h"
  29
  30
  31struct hl_host_info {
  32        struct list_head list;
  33        struct hpsb_host *host;
  34        size_t size;
  35        unsigned long key;
  36        void *data;
  37};
  38
  39
  40static LIST_HEAD(hl_drivers);
  41static rwlock_t hl_drivers_lock = RW_LOCK_UNLOCKED;
  42
  43static LIST_HEAD(addr_space);
  44static rwlock_t addr_space_lock = RW_LOCK_UNLOCKED;
  45
  46/* addr_space list will have zero and max already included as bounds */
  47static struct hpsb_address_ops dummy_ops = { NULL, NULL, NULL, NULL };
  48static struct hpsb_address_serve dummy_zero_addr, dummy_max_addr;
  49
  50
  51static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl,
  52                                              struct hpsb_host *host)
  53{
  54        struct hl_host_info *hi = NULL;
  55        struct list_head *lh;
  56
  57        if (!hl || !host)
  58                return NULL;
  59
  60        read_lock(&hl->host_info_lock);
  61        list_for_each (lh, &hl->host_info_list) {
  62                hi = list_entry(lh, struct hl_host_info, list);
  63                if (hi->host == host)
  64                        break;
  65                hi = NULL;
  66        }
  67        read_unlock(&hl->host_info_lock);
  68
  69        return hi;
  70}
  71
  72
  73/* Returns a per host/driver data structure that was previously stored by
  74 * hpsb_create_hostinfo. */
  75void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
  76{
  77        struct hl_host_info *hi = hl_get_hostinfo(hl, host);
  78
  79        if (hi)
  80                return hi->data;
  81
  82        return NULL;
  83}
  84
  85
  86/* If size is zero, then the return here is only valid for error checking */
  87void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
  88                           size_t data_size)
  89{
  90        struct hl_host_info *hi;
  91        void *data;
  92        unsigned long flags;
  93
  94        hi = hl_get_hostinfo(hl, host);
  95        if (hi) {
  96                HPSB_ERR("%s called hpsb_create_hostinfo when hostinfo already exists",
  97                         hl->name);
  98                return NULL;
  99        }
 100
 101        hi = kmalloc(sizeof(*hi) + data_size, GFP_KERNEL);
 102        if (!hi)
 103                return NULL;
 104
 105        memset(hi, 0, sizeof(*hi) + data_size);
 106
 107        if (data_size) {
 108                data = hi->data = hi + 1;
 109                hi->size = data_size;
 110        } else
 111                data = hi;
 112
 113        hi->host = host;
 114
 115        write_lock_irqsave(&hl->host_info_lock, flags);
 116        list_add_tail(&hi->list, &hl->host_info_list);
 117        write_unlock_irqrestore(&hl->host_info_lock, flags);
 118
 119        return data;
 120}
 121
 122
 123int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
 124                      void *data)
 125{
 126        struct hl_host_info *hi;
 127
 128        hi = hl_get_hostinfo(hl, host);
 129        if (hi) {
 130                if (!hi->size && !hi->data) {
 131                        hi->data = data;
 132                        return 0;
 133                } else
 134                        HPSB_ERR("%s called hpsb_set_hostinfo when hostinfo already has data",
 135                                 hl->name);
 136        } else
 137                HPSB_ERR("%s called hpsb_set_hostinfo when no hostinfo exists",
 138                         hl->name);
 139
 140        return -EINVAL;
 141}
 142
 143
 144void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
 145{
 146        struct hl_host_info *hi;
 147
 148        hi = hl_get_hostinfo(hl, host);
 149        if (hi) {
 150                unsigned long flags;
 151                write_lock_irqsave(&hl->host_info_lock, flags);
 152                list_del(&hi->list);
 153                write_unlock_irqrestore(&hl->host_info_lock, flags);
 154                kfree(hi);
 155        }
 156
 157        return;
 158}
 159
 160
 161void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key)
 162{
 163        struct hl_host_info *hi;
 164
 165        hi = hl_get_hostinfo(hl, host);
 166        if (hi)
 167                hi->key = key;
 168
 169        return;
 170}
 171
 172
 173unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host)
 174{
 175        struct hl_host_info *hi;
 176
 177        hi = hl_get_hostinfo(hl, host);
 178        if (hi)
 179                return hi->key;
 180
 181        return 0;
 182}
 183
 184
 185void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key)
 186{
 187        struct list_head *lh;
 188        struct hl_host_info *hi;
 189        void *data = NULL;
 190
 191        if (!hl)
 192                return NULL;
 193
 194        read_lock(&hl->host_info_lock);
 195        list_for_each (lh, &hl->host_info_list) {
 196                hi = list_entry(lh, struct hl_host_info, list);
 197                if (hi->key == key) {
 198                        data = hi->data;
 199                        break;
 200                }
 201        }
 202        read_unlock(&hl->host_info_lock);
 203
 204        return data;
 205}
 206
 207
 208struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl, unsigned long key)
 209{
 210        struct list_head *lh;
 211        struct hl_host_info *hi;
 212        struct hpsb_host *host = NULL;
 213
 214        if (!hl)
 215                return NULL;
 216
 217        read_lock(&hl->host_info_lock);
 218        list_for_each (lh, &hl->host_info_list) {
 219                hi = list_entry(lh, struct hl_host_info, list);
 220                if (hi->key == key) {
 221                        host = hi->host;
 222                        break;
 223                }
 224        }
 225        read_unlock(&hl->host_info_lock);
 226
 227        return host;
 228}
 229
 230
 231void hpsb_register_highlevel(struct hpsb_highlevel *hl)
 232{
 233        struct list_head *lh;
 234        unsigned long flags;
 235
 236        INIT_LIST_HEAD(&hl->addr_list);
 237        INIT_LIST_HEAD(&hl->host_info_list);
 238
 239        rwlock_init(&hl->host_info_lock);
 240
 241        write_lock_irqsave(&hl_drivers_lock, flags);
 242        list_add_tail(&hl->hl_list, &hl_drivers);
 243        write_unlock_irqrestore(&hl_drivers_lock, flags);
 244
 245        if (hl->add_host) {
 246                down(&hpsb_hosts_lock);
 247                list_for_each (lh, &hpsb_hosts) {
 248                        struct hpsb_host *host = list_entry(lh, struct hpsb_host, host_list);
 249                        hl->add_host(host);
 250                }
 251                up(&hpsb_hosts_lock);
 252        }
 253
 254        return;
 255}
 256
 257void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
 258{
 259        struct list_head *lh, *next;
 260        struct hpsb_address_serve *as;
 261        unsigned long flags;
 262
 263        write_lock_irqsave(&addr_space_lock, flags);
 264        list_for_each_safe (lh, next, &hl->addr_list) {
 265                as = list_entry(lh, struct hpsb_address_serve, addr_list);
 266                list_del(&as->as_list);
 267                kfree(as);
 268        }
 269        write_unlock_irqrestore(&addr_space_lock, flags);
 270
 271        write_lock_irqsave(&hl_drivers_lock, flags);
 272        list_del(&hl->hl_list);
 273        write_unlock_irqrestore(&hl_drivers_lock, flags);
 274
 275        if (hl->remove_host) {
 276                down(&hpsb_hosts_lock);
 277                list_for_each(lh, &hpsb_hosts) {
 278                        struct hpsb_host *host = list_entry(lh, struct hpsb_host, host_list);
 279
 280                        hl->remove_host(host);
 281                        hpsb_destroy_hostinfo(hl, host);
 282                }
 283                up(&hpsb_hosts_lock);
 284        }
 285}
 286
 287int hpsb_register_addrspace(struct hpsb_highlevel *hl,
 288                            struct hpsb_address_ops *ops, u64 start, u64 end)
 289{
 290        struct hpsb_address_serve *as;
 291        struct list_head *entry;
 292        int retval = 0;
 293        unsigned long flags;
 294
 295        if (((start|end) & 3) || (start >= end) || (end > 0x1000000000000ULL)) {
 296                HPSB_ERR("%s called with invalid addresses", __FUNCTION__);
 297                return 0;
 298        }
 299
 300        as = (struct hpsb_address_serve *)
 301                kmalloc(sizeof(struct hpsb_address_serve), GFP_KERNEL);
 302        if (as == NULL) {
 303                return 0;
 304        }
 305
 306        INIT_LIST_HEAD(&as->as_list);
 307        INIT_LIST_HEAD(&as->addr_list);
 308        as->op = ops;
 309        as->start = start;
 310        as->end = end;
 311
 312        write_lock_irqsave(&addr_space_lock, flags);
 313        entry = addr_space.next;
 314
 315        while (list_entry(entry, struct hpsb_address_serve, as_list)->end
 316               <= start) {
 317                if (list_entry(entry->next, struct hpsb_address_serve, as_list)
 318                    ->start >= end) {
 319                        list_add(&as->as_list, entry);
 320                        list_add_tail(&as->addr_list, &hl->addr_list);
 321                        retval = 1;
 322                        break;
 323                }
 324                entry = entry->next;
 325        }
 326        write_unlock_irqrestore(&addr_space_lock, flags);
 327
 328        if (retval == 0) {
 329                kfree(as);
 330        }
 331
 332        return retval;
 333}
 334
 335int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, u64 start)
 336{
 337        int retval = 0;
 338        struct hpsb_address_serve *as;
 339        struct list_head *entry;
 340        unsigned long flags;
 341
 342        write_lock_irqsave(&addr_space_lock, flags);
 343
 344        entry = hl->addr_list.next;
 345
 346        while (entry != &hl->addr_list) {
 347                as = list_entry(entry, struct hpsb_address_serve, addr_list);
 348                entry = entry->next;
 349                if (as->start == start) {
 350                        list_del(&as->as_list);
 351                        list_del(&as->addr_list);
 352                        kfree(as);
 353                        retval = 1;
 354                        break;
 355                }
 356        }
 357
 358        write_unlock_irqrestore(&addr_space_lock, flags);
 359
 360        return retval;
 361}
 362
 363int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
 364                         unsigned int channel)
 365{
 366        if (channel > 63) {
 367                HPSB_ERR("%s called with invalid channel", __FUNCTION__);
 368                return -EINVAL;
 369        }
 370
 371        if (host->iso_listen_count[channel]++ == 0) {
 372                return host->driver->devctl(host, ISO_LISTEN_CHANNEL, channel);
 373        }
 374
 375        return 0;
 376}
 377
 378void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, 
 379                           unsigned int channel)
 380{
 381        if (channel > 63) {
 382                HPSB_ERR("%s called with invalid channel", __FUNCTION__);
 383                return;
 384        }
 385
 386        if (--host->iso_listen_count[channel] == 0) {
 387                host->driver->devctl(host, ISO_UNLISTEN_CHANNEL, channel);
 388        }
 389}
 390
 391
 392void highlevel_add_host(struct hpsb_host *host)
 393{
 394        struct list_head *entry;
 395        struct hpsb_highlevel *hl;
 396
 397        read_lock(&hl_drivers_lock);
 398        list_for_each(entry, &hl_drivers) {
 399                hl = list_entry(entry, struct hpsb_highlevel, hl_list);
 400                if (hl->add_host)
 401                        hl->add_host(host);
 402        }
 403        read_unlock(&hl_drivers_lock);
 404}
 405
 406void highlevel_remove_host(struct hpsb_host *host)
 407{
 408        struct list_head *entry;
 409        struct hpsb_highlevel *hl;
 410
 411        read_lock(&hl_drivers_lock);
 412        list_for_each(entry, &hl_drivers) {
 413                hl = list_entry(entry, struct hpsb_highlevel, hl_list);
 414
 415                if (hl->remove_host) {
 416                        hl->remove_host(host);
 417                        hpsb_destroy_hostinfo(hl, host);
 418                }
 419        }
 420        read_unlock(&hl_drivers_lock);
 421}
 422
 423void highlevel_host_reset(struct hpsb_host *host)
 424{
 425        struct list_head *entry;
 426        struct hpsb_highlevel *hl;
 427
 428        read_lock(&hl_drivers_lock);
 429        list_for_each(entry, &hl_drivers) {
 430                hl = list_entry(entry, struct hpsb_highlevel, hl_list);
 431
 432                if (hl->host_reset)
 433                        hl->host_reset(host);
 434        }
 435        read_unlock(&hl_drivers_lock);
 436}
 437
 438void highlevel_iso_receive(struct hpsb_host *host, void *data,
 439                           size_t length)
 440{
 441        struct list_head *entry;
 442        struct hpsb_highlevel *hl;
 443        int channel = (((quadlet_t *)data)[0] >> 8) & 0x3f;
 444
 445        read_lock(&hl_drivers_lock);
 446        entry = hl_drivers.next;
 447
 448        while (entry != &hl_drivers) {
 449                hl = list_entry(entry, struct hpsb_highlevel, hl_list);
 450                if (hl->iso_receive) {
 451                        hl->iso_receive(host, channel, data, length);
 452                }
 453                entry = entry->next;
 454        }
 455        read_unlock(&hl_drivers_lock);
 456}
 457
 458void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
 459                           void *data, size_t length)
 460{
 461        struct list_head *entry;
 462        struct hpsb_highlevel *hl;
 463        int cts = ((quadlet_t *)data)[0] >> 4;
 464
 465        read_lock(&hl_drivers_lock);
 466        entry = hl_drivers.next;
 467
 468        while (entry != &hl_drivers) {
 469                hl = list_entry(entry, struct hpsb_highlevel, hl_list);
 470                if (hl->fcp_request) {
 471                        hl->fcp_request(host, nodeid, direction, cts, data,
 472                                            length);
 473                }
 474                entry = entry->next;
 475        }
 476        read_unlock(&hl_drivers_lock);
 477}
 478
 479int highlevel_read(struct hpsb_host *host, int nodeid, void *data,
 480                   u64 addr, unsigned int length, u16 flags)
 481{
 482        struct hpsb_address_serve *as;
 483        struct list_head *entry;
 484        unsigned int partlength;
 485        int rcode = RCODE_ADDRESS_ERROR;
 486
 487        read_lock(&addr_space_lock);
 488
 489        entry = addr_space.next;
 490        as = list_entry(entry, struct hpsb_address_serve, as_list);
 491
 492        while (as->start <= addr) {
 493                if (as->end > addr) {
 494                        partlength = min(as->end - addr, (u64) length);
 495
 496                        if (as->op->read) {
 497                                rcode = as->op->read(host, nodeid, data,
 498                                                     addr, partlength, flags);
 499                        } else {
 500                                rcode = RCODE_TYPE_ERROR;
 501                        }
 502
 503                        (u8 *)data += partlength;
 504                        length -= partlength;
 505                        addr += partlength;
 506
 507                        if ((rcode != RCODE_COMPLETE) || !length) {
 508                                break;
 509                        }
 510                }
 511
 512                entry = entry->next;
 513                as = list_entry(entry, struct hpsb_address_serve, as_list);
 514        }
 515
 516        read_unlock(&addr_space_lock);
 517
 518        if (length && (rcode == RCODE_COMPLETE)) {
 519                rcode = RCODE_ADDRESS_ERROR;
 520        }
 521
 522        return rcode;
 523}
 524
 525int highlevel_write(struct hpsb_host *host, int nodeid, int destid,
 526                    void *data, u64 addr, unsigned int length, u16 flags)
 527{
 528        struct hpsb_address_serve *as;
 529        struct list_head *entry;
 530        unsigned int partlength;
 531        int rcode = RCODE_ADDRESS_ERROR;
 532
 533        read_lock(&addr_space_lock);
 534
 535        entry = addr_space.next;
 536        as = list_entry(entry, struct hpsb_address_serve, as_list);
 537
 538        while (as->start <= addr) {
 539                if (as->end > addr) {
 540                        partlength = min(as->end - addr, (u64) length);
 541
 542                        if (as->op->write) {
 543                                rcode = as->op->write(host, nodeid, destid,
 544                                                      data, addr, partlength, flags);
 545                        } else {
 546                                rcode = RCODE_TYPE_ERROR;
 547                        }
 548
 549                        (u8 *)data += partlength;
 550                        length -= partlength;
 551                        addr += partlength;
 552
 553                        if ((rcode != RCODE_COMPLETE) || !length) {
 554                                break;
 555                        }
 556                }
 557
 558                entry = entry->next;
 559                as = list_entry(entry, struct hpsb_address_serve, as_list);
 560        }
 561
 562        read_unlock(&addr_space_lock);
 563
 564        if (length && (rcode == RCODE_COMPLETE)) {
 565                rcode = RCODE_ADDRESS_ERROR;
 566        }
 567
 568        return rcode;
 569}
 570
 571
 572int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
 573                   u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags)
 574{
 575        struct hpsb_address_serve *as;
 576        struct list_head *entry;
 577        int rcode = RCODE_ADDRESS_ERROR;
 578
 579        read_lock(&addr_space_lock);
 580
 581        entry = addr_space.next;
 582        as = list_entry(entry, struct hpsb_address_serve, as_list);
 583
 584        while (as->start <= addr) {
 585                if (as->end > addr) {
 586                        if (as->op->lock) {
 587                                rcode = as->op->lock(host, nodeid, store, addr,
 588                                                     data, arg, ext_tcode, flags);
 589                        } else {
 590                                rcode = RCODE_TYPE_ERROR;
 591                        }
 592
 593                        break;
 594                }
 595
 596                entry = entry->next;
 597                as = list_entry(entry, struct hpsb_address_serve, as_list);
 598        }
 599
 600        read_unlock(&addr_space_lock);
 601
 602        return rcode;
 603}
 604
 605int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
 606                     u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags)
 607{
 608        struct hpsb_address_serve *as;
 609        struct list_head *entry;
 610        int rcode = RCODE_ADDRESS_ERROR;
 611
 612        read_lock(&addr_space_lock);
 613
 614        entry = addr_space.next;
 615        as = list_entry(entry, struct hpsb_address_serve, as_list);
 616
 617        while (as->start <= addr) {
 618                if (as->end > addr) {
 619                        if (as->op->lock64) {
 620                                rcode = as->op->lock64(host, nodeid, store,
 621                                                       addr, data, arg,
 622                                                       ext_tcode, flags);
 623                        } else {
 624                                rcode = RCODE_TYPE_ERROR;
 625                        }
 626
 627                        break;
 628                }
 629
 630                entry = entry->next;
 631                as = list_entry(entry, struct hpsb_address_serve, as_list);
 632        }
 633
 634        read_unlock(&addr_space_lock);
 635
 636        return rcode;
 637}
 638
 639void init_hpsb_highlevel(void)
 640{
 641        INIT_LIST_HEAD(&dummy_zero_addr.as_list);
 642        INIT_LIST_HEAD(&dummy_zero_addr.addr_list);
 643        INIT_LIST_HEAD(&dummy_max_addr.as_list);
 644        INIT_LIST_HEAD(&dummy_max_addr.addr_list);
 645
 646        dummy_zero_addr.op = dummy_max_addr.op = &dummy_ops;
 647
 648        dummy_zero_addr.start = dummy_zero_addr.end = 0;
 649        dummy_max_addr.start = dummy_max_addr.end = ((u64) 1) << 48;
 650
 651        list_add_tail(&dummy_zero_addr.as_list, &addr_space);
 652        list_add_tail(&dummy_max_addr.as_list, &addr_space);
 653}
 654
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.