162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for s390 chsc subchannels 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2008, 2011 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/compat.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/uaccess.h> 1762306a36Sopenharmony_ci#include <linux/miscdevice.h> 1862306a36Sopenharmony_ci#include <linux/kernel_stat.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/cio.h> 2162306a36Sopenharmony_ci#include <asm/chsc.h> 2262306a36Sopenharmony_ci#include <asm/isc.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "cio.h" 2562306a36Sopenharmony_ci#include "cio_debug.h" 2662306a36Sopenharmony_ci#include "css.h" 2762306a36Sopenharmony_ci#include "chsc_sch.h" 2862306a36Sopenharmony_ci#include "ioasm.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic debug_info_t *chsc_debug_msg_id; 3162306a36Sopenharmony_cistatic debug_info_t *chsc_debug_log_id; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct chsc_request *on_close_request; 3462306a36Sopenharmony_cistatic struct chsc_async_area *on_close_chsc_area; 3562306a36Sopenharmony_cistatic DEFINE_MUTEX(on_close_mutex); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define CHSC_MSG(imp, args...) do { \ 3862306a36Sopenharmony_ci debug_sprintf_event(chsc_debug_msg_id, imp , ##args); \ 3962306a36Sopenharmony_ci } while (0) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define CHSC_LOG(imp, txt) do { \ 4262306a36Sopenharmony_ci debug_text_event(chsc_debug_log_id, imp , txt); \ 4362306a36Sopenharmony_ci } while (0) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void CHSC_LOG_HEX(int level, void *data, int length) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci debug_event(chsc_debug_log_id, level, data, length); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ciMODULE_AUTHOR("IBM Corporation"); 5162306a36Sopenharmony_ciMODULE_DESCRIPTION("driver for s390 chsc subchannels"); 5262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void chsc_subchannel_irq(struct subchannel *sch) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct chsc_private *private = dev_get_drvdata(&sch->dev); 5762306a36Sopenharmony_ci struct chsc_request *request = private->request; 5862306a36Sopenharmony_ci struct irb *irb = this_cpu_ptr(&cio_irb); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci CHSC_LOG(4, "irb"); 6162306a36Sopenharmony_ci CHSC_LOG_HEX(4, irb, sizeof(*irb)); 6262306a36Sopenharmony_ci inc_irq_stat(IRQIO_CSC); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* Copy irb to provided request and set done. */ 6562306a36Sopenharmony_ci if (!request) { 6662306a36Sopenharmony_ci CHSC_MSG(0, "Interrupt on sch 0.%x.%04x with no request\n", 6762306a36Sopenharmony_ci sch->schid.ssid, sch->schid.sch_no); 6862306a36Sopenharmony_ci return; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci private->request = NULL; 7162306a36Sopenharmony_ci memcpy(&request->irb, irb, sizeof(*irb)); 7262306a36Sopenharmony_ci cio_update_schib(sch); 7362306a36Sopenharmony_ci complete(&request->completion); 7462306a36Sopenharmony_ci put_device(&sch->dev); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int chsc_subchannel_probe(struct subchannel *sch) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct chsc_private *private; 8062306a36Sopenharmony_ci int ret; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci CHSC_MSG(6, "Detected chsc subchannel 0.%x.%04x\n", 8362306a36Sopenharmony_ci sch->schid.ssid, sch->schid.sch_no); 8462306a36Sopenharmony_ci sch->isc = CHSC_SCH_ISC; 8562306a36Sopenharmony_ci private = kzalloc(sizeof(*private), GFP_KERNEL); 8662306a36Sopenharmony_ci if (!private) 8762306a36Sopenharmony_ci return -ENOMEM; 8862306a36Sopenharmony_ci dev_set_drvdata(&sch->dev, private); 8962306a36Sopenharmony_ci ret = cio_enable_subchannel(sch, (u32)virt_to_phys(sch)); 9062306a36Sopenharmony_ci if (ret) { 9162306a36Sopenharmony_ci CHSC_MSG(0, "Failed to enable 0.%x.%04x: %d\n", 9262306a36Sopenharmony_ci sch->schid.ssid, sch->schid.sch_no, ret); 9362306a36Sopenharmony_ci dev_set_drvdata(&sch->dev, NULL); 9462306a36Sopenharmony_ci kfree(private); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci return ret; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void chsc_subchannel_remove(struct subchannel *sch) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct chsc_private *private; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci cio_disable_subchannel(sch); 10462306a36Sopenharmony_ci private = dev_get_drvdata(&sch->dev); 10562306a36Sopenharmony_ci dev_set_drvdata(&sch->dev, NULL); 10662306a36Sopenharmony_ci if (private->request) { 10762306a36Sopenharmony_ci complete(&private->request->completion); 10862306a36Sopenharmony_ci put_device(&sch->dev); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci kfree(private); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic void chsc_subchannel_shutdown(struct subchannel *sch) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci cio_disable_subchannel(sch); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic struct css_device_id chsc_subchannel_ids[] = { 11962306a36Sopenharmony_ci { .match_flags = 0x1, .type =SUBCHANNEL_TYPE_CHSC, }, 12062306a36Sopenharmony_ci { /* end of list */ }, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(css, chsc_subchannel_ids); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic struct css_driver chsc_subchannel_driver = { 12562306a36Sopenharmony_ci .drv = { 12662306a36Sopenharmony_ci .owner = THIS_MODULE, 12762306a36Sopenharmony_ci .name = "chsc_subchannel", 12862306a36Sopenharmony_ci }, 12962306a36Sopenharmony_ci .subchannel_type = chsc_subchannel_ids, 13062306a36Sopenharmony_ci .irq = chsc_subchannel_irq, 13162306a36Sopenharmony_ci .probe = chsc_subchannel_probe, 13262306a36Sopenharmony_ci .remove = chsc_subchannel_remove, 13362306a36Sopenharmony_ci .shutdown = chsc_subchannel_shutdown, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int __init chsc_init_dbfs(void) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci chsc_debug_msg_id = debug_register("chsc_msg", 8, 1, 4 * sizeof(long)); 13962306a36Sopenharmony_ci if (!chsc_debug_msg_id) 14062306a36Sopenharmony_ci goto out; 14162306a36Sopenharmony_ci debug_register_view(chsc_debug_msg_id, &debug_sprintf_view); 14262306a36Sopenharmony_ci debug_set_level(chsc_debug_msg_id, 2); 14362306a36Sopenharmony_ci chsc_debug_log_id = debug_register("chsc_log", 16, 1, 16); 14462306a36Sopenharmony_ci if (!chsc_debug_log_id) 14562306a36Sopenharmony_ci goto out; 14662306a36Sopenharmony_ci debug_register_view(chsc_debug_log_id, &debug_hex_ascii_view); 14762306a36Sopenharmony_ci debug_set_level(chsc_debug_log_id, 2); 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ciout: 15062306a36Sopenharmony_ci debug_unregister(chsc_debug_msg_id); 15162306a36Sopenharmony_ci return -ENOMEM; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void chsc_remove_dbfs(void) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci debug_unregister(chsc_debug_log_id); 15762306a36Sopenharmony_ci debug_unregister(chsc_debug_msg_id); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int __init chsc_init_sch_driver(void) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci return css_driver_register(&chsc_subchannel_driver); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void chsc_cleanup_sch_driver(void) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci css_driver_unregister(&chsc_subchannel_driver); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(chsc_lock); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int chsc_subchannel_match_next_free(struct device *dev, const void *data) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(dev); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return sch->schib.pmcw.ena && !scsw_fctl(&sch->schib.scsw); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic struct subchannel *chsc_get_next_subchannel(struct subchannel *sch) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct device *dev; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci dev = driver_find_device(&chsc_subchannel_driver.drv, 18462306a36Sopenharmony_ci sch ? &sch->dev : NULL, NULL, 18562306a36Sopenharmony_ci chsc_subchannel_match_next_free); 18662306a36Sopenharmony_ci return dev ? to_subchannel(dev) : NULL; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/** 19062306a36Sopenharmony_ci * chsc_async() - try to start a chsc request asynchronously 19162306a36Sopenharmony_ci * @chsc_area: request to be started 19262306a36Sopenharmony_ci * @request: request structure to associate 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Tries to start a chsc request on one of the existing chsc subchannels. 19562306a36Sopenharmony_ci * Returns: 19662306a36Sopenharmony_ci * %0 if the request was performed synchronously 19762306a36Sopenharmony_ci * %-EINPROGRESS if the request was successfully started 19862306a36Sopenharmony_ci * %-EBUSY if all chsc subchannels are busy 19962306a36Sopenharmony_ci * %-ENODEV if no chsc subchannels are available 20062306a36Sopenharmony_ci * Context: 20162306a36Sopenharmony_ci * interrupts disabled, chsc_lock held 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_cistatic int chsc_async(struct chsc_async_area *chsc_area, 20462306a36Sopenharmony_ci struct chsc_request *request) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int cc; 20762306a36Sopenharmony_ci struct chsc_private *private; 20862306a36Sopenharmony_ci struct subchannel *sch = NULL; 20962306a36Sopenharmony_ci int ret = -ENODEV; 21062306a36Sopenharmony_ci char dbf[10]; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci chsc_area->header.key = PAGE_DEFAULT_KEY >> 4; 21362306a36Sopenharmony_ci while ((sch = chsc_get_next_subchannel(sch))) { 21462306a36Sopenharmony_ci spin_lock(sch->lock); 21562306a36Sopenharmony_ci private = dev_get_drvdata(&sch->dev); 21662306a36Sopenharmony_ci if (private->request) { 21762306a36Sopenharmony_ci spin_unlock(sch->lock); 21862306a36Sopenharmony_ci ret = -EBUSY; 21962306a36Sopenharmony_ci continue; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci chsc_area->header.sid = sch->schid; 22262306a36Sopenharmony_ci CHSC_LOG(2, "schid"); 22362306a36Sopenharmony_ci CHSC_LOG_HEX(2, &sch->schid, sizeof(sch->schid)); 22462306a36Sopenharmony_ci cc = chsc(chsc_area); 22562306a36Sopenharmony_ci snprintf(dbf, sizeof(dbf), "cc:%d", cc); 22662306a36Sopenharmony_ci CHSC_LOG(2, dbf); 22762306a36Sopenharmony_ci switch (cc) { 22862306a36Sopenharmony_ci case 0: 22962306a36Sopenharmony_ci ret = 0; 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case 1: 23262306a36Sopenharmony_ci sch->schib.scsw.cmd.fctl |= SCSW_FCTL_START_FUNC; 23362306a36Sopenharmony_ci ret = -EINPROGRESS; 23462306a36Sopenharmony_ci private->request = request; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci case 2: 23762306a36Sopenharmony_ci ret = -EBUSY; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci default: 24062306a36Sopenharmony_ci ret = -ENODEV; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci spin_unlock(sch->lock); 24362306a36Sopenharmony_ci CHSC_MSG(2, "chsc on 0.%x.%04x returned cc=%d\n", 24462306a36Sopenharmony_ci sch->schid.ssid, sch->schid.sch_no, cc); 24562306a36Sopenharmony_ci if (ret == -EINPROGRESS) 24662306a36Sopenharmony_ci return -EINPROGRESS; 24762306a36Sopenharmony_ci put_device(&sch->dev); 24862306a36Sopenharmony_ci if (ret == 0) 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci return ret; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void chsc_log_command(void *chsc_area) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci char dbf[10]; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci snprintf(dbf, sizeof(dbf), "CHSC:%x", ((uint16_t *)chsc_area)[1]); 25962306a36Sopenharmony_ci CHSC_LOG(0, dbf); 26062306a36Sopenharmony_ci CHSC_LOG_HEX(0, chsc_area, 32); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int chsc_examine_irb(struct chsc_request *request) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci int backed_up; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (!(scsw_stctl(&request->irb.scsw) & SCSW_STCTL_STATUS_PEND)) 26862306a36Sopenharmony_ci return -EIO; 26962306a36Sopenharmony_ci backed_up = scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHAIN_CHECK; 27062306a36Sopenharmony_ci request->irb.scsw.cmd.cstat &= ~SCHN_STAT_CHAIN_CHECK; 27162306a36Sopenharmony_ci if (scsw_cstat(&request->irb.scsw) == 0) 27262306a36Sopenharmony_ci return 0; 27362306a36Sopenharmony_ci if (!backed_up) 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROG_CHECK) 27662306a36Sopenharmony_ci return -EIO; 27762306a36Sopenharmony_ci if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROT_CHECK) 27862306a36Sopenharmony_ci return -EPERM; 27962306a36Sopenharmony_ci if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_DATA_CHK) 28062306a36Sopenharmony_ci return -EAGAIN; 28162306a36Sopenharmony_ci if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_CTRL_CHK) 28262306a36Sopenharmony_ci return -EAGAIN; 28362306a36Sopenharmony_ci return -EIO; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int chsc_ioctl_start(void __user *user_area) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct chsc_request *request; 28962306a36Sopenharmony_ci struct chsc_async_area *chsc_area; 29062306a36Sopenharmony_ci int ret; 29162306a36Sopenharmony_ci char dbf[10]; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (!css_general_characteristics.dynio) 29462306a36Sopenharmony_ci /* It makes no sense to try. */ 29562306a36Sopenharmony_ci return -EOPNOTSUPP; 29662306a36Sopenharmony_ci chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); 29762306a36Sopenharmony_ci if (!chsc_area) 29862306a36Sopenharmony_ci return -ENOMEM; 29962306a36Sopenharmony_ci request = kzalloc(sizeof(*request), GFP_KERNEL); 30062306a36Sopenharmony_ci if (!request) { 30162306a36Sopenharmony_ci ret = -ENOMEM; 30262306a36Sopenharmony_ci goto out_free; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci init_completion(&request->completion); 30562306a36Sopenharmony_ci if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) { 30662306a36Sopenharmony_ci ret = -EFAULT; 30762306a36Sopenharmony_ci goto out_free; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci chsc_log_command(chsc_area); 31062306a36Sopenharmony_ci spin_lock_irq(&chsc_lock); 31162306a36Sopenharmony_ci ret = chsc_async(chsc_area, request); 31262306a36Sopenharmony_ci spin_unlock_irq(&chsc_lock); 31362306a36Sopenharmony_ci if (ret == -EINPROGRESS) { 31462306a36Sopenharmony_ci wait_for_completion(&request->completion); 31562306a36Sopenharmony_ci ret = chsc_examine_irb(request); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci /* copy area back to user */ 31862306a36Sopenharmony_ci if (!ret) 31962306a36Sopenharmony_ci if (copy_to_user(user_area, chsc_area, PAGE_SIZE)) 32062306a36Sopenharmony_ci ret = -EFAULT; 32162306a36Sopenharmony_ciout_free: 32262306a36Sopenharmony_ci snprintf(dbf, sizeof(dbf), "ret:%d", ret); 32362306a36Sopenharmony_ci CHSC_LOG(0, dbf); 32462306a36Sopenharmony_ci kfree(request); 32562306a36Sopenharmony_ci free_page((unsigned long)chsc_area); 32662306a36Sopenharmony_ci return ret; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int chsc_ioctl_on_close_set(void __user *user_area) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci char dbf[13]; 33262306a36Sopenharmony_ci int ret; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci mutex_lock(&on_close_mutex); 33562306a36Sopenharmony_ci if (on_close_chsc_area) { 33662306a36Sopenharmony_ci ret = -EBUSY; 33762306a36Sopenharmony_ci goto out_unlock; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci on_close_request = kzalloc(sizeof(*on_close_request), GFP_KERNEL); 34062306a36Sopenharmony_ci if (!on_close_request) { 34162306a36Sopenharmony_ci ret = -ENOMEM; 34262306a36Sopenharmony_ci goto out_unlock; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); 34562306a36Sopenharmony_ci if (!on_close_chsc_area) { 34662306a36Sopenharmony_ci ret = -ENOMEM; 34762306a36Sopenharmony_ci goto out_free_request; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci if (copy_from_user(on_close_chsc_area, user_area, PAGE_SIZE)) { 35062306a36Sopenharmony_ci ret = -EFAULT; 35162306a36Sopenharmony_ci goto out_free_chsc; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci ret = 0; 35462306a36Sopenharmony_ci goto out_unlock; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciout_free_chsc: 35762306a36Sopenharmony_ci free_page((unsigned long)on_close_chsc_area); 35862306a36Sopenharmony_ci on_close_chsc_area = NULL; 35962306a36Sopenharmony_ciout_free_request: 36062306a36Sopenharmony_ci kfree(on_close_request); 36162306a36Sopenharmony_ci on_close_request = NULL; 36262306a36Sopenharmony_ciout_unlock: 36362306a36Sopenharmony_ci mutex_unlock(&on_close_mutex); 36462306a36Sopenharmony_ci snprintf(dbf, sizeof(dbf), "ocsret:%d", ret); 36562306a36Sopenharmony_ci CHSC_LOG(0, dbf); 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int chsc_ioctl_on_close_remove(void) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci char dbf[13]; 37262306a36Sopenharmony_ci int ret; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci mutex_lock(&on_close_mutex); 37562306a36Sopenharmony_ci if (!on_close_chsc_area) { 37662306a36Sopenharmony_ci ret = -ENOENT; 37762306a36Sopenharmony_ci goto out_unlock; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci free_page((unsigned long)on_close_chsc_area); 38062306a36Sopenharmony_ci on_close_chsc_area = NULL; 38162306a36Sopenharmony_ci kfree(on_close_request); 38262306a36Sopenharmony_ci on_close_request = NULL; 38362306a36Sopenharmony_ci ret = 0; 38462306a36Sopenharmony_ciout_unlock: 38562306a36Sopenharmony_ci mutex_unlock(&on_close_mutex); 38662306a36Sopenharmony_ci snprintf(dbf, sizeof(dbf), "ocrret:%d", ret); 38762306a36Sopenharmony_ci CHSC_LOG(0, dbf); 38862306a36Sopenharmony_ci return ret; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int chsc_ioctl_start_sync(void __user *user_area) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct chsc_sync_area *chsc_area; 39462306a36Sopenharmony_ci int ret, ccode; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 39762306a36Sopenharmony_ci if (!chsc_area) 39862306a36Sopenharmony_ci return -ENOMEM; 39962306a36Sopenharmony_ci if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) { 40062306a36Sopenharmony_ci ret = -EFAULT; 40162306a36Sopenharmony_ci goto out_free; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci if (chsc_area->header.code & 0x4000) { 40462306a36Sopenharmony_ci ret = -EINVAL; 40562306a36Sopenharmony_ci goto out_free; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci chsc_log_command(chsc_area); 40862306a36Sopenharmony_ci ccode = chsc(chsc_area); 40962306a36Sopenharmony_ci if (ccode != 0) { 41062306a36Sopenharmony_ci ret = -EIO; 41162306a36Sopenharmony_ci goto out_free; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci if (copy_to_user(user_area, chsc_area, PAGE_SIZE)) 41462306a36Sopenharmony_ci ret = -EFAULT; 41562306a36Sopenharmony_ci else 41662306a36Sopenharmony_ci ret = 0; 41762306a36Sopenharmony_ciout_free: 41862306a36Sopenharmony_ci free_page((unsigned long)chsc_area); 41962306a36Sopenharmony_ci return ret; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int chsc_ioctl_info_channel_path(void __user *user_cd) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct chsc_chp_cd *cd; 42562306a36Sopenharmony_ci int ret, ccode; 42662306a36Sopenharmony_ci struct { 42762306a36Sopenharmony_ci struct chsc_header request; 42862306a36Sopenharmony_ci u32 : 2; 42962306a36Sopenharmony_ci u32 m : 1; 43062306a36Sopenharmony_ci u32 : 1; 43162306a36Sopenharmony_ci u32 fmt1 : 4; 43262306a36Sopenharmony_ci u32 cssid : 8; 43362306a36Sopenharmony_ci u32 : 8; 43462306a36Sopenharmony_ci u32 first_chpid : 8; 43562306a36Sopenharmony_ci u32 : 24; 43662306a36Sopenharmony_ci u32 last_chpid : 8; 43762306a36Sopenharmony_ci u32 : 32; 43862306a36Sopenharmony_ci struct chsc_header response; 43962306a36Sopenharmony_ci u8 data[PAGE_SIZE - 20]; 44062306a36Sopenharmony_ci } __attribute__ ((packed)) *scpcd_area; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 44362306a36Sopenharmony_ci if (!scpcd_area) 44462306a36Sopenharmony_ci return -ENOMEM; 44562306a36Sopenharmony_ci cd = kzalloc(sizeof(*cd), GFP_KERNEL); 44662306a36Sopenharmony_ci if (!cd) { 44762306a36Sopenharmony_ci ret = -ENOMEM; 44862306a36Sopenharmony_ci goto out_free; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci if (copy_from_user(cd, user_cd, sizeof(*cd))) { 45162306a36Sopenharmony_ci ret = -EFAULT; 45262306a36Sopenharmony_ci goto out_free; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci scpcd_area->request.length = 0x0010; 45562306a36Sopenharmony_ci scpcd_area->request.code = 0x0028; 45662306a36Sopenharmony_ci scpcd_area->m = cd->m; 45762306a36Sopenharmony_ci scpcd_area->fmt1 = cd->fmt; 45862306a36Sopenharmony_ci scpcd_area->cssid = cd->chpid.cssid; 45962306a36Sopenharmony_ci scpcd_area->first_chpid = cd->chpid.id; 46062306a36Sopenharmony_ci scpcd_area->last_chpid = cd->chpid.id; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ccode = chsc(scpcd_area); 46362306a36Sopenharmony_ci if (ccode != 0) { 46462306a36Sopenharmony_ci ret = -EIO; 46562306a36Sopenharmony_ci goto out_free; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci if (scpcd_area->response.code != 0x0001) { 46862306a36Sopenharmony_ci ret = -EIO; 46962306a36Sopenharmony_ci CHSC_MSG(0, "scpcd: response code=%x\n", 47062306a36Sopenharmony_ci scpcd_area->response.code); 47162306a36Sopenharmony_ci goto out_free; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci memcpy(&cd->cpcb, &scpcd_area->response, scpcd_area->response.length); 47462306a36Sopenharmony_ci if (copy_to_user(user_cd, cd, sizeof(*cd))) 47562306a36Sopenharmony_ci ret = -EFAULT; 47662306a36Sopenharmony_ci else 47762306a36Sopenharmony_ci ret = 0; 47862306a36Sopenharmony_ciout_free: 47962306a36Sopenharmony_ci kfree(cd); 48062306a36Sopenharmony_ci free_page((unsigned long)scpcd_area); 48162306a36Sopenharmony_ci return ret; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int chsc_ioctl_info_cu(void __user *user_cd) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct chsc_cu_cd *cd; 48762306a36Sopenharmony_ci int ret, ccode; 48862306a36Sopenharmony_ci struct { 48962306a36Sopenharmony_ci struct chsc_header request; 49062306a36Sopenharmony_ci u32 : 2; 49162306a36Sopenharmony_ci u32 m : 1; 49262306a36Sopenharmony_ci u32 : 1; 49362306a36Sopenharmony_ci u32 fmt1 : 4; 49462306a36Sopenharmony_ci u32 cssid : 8; 49562306a36Sopenharmony_ci u32 : 8; 49662306a36Sopenharmony_ci u32 first_cun : 8; 49762306a36Sopenharmony_ci u32 : 24; 49862306a36Sopenharmony_ci u32 last_cun : 8; 49962306a36Sopenharmony_ci u32 : 32; 50062306a36Sopenharmony_ci struct chsc_header response; 50162306a36Sopenharmony_ci u8 data[PAGE_SIZE - 20]; 50262306a36Sopenharmony_ci } __attribute__ ((packed)) *scucd_area; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 50562306a36Sopenharmony_ci if (!scucd_area) 50662306a36Sopenharmony_ci return -ENOMEM; 50762306a36Sopenharmony_ci cd = kzalloc(sizeof(*cd), GFP_KERNEL); 50862306a36Sopenharmony_ci if (!cd) { 50962306a36Sopenharmony_ci ret = -ENOMEM; 51062306a36Sopenharmony_ci goto out_free; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci if (copy_from_user(cd, user_cd, sizeof(*cd))) { 51362306a36Sopenharmony_ci ret = -EFAULT; 51462306a36Sopenharmony_ci goto out_free; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci scucd_area->request.length = 0x0010; 51762306a36Sopenharmony_ci scucd_area->request.code = 0x0026; 51862306a36Sopenharmony_ci scucd_area->m = cd->m; 51962306a36Sopenharmony_ci scucd_area->fmt1 = cd->fmt; 52062306a36Sopenharmony_ci scucd_area->cssid = cd->cssid; 52162306a36Sopenharmony_ci scucd_area->first_cun = cd->cun; 52262306a36Sopenharmony_ci scucd_area->last_cun = cd->cun; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci ccode = chsc(scucd_area); 52562306a36Sopenharmony_ci if (ccode != 0) { 52662306a36Sopenharmony_ci ret = -EIO; 52762306a36Sopenharmony_ci goto out_free; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci if (scucd_area->response.code != 0x0001) { 53062306a36Sopenharmony_ci ret = -EIO; 53162306a36Sopenharmony_ci CHSC_MSG(0, "scucd: response code=%x\n", 53262306a36Sopenharmony_ci scucd_area->response.code); 53362306a36Sopenharmony_ci goto out_free; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci memcpy(&cd->cucb, &scucd_area->response, scucd_area->response.length); 53662306a36Sopenharmony_ci if (copy_to_user(user_cd, cd, sizeof(*cd))) 53762306a36Sopenharmony_ci ret = -EFAULT; 53862306a36Sopenharmony_ci else 53962306a36Sopenharmony_ci ret = 0; 54062306a36Sopenharmony_ciout_free: 54162306a36Sopenharmony_ci kfree(cd); 54262306a36Sopenharmony_ci free_page((unsigned long)scucd_area); 54362306a36Sopenharmony_ci return ret; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int chsc_ioctl_info_sch_cu(void __user *user_cud) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct chsc_sch_cud *cud; 54962306a36Sopenharmony_ci int ret, ccode; 55062306a36Sopenharmony_ci struct { 55162306a36Sopenharmony_ci struct chsc_header request; 55262306a36Sopenharmony_ci u32 : 2; 55362306a36Sopenharmony_ci u32 m : 1; 55462306a36Sopenharmony_ci u32 : 5; 55562306a36Sopenharmony_ci u32 fmt1 : 4; 55662306a36Sopenharmony_ci u32 : 2; 55762306a36Sopenharmony_ci u32 ssid : 2; 55862306a36Sopenharmony_ci u32 first_sch : 16; 55962306a36Sopenharmony_ci u32 : 8; 56062306a36Sopenharmony_ci u32 cssid : 8; 56162306a36Sopenharmony_ci u32 last_sch : 16; 56262306a36Sopenharmony_ci u32 : 32; 56362306a36Sopenharmony_ci struct chsc_header response; 56462306a36Sopenharmony_ci u8 data[PAGE_SIZE - 20]; 56562306a36Sopenharmony_ci } __attribute__ ((packed)) *sscud_area; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 56862306a36Sopenharmony_ci if (!sscud_area) 56962306a36Sopenharmony_ci return -ENOMEM; 57062306a36Sopenharmony_ci cud = kzalloc(sizeof(*cud), GFP_KERNEL); 57162306a36Sopenharmony_ci if (!cud) { 57262306a36Sopenharmony_ci ret = -ENOMEM; 57362306a36Sopenharmony_ci goto out_free; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci if (copy_from_user(cud, user_cud, sizeof(*cud))) { 57662306a36Sopenharmony_ci ret = -EFAULT; 57762306a36Sopenharmony_ci goto out_free; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci sscud_area->request.length = 0x0010; 58062306a36Sopenharmony_ci sscud_area->request.code = 0x0006; 58162306a36Sopenharmony_ci sscud_area->m = cud->schid.m; 58262306a36Sopenharmony_ci sscud_area->fmt1 = cud->fmt; 58362306a36Sopenharmony_ci sscud_area->ssid = cud->schid.ssid; 58462306a36Sopenharmony_ci sscud_area->first_sch = cud->schid.sch_no; 58562306a36Sopenharmony_ci sscud_area->cssid = cud->schid.cssid; 58662306a36Sopenharmony_ci sscud_area->last_sch = cud->schid.sch_no; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ccode = chsc(sscud_area); 58962306a36Sopenharmony_ci if (ccode != 0) { 59062306a36Sopenharmony_ci ret = -EIO; 59162306a36Sopenharmony_ci goto out_free; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci if (sscud_area->response.code != 0x0001) { 59462306a36Sopenharmony_ci ret = -EIO; 59562306a36Sopenharmony_ci CHSC_MSG(0, "sscud: response code=%x\n", 59662306a36Sopenharmony_ci sscud_area->response.code); 59762306a36Sopenharmony_ci goto out_free; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci memcpy(&cud->scub, &sscud_area->response, sscud_area->response.length); 60062306a36Sopenharmony_ci if (copy_to_user(user_cud, cud, sizeof(*cud))) 60162306a36Sopenharmony_ci ret = -EFAULT; 60262306a36Sopenharmony_ci else 60362306a36Sopenharmony_ci ret = 0; 60462306a36Sopenharmony_ciout_free: 60562306a36Sopenharmony_ci kfree(cud); 60662306a36Sopenharmony_ci free_page((unsigned long)sscud_area); 60762306a36Sopenharmony_ci return ret; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic int chsc_ioctl_conf_info(void __user *user_ci) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct chsc_conf_info *ci; 61362306a36Sopenharmony_ci int ret, ccode; 61462306a36Sopenharmony_ci struct { 61562306a36Sopenharmony_ci struct chsc_header request; 61662306a36Sopenharmony_ci u32 : 2; 61762306a36Sopenharmony_ci u32 m : 1; 61862306a36Sopenharmony_ci u32 : 1; 61962306a36Sopenharmony_ci u32 fmt1 : 4; 62062306a36Sopenharmony_ci u32 cssid : 8; 62162306a36Sopenharmony_ci u32 : 6; 62262306a36Sopenharmony_ci u32 ssid : 2; 62362306a36Sopenharmony_ci u32 : 8; 62462306a36Sopenharmony_ci u64 : 64; 62562306a36Sopenharmony_ci struct chsc_header response; 62662306a36Sopenharmony_ci u8 data[PAGE_SIZE - 20]; 62762306a36Sopenharmony_ci } __attribute__ ((packed)) *sci_area; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 63062306a36Sopenharmony_ci if (!sci_area) 63162306a36Sopenharmony_ci return -ENOMEM; 63262306a36Sopenharmony_ci ci = kzalloc(sizeof(*ci), GFP_KERNEL); 63362306a36Sopenharmony_ci if (!ci) { 63462306a36Sopenharmony_ci ret = -ENOMEM; 63562306a36Sopenharmony_ci goto out_free; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci if (copy_from_user(ci, user_ci, sizeof(*ci))) { 63862306a36Sopenharmony_ci ret = -EFAULT; 63962306a36Sopenharmony_ci goto out_free; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci sci_area->request.length = 0x0010; 64262306a36Sopenharmony_ci sci_area->request.code = 0x0012; 64362306a36Sopenharmony_ci sci_area->m = ci->id.m; 64462306a36Sopenharmony_ci sci_area->fmt1 = ci->fmt; 64562306a36Sopenharmony_ci sci_area->cssid = ci->id.cssid; 64662306a36Sopenharmony_ci sci_area->ssid = ci->id.ssid; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci ccode = chsc(sci_area); 64962306a36Sopenharmony_ci if (ccode != 0) { 65062306a36Sopenharmony_ci ret = -EIO; 65162306a36Sopenharmony_ci goto out_free; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci if (sci_area->response.code != 0x0001) { 65462306a36Sopenharmony_ci ret = -EIO; 65562306a36Sopenharmony_ci CHSC_MSG(0, "sci: response code=%x\n", 65662306a36Sopenharmony_ci sci_area->response.code); 65762306a36Sopenharmony_ci goto out_free; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci memcpy(&ci->scid, &sci_area->response, sci_area->response.length); 66062306a36Sopenharmony_ci if (copy_to_user(user_ci, ci, sizeof(*ci))) 66162306a36Sopenharmony_ci ret = -EFAULT; 66262306a36Sopenharmony_ci else 66362306a36Sopenharmony_ci ret = 0; 66462306a36Sopenharmony_ciout_free: 66562306a36Sopenharmony_ci kfree(ci); 66662306a36Sopenharmony_ci free_page((unsigned long)sci_area); 66762306a36Sopenharmony_ci return ret; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int chsc_ioctl_conf_comp_list(void __user *user_ccl) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct chsc_comp_list *ccl; 67362306a36Sopenharmony_ci int ret, ccode; 67462306a36Sopenharmony_ci struct { 67562306a36Sopenharmony_ci struct chsc_header request; 67662306a36Sopenharmony_ci u32 ctype : 8; 67762306a36Sopenharmony_ci u32 : 4; 67862306a36Sopenharmony_ci u32 fmt : 4; 67962306a36Sopenharmony_ci u32 : 16; 68062306a36Sopenharmony_ci u64 : 64; 68162306a36Sopenharmony_ci u32 list_parm[2]; 68262306a36Sopenharmony_ci u64 : 64; 68362306a36Sopenharmony_ci struct chsc_header response; 68462306a36Sopenharmony_ci u8 data[PAGE_SIZE - 36]; 68562306a36Sopenharmony_ci } __attribute__ ((packed)) *sccl_area; 68662306a36Sopenharmony_ci struct { 68762306a36Sopenharmony_ci u32 m : 1; 68862306a36Sopenharmony_ci u32 : 31; 68962306a36Sopenharmony_ci u32 cssid : 8; 69062306a36Sopenharmony_ci u32 : 16; 69162306a36Sopenharmony_ci u32 chpid : 8; 69262306a36Sopenharmony_ci } __attribute__ ((packed)) *chpid_parm; 69362306a36Sopenharmony_ci struct { 69462306a36Sopenharmony_ci u32 f_cssid : 8; 69562306a36Sopenharmony_ci u32 l_cssid : 8; 69662306a36Sopenharmony_ci u32 : 16; 69762306a36Sopenharmony_ci u32 res; 69862306a36Sopenharmony_ci } __attribute__ ((packed)) *cssids_parm; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 70162306a36Sopenharmony_ci if (!sccl_area) 70262306a36Sopenharmony_ci return -ENOMEM; 70362306a36Sopenharmony_ci ccl = kzalloc(sizeof(*ccl), GFP_KERNEL); 70462306a36Sopenharmony_ci if (!ccl) { 70562306a36Sopenharmony_ci ret = -ENOMEM; 70662306a36Sopenharmony_ci goto out_free; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci if (copy_from_user(ccl, user_ccl, sizeof(*ccl))) { 70962306a36Sopenharmony_ci ret = -EFAULT; 71062306a36Sopenharmony_ci goto out_free; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci sccl_area->request.length = 0x0020; 71362306a36Sopenharmony_ci sccl_area->request.code = 0x0030; 71462306a36Sopenharmony_ci sccl_area->fmt = ccl->req.fmt; 71562306a36Sopenharmony_ci sccl_area->ctype = ccl->req.ctype; 71662306a36Sopenharmony_ci switch (sccl_area->ctype) { 71762306a36Sopenharmony_ci case CCL_CU_ON_CHP: 71862306a36Sopenharmony_ci case CCL_IOP_CHP: 71962306a36Sopenharmony_ci chpid_parm = (void *)&sccl_area->list_parm; 72062306a36Sopenharmony_ci chpid_parm->m = ccl->req.chpid.m; 72162306a36Sopenharmony_ci chpid_parm->cssid = ccl->req.chpid.chp.cssid; 72262306a36Sopenharmony_ci chpid_parm->chpid = ccl->req.chpid.chp.id; 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci case CCL_CSS_IMG: 72562306a36Sopenharmony_ci case CCL_CSS_IMG_CONF_CHAR: 72662306a36Sopenharmony_ci cssids_parm = (void *)&sccl_area->list_parm; 72762306a36Sopenharmony_ci cssids_parm->f_cssid = ccl->req.cssids.f_cssid; 72862306a36Sopenharmony_ci cssids_parm->l_cssid = ccl->req.cssids.l_cssid; 72962306a36Sopenharmony_ci break; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci ccode = chsc(sccl_area); 73262306a36Sopenharmony_ci if (ccode != 0) { 73362306a36Sopenharmony_ci ret = -EIO; 73462306a36Sopenharmony_ci goto out_free; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci if (sccl_area->response.code != 0x0001) { 73762306a36Sopenharmony_ci ret = -EIO; 73862306a36Sopenharmony_ci CHSC_MSG(0, "sccl: response code=%x\n", 73962306a36Sopenharmony_ci sccl_area->response.code); 74062306a36Sopenharmony_ci goto out_free; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci memcpy(&ccl->sccl, &sccl_area->response, sccl_area->response.length); 74362306a36Sopenharmony_ci if (copy_to_user(user_ccl, ccl, sizeof(*ccl))) 74462306a36Sopenharmony_ci ret = -EFAULT; 74562306a36Sopenharmony_ci else 74662306a36Sopenharmony_ci ret = 0; 74762306a36Sopenharmony_ciout_free: 74862306a36Sopenharmony_ci kfree(ccl); 74962306a36Sopenharmony_ci free_page((unsigned long)sccl_area); 75062306a36Sopenharmony_ci return ret; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic int chsc_ioctl_chpd(void __user *user_chpd) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci struct chsc_scpd *scpd_area; 75662306a36Sopenharmony_ci struct chsc_cpd_info *chpd; 75762306a36Sopenharmony_ci int ret; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci chpd = kzalloc(sizeof(*chpd), GFP_KERNEL); 76062306a36Sopenharmony_ci scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 76162306a36Sopenharmony_ci if (!scpd_area || !chpd) { 76262306a36Sopenharmony_ci ret = -ENOMEM; 76362306a36Sopenharmony_ci goto out_free; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) { 76662306a36Sopenharmony_ci ret = -EFAULT; 76762306a36Sopenharmony_ci goto out_free; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt, 77062306a36Sopenharmony_ci chpd->rfmt, chpd->c, chpd->m, 77162306a36Sopenharmony_ci scpd_area); 77262306a36Sopenharmony_ci if (ret) 77362306a36Sopenharmony_ci goto out_free; 77462306a36Sopenharmony_ci memcpy(&chpd->chpdb, &scpd_area->response, scpd_area->response.length); 77562306a36Sopenharmony_ci if (copy_to_user(user_chpd, chpd, sizeof(*chpd))) 77662306a36Sopenharmony_ci ret = -EFAULT; 77762306a36Sopenharmony_ciout_free: 77862306a36Sopenharmony_ci kfree(chpd); 77962306a36Sopenharmony_ci free_page((unsigned long)scpd_area); 78062306a36Sopenharmony_ci return ret; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int chsc_ioctl_dcal(void __user *user_dcal) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct chsc_dcal *dcal; 78662306a36Sopenharmony_ci int ret, ccode; 78762306a36Sopenharmony_ci struct { 78862306a36Sopenharmony_ci struct chsc_header request; 78962306a36Sopenharmony_ci u32 atype : 8; 79062306a36Sopenharmony_ci u32 : 4; 79162306a36Sopenharmony_ci u32 fmt : 4; 79262306a36Sopenharmony_ci u32 : 16; 79362306a36Sopenharmony_ci u32 res0[2]; 79462306a36Sopenharmony_ci u32 list_parm[2]; 79562306a36Sopenharmony_ci u32 res1[2]; 79662306a36Sopenharmony_ci struct chsc_header response; 79762306a36Sopenharmony_ci u8 data[PAGE_SIZE - 36]; 79862306a36Sopenharmony_ci } __attribute__ ((packed)) *sdcal_area; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 80162306a36Sopenharmony_ci if (!sdcal_area) 80262306a36Sopenharmony_ci return -ENOMEM; 80362306a36Sopenharmony_ci dcal = kzalloc(sizeof(*dcal), GFP_KERNEL); 80462306a36Sopenharmony_ci if (!dcal) { 80562306a36Sopenharmony_ci ret = -ENOMEM; 80662306a36Sopenharmony_ci goto out_free; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci if (copy_from_user(dcal, user_dcal, sizeof(*dcal))) { 80962306a36Sopenharmony_ci ret = -EFAULT; 81062306a36Sopenharmony_ci goto out_free; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci sdcal_area->request.length = 0x0020; 81362306a36Sopenharmony_ci sdcal_area->request.code = 0x0034; 81462306a36Sopenharmony_ci sdcal_area->atype = dcal->req.atype; 81562306a36Sopenharmony_ci sdcal_area->fmt = dcal->req.fmt; 81662306a36Sopenharmony_ci memcpy(&sdcal_area->list_parm, &dcal->req.list_parm, 81762306a36Sopenharmony_ci sizeof(sdcal_area->list_parm)); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci ccode = chsc(sdcal_area); 82062306a36Sopenharmony_ci if (ccode != 0) { 82162306a36Sopenharmony_ci ret = -EIO; 82262306a36Sopenharmony_ci goto out_free; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci if (sdcal_area->response.code != 0x0001) { 82562306a36Sopenharmony_ci ret = -EIO; 82662306a36Sopenharmony_ci CHSC_MSG(0, "sdcal: response code=%x\n", 82762306a36Sopenharmony_ci sdcal_area->response.code); 82862306a36Sopenharmony_ci goto out_free; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci memcpy(&dcal->sdcal, &sdcal_area->response, 83162306a36Sopenharmony_ci sdcal_area->response.length); 83262306a36Sopenharmony_ci if (copy_to_user(user_dcal, dcal, sizeof(*dcal))) 83362306a36Sopenharmony_ci ret = -EFAULT; 83462306a36Sopenharmony_ci else 83562306a36Sopenharmony_ci ret = 0; 83662306a36Sopenharmony_ciout_free: 83762306a36Sopenharmony_ci kfree(dcal); 83862306a36Sopenharmony_ci free_page((unsigned long)sdcal_area); 83962306a36Sopenharmony_ci return ret; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic long chsc_ioctl(struct file *filp, unsigned int cmd, 84362306a36Sopenharmony_ci unsigned long arg) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci void __user *argp; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci CHSC_MSG(2, "chsc_ioctl called, cmd=%x\n", cmd); 84862306a36Sopenharmony_ci if (is_compat_task()) 84962306a36Sopenharmony_ci argp = compat_ptr(arg); 85062306a36Sopenharmony_ci else 85162306a36Sopenharmony_ci argp = (void __user *)arg; 85262306a36Sopenharmony_ci switch (cmd) { 85362306a36Sopenharmony_ci case CHSC_START: 85462306a36Sopenharmony_ci return chsc_ioctl_start(argp); 85562306a36Sopenharmony_ci case CHSC_START_SYNC: 85662306a36Sopenharmony_ci return chsc_ioctl_start_sync(argp); 85762306a36Sopenharmony_ci case CHSC_INFO_CHANNEL_PATH: 85862306a36Sopenharmony_ci return chsc_ioctl_info_channel_path(argp); 85962306a36Sopenharmony_ci case CHSC_INFO_CU: 86062306a36Sopenharmony_ci return chsc_ioctl_info_cu(argp); 86162306a36Sopenharmony_ci case CHSC_INFO_SCH_CU: 86262306a36Sopenharmony_ci return chsc_ioctl_info_sch_cu(argp); 86362306a36Sopenharmony_ci case CHSC_INFO_CI: 86462306a36Sopenharmony_ci return chsc_ioctl_conf_info(argp); 86562306a36Sopenharmony_ci case CHSC_INFO_CCL: 86662306a36Sopenharmony_ci return chsc_ioctl_conf_comp_list(argp); 86762306a36Sopenharmony_ci case CHSC_INFO_CPD: 86862306a36Sopenharmony_ci return chsc_ioctl_chpd(argp); 86962306a36Sopenharmony_ci case CHSC_INFO_DCAL: 87062306a36Sopenharmony_ci return chsc_ioctl_dcal(argp); 87162306a36Sopenharmony_ci case CHSC_ON_CLOSE_SET: 87262306a36Sopenharmony_ci return chsc_ioctl_on_close_set(argp); 87362306a36Sopenharmony_ci case CHSC_ON_CLOSE_REMOVE: 87462306a36Sopenharmony_ci return chsc_ioctl_on_close_remove(); 87562306a36Sopenharmony_ci default: /* unknown ioctl number */ 87662306a36Sopenharmony_ci return -ENOIOCTLCMD; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic atomic_t chsc_ready_for_use = ATOMIC_INIT(1); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic int chsc_open(struct inode *inode, struct file *file) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci if (!atomic_dec_and_test(&chsc_ready_for_use)) { 88562306a36Sopenharmony_ci atomic_inc(&chsc_ready_for_use); 88662306a36Sopenharmony_ci return -EBUSY; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci return nonseekable_open(inode, file); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int chsc_release(struct inode *inode, struct file *filp) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci char dbf[13]; 89462306a36Sopenharmony_ci int ret; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci mutex_lock(&on_close_mutex); 89762306a36Sopenharmony_ci if (!on_close_chsc_area) 89862306a36Sopenharmony_ci goto out_unlock; 89962306a36Sopenharmony_ci init_completion(&on_close_request->completion); 90062306a36Sopenharmony_ci CHSC_LOG(0, "on_close"); 90162306a36Sopenharmony_ci chsc_log_command(on_close_chsc_area); 90262306a36Sopenharmony_ci spin_lock_irq(&chsc_lock); 90362306a36Sopenharmony_ci ret = chsc_async(on_close_chsc_area, on_close_request); 90462306a36Sopenharmony_ci spin_unlock_irq(&chsc_lock); 90562306a36Sopenharmony_ci if (ret == -EINPROGRESS) { 90662306a36Sopenharmony_ci wait_for_completion(&on_close_request->completion); 90762306a36Sopenharmony_ci ret = chsc_examine_irb(on_close_request); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci snprintf(dbf, sizeof(dbf), "relret:%d", ret); 91062306a36Sopenharmony_ci CHSC_LOG(0, dbf); 91162306a36Sopenharmony_ci free_page((unsigned long)on_close_chsc_area); 91262306a36Sopenharmony_ci on_close_chsc_area = NULL; 91362306a36Sopenharmony_ci kfree(on_close_request); 91462306a36Sopenharmony_ci on_close_request = NULL; 91562306a36Sopenharmony_ciout_unlock: 91662306a36Sopenharmony_ci mutex_unlock(&on_close_mutex); 91762306a36Sopenharmony_ci atomic_inc(&chsc_ready_for_use); 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic const struct file_operations chsc_fops = { 92262306a36Sopenharmony_ci .owner = THIS_MODULE, 92362306a36Sopenharmony_ci .open = chsc_open, 92462306a36Sopenharmony_ci .release = chsc_release, 92562306a36Sopenharmony_ci .unlocked_ioctl = chsc_ioctl, 92662306a36Sopenharmony_ci .compat_ioctl = chsc_ioctl, 92762306a36Sopenharmony_ci .llseek = no_llseek, 92862306a36Sopenharmony_ci}; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic struct miscdevice chsc_misc_device = { 93162306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 93262306a36Sopenharmony_ci .name = "chsc", 93362306a36Sopenharmony_ci .fops = &chsc_fops, 93462306a36Sopenharmony_ci}; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_cistatic int __init chsc_misc_init(void) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci return misc_register(&chsc_misc_device); 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic void chsc_misc_cleanup(void) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci misc_deregister(&chsc_misc_device); 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int __init chsc_sch_init(void) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci int ret; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci ret = chsc_init_dbfs(); 95162306a36Sopenharmony_ci if (ret) 95262306a36Sopenharmony_ci return ret; 95362306a36Sopenharmony_ci isc_register(CHSC_SCH_ISC); 95462306a36Sopenharmony_ci ret = chsc_init_sch_driver(); 95562306a36Sopenharmony_ci if (ret) 95662306a36Sopenharmony_ci goto out_dbf; 95762306a36Sopenharmony_ci ret = chsc_misc_init(); 95862306a36Sopenharmony_ci if (ret) 95962306a36Sopenharmony_ci goto out_driver; 96062306a36Sopenharmony_ci return ret; 96162306a36Sopenharmony_ciout_driver: 96262306a36Sopenharmony_ci chsc_cleanup_sch_driver(); 96362306a36Sopenharmony_ciout_dbf: 96462306a36Sopenharmony_ci isc_unregister(CHSC_SCH_ISC); 96562306a36Sopenharmony_ci chsc_remove_dbfs(); 96662306a36Sopenharmony_ci return ret; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic void __exit chsc_sch_exit(void) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci chsc_misc_cleanup(); 97262306a36Sopenharmony_ci chsc_cleanup_sch_driver(); 97362306a36Sopenharmony_ci isc_unregister(CHSC_SCH_ISC); 97462306a36Sopenharmony_ci chsc_remove_dbfs(); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cimodule_init(chsc_sch_init); 97862306a36Sopenharmony_cimodule_exit(chsc_sch_exit); 979