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