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