xref: /OK3568_Linux_fs/kernel/drivers/misc/mei/main.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (c) 2003-2020, Intel Corporation. All rights reserved.
4*4882a593Smuzhiyun  * Intel Management Engine Interface (Intel MEI) Linux driver
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun #include <linux/moduleparam.h>
9*4882a593Smuzhiyun #include <linux/kernel.h>
10*4882a593Smuzhiyun #include <linux/device.h>
11*4882a593Smuzhiyun #include <linux/slab.h>
12*4882a593Smuzhiyun #include <linux/fs.h>
13*4882a593Smuzhiyun #include <linux/errno.h>
14*4882a593Smuzhiyun #include <linux/types.h>
15*4882a593Smuzhiyun #include <linux/fcntl.h>
16*4882a593Smuzhiyun #include <linux/poll.h>
17*4882a593Smuzhiyun #include <linux/init.h>
18*4882a593Smuzhiyun #include <linux/ioctl.h>
19*4882a593Smuzhiyun #include <linux/cdev.h>
20*4882a593Smuzhiyun #include <linux/sched/signal.h>
21*4882a593Smuzhiyun #include <linux/uuid.h>
22*4882a593Smuzhiyun #include <linux/compat.h>
23*4882a593Smuzhiyun #include <linux/jiffies.h>
24*4882a593Smuzhiyun #include <linux/interrupt.h>
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #include <linux/mei.h>
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #include "mei_dev.h"
29*4882a593Smuzhiyun #include "client.h"
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun static struct class *mei_class;
32*4882a593Smuzhiyun static dev_t mei_devt;
33*4882a593Smuzhiyun #define MEI_MAX_DEVS  MINORMASK
34*4882a593Smuzhiyun static DEFINE_MUTEX(mei_minor_lock);
35*4882a593Smuzhiyun static DEFINE_IDR(mei_idr);
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /**
38*4882a593Smuzhiyun  * mei_open - the open function
39*4882a593Smuzhiyun  *
40*4882a593Smuzhiyun  * @inode: pointer to inode structure
41*4882a593Smuzhiyun  * @file: pointer to file structure
42*4882a593Smuzhiyun  *
43*4882a593Smuzhiyun  * Return: 0 on success, <0 on error
44*4882a593Smuzhiyun  */
mei_open(struct inode * inode,struct file * file)45*4882a593Smuzhiyun static int mei_open(struct inode *inode, struct file *file)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun 	struct mei_device *dev;
48*4882a593Smuzhiyun 	struct mei_cl *cl;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	int err;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	dev = container_of(inode->i_cdev, struct mei_device, cdev);
53*4882a593Smuzhiyun 	if (!dev)
54*4882a593Smuzhiyun 		return -ENODEV;
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	if (dev->dev_state != MEI_DEV_ENABLED) {
59*4882a593Smuzhiyun 		dev_dbg(dev->dev, "dev_state != MEI_ENABLED  dev_state = %s\n",
60*4882a593Smuzhiyun 		    mei_dev_state_str(dev->dev_state));
61*4882a593Smuzhiyun 		err = -ENODEV;
62*4882a593Smuzhiyun 		goto err_unlock;
63*4882a593Smuzhiyun 	}
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	cl = mei_cl_alloc_linked(dev);
66*4882a593Smuzhiyun 	if (IS_ERR(cl)) {
67*4882a593Smuzhiyun 		err = PTR_ERR(cl);
68*4882a593Smuzhiyun 		goto err_unlock;
69*4882a593Smuzhiyun 	}
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	cl->fp = file;
72*4882a593Smuzhiyun 	file->private_data = cl;
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	return nonseekable_open(inode, file);
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun err_unlock:
79*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
80*4882a593Smuzhiyun 	return err;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun /**
84*4882a593Smuzhiyun  * mei_cl_vtag_remove_by_fp - remove vtag that corresponds to fp from list
85*4882a593Smuzhiyun  *
86*4882a593Smuzhiyun  * @cl: host client
87*4882a593Smuzhiyun  * @fp: pointer to file structure
88*4882a593Smuzhiyun  *
89*4882a593Smuzhiyun  */
mei_cl_vtag_remove_by_fp(const struct mei_cl * cl,const struct file * fp)90*4882a593Smuzhiyun static void mei_cl_vtag_remove_by_fp(const struct mei_cl *cl,
91*4882a593Smuzhiyun 				     const struct file *fp)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	struct mei_cl_vtag *vtag_l, *next;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	list_for_each_entry_safe(vtag_l, next, &cl->vtag_map, list) {
96*4882a593Smuzhiyun 		if (vtag_l->fp == fp) {
97*4882a593Smuzhiyun 			list_del(&vtag_l->list);
98*4882a593Smuzhiyun 			kfree(vtag_l);
99*4882a593Smuzhiyun 			return;
100*4882a593Smuzhiyun 		}
101*4882a593Smuzhiyun 	}
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun /**
105*4882a593Smuzhiyun  * mei_release - the release function
106*4882a593Smuzhiyun  *
107*4882a593Smuzhiyun  * @inode: pointer to inode structure
108*4882a593Smuzhiyun  * @file: pointer to file structure
109*4882a593Smuzhiyun  *
110*4882a593Smuzhiyun  * Return: 0 on success, <0 on error
111*4882a593Smuzhiyun  */
mei_release(struct inode * inode,struct file * file)112*4882a593Smuzhiyun static int mei_release(struct inode *inode, struct file *file)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun 	struct mei_cl *cl = file->private_data;
115*4882a593Smuzhiyun 	struct mei_device *dev;
116*4882a593Smuzhiyun 	int rets;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	if (WARN_ON(!cl || !cl->dev))
119*4882a593Smuzhiyun 		return -ENODEV;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	dev = cl->dev;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	mei_cl_vtag_remove_by_fp(cl, file);
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	if (!list_empty(&cl->vtag_map)) {
128*4882a593Smuzhiyun 		cl_dbg(dev, cl, "not the last vtag\n");
129*4882a593Smuzhiyun 		mei_cl_flush_queues(cl, file);
130*4882a593Smuzhiyun 		rets = 0;
131*4882a593Smuzhiyun 		goto out;
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	rets = mei_cl_disconnect(cl);
135*4882a593Smuzhiyun 	/*
136*4882a593Smuzhiyun 	 * Check again: This is necessary since disconnect releases the lock
137*4882a593Smuzhiyun 	 * and another client can connect in the meantime.
138*4882a593Smuzhiyun 	 */
139*4882a593Smuzhiyun 	if (!list_empty(&cl->vtag_map)) {
140*4882a593Smuzhiyun 		cl_dbg(dev, cl, "not the last vtag after disconnect\n");
141*4882a593Smuzhiyun 		mei_cl_flush_queues(cl, file);
142*4882a593Smuzhiyun 		goto out;
143*4882a593Smuzhiyun 	}
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	mei_cl_flush_queues(cl, NULL);
146*4882a593Smuzhiyun 	cl_dbg(dev, cl, "removing\n");
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	mei_cl_unlink(cl);
149*4882a593Smuzhiyun 	kfree(cl);
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun out:
152*4882a593Smuzhiyun 	file->private_data = NULL;
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
155*4882a593Smuzhiyun 	return rets;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun /**
160*4882a593Smuzhiyun  * mei_read - the read function.
161*4882a593Smuzhiyun  *
162*4882a593Smuzhiyun  * @file: pointer to file structure
163*4882a593Smuzhiyun  * @ubuf: pointer to user buffer
164*4882a593Smuzhiyun  * @length: buffer length
165*4882a593Smuzhiyun  * @offset: data offset in buffer
166*4882a593Smuzhiyun  *
167*4882a593Smuzhiyun  * Return: >=0 data length on success , <0 on error
168*4882a593Smuzhiyun  */
mei_read(struct file * file,char __user * ubuf,size_t length,loff_t * offset)169*4882a593Smuzhiyun static ssize_t mei_read(struct file *file, char __user *ubuf,
170*4882a593Smuzhiyun 			size_t length, loff_t *offset)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun 	struct mei_cl *cl = file->private_data;
173*4882a593Smuzhiyun 	struct mei_device *dev;
174*4882a593Smuzhiyun 	struct mei_cl_cb *cb = NULL;
175*4882a593Smuzhiyun 	bool nonblock = !!(file->f_flags & O_NONBLOCK);
176*4882a593Smuzhiyun 	ssize_t rets;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	if (WARN_ON(!cl || !cl->dev))
179*4882a593Smuzhiyun 		return -ENODEV;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	dev = cl->dev;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
185*4882a593Smuzhiyun 	if (dev->dev_state != MEI_DEV_ENABLED) {
186*4882a593Smuzhiyun 		rets = -ENODEV;
187*4882a593Smuzhiyun 		goto out;
188*4882a593Smuzhiyun 	}
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	if (length == 0) {
191*4882a593Smuzhiyun 		rets = 0;
192*4882a593Smuzhiyun 		goto out;
193*4882a593Smuzhiyun 	}
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	if (ubuf == NULL) {
196*4882a593Smuzhiyun 		rets = -EMSGSIZE;
197*4882a593Smuzhiyun 		goto out;
198*4882a593Smuzhiyun 	}
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	cb = mei_cl_read_cb(cl, file);
201*4882a593Smuzhiyun 	if (cb)
202*4882a593Smuzhiyun 		goto copy_buffer;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	if (*offset > 0)
205*4882a593Smuzhiyun 		*offset = 0;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	rets = mei_cl_read_start(cl, length, file);
208*4882a593Smuzhiyun 	if (rets && rets != -EBUSY) {
209*4882a593Smuzhiyun 		cl_dbg(dev, cl, "mei start read failure status = %zd\n", rets);
210*4882a593Smuzhiyun 		goto out;
211*4882a593Smuzhiyun 	}
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	if (nonblock) {
214*4882a593Smuzhiyun 		rets = -EAGAIN;
215*4882a593Smuzhiyun 		goto out;
216*4882a593Smuzhiyun 	}
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
219*4882a593Smuzhiyun 	if (wait_event_interruptible(cl->rx_wait,
220*4882a593Smuzhiyun 				     mei_cl_read_cb(cl, file) ||
221*4882a593Smuzhiyun 				     !mei_cl_is_connected(cl))) {
222*4882a593Smuzhiyun 		if (signal_pending(current))
223*4882a593Smuzhiyun 			return -EINTR;
224*4882a593Smuzhiyun 		return -ERESTARTSYS;
225*4882a593Smuzhiyun 	}
226*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	if (!mei_cl_is_connected(cl)) {
229*4882a593Smuzhiyun 		rets = -ENODEV;
230*4882a593Smuzhiyun 		goto out;
231*4882a593Smuzhiyun 	}
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	cb = mei_cl_read_cb(cl, file);
234*4882a593Smuzhiyun 	if (!cb) {
235*4882a593Smuzhiyun 		rets = 0;
236*4882a593Smuzhiyun 		goto out;
237*4882a593Smuzhiyun 	}
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun copy_buffer:
240*4882a593Smuzhiyun 	/* now copy the data to user space */
241*4882a593Smuzhiyun 	if (cb->status) {
242*4882a593Smuzhiyun 		rets = cb->status;
243*4882a593Smuzhiyun 		cl_dbg(dev, cl, "read operation failed %zd\n", rets);
244*4882a593Smuzhiyun 		goto free;
245*4882a593Smuzhiyun 	}
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	cl_dbg(dev, cl, "buf.size = %zu buf.idx = %zu offset = %lld\n",
248*4882a593Smuzhiyun 	       cb->buf.size, cb->buf_idx, *offset);
249*4882a593Smuzhiyun 	if (*offset >= cb->buf_idx) {
250*4882a593Smuzhiyun 		rets = 0;
251*4882a593Smuzhiyun 		goto free;
252*4882a593Smuzhiyun 	}
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	/* length is being truncated to PAGE_SIZE,
255*4882a593Smuzhiyun 	 * however buf_idx may point beyond that */
256*4882a593Smuzhiyun 	length = min_t(size_t, length, cb->buf_idx - *offset);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	if (copy_to_user(ubuf, cb->buf.data + *offset, length)) {
259*4882a593Smuzhiyun 		dev_dbg(dev->dev, "failed to copy data to userland\n");
260*4882a593Smuzhiyun 		rets = -EFAULT;
261*4882a593Smuzhiyun 		goto free;
262*4882a593Smuzhiyun 	}
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	rets = length;
265*4882a593Smuzhiyun 	*offset += length;
266*4882a593Smuzhiyun 	/* not all data was read, keep the cb */
267*4882a593Smuzhiyun 	if (*offset < cb->buf_idx)
268*4882a593Smuzhiyun 		goto out;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun free:
271*4882a593Smuzhiyun 	mei_cl_del_rd_completed(cl, cb);
272*4882a593Smuzhiyun 	*offset = 0;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun out:
275*4882a593Smuzhiyun 	cl_dbg(dev, cl, "end mei read rets = %zd\n", rets);
276*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
277*4882a593Smuzhiyun 	return rets;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun /**
281*4882a593Smuzhiyun  * mei_cl_vtag_by_fp - obtain the vtag by file pointer
282*4882a593Smuzhiyun  *
283*4882a593Smuzhiyun  * @cl: host client
284*4882a593Smuzhiyun  * @fp: pointer to file structure
285*4882a593Smuzhiyun  *
286*4882a593Smuzhiyun  * Return: vtag value on success, otherwise 0
287*4882a593Smuzhiyun  */
mei_cl_vtag_by_fp(const struct mei_cl * cl,const struct file * fp)288*4882a593Smuzhiyun static u8 mei_cl_vtag_by_fp(const struct mei_cl *cl, const struct file *fp)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun 	struct mei_cl_vtag *cl_vtag;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	if (!fp)
293*4882a593Smuzhiyun 		return 0;
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	list_for_each_entry(cl_vtag, &cl->vtag_map, list)
296*4882a593Smuzhiyun 		if (cl_vtag->fp == fp)
297*4882a593Smuzhiyun 			return cl_vtag->vtag;
298*4882a593Smuzhiyun 	return 0;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun /**
302*4882a593Smuzhiyun  * mei_write - the write function.
303*4882a593Smuzhiyun  *
304*4882a593Smuzhiyun  * @file: pointer to file structure
305*4882a593Smuzhiyun  * @ubuf: pointer to user buffer
306*4882a593Smuzhiyun  * @length: buffer length
307*4882a593Smuzhiyun  * @offset: data offset in buffer
308*4882a593Smuzhiyun  *
309*4882a593Smuzhiyun  * Return: >=0 data length on success , <0 on error
310*4882a593Smuzhiyun  */
mei_write(struct file * file,const char __user * ubuf,size_t length,loff_t * offset)311*4882a593Smuzhiyun static ssize_t mei_write(struct file *file, const char __user *ubuf,
312*4882a593Smuzhiyun 			 size_t length, loff_t *offset)
313*4882a593Smuzhiyun {
314*4882a593Smuzhiyun 	struct mei_cl *cl = file->private_data;
315*4882a593Smuzhiyun 	struct mei_cl_cb *cb;
316*4882a593Smuzhiyun 	struct mei_device *dev;
317*4882a593Smuzhiyun 	ssize_t rets;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	if (WARN_ON(!cl || !cl->dev))
320*4882a593Smuzhiyun 		return -ENODEV;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	dev = cl->dev;
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	if (dev->dev_state != MEI_DEV_ENABLED) {
327*4882a593Smuzhiyun 		rets = -ENODEV;
328*4882a593Smuzhiyun 		goto out;
329*4882a593Smuzhiyun 	}
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	if (!mei_cl_is_connected(cl)) {
332*4882a593Smuzhiyun 		cl_err(dev, cl, "is not connected");
333*4882a593Smuzhiyun 		rets = -ENODEV;
334*4882a593Smuzhiyun 		goto out;
335*4882a593Smuzhiyun 	}
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	if (!mei_me_cl_is_active(cl->me_cl)) {
338*4882a593Smuzhiyun 		rets = -ENOTTY;
339*4882a593Smuzhiyun 		goto out;
340*4882a593Smuzhiyun 	}
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	if (length > mei_cl_mtu(cl)) {
343*4882a593Smuzhiyun 		rets = -EFBIG;
344*4882a593Smuzhiyun 		goto out;
345*4882a593Smuzhiyun 	}
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	if (length == 0) {
348*4882a593Smuzhiyun 		rets = 0;
349*4882a593Smuzhiyun 		goto out;
350*4882a593Smuzhiyun 	}
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	while (cl->tx_cb_queued >= dev->tx_queue_limit) {
353*4882a593Smuzhiyun 		if (file->f_flags & O_NONBLOCK) {
354*4882a593Smuzhiyun 			rets = -EAGAIN;
355*4882a593Smuzhiyun 			goto out;
356*4882a593Smuzhiyun 		}
357*4882a593Smuzhiyun 		mutex_unlock(&dev->device_lock);
358*4882a593Smuzhiyun 		rets = wait_event_interruptible(cl->tx_wait,
359*4882a593Smuzhiyun 				cl->writing_state == MEI_WRITE_COMPLETE ||
360*4882a593Smuzhiyun 				(!mei_cl_is_connected(cl)));
361*4882a593Smuzhiyun 		mutex_lock(&dev->device_lock);
362*4882a593Smuzhiyun 		if (rets) {
363*4882a593Smuzhiyun 			if (signal_pending(current))
364*4882a593Smuzhiyun 				rets = -EINTR;
365*4882a593Smuzhiyun 			goto out;
366*4882a593Smuzhiyun 		}
367*4882a593Smuzhiyun 		if (!mei_cl_is_connected(cl)) {
368*4882a593Smuzhiyun 			rets = -ENODEV;
369*4882a593Smuzhiyun 			goto out;
370*4882a593Smuzhiyun 		}
371*4882a593Smuzhiyun 	}
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
374*4882a593Smuzhiyun 	if (!cb) {
375*4882a593Smuzhiyun 		rets = -ENOMEM;
376*4882a593Smuzhiyun 		goto out;
377*4882a593Smuzhiyun 	}
378*4882a593Smuzhiyun 	cb->vtag = mei_cl_vtag_by_fp(cl, file);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	rets = copy_from_user(cb->buf.data, ubuf, length);
381*4882a593Smuzhiyun 	if (rets) {
382*4882a593Smuzhiyun 		dev_dbg(dev->dev, "failed to copy data from userland\n");
383*4882a593Smuzhiyun 		rets = -EFAULT;
384*4882a593Smuzhiyun 		mei_io_cb_free(cb);
385*4882a593Smuzhiyun 		goto out;
386*4882a593Smuzhiyun 	}
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	rets = mei_cl_write(cl, cb);
389*4882a593Smuzhiyun out:
390*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
391*4882a593Smuzhiyun 	return rets;
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun /**
395*4882a593Smuzhiyun  * mei_ioctl_connect_client - the connect to fw client IOCTL function
396*4882a593Smuzhiyun  *
397*4882a593Smuzhiyun  * @file: private data of the file object
398*4882a593Smuzhiyun  * @in_client_uuid: requested UUID for connection
399*4882a593Smuzhiyun  * @client: IOCTL connect data, output parameters
400*4882a593Smuzhiyun  *
401*4882a593Smuzhiyun  * Locking: called under "dev->device_lock" lock
402*4882a593Smuzhiyun  *
403*4882a593Smuzhiyun  * Return: 0 on success, <0 on failure.
404*4882a593Smuzhiyun  */
mei_ioctl_connect_client(struct file * file,const uuid_le * in_client_uuid,struct mei_client * client)405*4882a593Smuzhiyun static int mei_ioctl_connect_client(struct file *file,
406*4882a593Smuzhiyun 				    const uuid_le *in_client_uuid,
407*4882a593Smuzhiyun 				    struct mei_client *client)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun 	struct mei_device *dev;
410*4882a593Smuzhiyun 	struct mei_me_client *me_cl;
411*4882a593Smuzhiyun 	struct mei_cl *cl;
412*4882a593Smuzhiyun 	int rets;
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	cl = file->private_data;
415*4882a593Smuzhiyun 	dev = cl->dev;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	if (cl->state != MEI_FILE_INITIALIZING &&
418*4882a593Smuzhiyun 	    cl->state != MEI_FILE_DISCONNECTED)
419*4882a593Smuzhiyun 		return  -EBUSY;
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	/* find ME client we're trying to connect to */
422*4882a593Smuzhiyun 	me_cl = mei_me_cl_by_uuid(dev, in_client_uuid);
423*4882a593Smuzhiyun 	if (!me_cl) {
424*4882a593Smuzhiyun 		dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n",
425*4882a593Smuzhiyun 			in_client_uuid);
426*4882a593Smuzhiyun 		rets = -ENOTTY;
427*4882a593Smuzhiyun 		goto end;
428*4882a593Smuzhiyun 	}
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	if (me_cl->props.fixed_address) {
431*4882a593Smuzhiyun 		bool forbidden = dev->override_fixed_address ?
432*4882a593Smuzhiyun 			 !dev->allow_fixed_address : !dev->hbm_f_fa_supported;
433*4882a593Smuzhiyun 		if (forbidden) {
434*4882a593Smuzhiyun 			dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n",
435*4882a593Smuzhiyun 				in_client_uuid);
436*4882a593Smuzhiyun 			rets = -ENOTTY;
437*4882a593Smuzhiyun 			goto end;
438*4882a593Smuzhiyun 		}
439*4882a593Smuzhiyun 	}
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	dev_dbg(dev->dev, "Connect to FW Client ID = %d\n",
442*4882a593Smuzhiyun 			me_cl->client_id);
443*4882a593Smuzhiyun 	dev_dbg(dev->dev, "FW Client - Protocol Version = %d\n",
444*4882a593Smuzhiyun 			me_cl->props.protocol_version);
445*4882a593Smuzhiyun 	dev_dbg(dev->dev, "FW Client - Max Msg Len = %d\n",
446*4882a593Smuzhiyun 			me_cl->props.max_msg_length);
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	/* prepare the output buffer */
449*4882a593Smuzhiyun 	client->max_msg_length = me_cl->props.max_msg_length;
450*4882a593Smuzhiyun 	client->protocol_version = me_cl->props.protocol_version;
451*4882a593Smuzhiyun 	dev_dbg(dev->dev, "Can connect?\n");
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	rets = mei_cl_connect(cl, me_cl, file);
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun end:
456*4882a593Smuzhiyun 	mei_me_cl_put(me_cl);
457*4882a593Smuzhiyun 	return rets;
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun /**
461*4882a593Smuzhiyun  * mei_vt_support_check - check if client support vtags
462*4882a593Smuzhiyun  *
463*4882a593Smuzhiyun  * Locking: called under "dev->device_lock" lock
464*4882a593Smuzhiyun  *
465*4882a593Smuzhiyun  * @dev: mei_device
466*4882a593Smuzhiyun  * @uuid: client UUID
467*4882a593Smuzhiyun  *
468*4882a593Smuzhiyun  * Return:
469*4882a593Smuzhiyun  *	0 - supported
470*4882a593Smuzhiyun  *	-ENOTTY - no such client
471*4882a593Smuzhiyun  *	-EOPNOTSUPP - vtags are not supported by client
472*4882a593Smuzhiyun  */
mei_vt_support_check(struct mei_device * dev,const uuid_le * uuid)473*4882a593Smuzhiyun static int mei_vt_support_check(struct mei_device *dev, const uuid_le *uuid)
474*4882a593Smuzhiyun {
475*4882a593Smuzhiyun 	struct mei_me_client *me_cl;
476*4882a593Smuzhiyun 	int ret;
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	if (!dev->hbm_f_vt_supported)
479*4882a593Smuzhiyun 		return -EOPNOTSUPP;
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	me_cl = mei_me_cl_by_uuid(dev, uuid);
482*4882a593Smuzhiyun 	if (!me_cl) {
483*4882a593Smuzhiyun 		dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n",
484*4882a593Smuzhiyun 			uuid);
485*4882a593Smuzhiyun 		return -ENOTTY;
486*4882a593Smuzhiyun 	}
487*4882a593Smuzhiyun 	ret = me_cl->props.vt_supported ? 0 : -EOPNOTSUPP;
488*4882a593Smuzhiyun 	mei_me_cl_put(me_cl);
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun 	return ret;
491*4882a593Smuzhiyun }
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun /**
494*4882a593Smuzhiyun  * mei_ioctl_connect_vtag - connect to fw client with vtag IOCTL function
495*4882a593Smuzhiyun  *
496*4882a593Smuzhiyun  * @file: private data of the file object
497*4882a593Smuzhiyun  * @in_client_uuid: requested UUID for connection
498*4882a593Smuzhiyun  * @client: IOCTL connect data, output parameters
499*4882a593Smuzhiyun  * @vtag: vm tag
500*4882a593Smuzhiyun  *
501*4882a593Smuzhiyun  * Locking: called under "dev->device_lock" lock
502*4882a593Smuzhiyun  *
503*4882a593Smuzhiyun  * Return: 0 on success, <0 on failure.
504*4882a593Smuzhiyun  */
mei_ioctl_connect_vtag(struct file * file,const uuid_le * in_client_uuid,struct mei_client * client,u8 vtag)505*4882a593Smuzhiyun static int mei_ioctl_connect_vtag(struct file *file,
506*4882a593Smuzhiyun 				  const uuid_le *in_client_uuid,
507*4882a593Smuzhiyun 				  struct mei_client *client,
508*4882a593Smuzhiyun 				  u8 vtag)
509*4882a593Smuzhiyun {
510*4882a593Smuzhiyun 	struct mei_device *dev;
511*4882a593Smuzhiyun 	struct mei_cl *cl;
512*4882a593Smuzhiyun 	struct mei_cl *pos;
513*4882a593Smuzhiyun 	struct mei_cl_vtag *cl_vtag;
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	cl = file->private_data;
516*4882a593Smuzhiyun 	dev = cl->dev;
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 	dev_dbg(dev->dev, "FW Client %pUl vtag %d\n", in_client_uuid, vtag);
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	switch (cl->state) {
521*4882a593Smuzhiyun 	case MEI_FILE_DISCONNECTED:
522*4882a593Smuzhiyun 		if (mei_cl_vtag_by_fp(cl, file) != vtag) {
523*4882a593Smuzhiyun 			dev_err(dev->dev, "reconnect with different vtag\n");
524*4882a593Smuzhiyun 			return -EINVAL;
525*4882a593Smuzhiyun 		}
526*4882a593Smuzhiyun 		break;
527*4882a593Smuzhiyun 	case MEI_FILE_INITIALIZING:
528*4882a593Smuzhiyun 		/* malicious connect from another thread may push vtag */
529*4882a593Smuzhiyun 		if (!IS_ERR(mei_cl_fp_by_vtag(cl, vtag))) {
530*4882a593Smuzhiyun 			dev_err(dev->dev, "vtag already filled\n");
531*4882a593Smuzhiyun 			return -EINVAL;
532*4882a593Smuzhiyun 		}
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 		list_for_each_entry(pos, &dev->file_list, link) {
535*4882a593Smuzhiyun 			if (pos == cl)
536*4882a593Smuzhiyun 				continue;
537*4882a593Smuzhiyun 			if (!pos->me_cl)
538*4882a593Smuzhiyun 				continue;
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 			/* only search for same UUID */
541*4882a593Smuzhiyun 			if (uuid_le_cmp(*mei_cl_uuid(pos), *in_client_uuid))
542*4882a593Smuzhiyun 				continue;
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 			/* if tag already exist try another fp */
545*4882a593Smuzhiyun 			if (!IS_ERR(mei_cl_fp_by_vtag(pos, vtag)))
546*4882a593Smuzhiyun 				continue;
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 			/* replace cl with acquired one */
549*4882a593Smuzhiyun 			dev_dbg(dev->dev, "replacing with existing cl\n");
550*4882a593Smuzhiyun 			mei_cl_unlink(cl);
551*4882a593Smuzhiyun 			kfree(cl);
552*4882a593Smuzhiyun 			file->private_data = pos;
553*4882a593Smuzhiyun 			cl = pos;
554*4882a593Smuzhiyun 			break;
555*4882a593Smuzhiyun 		}
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 		cl_vtag = mei_cl_vtag_alloc(file, vtag);
558*4882a593Smuzhiyun 		if (IS_ERR(cl_vtag))
559*4882a593Smuzhiyun 			return -ENOMEM;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 		list_add_tail(&cl_vtag->list, &cl->vtag_map);
562*4882a593Smuzhiyun 		break;
563*4882a593Smuzhiyun 	default:
564*4882a593Smuzhiyun 		return -EBUSY;
565*4882a593Smuzhiyun 	}
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	while (cl->state != MEI_FILE_INITIALIZING &&
568*4882a593Smuzhiyun 	       cl->state != MEI_FILE_DISCONNECTED &&
569*4882a593Smuzhiyun 	       cl->state != MEI_FILE_CONNECTED) {
570*4882a593Smuzhiyun 		mutex_unlock(&dev->device_lock);
571*4882a593Smuzhiyun 		wait_event_timeout(cl->wait,
572*4882a593Smuzhiyun 				   (cl->state == MEI_FILE_CONNECTED ||
573*4882a593Smuzhiyun 				    cl->state == MEI_FILE_DISCONNECTED ||
574*4882a593Smuzhiyun 				    cl->state == MEI_FILE_DISCONNECT_REQUIRED ||
575*4882a593Smuzhiyun 				    cl->state == MEI_FILE_DISCONNECT_REPLY),
576*4882a593Smuzhiyun 				   mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
577*4882a593Smuzhiyun 		mutex_lock(&dev->device_lock);
578*4882a593Smuzhiyun 	}
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	if (!mei_cl_is_connected(cl))
581*4882a593Smuzhiyun 		return mei_ioctl_connect_client(file, in_client_uuid, client);
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	client->max_msg_length = cl->me_cl->props.max_msg_length;
584*4882a593Smuzhiyun 	client->protocol_version = cl->me_cl->props.protocol_version;
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	return 0;
587*4882a593Smuzhiyun }
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun /**
590*4882a593Smuzhiyun  * mei_ioctl_client_notify_request -
591*4882a593Smuzhiyun  *     propagate event notification request to client
592*4882a593Smuzhiyun  *
593*4882a593Smuzhiyun  * @file: pointer to file structure
594*4882a593Smuzhiyun  * @request: 0 - disable, 1 - enable
595*4882a593Smuzhiyun  *
596*4882a593Smuzhiyun  * Return: 0 on success , <0 on error
597*4882a593Smuzhiyun  */
mei_ioctl_client_notify_request(const struct file * file,u32 request)598*4882a593Smuzhiyun static int mei_ioctl_client_notify_request(const struct file *file, u32 request)
599*4882a593Smuzhiyun {
600*4882a593Smuzhiyun 	struct mei_cl *cl = file->private_data;
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	if (request != MEI_HBM_NOTIFICATION_START &&
603*4882a593Smuzhiyun 	    request != MEI_HBM_NOTIFICATION_STOP)
604*4882a593Smuzhiyun 		return -EINVAL;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	return mei_cl_notify_request(cl, file, (u8)request);
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun /**
610*4882a593Smuzhiyun  * mei_ioctl_client_notify_get -  wait for notification request
611*4882a593Smuzhiyun  *
612*4882a593Smuzhiyun  * @file: pointer to file structure
613*4882a593Smuzhiyun  * @notify_get: 0 - disable, 1 - enable
614*4882a593Smuzhiyun  *
615*4882a593Smuzhiyun  * Return: 0 on success , <0 on error
616*4882a593Smuzhiyun  */
mei_ioctl_client_notify_get(const struct file * file,u32 * notify_get)617*4882a593Smuzhiyun static int mei_ioctl_client_notify_get(const struct file *file, u32 *notify_get)
618*4882a593Smuzhiyun {
619*4882a593Smuzhiyun 	struct mei_cl *cl = file->private_data;
620*4882a593Smuzhiyun 	bool notify_ev;
621*4882a593Smuzhiyun 	bool block = (file->f_flags & O_NONBLOCK) == 0;
622*4882a593Smuzhiyun 	int rets;
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	rets = mei_cl_notify_get(cl, block, &notify_ev);
625*4882a593Smuzhiyun 	if (rets)
626*4882a593Smuzhiyun 		return rets;
627*4882a593Smuzhiyun 
628*4882a593Smuzhiyun 	*notify_get = notify_ev ? 1 : 0;
629*4882a593Smuzhiyun 	return 0;
630*4882a593Smuzhiyun }
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun /**
633*4882a593Smuzhiyun  * mei_ioctl - the IOCTL function
634*4882a593Smuzhiyun  *
635*4882a593Smuzhiyun  * @file: pointer to file structure
636*4882a593Smuzhiyun  * @cmd: ioctl command
637*4882a593Smuzhiyun  * @data: pointer to mei message structure
638*4882a593Smuzhiyun  *
639*4882a593Smuzhiyun  * Return: 0 on success , <0 on error
640*4882a593Smuzhiyun  */
mei_ioctl(struct file * file,unsigned int cmd,unsigned long data)641*4882a593Smuzhiyun static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
642*4882a593Smuzhiyun {
643*4882a593Smuzhiyun 	struct mei_device *dev;
644*4882a593Smuzhiyun 	struct mei_cl *cl = file->private_data;
645*4882a593Smuzhiyun 	struct mei_connect_client_data conn;
646*4882a593Smuzhiyun 	struct mei_connect_client_data_vtag conn_vtag;
647*4882a593Smuzhiyun 	const uuid_le *cl_uuid;
648*4882a593Smuzhiyun 	struct mei_client *props;
649*4882a593Smuzhiyun 	u8 vtag;
650*4882a593Smuzhiyun 	u32 notify_get, notify_req;
651*4882a593Smuzhiyun 	int rets;
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun 	if (WARN_ON(!cl || !cl->dev))
655*4882a593Smuzhiyun 		return -ENODEV;
656*4882a593Smuzhiyun 
657*4882a593Smuzhiyun 	dev = cl->dev;
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 	dev_dbg(dev->dev, "IOCTL cmd = 0x%x", cmd);
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
662*4882a593Smuzhiyun 	if (dev->dev_state != MEI_DEV_ENABLED) {
663*4882a593Smuzhiyun 		rets = -ENODEV;
664*4882a593Smuzhiyun 		goto out;
665*4882a593Smuzhiyun 	}
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	switch (cmd) {
668*4882a593Smuzhiyun 	case IOCTL_MEI_CONNECT_CLIENT:
669*4882a593Smuzhiyun 		dev_dbg(dev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n");
670*4882a593Smuzhiyun 		if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) {
671*4882a593Smuzhiyun 			dev_dbg(dev->dev, "failed to copy data from userland\n");
672*4882a593Smuzhiyun 			rets = -EFAULT;
673*4882a593Smuzhiyun 			goto out;
674*4882a593Smuzhiyun 		}
675*4882a593Smuzhiyun 		cl_uuid = &conn.in_client_uuid;
676*4882a593Smuzhiyun 		props = &conn.out_client_properties;
677*4882a593Smuzhiyun 		vtag = 0;
678*4882a593Smuzhiyun 
679*4882a593Smuzhiyun 		rets = mei_vt_support_check(dev, cl_uuid);
680*4882a593Smuzhiyun 		if (rets == -ENOTTY)
681*4882a593Smuzhiyun 			goto out;
682*4882a593Smuzhiyun 		if (!rets)
683*4882a593Smuzhiyun 			rets = mei_ioctl_connect_vtag(file, cl_uuid, props,
684*4882a593Smuzhiyun 						      vtag);
685*4882a593Smuzhiyun 		else
686*4882a593Smuzhiyun 			rets = mei_ioctl_connect_client(file, cl_uuid, props);
687*4882a593Smuzhiyun 		if (rets)
688*4882a593Smuzhiyun 			goto out;
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun 		/* if all is ok, copying the data back to user. */
691*4882a593Smuzhiyun 		if (copy_to_user((char __user *)data, &conn, sizeof(conn))) {
692*4882a593Smuzhiyun 			dev_dbg(dev->dev, "failed to copy data to userland\n");
693*4882a593Smuzhiyun 			rets = -EFAULT;
694*4882a593Smuzhiyun 			goto out;
695*4882a593Smuzhiyun 		}
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 		break;
698*4882a593Smuzhiyun 
699*4882a593Smuzhiyun 	case IOCTL_MEI_CONNECT_CLIENT_VTAG:
700*4882a593Smuzhiyun 		dev_dbg(dev->dev, "IOCTL_MEI_CONNECT_CLIENT_VTAG\n");
701*4882a593Smuzhiyun 		if (copy_from_user(&conn_vtag, (char __user *)data,
702*4882a593Smuzhiyun 				   sizeof(conn_vtag))) {
703*4882a593Smuzhiyun 			dev_dbg(dev->dev, "failed to copy data from userland\n");
704*4882a593Smuzhiyun 			rets = -EFAULT;
705*4882a593Smuzhiyun 			goto out;
706*4882a593Smuzhiyun 		}
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun 		cl_uuid = &conn_vtag.connect.in_client_uuid;
709*4882a593Smuzhiyun 		props = &conn_vtag.out_client_properties;
710*4882a593Smuzhiyun 		vtag = conn_vtag.connect.vtag;
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun 		rets = mei_vt_support_check(dev, cl_uuid);
713*4882a593Smuzhiyun 		if (rets == -EOPNOTSUPP)
714*4882a593Smuzhiyun 			dev_dbg(dev->dev, "FW Client %pUl does not support vtags\n",
715*4882a593Smuzhiyun 				cl_uuid);
716*4882a593Smuzhiyun 		if (rets)
717*4882a593Smuzhiyun 			goto out;
718*4882a593Smuzhiyun 
719*4882a593Smuzhiyun 		if (!vtag) {
720*4882a593Smuzhiyun 			dev_dbg(dev->dev, "vtag can't be zero\n");
721*4882a593Smuzhiyun 			rets = -EINVAL;
722*4882a593Smuzhiyun 			goto out;
723*4882a593Smuzhiyun 		}
724*4882a593Smuzhiyun 
725*4882a593Smuzhiyun 		rets = mei_ioctl_connect_vtag(file, cl_uuid, props, vtag);
726*4882a593Smuzhiyun 		if (rets)
727*4882a593Smuzhiyun 			goto out;
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun 		/* if all is ok, copying the data back to user. */
730*4882a593Smuzhiyun 		if (copy_to_user((char __user *)data, &conn_vtag,
731*4882a593Smuzhiyun 				 sizeof(conn_vtag))) {
732*4882a593Smuzhiyun 			dev_dbg(dev->dev, "failed to copy data to userland\n");
733*4882a593Smuzhiyun 			rets = -EFAULT;
734*4882a593Smuzhiyun 			goto out;
735*4882a593Smuzhiyun 		}
736*4882a593Smuzhiyun 
737*4882a593Smuzhiyun 		break;
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 	case IOCTL_MEI_NOTIFY_SET:
740*4882a593Smuzhiyun 		dev_dbg(dev->dev, ": IOCTL_MEI_NOTIFY_SET.\n");
741*4882a593Smuzhiyun 		if (copy_from_user(&notify_req,
742*4882a593Smuzhiyun 				   (char __user *)data, sizeof(notify_req))) {
743*4882a593Smuzhiyun 			dev_dbg(dev->dev, "failed to copy data from userland\n");
744*4882a593Smuzhiyun 			rets = -EFAULT;
745*4882a593Smuzhiyun 			goto out;
746*4882a593Smuzhiyun 		}
747*4882a593Smuzhiyun 		rets = mei_ioctl_client_notify_request(file, notify_req);
748*4882a593Smuzhiyun 		break;
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun 	case IOCTL_MEI_NOTIFY_GET:
751*4882a593Smuzhiyun 		dev_dbg(dev->dev, ": IOCTL_MEI_NOTIFY_GET.\n");
752*4882a593Smuzhiyun 		rets = mei_ioctl_client_notify_get(file, &notify_get);
753*4882a593Smuzhiyun 		if (rets)
754*4882a593Smuzhiyun 			goto out;
755*4882a593Smuzhiyun 
756*4882a593Smuzhiyun 		dev_dbg(dev->dev, "copy connect data to user\n");
757*4882a593Smuzhiyun 		if (copy_to_user((char __user *)data,
758*4882a593Smuzhiyun 				&notify_get, sizeof(notify_get))) {
759*4882a593Smuzhiyun 			dev_dbg(dev->dev, "failed to copy data to userland\n");
760*4882a593Smuzhiyun 			rets = -EFAULT;
761*4882a593Smuzhiyun 			goto out;
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun 		}
764*4882a593Smuzhiyun 		break;
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 	default:
767*4882a593Smuzhiyun 		rets = -ENOIOCTLCMD;
768*4882a593Smuzhiyun 	}
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun out:
771*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
772*4882a593Smuzhiyun 	return rets;
773*4882a593Smuzhiyun }
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun /**
776*4882a593Smuzhiyun  * mei_poll - the poll function
777*4882a593Smuzhiyun  *
778*4882a593Smuzhiyun  * @file: pointer to file structure
779*4882a593Smuzhiyun  * @wait: pointer to poll_table structure
780*4882a593Smuzhiyun  *
781*4882a593Smuzhiyun  * Return: poll mask
782*4882a593Smuzhiyun  */
mei_poll(struct file * file,poll_table * wait)783*4882a593Smuzhiyun static __poll_t mei_poll(struct file *file, poll_table *wait)
784*4882a593Smuzhiyun {
785*4882a593Smuzhiyun 	__poll_t req_events = poll_requested_events(wait);
786*4882a593Smuzhiyun 	struct mei_cl *cl = file->private_data;
787*4882a593Smuzhiyun 	struct mei_device *dev;
788*4882a593Smuzhiyun 	__poll_t mask = 0;
789*4882a593Smuzhiyun 	bool notify_en;
790*4882a593Smuzhiyun 
791*4882a593Smuzhiyun 	if (WARN_ON(!cl || !cl->dev))
792*4882a593Smuzhiyun 		return EPOLLERR;
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	dev = cl->dev;
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
797*4882a593Smuzhiyun 
798*4882a593Smuzhiyun 	notify_en = cl->notify_en && (req_events & EPOLLPRI);
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun 	if (dev->dev_state != MEI_DEV_ENABLED ||
801*4882a593Smuzhiyun 	    !mei_cl_is_connected(cl)) {
802*4882a593Smuzhiyun 		mask = EPOLLERR;
803*4882a593Smuzhiyun 		goto out;
804*4882a593Smuzhiyun 	}
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun 	if (notify_en) {
807*4882a593Smuzhiyun 		poll_wait(file, &cl->ev_wait, wait);
808*4882a593Smuzhiyun 		if (cl->notify_ev)
809*4882a593Smuzhiyun 			mask |= EPOLLPRI;
810*4882a593Smuzhiyun 	}
811*4882a593Smuzhiyun 
812*4882a593Smuzhiyun 	if (req_events & (EPOLLIN | EPOLLRDNORM)) {
813*4882a593Smuzhiyun 		poll_wait(file, &cl->rx_wait, wait);
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 		if (mei_cl_read_cb(cl, file))
816*4882a593Smuzhiyun 			mask |= EPOLLIN | EPOLLRDNORM;
817*4882a593Smuzhiyun 		else
818*4882a593Smuzhiyun 			mei_cl_read_start(cl, mei_cl_mtu(cl), file);
819*4882a593Smuzhiyun 	}
820*4882a593Smuzhiyun 
821*4882a593Smuzhiyun 	if (req_events & (EPOLLOUT | EPOLLWRNORM)) {
822*4882a593Smuzhiyun 		poll_wait(file, &cl->tx_wait, wait);
823*4882a593Smuzhiyun 		if (cl->tx_cb_queued < dev->tx_queue_limit)
824*4882a593Smuzhiyun 			mask |= EPOLLOUT | EPOLLWRNORM;
825*4882a593Smuzhiyun 	}
826*4882a593Smuzhiyun 
827*4882a593Smuzhiyun out:
828*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
829*4882a593Smuzhiyun 	return mask;
830*4882a593Smuzhiyun }
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun /**
833*4882a593Smuzhiyun  * mei_cl_is_write_queued - check if the client has pending writes.
834*4882a593Smuzhiyun  *
835*4882a593Smuzhiyun  * @cl: writing host client
836*4882a593Smuzhiyun  *
837*4882a593Smuzhiyun  * Return: true if client is writing, false otherwise.
838*4882a593Smuzhiyun  */
mei_cl_is_write_queued(struct mei_cl * cl)839*4882a593Smuzhiyun static bool mei_cl_is_write_queued(struct mei_cl *cl)
840*4882a593Smuzhiyun {
841*4882a593Smuzhiyun 	struct mei_device *dev = cl->dev;
842*4882a593Smuzhiyun 	struct mei_cl_cb *cb;
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun 	list_for_each_entry(cb, &dev->write_list, list)
845*4882a593Smuzhiyun 		if (cb->cl == cl)
846*4882a593Smuzhiyun 			return true;
847*4882a593Smuzhiyun 	list_for_each_entry(cb, &dev->write_waiting_list, list)
848*4882a593Smuzhiyun 		if (cb->cl == cl)
849*4882a593Smuzhiyun 			return true;
850*4882a593Smuzhiyun 	return false;
851*4882a593Smuzhiyun }
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun /**
854*4882a593Smuzhiyun  * mei_fsync - the fsync handler
855*4882a593Smuzhiyun  *
856*4882a593Smuzhiyun  * @fp:       pointer to file structure
857*4882a593Smuzhiyun  * @start:    unused
858*4882a593Smuzhiyun  * @end:      unused
859*4882a593Smuzhiyun  * @datasync: unused
860*4882a593Smuzhiyun  *
861*4882a593Smuzhiyun  * Return: 0 on success, -ENODEV if client is not connected
862*4882a593Smuzhiyun  */
mei_fsync(struct file * fp,loff_t start,loff_t end,int datasync)863*4882a593Smuzhiyun static int mei_fsync(struct file *fp, loff_t start, loff_t end, int datasync)
864*4882a593Smuzhiyun {
865*4882a593Smuzhiyun 	struct mei_cl *cl = fp->private_data;
866*4882a593Smuzhiyun 	struct mei_device *dev;
867*4882a593Smuzhiyun 	int rets;
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 	if (WARN_ON(!cl || !cl->dev))
870*4882a593Smuzhiyun 		return -ENODEV;
871*4882a593Smuzhiyun 
872*4882a593Smuzhiyun 	dev = cl->dev;
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
875*4882a593Smuzhiyun 
876*4882a593Smuzhiyun 	if (dev->dev_state != MEI_DEV_ENABLED || !mei_cl_is_connected(cl)) {
877*4882a593Smuzhiyun 		rets = -ENODEV;
878*4882a593Smuzhiyun 		goto out;
879*4882a593Smuzhiyun 	}
880*4882a593Smuzhiyun 
881*4882a593Smuzhiyun 	while (mei_cl_is_write_queued(cl)) {
882*4882a593Smuzhiyun 		mutex_unlock(&dev->device_lock);
883*4882a593Smuzhiyun 		rets = wait_event_interruptible(cl->tx_wait,
884*4882a593Smuzhiyun 				cl->writing_state == MEI_WRITE_COMPLETE ||
885*4882a593Smuzhiyun 				!mei_cl_is_connected(cl));
886*4882a593Smuzhiyun 		mutex_lock(&dev->device_lock);
887*4882a593Smuzhiyun 		if (rets) {
888*4882a593Smuzhiyun 			if (signal_pending(current))
889*4882a593Smuzhiyun 				rets = -EINTR;
890*4882a593Smuzhiyun 			goto out;
891*4882a593Smuzhiyun 		}
892*4882a593Smuzhiyun 		if (!mei_cl_is_connected(cl)) {
893*4882a593Smuzhiyun 			rets = -ENODEV;
894*4882a593Smuzhiyun 			goto out;
895*4882a593Smuzhiyun 		}
896*4882a593Smuzhiyun 	}
897*4882a593Smuzhiyun 	rets = 0;
898*4882a593Smuzhiyun out:
899*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
900*4882a593Smuzhiyun 	return rets;
901*4882a593Smuzhiyun }
902*4882a593Smuzhiyun 
903*4882a593Smuzhiyun /**
904*4882a593Smuzhiyun  * mei_fasync - asynchronous io support
905*4882a593Smuzhiyun  *
906*4882a593Smuzhiyun  * @fd: file descriptor
907*4882a593Smuzhiyun  * @file: pointer to file structure
908*4882a593Smuzhiyun  * @band: band bitmap
909*4882a593Smuzhiyun  *
910*4882a593Smuzhiyun  * Return: negative on error,
911*4882a593Smuzhiyun  *         0 if it did no changes,
912*4882a593Smuzhiyun  *         and positive a process was added or deleted
913*4882a593Smuzhiyun  */
mei_fasync(int fd,struct file * file,int band)914*4882a593Smuzhiyun static int mei_fasync(int fd, struct file *file, int band)
915*4882a593Smuzhiyun {
916*4882a593Smuzhiyun 
917*4882a593Smuzhiyun 	struct mei_cl *cl = file->private_data;
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun 	if (!mei_cl_is_connected(cl))
920*4882a593Smuzhiyun 		return -ENODEV;
921*4882a593Smuzhiyun 
922*4882a593Smuzhiyun 	return fasync_helper(fd, file, band, &cl->ev_async);
923*4882a593Smuzhiyun }
924*4882a593Smuzhiyun 
925*4882a593Smuzhiyun /**
926*4882a593Smuzhiyun  * trc_show - mei device trc attribute show method
927*4882a593Smuzhiyun  *
928*4882a593Smuzhiyun  * @device: device pointer
929*4882a593Smuzhiyun  * @attr: attribute pointer
930*4882a593Smuzhiyun  * @buf:  char out buffer
931*4882a593Smuzhiyun  *
932*4882a593Smuzhiyun  * Return: number of the bytes printed into buf or error
933*4882a593Smuzhiyun  */
trc_show(struct device * device,struct device_attribute * attr,char * buf)934*4882a593Smuzhiyun static ssize_t trc_show(struct device *device,
935*4882a593Smuzhiyun 			struct device_attribute *attr, char *buf)
936*4882a593Smuzhiyun {
937*4882a593Smuzhiyun 	struct mei_device *dev = dev_get_drvdata(device);
938*4882a593Smuzhiyun 	u32 trc;
939*4882a593Smuzhiyun 	int ret;
940*4882a593Smuzhiyun 
941*4882a593Smuzhiyun 	ret = mei_trc_status(dev, &trc);
942*4882a593Smuzhiyun 	if (ret)
943*4882a593Smuzhiyun 		return ret;
944*4882a593Smuzhiyun 	return sprintf(buf, "%08X\n", trc);
945*4882a593Smuzhiyun }
946*4882a593Smuzhiyun static DEVICE_ATTR_RO(trc);
947*4882a593Smuzhiyun 
948*4882a593Smuzhiyun /**
949*4882a593Smuzhiyun  * fw_status_show - mei device fw_status attribute show method
950*4882a593Smuzhiyun  *
951*4882a593Smuzhiyun  * @device: device pointer
952*4882a593Smuzhiyun  * @attr: attribute pointer
953*4882a593Smuzhiyun  * @buf:  char out buffer
954*4882a593Smuzhiyun  *
955*4882a593Smuzhiyun  * Return: number of the bytes printed into buf or error
956*4882a593Smuzhiyun  */
fw_status_show(struct device * device,struct device_attribute * attr,char * buf)957*4882a593Smuzhiyun static ssize_t fw_status_show(struct device *device,
958*4882a593Smuzhiyun 		struct device_attribute *attr, char *buf)
959*4882a593Smuzhiyun {
960*4882a593Smuzhiyun 	struct mei_device *dev = dev_get_drvdata(device);
961*4882a593Smuzhiyun 	struct mei_fw_status fw_status;
962*4882a593Smuzhiyun 	int err, i;
963*4882a593Smuzhiyun 	ssize_t cnt = 0;
964*4882a593Smuzhiyun 
965*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
966*4882a593Smuzhiyun 	err = mei_fw_status(dev, &fw_status);
967*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
968*4882a593Smuzhiyun 	if (err) {
969*4882a593Smuzhiyun 		dev_err(device, "read fw_status error = %d\n", err);
970*4882a593Smuzhiyun 		return err;
971*4882a593Smuzhiyun 	}
972*4882a593Smuzhiyun 
973*4882a593Smuzhiyun 	for (i = 0; i < fw_status.count; i++)
974*4882a593Smuzhiyun 		cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, "%08X\n",
975*4882a593Smuzhiyun 				fw_status.status[i]);
976*4882a593Smuzhiyun 	return cnt;
977*4882a593Smuzhiyun }
978*4882a593Smuzhiyun static DEVICE_ATTR_RO(fw_status);
979*4882a593Smuzhiyun 
980*4882a593Smuzhiyun /**
981*4882a593Smuzhiyun  * hbm_ver_show - display HBM protocol version negotiated with FW
982*4882a593Smuzhiyun  *
983*4882a593Smuzhiyun  * @device: device pointer
984*4882a593Smuzhiyun  * @attr: attribute pointer
985*4882a593Smuzhiyun  * @buf:  char out buffer
986*4882a593Smuzhiyun  *
987*4882a593Smuzhiyun  * Return: number of the bytes printed into buf or error
988*4882a593Smuzhiyun  */
hbm_ver_show(struct device * device,struct device_attribute * attr,char * buf)989*4882a593Smuzhiyun static ssize_t hbm_ver_show(struct device *device,
990*4882a593Smuzhiyun 			    struct device_attribute *attr, char *buf)
991*4882a593Smuzhiyun {
992*4882a593Smuzhiyun 	struct mei_device *dev = dev_get_drvdata(device);
993*4882a593Smuzhiyun 	struct hbm_version ver;
994*4882a593Smuzhiyun 
995*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
996*4882a593Smuzhiyun 	ver = dev->version;
997*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
998*4882a593Smuzhiyun 
999*4882a593Smuzhiyun 	return sprintf(buf, "%u.%u\n", ver.major_version, ver.minor_version);
1000*4882a593Smuzhiyun }
1001*4882a593Smuzhiyun static DEVICE_ATTR_RO(hbm_ver);
1002*4882a593Smuzhiyun 
1003*4882a593Smuzhiyun /**
1004*4882a593Smuzhiyun  * hbm_ver_drv_show - display HBM protocol version advertised by driver
1005*4882a593Smuzhiyun  *
1006*4882a593Smuzhiyun  * @device: device pointer
1007*4882a593Smuzhiyun  * @attr: attribute pointer
1008*4882a593Smuzhiyun  * @buf:  char out buffer
1009*4882a593Smuzhiyun  *
1010*4882a593Smuzhiyun  * Return: number of the bytes printed into buf or error
1011*4882a593Smuzhiyun  */
hbm_ver_drv_show(struct device * device,struct device_attribute * attr,char * buf)1012*4882a593Smuzhiyun static ssize_t hbm_ver_drv_show(struct device *device,
1013*4882a593Smuzhiyun 				struct device_attribute *attr, char *buf)
1014*4882a593Smuzhiyun {
1015*4882a593Smuzhiyun 	return sprintf(buf, "%u.%u\n", HBM_MAJOR_VERSION, HBM_MINOR_VERSION);
1016*4882a593Smuzhiyun }
1017*4882a593Smuzhiyun static DEVICE_ATTR_RO(hbm_ver_drv);
1018*4882a593Smuzhiyun 
tx_queue_limit_show(struct device * device,struct device_attribute * attr,char * buf)1019*4882a593Smuzhiyun static ssize_t tx_queue_limit_show(struct device *device,
1020*4882a593Smuzhiyun 				   struct device_attribute *attr, char *buf)
1021*4882a593Smuzhiyun {
1022*4882a593Smuzhiyun 	struct mei_device *dev = dev_get_drvdata(device);
1023*4882a593Smuzhiyun 	u8 size = 0;
1024*4882a593Smuzhiyun 
1025*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
1026*4882a593Smuzhiyun 	size = dev->tx_queue_limit;
1027*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%u\n", size);
1030*4882a593Smuzhiyun }
1031*4882a593Smuzhiyun 
tx_queue_limit_store(struct device * device,struct device_attribute * attr,const char * buf,size_t count)1032*4882a593Smuzhiyun static ssize_t tx_queue_limit_store(struct device *device,
1033*4882a593Smuzhiyun 				    struct device_attribute *attr,
1034*4882a593Smuzhiyun 				    const char *buf, size_t count)
1035*4882a593Smuzhiyun {
1036*4882a593Smuzhiyun 	struct mei_device *dev = dev_get_drvdata(device);
1037*4882a593Smuzhiyun 	u8 limit;
1038*4882a593Smuzhiyun 	unsigned int inp;
1039*4882a593Smuzhiyun 	int err;
1040*4882a593Smuzhiyun 
1041*4882a593Smuzhiyun 	err = kstrtouint(buf, 10, &inp);
1042*4882a593Smuzhiyun 	if (err)
1043*4882a593Smuzhiyun 		return err;
1044*4882a593Smuzhiyun 	if (inp > MEI_TX_QUEUE_LIMIT_MAX || inp < MEI_TX_QUEUE_LIMIT_MIN)
1045*4882a593Smuzhiyun 		return -EINVAL;
1046*4882a593Smuzhiyun 	limit = inp;
1047*4882a593Smuzhiyun 
1048*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
1049*4882a593Smuzhiyun 	dev->tx_queue_limit = limit;
1050*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
1051*4882a593Smuzhiyun 
1052*4882a593Smuzhiyun 	return count;
1053*4882a593Smuzhiyun }
1054*4882a593Smuzhiyun static DEVICE_ATTR_RW(tx_queue_limit);
1055*4882a593Smuzhiyun 
1056*4882a593Smuzhiyun /**
1057*4882a593Smuzhiyun  * fw_ver_show - display ME FW version
1058*4882a593Smuzhiyun  *
1059*4882a593Smuzhiyun  * @device: device pointer
1060*4882a593Smuzhiyun  * @attr: attribute pointer
1061*4882a593Smuzhiyun  * @buf:  char out buffer
1062*4882a593Smuzhiyun  *
1063*4882a593Smuzhiyun  * Return: number of the bytes printed into buf or error
1064*4882a593Smuzhiyun  */
fw_ver_show(struct device * device,struct device_attribute * attr,char * buf)1065*4882a593Smuzhiyun static ssize_t fw_ver_show(struct device *device,
1066*4882a593Smuzhiyun 			   struct device_attribute *attr, char *buf)
1067*4882a593Smuzhiyun {
1068*4882a593Smuzhiyun 	struct mei_device *dev = dev_get_drvdata(device);
1069*4882a593Smuzhiyun 	struct mei_fw_version *ver;
1070*4882a593Smuzhiyun 	ssize_t cnt = 0;
1071*4882a593Smuzhiyun 	int i;
1072*4882a593Smuzhiyun 
1073*4882a593Smuzhiyun 	ver = dev->fw_ver;
1074*4882a593Smuzhiyun 
1075*4882a593Smuzhiyun 	for (i = 0; i < MEI_MAX_FW_VER_BLOCKS; i++)
1076*4882a593Smuzhiyun 		cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, "%u:%u.%u.%u.%u\n",
1077*4882a593Smuzhiyun 				 ver[i].platform, ver[i].major, ver[i].minor,
1078*4882a593Smuzhiyun 				 ver[i].hotfix, ver[i].buildno);
1079*4882a593Smuzhiyun 	return cnt;
1080*4882a593Smuzhiyun }
1081*4882a593Smuzhiyun static DEVICE_ATTR_RO(fw_ver);
1082*4882a593Smuzhiyun 
1083*4882a593Smuzhiyun /**
1084*4882a593Smuzhiyun  * dev_state_show - display device state
1085*4882a593Smuzhiyun  *
1086*4882a593Smuzhiyun  * @device: device pointer
1087*4882a593Smuzhiyun  * @attr: attribute pointer
1088*4882a593Smuzhiyun  * @buf:  char out buffer
1089*4882a593Smuzhiyun  *
1090*4882a593Smuzhiyun  * Return: number of the bytes printed into buf or error
1091*4882a593Smuzhiyun  */
dev_state_show(struct device * device,struct device_attribute * attr,char * buf)1092*4882a593Smuzhiyun static ssize_t dev_state_show(struct device *device,
1093*4882a593Smuzhiyun 			      struct device_attribute *attr, char *buf)
1094*4882a593Smuzhiyun {
1095*4882a593Smuzhiyun 	struct mei_device *dev = dev_get_drvdata(device);
1096*4882a593Smuzhiyun 	enum mei_dev_state dev_state;
1097*4882a593Smuzhiyun 
1098*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
1099*4882a593Smuzhiyun 	dev_state = dev->dev_state;
1100*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
1101*4882a593Smuzhiyun 
1102*4882a593Smuzhiyun 	return sprintf(buf, "%s", mei_dev_state_str(dev_state));
1103*4882a593Smuzhiyun }
1104*4882a593Smuzhiyun static DEVICE_ATTR_RO(dev_state);
1105*4882a593Smuzhiyun 
1106*4882a593Smuzhiyun /**
1107*4882a593Smuzhiyun  * dev_set_devstate: set to new device state and notify sysfs file.
1108*4882a593Smuzhiyun  *
1109*4882a593Smuzhiyun  * @dev: mei_device
1110*4882a593Smuzhiyun  * @state: new device state
1111*4882a593Smuzhiyun  */
mei_set_devstate(struct mei_device * dev,enum mei_dev_state state)1112*4882a593Smuzhiyun void mei_set_devstate(struct mei_device *dev, enum mei_dev_state state)
1113*4882a593Smuzhiyun {
1114*4882a593Smuzhiyun 	struct device *clsdev;
1115*4882a593Smuzhiyun 
1116*4882a593Smuzhiyun 	if (dev->dev_state == state)
1117*4882a593Smuzhiyun 		return;
1118*4882a593Smuzhiyun 
1119*4882a593Smuzhiyun 	dev->dev_state = state;
1120*4882a593Smuzhiyun 
1121*4882a593Smuzhiyun 	clsdev = class_find_device_by_devt(mei_class, dev->cdev.dev);
1122*4882a593Smuzhiyun 	if (clsdev) {
1123*4882a593Smuzhiyun 		sysfs_notify(&clsdev->kobj, NULL, "dev_state");
1124*4882a593Smuzhiyun 		put_device(clsdev);
1125*4882a593Smuzhiyun 	}
1126*4882a593Smuzhiyun }
1127*4882a593Smuzhiyun 
1128*4882a593Smuzhiyun /**
1129*4882a593Smuzhiyun  * kind_show - display device kind
1130*4882a593Smuzhiyun  *
1131*4882a593Smuzhiyun  * @device: device pointer
1132*4882a593Smuzhiyun  * @attr: attribute pointer
1133*4882a593Smuzhiyun  * @buf: char out buffer
1134*4882a593Smuzhiyun  *
1135*4882a593Smuzhiyun  * Return: number of the bytes printed into buf or error
1136*4882a593Smuzhiyun  */
kind_show(struct device * device,struct device_attribute * attr,char * buf)1137*4882a593Smuzhiyun static ssize_t kind_show(struct device *device,
1138*4882a593Smuzhiyun 			 struct device_attribute *attr, char *buf)
1139*4882a593Smuzhiyun {
1140*4882a593Smuzhiyun 	struct mei_device *dev = dev_get_drvdata(device);
1141*4882a593Smuzhiyun 	ssize_t ret;
1142*4882a593Smuzhiyun 
1143*4882a593Smuzhiyun 	if (dev->kind)
1144*4882a593Smuzhiyun 		ret = sprintf(buf, "%s\n", dev->kind);
1145*4882a593Smuzhiyun 	else
1146*4882a593Smuzhiyun 		ret = sprintf(buf, "%s\n", "mei");
1147*4882a593Smuzhiyun 
1148*4882a593Smuzhiyun 	return ret;
1149*4882a593Smuzhiyun }
1150*4882a593Smuzhiyun static DEVICE_ATTR_RO(kind);
1151*4882a593Smuzhiyun 
1152*4882a593Smuzhiyun static struct attribute *mei_attrs[] = {
1153*4882a593Smuzhiyun 	&dev_attr_fw_status.attr,
1154*4882a593Smuzhiyun 	&dev_attr_hbm_ver.attr,
1155*4882a593Smuzhiyun 	&dev_attr_hbm_ver_drv.attr,
1156*4882a593Smuzhiyun 	&dev_attr_tx_queue_limit.attr,
1157*4882a593Smuzhiyun 	&dev_attr_fw_ver.attr,
1158*4882a593Smuzhiyun 	&dev_attr_dev_state.attr,
1159*4882a593Smuzhiyun 	&dev_attr_trc.attr,
1160*4882a593Smuzhiyun 	&dev_attr_kind.attr,
1161*4882a593Smuzhiyun 	NULL
1162*4882a593Smuzhiyun };
1163*4882a593Smuzhiyun ATTRIBUTE_GROUPS(mei);
1164*4882a593Smuzhiyun 
1165*4882a593Smuzhiyun /*
1166*4882a593Smuzhiyun  * file operations structure will be used for mei char device.
1167*4882a593Smuzhiyun  */
1168*4882a593Smuzhiyun static const struct file_operations mei_fops = {
1169*4882a593Smuzhiyun 	.owner = THIS_MODULE,
1170*4882a593Smuzhiyun 	.read = mei_read,
1171*4882a593Smuzhiyun 	.unlocked_ioctl = mei_ioctl,
1172*4882a593Smuzhiyun 	.compat_ioctl = compat_ptr_ioctl,
1173*4882a593Smuzhiyun 	.open = mei_open,
1174*4882a593Smuzhiyun 	.release = mei_release,
1175*4882a593Smuzhiyun 	.write = mei_write,
1176*4882a593Smuzhiyun 	.poll = mei_poll,
1177*4882a593Smuzhiyun 	.fsync = mei_fsync,
1178*4882a593Smuzhiyun 	.fasync = mei_fasync,
1179*4882a593Smuzhiyun 	.llseek = no_llseek
1180*4882a593Smuzhiyun };
1181*4882a593Smuzhiyun 
1182*4882a593Smuzhiyun /**
1183*4882a593Smuzhiyun  * mei_minor_get - obtain next free device minor number
1184*4882a593Smuzhiyun  *
1185*4882a593Smuzhiyun  * @dev:  device pointer
1186*4882a593Smuzhiyun  *
1187*4882a593Smuzhiyun  * Return: allocated minor, or -ENOSPC if no free minor left
1188*4882a593Smuzhiyun  */
mei_minor_get(struct mei_device * dev)1189*4882a593Smuzhiyun static int mei_minor_get(struct mei_device *dev)
1190*4882a593Smuzhiyun {
1191*4882a593Smuzhiyun 	int ret;
1192*4882a593Smuzhiyun 
1193*4882a593Smuzhiyun 	mutex_lock(&mei_minor_lock);
1194*4882a593Smuzhiyun 	ret = idr_alloc(&mei_idr, dev, 0, MEI_MAX_DEVS, GFP_KERNEL);
1195*4882a593Smuzhiyun 	if (ret >= 0)
1196*4882a593Smuzhiyun 		dev->minor = ret;
1197*4882a593Smuzhiyun 	else if (ret == -ENOSPC)
1198*4882a593Smuzhiyun 		dev_err(dev->dev, "too many mei devices\n");
1199*4882a593Smuzhiyun 
1200*4882a593Smuzhiyun 	mutex_unlock(&mei_minor_lock);
1201*4882a593Smuzhiyun 	return ret;
1202*4882a593Smuzhiyun }
1203*4882a593Smuzhiyun 
1204*4882a593Smuzhiyun /**
1205*4882a593Smuzhiyun  * mei_minor_free - mark device minor number as free
1206*4882a593Smuzhiyun  *
1207*4882a593Smuzhiyun  * @dev:  device pointer
1208*4882a593Smuzhiyun  */
mei_minor_free(struct mei_device * dev)1209*4882a593Smuzhiyun static void mei_minor_free(struct mei_device *dev)
1210*4882a593Smuzhiyun {
1211*4882a593Smuzhiyun 	mutex_lock(&mei_minor_lock);
1212*4882a593Smuzhiyun 	idr_remove(&mei_idr, dev->minor);
1213*4882a593Smuzhiyun 	mutex_unlock(&mei_minor_lock);
1214*4882a593Smuzhiyun }
1215*4882a593Smuzhiyun 
mei_register(struct mei_device * dev,struct device * parent)1216*4882a593Smuzhiyun int mei_register(struct mei_device *dev, struct device *parent)
1217*4882a593Smuzhiyun {
1218*4882a593Smuzhiyun 	struct device *clsdev; /* class device */
1219*4882a593Smuzhiyun 	int ret, devno;
1220*4882a593Smuzhiyun 
1221*4882a593Smuzhiyun 	ret = mei_minor_get(dev);
1222*4882a593Smuzhiyun 	if (ret < 0)
1223*4882a593Smuzhiyun 		return ret;
1224*4882a593Smuzhiyun 
1225*4882a593Smuzhiyun 	/* Fill in the data structures */
1226*4882a593Smuzhiyun 	devno = MKDEV(MAJOR(mei_devt), dev->minor);
1227*4882a593Smuzhiyun 	cdev_init(&dev->cdev, &mei_fops);
1228*4882a593Smuzhiyun 	dev->cdev.owner = parent->driver->owner;
1229*4882a593Smuzhiyun 
1230*4882a593Smuzhiyun 	/* Add the device */
1231*4882a593Smuzhiyun 	ret = cdev_add(&dev->cdev, devno, 1);
1232*4882a593Smuzhiyun 	if (ret) {
1233*4882a593Smuzhiyun 		dev_err(parent, "unable to add device %d:%d\n",
1234*4882a593Smuzhiyun 			MAJOR(mei_devt), dev->minor);
1235*4882a593Smuzhiyun 		goto err_dev_add;
1236*4882a593Smuzhiyun 	}
1237*4882a593Smuzhiyun 
1238*4882a593Smuzhiyun 	clsdev = device_create_with_groups(mei_class, parent, devno,
1239*4882a593Smuzhiyun 					   dev, mei_groups,
1240*4882a593Smuzhiyun 					   "mei%d", dev->minor);
1241*4882a593Smuzhiyun 
1242*4882a593Smuzhiyun 	if (IS_ERR(clsdev)) {
1243*4882a593Smuzhiyun 		dev_err(parent, "unable to create device %d:%d\n",
1244*4882a593Smuzhiyun 			MAJOR(mei_devt), dev->minor);
1245*4882a593Smuzhiyun 		ret = PTR_ERR(clsdev);
1246*4882a593Smuzhiyun 		goto err_dev_create;
1247*4882a593Smuzhiyun 	}
1248*4882a593Smuzhiyun 
1249*4882a593Smuzhiyun 	mei_dbgfs_register(dev, dev_name(clsdev));
1250*4882a593Smuzhiyun 
1251*4882a593Smuzhiyun 	return 0;
1252*4882a593Smuzhiyun 
1253*4882a593Smuzhiyun err_dev_create:
1254*4882a593Smuzhiyun 	cdev_del(&dev->cdev);
1255*4882a593Smuzhiyun err_dev_add:
1256*4882a593Smuzhiyun 	mei_minor_free(dev);
1257*4882a593Smuzhiyun 	return ret;
1258*4882a593Smuzhiyun }
1259*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mei_register);
1260*4882a593Smuzhiyun 
mei_deregister(struct mei_device * dev)1261*4882a593Smuzhiyun void mei_deregister(struct mei_device *dev)
1262*4882a593Smuzhiyun {
1263*4882a593Smuzhiyun 	int devno;
1264*4882a593Smuzhiyun 
1265*4882a593Smuzhiyun 	devno = dev->cdev.dev;
1266*4882a593Smuzhiyun 	cdev_del(&dev->cdev);
1267*4882a593Smuzhiyun 
1268*4882a593Smuzhiyun 	mei_dbgfs_deregister(dev);
1269*4882a593Smuzhiyun 
1270*4882a593Smuzhiyun 	device_destroy(mei_class, devno);
1271*4882a593Smuzhiyun 
1272*4882a593Smuzhiyun 	mei_minor_free(dev);
1273*4882a593Smuzhiyun }
1274*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mei_deregister);
1275*4882a593Smuzhiyun 
mei_init(void)1276*4882a593Smuzhiyun static int __init mei_init(void)
1277*4882a593Smuzhiyun {
1278*4882a593Smuzhiyun 	int ret;
1279*4882a593Smuzhiyun 
1280*4882a593Smuzhiyun 	mei_class = class_create(THIS_MODULE, "mei");
1281*4882a593Smuzhiyun 	if (IS_ERR(mei_class)) {
1282*4882a593Smuzhiyun 		pr_err("couldn't create class\n");
1283*4882a593Smuzhiyun 		ret = PTR_ERR(mei_class);
1284*4882a593Smuzhiyun 		goto err;
1285*4882a593Smuzhiyun 	}
1286*4882a593Smuzhiyun 
1287*4882a593Smuzhiyun 	ret = alloc_chrdev_region(&mei_devt, 0, MEI_MAX_DEVS, "mei");
1288*4882a593Smuzhiyun 	if (ret < 0) {
1289*4882a593Smuzhiyun 		pr_err("unable to allocate char dev region\n");
1290*4882a593Smuzhiyun 		goto err_class;
1291*4882a593Smuzhiyun 	}
1292*4882a593Smuzhiyun 
1293*4882a593Smuzhiyun 	ret = mei_cl_bus_init();
1294*4882a593Smuzhiyun 	if (ret < 0) {
1295*4882a593Smuzhiyun 		pr_err("unable to initialize bus\n");
1296*4882a593Smuzhiyun 		goto err_chrdev;
1297*4882a593Smuzhiyun 	}
1298*4882a593Smuzhiyun 
1299*4882a593Smuzhiyun 	return 0;
1300*4882a593Smuzhiyun 
1301*4882a593Smuzhiyun err_chrdev:
1302*4882a593Smuzhiyun 	unregister_chrdev_region(mei_devt, MEI_MAX_DEVS);
1303*4882a593Smuzhiyun err_class:
1304*4882a593Smuzhiyun 	class_destroy(mei_class);
1305*4882a593Smuzhiyun err:
1306*4882a593Smuzhiyun 	return ret;
1307*4882a593Smuzhiyun }
1308*4882a593Smuzhiyun 
mei_exit(void)1309*4882a593Smuzhiyun static void __exit mei_exit(void)
1310*4882a593Smuzhiyun {
1311*4882a593Smuzhiyun 	unregister_chrdev_region(mei_devt, MEI_MAX_DEVS);
1312*4882a593Smuzhiyun 	class_destroy(mei_class);
1313*4882a593Smuzhiyun 	mei_cl_bus_exit();
1314*4882a593Smuzhiyun }
1315*4882a593Smuzhiyun 
1316*4882a593Smuzhiyun module_init(mei_init);
1317*4882a593Smuzhiyun module_exit(mei_exit);
1318*4882a593Smuzhiyun 
1319*4882a593Smuzhiyun MODULE_AUTHOR("Intel Corporation");
1320*4882a593Smuzhiyun MODULE_DESCRIPTION("Intel(R) Management Engine Interface");
1321*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
1322*4882a593Smuzhiyun 
1323