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