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