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, ¬ify_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(¬ify_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, ¬ify_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 ¬ify_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