linux/drivers/s390/cio/chsc_sch.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Driver for s390 chsc subchannels
   4 *
   5 * Copyright IBM Corp. 2008, 2011
   6 *
   7 * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
   8 *
   9 */
  10
  11#include <linux/slab.h>
  12#include <linux/compat.h>
  13#include <linux/device.h>
  14#include <linux/module.h>
  15#include <linux/uaccess.h>
  16#include <linux/miscdevice.h>
  17#include <linux/kernel_stat.h>
  18
  19#include <asm/cio.h>
  20#include <asm/chsc.h>
  21#include <asm/isc.h>
  22
  23#include "cio.h"
  24#include "cio_debug.h"
  25#include "css.h"
  26#include "chsc_sch.h"
  27#include "ioasm.h"
  28
  29static debug_info_t *chsc_debug_msg_id;
  30static debug_info_t *chsc_debug_log_id;
  31
  32static struct chsc_request *on_close_request;
  33static struct chsc_async_area *on_close_chsc_area;
  34static DEFINE_MUTEX(on_close_mutex);
  35
  36#define CHSC_MSG(imp, args...) do {                                     \
  37                debug_sprintf_event(chsc_debug_msg_id, imp , ##args);   \
  38        } while (0)
  39
  40#define CHSC_LOG(imp, txt) do {                                 \
  41                debug_text_event(chsc_debug_log_id, imp , txt); \
  42        } while (0)
  43
  44static void CHSC_LOG_HEX(int level, void *data, int length)
  45{
  46        debug_event(chsc_debug_log_id, level, data, length);
  47}
  48
  49MODULE_AUTHOR("IBM Corporation");
  50MODULE_DESCRIPTION("driver for s390 chsc subchannels");
  51MODULE_LICENSE("GPL");
  52
  53static void chsc_subchannel_irq(struct subchannel *sch)
  54{
  55        struct chsc_private *private = dev_get_drvdata(&sch->dev);
  56        struct chsc_request *request = private->request;
  57        struct irb *irb = this_cpu_ptr(&cio_irb);
  58
  59        CHSC_LOG(4, "irb");
  60        CHSC_LOG_HEX(4, irb, sizeof(*irb));
  61        inc_irq_stat(IRQIO_CSC);
  62
  63        /* Copy irb to provided request and set done. */
  64        if (!request) {
  65                CHSC_MSG(0, "Interrupt on sch 0.%x.%04x with no request\n",
  66                         sch->schid.ssid, sch->schid.sch_no);
  67                return;
  68        }
  69        private->request = NULL;
  70        memcpy(&request->irb, irb, sizeof(*irb));
  71        cio_update_schib(sch);
  72        complete(&request->completion);
  73        put_device(&sch->dev);
  74}
  75
  76static int chsc_subchannel_probe(struct subchannel *sch)
  77{
  78        struct chsc_private *private;
  79        int ret;
  80
  81        CHSC_MSG(6, "Detected chsc subchannel 0.%x.%04x\n",
  82                 sch->schid.ssid, sch->schid.sch_no);
  83        sch->isc = CHSC_SCH_ISC;
  84        private = kzalloc(sizeof(*private), GFP_KERNEL);
  85        if (!private)
  86                return -ENOMEM;
  87        dev_set_drvdata(&sch->dev, private);
  88        ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
  89        if (ret) {
  90                CHSC_MSG(0, "Failed to enable 0.%x.%04x: %d\n",
  91                         sch->schid.ssid, sch->schid.sch_no, ret);
  92                dev_set_drvdata(&sch->dev, NULL);
  93                kfree(private);
  94        }
  95        return ret;
  96}
  97
  98static void chsc_subchannel_remove(struct subchannel *sch)
  99{
 100        struct chsc_private *private;
 101
 102        cio_disable_subchannel(sch);
 103        private = dev_get_drvdata(&sch->dev);
 104        dev_set_drvdata(&sch->dev, NULL);
 105        if (private->request) {
 106                complete(&private->request->completion);
 107                put_device(&sch->dev);
 108        }
 109        kfree(private);
 110}
 111
 112static void chsc_subchannel_shutdown(struct subchannel *sch)
 113{
 114        cio_disable_subchannel(sch);
 115}
 116
 117static struct css_device_id chsc_subchannel_ids[] = {
 118        { .match_flags = 0x1, .type =SUBCHANNEL_TYPE_CHSC, },
 119        { /* end of list */ },
 120};
 121MODULE_DEVICE_TABLE(css, chsc_subchannel_ids);
 122
 123static struct css_driver chsc_subchannel_driver = {
 124        .drv = {
 125                .owner = THIS_MODULE,
 126                .name = "chsc_subchannel",
 127        },
 128        .subchannel_type = chsc_subchannel_ids,
 129        .irq = chsc_subchannel_irq,
 130        .probe = chsc_subchannel_probe,
 131        .remove = chsc_subchannel_remove,
 132        .shutdown = chsc_subchannel_shutdown,
 133};
 134
 135static int __init chsc_init_dbfs(void)
 136{
 137        chsc_debug_msg_id = debug_register("chsc_msg", 8, 1, 4 * sizeof(long));
 138        if (!chsc_debug_msg_id)
 139                goto out;
 140        debug_register_view(chsc_debug_msg_id, &debug_sprintf_view);
 141        debug_set_level(chsc_debug_msg_id, 2);
 142        chsc_debug_log_id = debug_register("chsc_log", 16, 1, 16);
 143        if (!chsc_debug_log_id)
 144                goto out;
 145        debug_register_view(chsc_debug_log_id, &debug_hex_ascii_view);
 146        debug_set_level(chsc_debug_log_id, 2);
 147        return 0;
 148out:
 149        debug_unregister(chsc_debug_msg_id);
 150        return -ENOMEM;
 151}
 152
 153static void chsc_remove_dbfs(void)
 154{
 155        debug_unregister(chsc_debug_log_id);
 156        debug_unregister(chsc_debug_msg_id);
 157}
 158
 159static int __init chsc_init_sch_driver(void)
 160{
 161        return css_driver_register(&chsc_subchannel_driver);
 162}
 163
 164static void chsc_cleanup_sch_driver(void)
 165{
 166        css_driver_unregister(&chsc_subchannel_driver);
 167}
 168
 169static DEFINE_SPINLOCK(chsc_lock);
 170
 171static int chsc_subchannel_match_next_free(struct device *dev, const void *data)
 172{
 173        struct subchannel *sch = to_subchannel(dev);
 174
 175        return sch->schib.pmcw.ena && !scsw_fctl(&sch->schib.scsw);
 176}
 177
 178static struct subchannel *chsc_get_next_subchannel(struct subchannel *sch)
 179{
 180        struct device *dev;
 181
 182        dev = driver_find_device(&chsc_subchannel_driver.drv,
 183                                 sch ? &sch->dev : NULL, NULL,
 184                                 chsc_subchannel_match_next_free);
 185        return dev ? to_subchannel(dev) : NULL;
 186}
 187
 188/**
 189 * chsc_async() - try to start a chsc request asynchronously
 190 * @chsc_area: request to be started
 191 * @request: request structure to associate
 192 *
 193 * Tries to start a chsc request on one of the existing chsc subchannels.
 194 * Returns:
 195 *  %0 if the request was performed synchronously
 196 *  %-EINPROGRESS if the request was successfully started
 197 *  %-EBUSY if all chsc subchannels are busy
 198 *  %-ENODEV if no chsc subchannels are available
 199 * Context:
 200 *  interrupts disabled, chsc_lock held
 201 */
 202static int chsc_async(struct chsc_async_area *chsc_area,
 203                      struct chsc_request *request)
 204{
 205        int cc;
 206        struct chsc_private *private;
 207        struct subchannel *sch = NULL;
 208        int ret = -ENODEV;
 209        char dbf[10];
 210
 211        chsc_area->header.key = PAGE_DEFAULT_KEY >> 4;
 212        while ((sch = chsc_get_next_subchannel(sch))) {
 213                spin_lock(sch->lock);
 214                private = dev_get_drvdata(&sch->dev);
 215                if (private->request) {
 216                        spin_unlock(sch->lock);
 217                        ret = -EBUSY;
 218                        continue;
 219                }
 220                chsc_area->header.sid = sch->schid;
 221                CHSC_LOG(2, "schid");
 222                CHSC_LOG_HEX(2, &sch->schid, sizeof(sch->schid));
 223                cc = chsc(chsc_area);
 224                snprintf(dbf, sizeof(dbf), "cc:%d", cc);
 225                CHSC_LOG(2, dbf);
 226                switch (cc) {
 227                case 0:
 228                        ret = 0;
 229                        break;
 230                case 1:
 231                        sch->schib.scsw.cmd.fctl |= SCSW_FCTL_START_FUNC;
 232                        ret = -EINPROGRESS;
 233                        private->request = request;
 234                        break;
 235                case 2:
 236                        ret = -EBUSY;
 237                        break;
 238                default:
 239                        ret = -ENODEV;
 240                }
 241                spin_unlock(sch->lock);
 242                CHSC_MSG(2, "chsc on 0.%x.%04x returned cc=%d\n",
 243                         sch->schid.ssid, sch->schid.sch_no, cc);
 244                if (ret == -EINPROGRESS)
 245                        return -EINPROGRESS;
 246                put_device(&sch->dev);
 247                if (ret == 0)
 248                        return 0;
 249        }
 250        return ret;
 251}
 252
 253static void chsc_log_command(void *chsc_area)
 254{
 255        char dbf[10];
 256
 257        snprintf(dbf, sizeof(dbf), "CHSC:%x", ((uint16_t *)chsc_area)[1]);
 258        CHSC_LOG(0, dbf);
 259        CHSC_LOG_HEX(0, chsc_area, 32);
 260}
 261
 262static int chsc_examine_irb(struct chsc_request *request)
 263{
 264        int backed_up;
 265
 266        if (!(scsw_stctl(&request->irb.scsw) & SCSW_STCTL_STATUS_PEND))
 267                return -EIO;
 268        backed_up = scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHAIN_CHECK;
 269        request->irb.scsw.cmd.cstat &= ~SCHN_STAT_CHAIN_CHECK;
 270        if (scsw_cstat(&request->irb.scsw) == 0)
 271                return 0;
 272        if (!backed_up)
 273                return 0;
 274        if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROG_CHECK)
 275                return -EIO;
 276        if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROT_CHECK)
 277                return -EPERM;
 278        if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_DATA_CHK)
 279                return -EAGAIN;
 280        if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_CTRL_CHK)
 281                return -EAGAIN;
 282        return -EIO;
 283}
 284
 285static int chsc_ioctl_start(void __user *user_area)
 286{
 287        struct chsc_request *request;
 288        struct chsc_async_area *chsc_area;
 289        int ret;
 290        char dbf[10];
 291
 292        if (!css_general_characteristics.dynio)
 293                /* It makes no sense to try. */
 294                return -EOPNOTSUPP;
 295        chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
 296        if (!chsc_area)
 297                return -ENOMEM;
 298        request = kzalloc(sizeof(*request), GFP_KERNEL);
 299        if (!request) {
 300                ret = -ENOMEM;
 301                goto out_free;
 302        }
 303        init_completion(&request->completion);
 304        if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
 305                ret = -EFAULT;
 306                goto out_free;
 307        }
 308        chsc_log_command(chsc_area);
 309        spin_lock_irq(&chsc_lock);
 310        ret = chsc_async(chsc_area, request);
 311        spin_unlock_irq(&chsc_lock);
 312        if (ret == -EINPROGRESS) {
 313                wait_for_completion(&request->completion);
 314                ret = chsc_examine_irb(request);
 315        }
 316        /* copy area back to user */
 317        if (!ret)
 318                if (copy_to_user(user_area, chsc_area, PAGE_SIZE))
 319                        ret = -EFAULT;
 320out_free:
 321        snprintf(dbf, sizeof(dbf), "ret:%d", ret);
 322        CHSC_LOG(0, dbf);
 323        kfree(request);
 324        free_page((unsigned long)chsc_area);
 325        return ret;
 326}
 327
 328static int chsc_ioctl_on_close_set(void __user *user_area)
 329{
 330        char dbf[13];
 331        int ret;
 332
 333        mutex_lock(&on_close_mutex);
 334        if (on_close_chsc_area) {
 335                ret = -EBUSY;
 336                goto out_unlock;
 337        }
 338        on_close_request = kzalloc(sizeof(*on_close_request), GFP_KERNEL);
 339        if (!on_close_request) {
 340                ret = -ENOMEM;
 341                goto out_unlock;
 342        }
 343        on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
 344        if (!on_close_chsc_area) {
 345                ret = -ENOMEM;
 346                goto out_free_request;
 347        }
 348        if (copy_from_user(on_close_chsc_area, user_area, PAGE_SIZE)) {
 349                ret = -EFAULT;
 350                goto out_free_chsc;
 351        }
 352        ret = 0;
 353        goto out_unlock;
 354
 355out_free_chsc:
 356        free_page((unsigned long)on_close_chsc_area);
 357        on_close_chsc_area = NULL;
 358out_free_request:
 359        kfree(on_close_request);
 360        on_close_request = NULL;
 361out_unlock:
 362        mutex_unlock(&on_close_mutex);
 363        snprintf(dbf, sizeof(dbf), "ocsret:%d", ret);
 364        CHSC_LOG(0, dbf);
 365        return ret;
 366}
 367
 368static int chsc_ioctl_on_close_remove(void)
 369{
 370        char dbf[13];
 371        int ret;
 372
 373        mutex_lock(&on_close_mutex);
 374        if (!on_close_chsc_area) {
 375                ret = -ENOENT;
 376                goto out_unlock;
 377        }
 378        free_page((unsigned long)on_close_chsc_area);
 379        on_close_chsc_area = NULL;
 380        kfree(on_close_request);
 381        on_close_request = NULL;
 382        ret = 0;
 383out_unlock:
 384        mutex_unlock(&on_close_mutex);
 385        snprintf(dbf, sizeof(dbf), "ocrret:%d", ret);
 386        CHSC_LOG(0, dbf);
 387        return ret;
 388}
 389
 390static int chsc_ioctl_start_sync(void __user *user_area)
 391{
 392        struct chsc_sync_area *chsc_area;
 393        int ret, ccode;
 394
 395        chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
 396        if (!chsc_area)
 397                return -ENOMEM;
 398        if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
 399                ret = -EFAULT;
 400                goto out_free;
 401        }
 402        if (chsc_area->header.code & 0x4000) {
 403                ret = -EINVAL;
 404                goto out_free;
 405        }
 406        chsc_log_command(chsc_area);
 407        ccode = chsc(chsc_area);
 408        if (ccode != 0) {
 409                ret = -EIO;
 410                goto out_free;
 411        }
 412        if (copy_to_user(user_area, chsc_area, PAGE_SIZE))
 413                ret = -EFAULT;
 414        else
 415                ret = 0;
 416out_free:
 417        free_page((unsigned long)chsc_area);
 418        return ret;
 419}
 420
 421static int chsc_ioctl_info_channel_path(void __user *user_cd)
 422{
 423        struct chsc_chp_cd *cd;
 424        int ret, ccode;
 425        struct {
 426                struct chsc_header request;
 427                u32 : 2;
 428                u32 m : 1;
 429                u32 : 1;
 430                u32 fmt1 : 4;
 431                u32 cssid : 8;
 432                u32 : 8;
 433                u32 first_chpid : 8;
 434                u32 : 24;
 435                u32 last_chpid : 8;
 436                u32 : 32;
 437                struct chsc_header response;
 438                u8 data[PAGE_SIZE - 20];
 439        } __attribute__ ((packed)) *scpcd_area;
 440
 441        scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
 442        if (!scpcd_area)
 443                return -ENOMEM;
 444        cd = kzalloc(sizeof(*cd), GFP_KERNEL);
 445        if (!cd) {
 446                ret = -ENOMEM;
 447                goto out_free;
 448        }
 449        if (copy_from_user(cd, user_cd, sizeof(*cd))) {
 450                ret = -EFAULT;
 451                goto out_free;
 452        }
 453        scpcd_area->request.length = 0x0010;
 454        scpcd_area->request.code = 0x0028;
 455        scpcd_area->m = cd->m;
 456        scpcd_area->fmt1 = cd->fmt;
 457        scpcd_area->cssid = cd->chpid.cssid;
 458        scpcd_area->first_chpid = cd->chpid.id;
 459        scpcd_area->last_chpid = cd->chpid.id;
 460
 461        ccode = chsc(scpcd_area);
 462        if (ccode != 0) {
 463                ret = -EIO;
 464                goto out_free;
 465        }
 466        if (scpcd_area->response.code != 0x0001) {
 467                ret = -EIO;
 468                CHSC_MSG(0, "scpcd: response code=%x\n",
 469                         scpcd_area->response.code);
 470                goto out_free;
 471        }
 472        memcpy(&cd->cpcb, &scpcd_area->response, scpcd_area->response.length);
 473        if (copy_to_user(user_cd, cd, sizeof(*cd)))
 474                ret = -EFAULT;
 475        else
 476                ret = 0;
 477out_free:
 478        kfree(cd);
 479        free_page((unsigned long)scpcd_area);
 480        return ret;
 481}
 482
 483static int chsc_ioctl_info_cu(void __user *user_cd)
 484{
 485        struct chsc_cu_cd *cd;
 486        int ret, ccode;
 487        struct {
 488                struct chsc_header request;
 489                u32 : 2;
 490                u32 m : 1;
 491                u32 : 1;
 492                u32 fmt1 : 4;
 493                u32 cssid : 8;
 494                u32 : 8;
 495                u32 first_cun : 8;
 496                u32 : 24;
 497                u32 last_cun : 8;
 498                u32 : 32;
 499                struct chsc_header response;
 500                u8 data[PAGE_SIZE - 20];
 501        } __attribute__ ((packed)) *scucd_area;
 502
 503        scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
 504        if (!scucd_area)
 505                return -ENOMEM;
 506        cd = kzalloc(sizeof(*cd), GFP_KERNEL);
 507        if (!cd) {
 508                ret = -ENOMEM;
 509                goto out_free;
 510        }
 511        if (copy_from_user(cd, user_cd, sizeof(*cd))) {
 512                ret = -EFAULT;
 513                goto out_free;
 514        }
 515        scucd_area->request.length = 0x0010;
 516        scucd_area->request.code = 0x0026;
 517        scucd_area->m = cd->m;
 518        scucd_area->fmt1 = cd->fmt;
 519        scucd_area->cssid = cd->cssid;
 520        scucd_area->first_cun = cd->cun;
 521        scucd_area->last_cun = cd->cun;
 522
 523        ccode = chsc(scucd_area);
 524        if (ccode != 0) {
 525                ret = -EIO;
 526                goto out_free;
 527        }
 528        if (scucd_area->response.code != 0x0001) {
 529                ret = -EIO;
 530                CHSC_MSG(0, "scucd: response code=%x\n",
 531                         scucd_area->response.code);
 532                goto out_free;
 533        }
 534        memcpy(&cd->cucb, &scucd_area->response, scucd_area->response.length);
 535        if (copy_to_user(user_cd, cd, sizeof(*cd)))
 536                ret = -EFAULT;
 537        else
 538                ret = 0;
 539out_free:
 540        kfree(cd);
 541        free_page((unsigned long)scucd_area);
 542        return ret;
 543}
 544
 545static int chsc_ioctl_info_sch_cu(void __user *user_cud)
 546{
 547        struct chsc_sch_cud *cud;
 548        int ret, ccode;
 549        struct {
 550                struct chsc_header request;
 551                u32 : 2;
 552                u32 m : 1;
 553                u32 : 5;
 554                u32 fmt1 : 4;
 555                u32 : 2;
 556                u32 ssid : 2;
 557                u32 first_sch : 16;
 558                u32 : 8;
 559                u32 cssid : 8;
 560                u32 last_sch : 16;
 561                u32 : 32;
 562                struct chsc_header response;
 563                u8 data[PAGE_SIZE - 20];
 564        } __attribute__ ((packed)) *sscud_area;
 565
 566        sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
 567        if (!sscud_area)
 568                return -ENOMEM;
 569        cud = kzalloc(sizeof(*cud), GFP_KERNEL);
 570        if (!cud) {
 571                ret = -ENOMEM;
 572                goto out_free;
 573        }
 574        if (copy_from_user(cud, user_cud, sizeof(*cud))) {
 575                ret = -EFAULT;
 576                goto out_free;
 577        }
 578        sscud_area->request.length = 0x0010;
 579        sscud_area->request.code = 0x0006;
 580        sscud_area->m = cud->schid.m;
 581        sscud_area->fmt1 = cud->fmt;
 582        sscud_area->ssid = cud->schid.ssid;
 583        sscud_area->first_sch = cud->schid.sch_no;
 584        sscud_area->cssid = cud->schid.cssid;
 585        sscud_area->last_sch = cud->schid.sch_no;
 586
 587        ccode = chsc(sscud_area);
 588        if (ccode != 0) {
 589                ret = -EIO;
 590                goto out_free;
 591        }
 592        if (sscud_area->response.code != 0x0001) {
 593                ret = -EIO;
 594                CHSC_MSG(0, "sscud: response code=%x\n",
 595                         sscud_area->response.code);
 596                goto out_free;
 597        }
 598        memcpy(&cud->scub, &sscud_area->response, sscud_area->response.length);
 599        if (copy_to_user(user_cud, cud, sizeof(*cud)))
 600                ret = -EFAULT;
 601        else
 602                ret = 0;
 603out_free:
 604        kfree(cud);
 605        free_page((unsigned long)sscud_area);
 606        return ret;
 607}
 608
 609static int chsc_ioctl_conf_info(void __user *user_ci)
 610{
 611        struct chsc_conf_info *ci;
 612        int ret, ccode;
 613        struct {
 614                struct chsc_header request;
 615                u32 : 2;
 616                u32 m : 1;
 617                u32 : 1;
 618                u32 fmt1 : 4;
 619                u32 cssid : 8;
 620                u32 : 6;
 621                u32 ssid : 2;
 622                u32 : 8;
 623                u64 : 64;
 624                struct chsc_header response;
 625                u8 data[PAGE_SIZE - 20];
 626        } __attribute__ ((packed)) *sci_area;
 627
 628        sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
 629        if (!sci_area)
 630                return -ENOMEM;
 631        ci = kzalloc(sizeof(*ci), GFP_KERNEL);
 632        if (!ci) {
 633                ret = -ENOMEM;
 634                goto out_free;
 635        }
 636        if (copy_from_user(ci, user_ci, sizeof(*ci))) {
 637                ret = -EFAULT;
 638                goto out_free;
 639        }
 640        sci_area->request.length = 0x0010;
 641        sci_area->request.code = 0x0012;
 642        sci_area->m = ci->id.m;
 643        sci_area->fmt1 = ci->fmt;
 644        sci_area->cssid = ci->id.cssid;
 645        sci_area->ssid = ci->id.ssid;
 646
 647        ccode = chsc(sci_area);
 648        if (ccode != 0) {
 649                ret = -EIO;
 650                goto out_free;
 651        }
 652        if (sci_area->response.code != 0x0001) {
 653                ret = -EIO;
 654                CHSC_MSG(0, "sci: response code=%x\n",
 655                         sci_area->response.code);
 656                goto out_free;
 657        }
 658        memcpy(&ci->scid, &sci_area->response, sci_area->response.length);
 659        if (copy_to_user(user_ci, ci, sizeof(*ci)))
 660                ret = -EFAULT;
 661        else
 662                ret = 0;
 663out_free:
 664        kfree(ci);
 665        free_page((unsigned long)sci_area);
 666        return ret;
 667}
 668
 669static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
 670{
 671        struct chsc_comp_list *ccl;
 672        int ret, ccode;
 673        struct {
 674                struct chsc_header request;
 675                u32 ctype : 8;
 676                u32 : 4;
 677                u32 fmt : 4;
 678                u32 : 16;
 679                u64 : 64;
 680                u32 list_parm[2];
 681                u64 : 64;
 682                struct chsc_header response;
 683                u8 data[PAGE_SIZE - 36];
 684        } __attribute__ ((packed)) *sccl_area;
 685        struct {
 686                u32 m : 1;
 687                u32 : 31;
 688                u32 cssid : 8;
 689                u32 : 16;
 690                u32 chpid : 8;
 691        } __attribute__ ((packed)) *chpid_parm;
 692        struct {
 693                u32 f_cssid : 8;
 694                u32 l_cssid : 8;
 695                u32 : 16;
 696                u32 res;
 697        } __attribute__ ((packed)) *cssids_parm;
 698
 699        sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
 700        if (!sccl_area)
 701                return -ENOMEM;
 702        ccl = kzalloc(sizeof(*ccl), GFP_KERNEL);
 703        if (!ccl) {
 704                ret = -ENOMEM;
 705                goto out_free;
 706        }
 707        if (copy_from_user(ccl, user_ccl, sizeof(*ccl))) {
 708                ret = -EFAULT;
 709                goto out_free;
 710        }
 711        sccl_area->request.length = 0x0020;
 712        sccl_area->request.code = 0x0030;
 713        sccl_area->fmt = ccl->req.fmt;
 714        sccl_area->ctype = ccl->req.ctype;
 715        switch (sccl_area->ctype) {
 716        case CCL_CU_ON_CHP:
 717        case CCL_IOP_CHP:
 718                chpid_parm = (void *)&sccl_area->list_parm;
 719                chpid_parm->m = ccl->req.chpid.m;
 720                chpid_parm->cssid = ccl->req.chpid.chp.cssid;
 721                chpid_parm->chpid = ccl->req.chpid.chp.id;
 722                break;
 723        case CCL_CSS_IMG:
 724        case CCL_CSS_IMG_CONF_CHAR:
 725                cssids_parm = (void *)&sccl_area->list_parm;
 726                cssids_parm->f_cssid = ccl->req.cssids.f_cssid;
 727                cssids_parm->l_cssid = ccl->req.cssids.l_cssid;
 728                break;
 729        }
 730        ccode = chsc(sccl_area);
 731        if (ccode != 0) {
 732                ret = -EIO;
 733                goto out_free;
 734        }
 735        if (sccl_area->response.code != 0x0001) {
 736                ret = -EIO;
 737                CHSC_MSG(0, "sccl: response code=%x\n",
 738                         sccl_area->response.code);
 739                goto out_free;
 740        }
 741        memcpy(&ccl->sccl, &sccl_area->response, sccl_area->response.length);
 742        if (copy_to_user(user_ccl, ccl, sizeof(*ccl)))
 743                ret = -EFAULT;
 744        else
 745                ret = 0;
 746out_free:
 747        kfree(ccl);
 748        free_page((unsigned long)sccl_area);
 749        return ret;
 750}
 751
 752static int chsc_ioctl_chpd(void __user *user_chpd)
 753{
 754        struct chsc_scpd *scpd_area;
 755        struct chsc_cpd_info *chpd;
 756        int ret;
 757
 758        chpd = kzalloc(sizeof(*chpd), GFP_KERNEL);
 759        scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
 760        if (!scpd_area || !chpd) {
 761                ret = -ENOMEM;
 762                goto out_free;
 763        }
 764        if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) {
 765                ret = -EFAULT;
 766                goto out_free;
 767        }
 768        ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt,
 769                                               chpd->rfmt, chpd->c, chpd->m,
 770                                               scpd_area);
 771        if (ret)
 772                goto out_free;
 773        memcpy(&chpd->chpdb, &scpd_area->response, scpd_area->response.length);
 774        if (copy_to_user(user_chpd, chpd, sizeof(*chpd)))
 775                ret = -EFAULT;
 776out_free:
 777        kfree(chpd);
 778        free_page((unsigned long)scpd_area);
 779        return ret;
 780}
 781
 782static int chsc_ioctl_dcal(void __user *user_dcal)
 783{
 784        struct chsc_dcal *dcal;
 785        int ret, ccode;
 786        struct {
 787                struct chsc_header request;
 788                u32 atype : 8;
 789                u32 : 4;
 790                u32 fmt : 4;
 791                u32 : 16;
 792                u32 res0[2];
 793                u32 list_parm[2];
 794                u32 res1[2];
 795                struct chsc_header response;
 796                u8 data[PAGE_SIZE - 36];
 797        } __attribute__ ((packed)) *sdcal_area;
 798
 799        sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
 800        if (!sdcal_area)
 801                return -ENOMEM;
 802        dcal = kzalloc(sizeof(*dcal), GFP_KERNEL);
 803        if (!dcal) {
 804                ret = -ENOMEM;
 805                goto out_free;
 806        }
 807        if (copy_from_user(dcal, user_dcal, sizeof(*dcal))) {
 808                ret = -EFAULT;
 809                goto out_free;
 810        }
 811        sdcal_area->request.length = 0x0020;
 812        sdcal_area->request.code = 0x0034;
 813        sdcal_area->atype = dcal->req.atype;
 814        sdcal_area->fmt = dcal->req.fmt;
 815        memcpy(&sdcal_area->list_parm, &dcal->req.list_parm,
 816               sizeof(sdcal_area->list_parm));
 817
 818        ccode = chsc(sdcal_area);
 819        if (ccode != 0) {
 820                ret = -EIO;
 821                goto out_free;
 822        }
 823        if (sdcal_area->response.code != 0x0001) {
 824                ret = -EIO;
 825                CHSC_MSG(0, "sdcal: response code=%x\n",
 826                         sdcal_area->response.code);
 827                goto out_free;
 828        }
 829        memcpy(&dcal->sdcal, &sdcal_area->response,
 830               sdcal_area->response.length);
 831        if (copy_to_user(user_dcal, dcal, sizeof(*dcal)))
 832                ret = -EFAULT;
 833        else
 834                ret = 0;
 835out_free:
 836        kfree(dcal);
 837        free_page((unsigned long)sdcal_area);
 838        return ret;
 839}
 840
 841static long chsc_ioctl(struct file *filp, unsigned int cmd,
 842                       unsigned long arg)
 843{
 844        void __user *argp;
 845
 846        CHSC_MSG(2, "chsc_ioctl called, cmd=%x\n", cmd);
 847        if (is_compat_task())
 848                argp = compat_ptr(arg);
 849        else
 850                argp = (void __user *)arg;
 851        switch (cmd) {
 852        case CHSC_START:
 853                return chsc_ioctl_start(argp);
 854        case CHSC_START_SYNC:
 855                return chsc_ioctl_start_sync(argp);
 856        case CHSC_INFO_CHANNEL_PATH:
 857                return chsc_ioctl_info_channel_path(argp);
 858        case CHSC_INFO_CU:
 859                return chsc_ioctl_info_cu(argp);
 860        case CHSC_INFO_SCH_CU:
 861                return chsc_ioctl_info_sch_cu(argp);
 862        case CHSC_INFO_CI:
 863                return chsc_ioctl_conf_info(argp);
 864        case CHSC_INFO_CCL:
 865                return chsc_ioctl_conf_comp_list(argp);
 866        case CHSC_INFO_CPD:
 867                return chsc_ioctl_chpd(argp);
 868        case CHSC_INFO_DCAL:
 869                return chsc_ioctl_dcal(argp);
 870        case CHSC_ON_CLOSE_SET:
 871                return chsc_ioctl_on_close_set(argp);
 872        case CHSC_ON_CLOSE_REMOVE:
 873                return chsc_ioctl_on_close_remove();
 874        default: /* unknown ioctl number */
 875                return -ENOIOCTLCMD;
 876        }
 877}
 878
 879static atomic_t chsc_ready_for_use = ATOMIC_INIT(1);
 880
 881static int chsc_open(struct inode *inode, struct file *file)
 882{
 883        if (!atomic_dec_and_test(&chsc_ready_for_use)) {
 884                atomic_inc(&chsc_ready_for_use);
 885                return -EBUSY;
 886        }
 887        return nonseekable_open(inode, file);
 888}
 889
 890static int chsc_release(struct inode *inode, struct file *filp)
 891{
 892        char dbf[13];
 893        int ret;
 894
 895        mutex_lock(&on_close_mutex);
 896        if (!on_close_chsc_area)
 897                goto out_unlock;
 898        init_completion(&on_close_request->completion);
 899        CHSC_LOG(0, "on_close");
 900        chsc_log_command(on_close_chsc_area);
 901        spin_lock_irq(&chsc_lock);
 902        ret = chsc_async(on_close_chsc_area, on_close_request);
 903        spin_unlock_irq(&chsc_lock);
 904        if (ret == -EINPROGRESS) {
 905                wait_for_completion(&on_close_request->completion);
 906                ret = chsc_examine_irb(on_close_request);
 907        }
 908        snprintf(dbf, sizeof(dbf), "relret:%d", ret);
 909        CHSC_LOG(0, dbf);
 910        free_page((unsigned long)on_close_chsc_area);
 911        on_close_chsc_area = NULL;
 912        kfree(on_close_request);
 913        on_close_request = NULL;
 914out_unlock:
 915        mutex_unlock(&on_close_mutex);
 916        atomic_inc(&chsc_ready_for_use);
 917        return 0;
 918}
 919
 920static const struct file_operations chsc_fops = {
 921        .owner = THIS_MODULE,
 922        .open = chsc_open,
 923        .release = chsc_release,
 924        .unlocked_ioctl = chsc_ioctl,
 925        .compat_ioctl = chsc_ioctl,
 926        .llseek = no_llseek,
 927};
 928
 929static struct miscdevice chsc_misc_device = {
 930        .minor = MISC_DYNAMIC_MINOR,
 931        .name = "chsc",
 932        .fops = &chsc_fops,
 933};
 934
 935static int __init chsc_misc_init(void)
 936{
 937        return misc_register(&chsc_misc_device);
 938}
 939
 940static void chsc_misc_cleanup(void)
 941{
 942        misc_deregister(&chsc_misc_device);
 943}
 944
 945static int __init chsc_sch_init(void)
 946{
 947        int ret;
 948
 949        ret = chsc_init_dbfs();
 950        if (ret)
 951                return ret;
 952        isc_register(CHSC_SCH_ISC);
 953        ret = chsc_init_sch_driver();
 954        if (ret)
 955                goto out_dbf;
 956        ret = chsc_misc_init();
 957        if (ret)
 958                goto out_driver;
 959        return ret;
 960out_driver:
 961        chsc_cleanup_sch_driver();
 962out_dbf:
 963        isc_unregister(CHSC_SCH_ISC);
 964        chsc_remove_dbfs();
 965        return ret;
 966}
 967
 968static void __exit chsc_sch_exit(void)
 969{
 970        chsc_misc_cleanup();
 971        chsc_cleanup_sch_driver();
 972        isc_unregister(CHSC_SCH_ISC);
 973        chsc_remove_dbfs();
 974}
 975
 976module_init(chsc_sch_init);
 977module_exit(chsc_sch_exit);
 978