xref: /OK3568_Linux_fs/kernel/samples/vfio-mdev/mdpy.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Mediated virtual PCI display host device driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * See mdpy-defs.h for device specs
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *   (c) Gerd Hoffmann <kraxel@redhat.com>
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * based on mtty driver which is:
10*4882a593Smuzhiyun  *   Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
11*4882a593Smuzhiyun  *	 Author: Neo Jia <cjia@nvidia.com>
12*4882a593Smuzhiyun  *		 Kirti Wankhede <kwankhede@nvidia.com>
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or modify
15*4882a593Smuzhiyun  * it under the terms of the GNU General Public License version 2 as
16*4882a593Smuzhiyun  * published by the Free Software Foundation.
17*4882a593Smuzhiyun  */
18*4882a593Smuzhiyun #include <linux/init.h>
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/device.h>
21*4882a593Smuzhiyun #include <linux/kernel.h>
22*4882a593Smuzhiyun #include <linux/slab.h>
23*4882a593Smuzhiyun #include <linux/vmalloc.h>
24*4882a593Smuzhiyun #include <linux/cdev.h>
25*4882a593Smuzhiyun #include <linux/vfio.h>
26*4882a593Smuzhiyun #include <linux/iommu.h>
27*4882a593Smuzhiyun #include <linux/sysfs.h>
28*4882a593Smuzhiyun #include <linux/mdev.h>
29*4882a593Smuzhiyun #include <linux/pci.h>
30*4882a593Smuzhiyun #include <drm/drm_fourcc.h>
31*4882a593Smuzhiyun #include "mdpy-defs.h"
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #define MDPY_NAME		"mdpy"
34*4882a593Smuzhiyun #define MDPY_CLASS_NAME		"mdpy"
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #define MDPY_CONFIG_SPACE_SIZE	0xff
37*4882a593Smuzhiyun #define MDPY_MEMORY_BAR_OFFSET	PAGE_SIZE
38*4882a593Smuzhiyun #define MDPY_DISPLAY_REGION	16
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #define STORE_LE16(addr, val)	(*(u16 *)addr = val)
41*4882a593Smuzhiyun #define STORE_LE32(addr, val)	(*(u32 *)addr = val)
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun static int max_devices = 4;
47*4882a593Smuzhiyun module_param_named(count, max_devices, int, 0444);
48*4882a593Smuzhiyun MODULE_PARM_DESC(count, "number of " MDPY_NAME " devices");
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun #define MDPY_TYPE_1 "vga"
52*4882a593Smuzhiyun #define MDPY_TYPE_2 "xga"
53*4882a593Smuzhiyun #define MDPY_TYPE_3 "hd"
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun static const struct mdpy_type {
56*4882a593Smuzhiyun 	const char *name;
57*4882a593Smuzhiyun 	u32 format;
58*4882a593Smuzhiyun 	u32 bytepp;
59*4882a593Smuzhiyun 	u32 width;
60*4882a593Smuzhiyun 	u32 height;
61*4882a593Smuzhiyun } mdpy_types[] = {
62*4882a593Smuzhiyun 	{
63*4882a593Smuzhiyun 		.name	= MDPY_CLASS_NAME "-" MDPY_TYPE_1,
64*4882a593Smuzhiyun 		.format = DRM_FORMAT_XRGB8888,
65*4882a593Smuzhiyun 		.bytepp = 4,
66*4882a593Smuzhiyun 		.width	= 640,
67*4882a593Smuzhiyun 		.height = 480,
68*4882a593Smuzhiyun 	}, {
69*4882a593Smuzhiyun 		.name	= MDPY_CLASS_NAME "-" MDPY_TYPE_2,
70*4882a593Smuzhiyun 		.format = DRM_FORMAT_XRGB8888,
71*4882a593Smuzhiyun 		.bytepp = 4,
72*4882a593Smuzhiyun 		.width	= 1024,
73*4882a593Smuzhiyun 		.height = 768,
74*4882a593Smuzhiyun 	}, {
75*4882a593Smuzhiyun 		.name	= MDPY_CLASS_NAME "-" MDPY_TYPE_3,
76*4882a593Smuzhiyun 		.format = DRM_FORMAT_XRGB8888,
77*4882a593Smuzhiyun 		.bytepp = 4,
78*4882a593Smuzhiyun 		.width	= 1920,
79*4882a593Smuzhiyun 		.height = 1080,
80*4882a593Smuzhiyun 	},
81*4882a593Smuzhiyun };
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun static dev_t		mdpy_devt;
84*4882a593Smuzhiyun static struct class	*mdpy_class;
85*4882a593Smuzhiyun static struct cdev	mdpy_cdev;
86*4882a593Smuzhiyun static struct device	mdpy_dev;
87*4882a593Smuzhiyun static u32		mdpy_count;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun /* State of each mdev device */
90*4882a593Smuzhiyun struct mdev_state {
91*4882a593Smuzhiyun 	u8 *vconfig;
92*4882a593Smuzhiyun 	u32 bar_mask;
93*4882a593Smuzhiyun 	struct mutex ops_lock;
94*4882a593Smuzhiyun 	struct mdev_device *mdev;
95*4882a593Smuzhiyun 	struct vfio_device_info dev_info;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	const struct mdpy_type *type;
98*4882a593Smuzhiyun 	u32 memsize;
99*4882a593Smuzhiyun 	void *memblk;
100*4882a593Smuzhiyun };
101*4882a593Smuzhiyun 
mdpy_find_type(struct kobject * kobj)102*4882a593Smuzhiyun static const struct mdpy_type *mdpy_find_type(struct kobject *kobj)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun 	int i;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(mdpy_types); i++)
107*4882a593Smuzhiyun 		if (strcmp(mdpy_types[i].name, kobj->name) == 0)
108*4882a593Smuzhiyun 			return mdpy_types + i;
109*4882a593Smuzhiyun 	return NULL;
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun 
mdpy_create_config_space(struct mdev_state * mdev_state)112*4882a593Smuzhiyun static void mdpy_create_config_space(struct mdev_state *mdev_state)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun 	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_VENDOR_ID],
115*4882a593Smuzhiyun 		   MDPY_PCI_VENDOR_ID);
116*4882a593Smuzhiyun 	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_DEVICE_ID],
117*4882a593Smuzhiyun 		   MDPY_PCI_DEVICE_ID);
118*4882a593Smuzhiyun 	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_VENDOR_ID],
119*4882a593Smuzhiyun 		   MDPY_PCI_SUBVENDOR_ID);
120*4882a593Smuzhiyun 	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_ID],
121*4882a593Smuzhiyun 		   MDPY_PCI_SUBDEVICE_ID);
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_COMMAND],
124*4882a593Smuzhiyun 		   PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
125*4882a593Smuzhiyun 	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_STATUS],
126*4882a593Smuzhiyun 		   PCI_STATUS_CAP_LIST);
127*4882a593Smuzhiyun 	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_CLASS_DEVICE],
128*4882a593Smuzhiyun 		   PCI_CLASS_DISPLAY_OTHER);
129*4882a593Smuzhiyun 	mdev_state->vconfig[PCI_CLASS_REVISION] =  0x01;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	STORE_LE32((u32 *) &mdev_state->vconfig[PCI_BASE_ADDRESS_0],
132*4882a593Smuzhiyun 		   PCI_BASE_ADDRESS_SPACE_MEMORY |
133*4882a593Smuzhiyun 		   PCI_BASE_ADDRESS_MEM_TYPE_32	 |
134*4882a593Smuzhiyun 		   PCI_BASE_ADDRESS_MEM_PREFETCH);
135*4882a593Smuzhiyun 	mdev_state->bar_mask = ~(mdev_state->memsize) + 1;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	/* vendor specific capability for the config registers */
138*4882a593Smuzhiyun 	mdev_state->vconfig[PCI_CAPABILITY_LIST]       = MDPY_VENDORCAP_OFFSET;
139*4882a593Smuzhiyun 	mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 0] = 0x09; /* vendor cap */
140*4882a593Smuzhiyun 	mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 1] = 0x00; /* next ptr */
141*4882a593Smuzhiyun 	mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 2] = MDPY_VENDORCAP_SIZE;
142*4882a593Smuzhiyun 	STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_FORMAT_OFFSET],
143*4882a593Smuzhiyun 		   mdev_state->type->format);
144*4882a593Smuzhiyun 	STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_WIDTH_OFFSET],
145*4882a593Smuzhiyun 		   mdev_state->type->width);
146*4882a593Smuzhiyun 	STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_HEIGHT_OFFSET],
147*4882a593Smuzhiyun 		   mdev_state->type->height);
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
handle_pci_cfg_write(struct mdev_state * mdev_state,u16 offset,char * buf,u32 count)150*4882a593Smuzhiyun static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset,
151*4882a593Smuzhiyun 				 char *buf, u32 count)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	struct device *dev = mdev_dev(mdev_state->mdev);
154*4882a593Smuzhiyun 	u32 cfg_addr;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	switch (offset) {
157*4882a593Smuzhiyun 	case PCI_BASE_ADDRESS_0:
158*4882a593Smuzhiyun 		cfg_addr = *(u32 *)buf;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 		if (cfg_addr == 0xffffffff) {
161*4882a593Smuzhiyun 			cfg_addr = (cfg_addr & mdev_state->bar_mask);
162*4882a593Smuzhiyun 		} else {
163*4882a593Smuzhiyun 			cfg_addr &= PCI_BASE_ADDRESS_MEM_MASK;
164*4882a593Smuzhiyun 			if (cfg_addr)
165*4882a593Smuzhiyun 				dev_info(dev, "BAR0 @ 0x%x\n", cfg_addr);
166*4882a593Smuzhiyun 		}
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 		cfg_addr |= (mdev_state->vconfig[offset] &
169*4882a593Smuzhiyun 			     ~PCI_BASE_ADDRESS_MEM_MASK);
170*4882a593Smuzhiyun 		STORE_LE32(&mdev_state->vconfig[offset], cfg_addr);
171*4882a593Smuzhiyun 		break;
172*4882a593Smuzhiyun 	}
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun 
mdev_access(struct mdev_device * mdev,char * buf,size_t count,loff_t pos,bool is_write)175*4882a593Smuzhiyun static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count,
176*4882a593Smuzhiyun 			   loff_t pos, bool is_write)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun 	struct mdev_state *mdev_state = mdev_get_drvdata(mdev);
179*4882a593Smuzhiyun 	struct device *dev = mdev_dev(mdev);
180*4882a593Smuzhiyun 	int ret = 0;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	mutex_lock(&mdev_state->ops_lock);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	if (pos < MDPY_CONFIG_SPACE_SIZE) {
185*4882a593Smuzhiyun 		if (is_write)
186*4882a593Smuzhiyun 			handle_pci_cfg_write(mdev_state, pos, buf, count);
187*4882a593Smuzhiyun 		else
188*4882a593Smuzhiyun 			memcpy(buf, (mdev_state->vconfig + pos), count);
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	} else if ((pos >= MDPY_MEMORY_BAR_OFFSET) &&
191*4882a593Smuzhiyun 		   (pos + count <=
192*4882a593Smuzhiyun 		    MDPY_MEMORY_BAR_OFFSET + mdev_state->memsize)) {
193*4882a593Smuzhiyun 		pos -= MDPY_MEMORY_BAR_OFFSET;
194*4882a593Smuzhiyun 		if (is_write)
195*4882a593Smuzhiyun 			memcpy(mdev_state->memblk, buf, count);
196*4882a593Smuzhiyun 		else
197*4882a593Smuzhiyun 			memcpy(buf, mdev_state->memblk, count);
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	} else {
200*4882a593Smuzhiyun 		dev_info(dev, "%s: %s @0x%llx (unhandled)\n",
201*4882a593Smuzhiyun 			 __func__, is_write ? "WR" : "RD", pos);
202*4882a593Smuzhiyun 		ret = -1;
203*4882a593Smuzhiyun 		goto accessfailed;
204*4882a593Smuzhiyun 	}
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	ret = count;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun accessfailed:
210*4882a593Smuzhiyun 	mutex_unlock(&mdev_state->ops_lock);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	return ret;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun 
mdpy_reset(struct mdev_device * mdev)215*4882a593Smuzhiyun static int mdpy_reset(struct mdev_device *mdev)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	struct mdev_state *mdev_state = mdev_get_drvdata(mdev);
218*4882a593Smuzhiyun 	u32 stride, i;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	/* initialize with gray gradient */
221*4882a593Smuzhiyun 	stride = mdev_state->type->width * mdev_state->type->bytepp;
222*4882a593Smuzhiyun 	for (i = 0; i < mdev_state->type->height; i++)
223*4882a593Smuzhiyun 		memset(mdev_state->memblk + i * stride,
224*4882a593Smuzhiyun 		       i * 255 / mdev_state->type->height,
225*4882a593Smuzhiyun 		       stride);
226*4882a593Smuzhiyun 	return 0;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun 
mdpy_create(struct kobject * kobj,struct mdev_device * mdev)229*4882a593Smuzhiyun static int mdpy_create(struct kobject *kobj, struct mdev_device *mdev)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun 	const struct mdpy_type *type = mdpy_find_type(kobj);
232*4882a593Smuzhiyun 	struct device *dev = mdev_dev(mdev);
233*4882a593Smuzhiyun 	struct mdev_state *mdev_state;
234*4882a593Smuzhiyun 	u32 fbsize;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	if (mdpy_count >= max_devices)
237*4882a593Smuzhiyun 		return -ENOMEM;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL);
240*4882a593Smuzhiyun 	if (mdev_state == NULL)
241*4882a593Smuzhiyun 		return -ENOMEM;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	mdev_state->vconfig = kzalloc(MDPY_CONFIG_SPACE_SIZE, GFP_KERNEL);
244*4882a593Smuzhiyun 	if (mdev_state->vconfig == NULL) {
245*4882a593Smuzhiyun 		kfree(mdev_state);
246*4882a593Smuzhiyun 		return -ENOMEM;
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	if (!type)
250*4882a593Smuzhiyun 		type = &mdpy_types[0];
251*4882a593Smuzhiyun 	fbsize = roundup_pow_of_two(type->width * type->height * type->bytepp);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	mdev_state->memblk = vmalloc_user(fbsize);
254*4882a593Smuzhiyun 	if (!mdev_state->memblk) {
255*4882a593Smuzhiyun 		kfree(mdev_state->vconfig);
256*4882a593Smuzhiyun 		kfree(mdev_state);
257*4882a593Smuzhiyun 		return -ENOMEM;
258*4882a593Smuzhiyun 	}
259*4882a593Smuzhiyun 	dev_info(dev, "%s: %s (%dx%d)\n",
260*4882a593Smuzhiyun 		 __func__, kobj->name, type->width, type->height);
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	mutex_init(&mdev_state->ops_lock);
263*4882a593Smuzhiyun 	mdev_state->mdev = mdev;
264*4882a593Smuzhiyun 	mdev_set_drvdata(mdev, mdev_state);
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	mdev_state->type    = type;
267*4882a593Smuzhiyun 	mdev_state->memsize = fbsize;
268*4882a593Smuzhiyun 	mdpy_create_config_space(mdev_state);
269*4882a593Smuzhiyun 	mdpy_reset(mdev);
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	mdpy_count++;
272*4882a593Smuzhiyun 	return 0;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
mdpy_remove(struct mdev_device * mdev)275*4882a593Smuzhiyun static int mdpy_remove(struct mdev_device *mdev)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	struct mdev_state *mdev_state = mdev_get_drvdata(mdev);
278*4882a593Smuzhiyun 	struct device *dev = mdev_dev(mdev);
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	dev_info(dev, "%s\n", __func__);
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	mdev_set_drvdata(mdev, NULL);
283*4882a593Smuzhiyun 	vfree(mdev_state->memblk);
284*4882a593Smuzhiyun 	kfree(mdev_state->vconfig);
285*4882a593Smuzhiyun 	kfree(mdev_state);
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	mdpy_count--;
288*4882a593Smuzhiyun 	return 0;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun 
mdpy_read(struct mdev_device * mdev,char __user * buf,size_t count,loff_t * ppos)291*4882a593Smuzhiyun static ssize_t mdpy_read(struct mdev_device *mdev, char __user *buf,
292*4882a593Smuzhiyun 			 size_t count, loff_t *ppos)
293*4882a593Smuzhiyun {
294*4882a593Smuzhiyun 	unsigned int done = 0;
295*4882a593Smuzhiyun 	int ret;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	while (count) {
298*4882a593Smuzhiyun 		size_t filled;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 		if (count >= 4 && !(*ppos % 4)) {
301*4882a593Smuzhiyun 			u32 val;
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 			ret =  mdev_access(mdev, (char *)&val, sizeof(val),
304*4882a593Smuzhiyun 					   *ppos, false);
305*4882a593Smuzhiyun 			if (ret <= 0)
306*4882a593Smuzhiyun 				goto read_err;
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 			if (copy_to_user(buf, &val, sizeof(val)))
309*4882a593Smuzhiyun 				goto read_err;
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 			filled = 4;
312*4882a593Smuzhiyun 		} else if (count >= 2 && !(*ppos % 2)) {
313*4882a593Smuzhiyun 			u16 val;
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 			ret = mdev_access(mdev, (char *)&val, sizeof(val),
316*4882a593Smuzhiyun 					  *ppos, false);
317*4882a593Smuzhiyun 			if (ret <= 0)
318*4882a593Smuzhiyun 				goto read_err;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 			if (copy_to_user(buf, &val, sizeof(val)))
321*4882a593Smuzhiyun 				goto read_err;
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 			filled = 2;
324*4882a593Smuzhiyun 		} else {
325*4882a593Smuzhiyun 			u8 val;
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 			ret = mdev_access(mdev, (char *)&val, sizeof(val),
328*4882a593Smuzhiyun 					  *ppos, false);
329*4882a593Smuzhiyun 			if (ret <= 0)
330*4882a593Smuzhiyun 				goto read_err;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 			if (copy_to_user(buf, &val, sizeof(val)))
333*4882a593Smuzhiyun 				goto read_err;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 			filled = 1;
336*4882a593Smuzhiyun 		}
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 		count -= filled;
339*4882a593Smuzhiyun 		done += filled;
340*4882a593Smuzhiyun 		*ppos += filled;
341*4882a593Smuzhiyun 		buf += filled;
342*4882a593Smuzhiyun 	}
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	return done;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun read_err:
347*4882a593Smuzhiyun 	return -EFAULT;
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun 
mdpy_write(struct mdev_device * mdev,const char __user * buf,size_t count,loff_t * ppos)350*4882a593Smuzhiyun static ssize_t mdpy_write(struct mdev_device *mdev, const char __user *buf,
351*4882a593Smuzhiyun 			  size_t count, loff_t *ppos)
352*4882a593Smuzhiyun {
353*4882a593Smuzhiyun 	unsigned int done = 0;
354*4882a593Smuzhiyun 	int ret;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	while (count) {
357*4882a593Smuzhiyun 		size_t filled;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 		if (count >= 4 && !(*ppos % 4)) {
360*4882a593Smuzhiyun 			u32 val;
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 			if (copy_from_user(&val, buf, sizeof(val)))
363*4882a593Smuzhiyun 				goto write_err;
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 			ret = mdev_access(mdev, (char *)&val, sizeof(val),
366*4882a593Smuzhiyun 					  *ppos, true);
367*4882a593Smuzhiyun 			if (ret <= 0)
368*4882a593Smuzhiyun 				goto write_err;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 			filled = 4;
371*4882a593Smuzhiyun 		} else if (count >= 2 && !(*ppos % 2)) {
372*4882a593Smuzhiyun 			u16 val;
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 			if (copy_from_user(&val, buf, sizeof(val)))
375*4882a593Smuzhiyun 				goto write_err;
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 			ret = mdev_access(mdev, (char *)&val, sizeof(val),
378*4882a593Smuzhiyun 					  *ppos, true);
379*4882a593Smuzhiyun 			if (ret <= 0)
380*4882a593Smuzhiyun 				goto write_err;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 			filled = 2;
383*4882a593Smuzhiyun 		} else {
384*4882a593Smuzhiyun 			u8 val;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 			if (copy_from_user(&val, buf, sizeof(val)))
387*4882a593Smuzhiyun 				goto write_err;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 			ret = mdev_access(mdev, (char *)&val, sizeof(val),
390*4882a593Smuzhiyun 					  *ppos, true);
391*4882a593Smuzhiyun 			if (ret <= 0)
392*4882a593Smuzhiyun 				goto write_err;
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 			filled = 1;
395*4882a593Smuzhiyun 		}
396*4882a593Smuzhiyun 		count -= filled;
397*4882a593Smuzhiyun 		done += filled;
398*4882a593Smuzhiyun 		*ppos += filled;
399*4882a593Smuzhiyun 		buf += filled;
400*4882a593Smuzhiyun 	}
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	return done;
403*4882a593Smuzhiyun write_err:
404*4882a593Smuzhiyun 	return -EFAULT;
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun 
mdpy_mmap(struct mdev_device * mdev,struct vm_area_struct * vma)407*4882a593Smuzhiyun static int mdpy_mmap(struct mdev_device *mdev, struct vm_area_struct *vma)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun 	struct mdev_state *mdev_state = mdev_get_drvdata(mdev);
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	if (vma->vm_pgoff != MDPY_MEMORY_BAR_OFFSET >> PAGE_SHIFT)
412*4882a593Smuzhiyun 		return -EINVAL;
413*4882a593Smuzhiyun 	if (vma->vm_end < vma->vm_start)
414*4882a593Smuzhiyun 		return -EINVAL;
415*4882a593Smuzhiyun 	if (vma->vm_end - vma->vm_start > mdev_state->memsize)
416*4882a593Smuzhiyun 		return -EINVAL;
417*4882a593Smuzhiyun 	if ((vma->vm_flags & VM_SHARED) == 0)
418*4882a593Smuzhiyun 		return -EINVAL;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	return remap_vmalloc_range_partial(vma, vma->vm_start,
421*4882a593Smuzhiyun 					   mdev_state->memblk, 0,
422*4882a593Smuzhiyun 					   vma->vm_end - vma->vm_start);
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun 
mdpy_get_region_info(struct mdev_device * mdev,struct vfio_region_info * region_info,u16 * cap_type_id,void ** cap_type)425*4882a593Smuzhiyun static int mdpy_get_region_info(struct mdev_device *mdev,
426*4882a593Smuzhiyun 				struct vfio_region_info *region_info,
427*4882a593Smuzhiyun 				u16 *cap_type_id, void **cap_type)
428*4882a593Smuzhiyun {
429*4882a593Smuzhiyun 	struct mdev_state *mdev_state;
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	mdev_state = mdev_get_drvdata(mdev);
432*4882a593Smuzhiyun 	if (!mdev_state)
433*4882a593Smuzhiyun 		return -EINVAL;
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	if (region_info->index >= VFIO_PCI_NUM_REGIONS &&
436*4882a593Smuzhiyun 	    region_info->index != MDPY_DISPLAY_REGION)
437*4882a593Smuzhiyun 		return -EINVAL;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	switch (region_info->index) {
440*4882a593Smuzhiyun 	case VFIO_PCI_CONFIG_REGION_INDEX:
441*4882a593Smuzhiyun 		region_info->offset = 0;
442*4882a593Smuzhiyun 		region_info->size   = MDPY_CONFIG_SPACE_SIZE;
443*4882a593Smuzhiyun 		region_info->flags  = (VFIO_REGION_INFO_FLAG_READ |
444*4882a593Smuzhiyun 				       VFIO_REGION_INFO_FLAG_WRITE);
445*4882a593Smuzhiyun 		break;
446*4882a593Smuzhiyun 	case VFIO_PCI_BAR0_REGION_INDEX:
447*4882a593Smuzhiyun 	case MDPY_DISPLAY_REGION:
448*4882a593Smuzhiyun 		region_info->offset = MDPY_MEMORY_BAR_OFFSET;
449*4882a593Smuzhiyun 		region_info->size   = mdev_state->memsize;
450*4882a593Smuzhiyun 		region_info->flags  = (VFIO_REGION_INFO_FLAG_READ  |
451*4882a593Smuzhiyun 				       VFIO_REGION_INFO_FLAG_WRITE |
452*4882a593Smuzhiyun 				       VFIO_REGION_INFO_FLAG_MMAP);
453*4882a593Smuzhiyun 		break;
454*4882a593Smuzhiyun 	default:
455*4882a593Smuzhiyun 		region_info->size   = 0;
456*4882a593Smuzhiyun 		region_info->offset = 0;
457*4882a593Smuzhiyun 		region_info->flags  = 0;
458*4882a593Smuzhiyun 	}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	return 0;
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun 
mdpy_get_irq_info(struct mdev_device * mdev,struct vfio_irq_info * irq_info)463*4882a593Smuzhiyun static int mdpy_get_irq_info(struct mdev_device *mdev,
464*4882a593Smuzhiyun 			     struct vfio_irq_info *irq_info)
465*4882a593Smuzhiyun {
466*4882a593Smuzhiyun 	irq_info->count = 0;
467*4882a593Smuzhiyun 	return 0;
468*4882a593Smuzhiyun }
469*4882a593Smuzhiyun 
mdpy_get_device_info(struct mdev_device * mdev,struct vfio_device_info * dev_info)470*4882a593Smuzhiyun static int mdpy_get_device_info(struct mdev_device *mdev,
471*4882a593Smuzhiyun 				struct vfio_device_info *dev_info)
472*4882a593Smuzhiyun {
473*4882a593Smuzhiyun 	dev_info->flags = VFIO_DEVICE_FLAGS_PCI;
474*4882a593Smuzhiyun 	dev_info->num_regions = VFIO_PCI_NUM_REGIONS;
475*4882a593Smuzhiyun 	dev_info->num_irqs = VFIO_PCI_NUM_IRQS;
476*4882a593Smuzhiyun 	return 0;
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun 
mdpy_query_gfx_plane(struct mdev_device * mdev,struct vfio_device_gfx_plane_info * plane)479*4882a593Smuzhiyun static int mdpy_query_gfx_plane(struct mdev_device *mdev,
480*4882a593Smuzhiyun 				struct vfio_device_gfx_plane_info *plane)
481*4882a593Smuzhiyun {
482*4882a593Smuzhiyun 	struct mdev_state *mdev_state = mdev_get_drvdata(mdev);
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	if (plane->flags & VFIO_GFX_PLANE_TYPE_PROBE) {
485*4882a593Smuzhiyun 		if (plane->flags == (VFIO_GFX_PLANE_TYPE_PROBE |
486*4882a593Smuzhiyun 				     VFIO_GFX_PLANE_TYPE_REGION))
487*4882a593Smuzhiyun 			return 0;
488*4882a593Smuzhiyun 		return -EINVAL;
489*4882a593Smuzhiyun 	}
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	if (plane->flags != VFIO_GFX_PLANE_TYPE_REGION)
492*4882a593Smuzhiyun 		return -EINVAL;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	plane->drm_format     = mdev_state->type->format;
495*4882a593Smuzhiyun 	plane->width	      = mdev_state->type->width;
496*4882a593Smuzhiyun 	plane->height	      = mdev_state->type->height;
497*4882a593Smuzhiyun 	plane->stride	      = (mdev_state->type->width *
498*4882a593Smuzhiyun 				 mdev_state->type->bytepp);
499*4882a593Smuzhiyun 	plane->size	      = mdev_state->memsize;
500*4882a593Smuzhiyun 	plane->region_index   = MDPY_DISPLAY_REGION;
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	/* unused */
503*4882a593Smuzhiyun 	plane->drm_format_mod = 0;
504*4882a593Smuzhiyun 	plane->x_pos	      = 0;
505*4882a593Smuzhiyun 	plane->y_pos	      = 0;
506*4882a593Smuzhiyun 	plane->x_hot	      = 0;
507*4882a593Smuzhiyun 	plane->y_hot	      = 0;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	return 0;
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun 
mdpy_ioctl(struct mdev_device * mdev,unsigned int cmd,unsigned long arg)512*4882a593Smuzhiyun static long mdpy_ioctl(struct mdev_device *mdev, unsigned int cmd,
513*4882a593Smuzhiyun 		       unsigned long arg)
514*4882a593Smuzhiyun {
515*4882a593Smuzhiyun 	int ret = 0;
516*4882a593Smuzhiyun 	unsigned long minsz;
517*4882a593Smuzhiyun 	struct mdev_state *mdev_state;
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	mdev_state = mdev_get_drvdata(mdev);
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 	switch (cmd) {
522*4882a593Smuzhiyun 	case VFIO_DEVICE_GET_INFO:
523*4882a593Smuzhiyun 	{
524*4882a593Smuzhiyun 		struct vfio_device_info info;
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 		minsz = offsetofend(struct vfio_device_info, num_irqs);
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 		if (copy_from_user(&info, (void __user *)arg, minsz))
529*4882a593Smuzhiyun 			return -EFAULT;
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun 		if (info.argsz < minsz)
532*4882a593Smuzhiyun 			return -EINVAL;
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 		ret = mdpy_get_device_info(mdev, &info);
535*4882a593Smuzhiyun 		if (ret)
536*4882a593Smuzhiyun 			return ret;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 		memcpy(&mdev_state->dev_info, &info, sizeof(info));
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 		if (copy_to_user((void __user *)arg, &info, minsz))
541*4882a593Smuzhiyun 			return -EFAULT;
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 		return 0;
544*4882a593Smuzhiyun 	}
545*4882a593Smuzhiyun 	case VFIO_DEVICE_GET_REGION_INFO:
546*4882a593Smuzhiyun 	{
547*4882a593Smuzhiyun 		struct vfio_region_info info;
548*4882a593Smuzhiyun 		u16 cap_type_id = 0;
549*4882a593Smuzhiyun 		void *cap_type = NULL;
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 		minsz = offsetofend(struct vfio_region_info, offset);
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 		if (copy_from_user(&info, (void __user *)arg, minsz))
554*4882a593Smuzhiyun 			return -EFAULT;
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 		if (info.argsz < minsz)
557*4882a593Smuzhiyun 			return -EINVAL;
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 		ret = mdpy_get_region_info(mdev, &info, &cap_type_id,
560*4882a593Smuzhiyun 					   &cap_type);
561*4882a593Smuzhiyun 		if (ret)
562*4882a593Smuzhiyun 			return ret;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 		if (copy_to_user((void __user *)arg, &info, minsz))
565*4882a593Smuzhiyun 			return -EFAULT;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 		return 0;
568*4882a593Smuzhiyun 	}
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	case VFIO_DEVICE_GET_IRQ_INFO:
571*4882a593Smuzhiyun 	{
572*4882a593Smuzhiyun 		struct vfio_irq_info info;
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 		minsz = offsetofend(struct vfio_irq_info, count);
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 		if (copy_from_user(&info, (void __user *)arg, minsz))
577*4882a593Smuzhiyun 			return -EFAULT;
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 		if ((info.argsz < minsz) ||
580*4882a593Smuzhiyun 		    (info.index >= mdev_state->dev_info.num_irqs))
581*4882a593Smuzhiyun 			return -EINVAL;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 		ret = mdpy_get_irq_info(mdev, &info);
584*4882a593Smuzhiyun 		if (ret)
585*4882a593Smuzhiyun 			return ret;
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 		if (copy_to_user((void __user *)arg, &info, minsz))
588*4882a593Smuzhiyun 			return -EFAULT;
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 		return 0;
591*4882a593Smuzhiyun 	}
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	case VFIO_DEVICE_QUERY_GFX_PLANE:
594*4882a593Smuzhiyun 	{
595*4882a593Smuzhiyun 		struct vfio_device_gfx_plane_info plane;
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 		minsz = offsetofend(struct vfio_device_gfx_plane_info,
598*4882a593Smuzhiyun 				    region_index);
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 		if (copy_from_user(&plane, (void __user *)arg, minsz))
601*4882a593Smuzhiyun 			return -EFAULT;
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 		if (plane.argsz < minsz)
604*4882a593Smuzhiyun 			return -EINVAL;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 		ret = mdpy_query_gfx_plane(mdev, &plane);
607*4882a593Smuzhiyun 		if (ret)
608*4882a593Smuzhiyun 			return ret;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 		if (copy_to_user((void __user *)arg, &plane, minsz))
611*4882a593Smuzhiyun 			return -EFAULT;
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 		return 0;
614*4882a593Smuzhiyun 	}
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 	case VFIO_DEVICE_SET_IRQS:
617*4882a593Smuzhiyun 		return -EINVAL;
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	case VFIO_DEVICE_RESET:
620*4882a593Smuzhiyun 		return mdpy_reset(mdev);
621*4882a593Smuzhiyun 	}
622*4882a593Smuzhiyun 	return -ENOTTY;
623*4882a593Smuzhiyun }
624*4882a593Smuzhiyun 
mdpy_open(struct mdev_device * mdev)625*4882a593Smuzhiyun static int mdpy_open(struct mdev_device *mdev)
626*4882a593Smuzhiyun {
627*4882a593Smuzhiyun 	if (!try_module_get(THIS_MODULE))
628*4882a593Smuzhiyun 		return -ENODEV;
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 	return 0;
631*4882a593Smuzhiyun }
632*4882a593Smuzhiyun 
mdpy_close(struct mdev_device * mdev)633*4882a593Smuzhiyun static void mdpy_close(struct mdev_device *mdev)
634*4882a593Smuzhiyun {
635*4882a593Smuzhiyun 	module_put(THIS_MODULE);
636*4882a593Smuzhiyun }
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun static ssize_t
resolution_show(struct device * dev,struct device_attribute * attr,char * buf)639*4882a593Smuzhiyun resolution_show(struct device *dev, struct device_attribute *attr,
640*4882a593Smuzhiyun 		char *buf)
641*4882a593Smuzhiyun {
642*4882a593Smuzhiyun 	struct mdev_device *mdev = mdev_from_dev(dev);
643*4882a593Smuzhiyun 	struct mdev_state *mdev_state = mdev_get_drvdata(mdev);
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 	return sprintf(buf, "%dx%d\n",
646*4882a593Smuzhiyun 		       mdev_state->type->width,
647*4882a593Smuzhiyun 		       mdev_state->type->height);
648*4882a593Smuzhiyun }
649*4882a593Smuzhiyun static DEVICE_ATTR_RO(resolution);
650*4882a593Smuzhiyun 
651*4882a593Smuzhiyun static struct attribute *mdev_dev_attrs[] = {
652*4882a593Smuzhiyun 	&dev_attr_resolution.attr,
653*4882a593Smuzhiyun 	NULL,
654*4882a593Smuzhiyun };
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun static const struct attribute_group mdev_dev_group = {
657*4882a593Smuzhiyun 	.name  = "vendor",
658*4882a593Smuzhiyun 	.attrs = mdev_dev_attrs,
659*4882a593Smuzhiyun };
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun const struct attribute_group *mdev_dev_groups[] = {
662*4882a593Smuzhiyun 	&mdev_dev_group,
663*4882a593Smuzhiyun 	NULL,
664*4882a593Smuzhiyun };
665*4882a593Smuzhiyun 
666*4882a593Smuzhiyun static ssize_t
name_show(struct kobject * kobj,struct device * dev,char * buf)667*4882a593Smuzhiyun name_show(struct kobject *kobj, struct device *dev, char *buf)
668*4882a593Smuzhiyun {
669*4882a593Smuzhiyun 	return sprintf(buf, "%s\n", kobj->name);
670*4882a593Smuzhiyun }
671*4882a593Smuzhiyun MDEV_TYPE_ATTR_RO(name);
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun static ssize_t
description_show(struct kobject * kobj,struct device * dev,char * buf)674*4882a593Smuzhiyun description_show(struct kobject *kobj, struct device *dev, char *buf)
675*4882a593Smuzhiyun {
676*4882a593Smuzhiyun 	const struct mdpy_type *type = mdpy_find_type(kobj);
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	return sprintf(buf, "virtual display, %dx%d framebuffer\n",
679*4882a593Smuzhiyun 		       type ? type->width  : 0,
680*4882a593Smuzhiyun 		       type ? type->height : 0);
681*4882a593Smuzhiyun }
682*4882a593Smuzhiyun MDEV_TYPE_ATTR_RO(description);
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun static ssize_t
available_instances_show(struct kobject * kobj,struct device * dev,char * buf)685*4882a593Smuzhiyun available_instances_show(struct kobject *kobj, struct device *dev, char *buf)
686*4882a593Smuzhiyun {
687*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", max_devices - mdpy_count);
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun MDEV_TYPE_ATTR_RO(available_instances);
690*4882a593Smuzhiyun 
device_api_show(struct kobject * kobj,struct device * dev,char * buf)691*4882a593Smuzhiyun static ssize_t device_api_show(struct kobject *kobj, struct device *dev,
692*4882a593Smuzhiyun 			       char *buf)
693*4882a593Smuzhiyun {
694*4882a593Smuzhiyun 	return sprintf(buf, "%s\n", VFIO_DEVICE_API_PCI_STRING);
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun MDEV_TYPE_ATTR_RO(device_api);
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun static struct attribute *mdev_types_attrs[] = {
699*4882a593Smuzhiyun 	&mdev_type_attr_name.attr,
700*4882a593Smuzhiyun 	&mdev_type_attr_description.attr,
701*4882a593Smuzhiyun 	&mdev_type_attr_device_api.attr,
702*4882a593Smuzhiyun 	&mdev_type_attr_available_instances.attr,
703*4882a593Smuzhiyun 	NULL,
704*4882a593Smuzhiyun };
705*4882a593Smuzhiyun 
706*4882a593Smuzhiyun static struct attribute_group mdev_type_group1 = {
707*4882a593Smuzhiyun 	.name  = MDPY_TYPE_1,
708*4882a593Smuzhiyun 	.attrs = mdev_types_attrs,
709*4882a593Smuzhiyun };
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun static struct attribute_group mdev_type_group2 = {
712*4882a593Smuzhiyun 	.name  = MDPY_TYPE_2,
713*4882a593Smuzhiyun 	.attrs = mdev_types_attrs,
714*4882a593Smuzhiyun };
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun static struct attribute_group mdev_type_group3 = {
717*4882a593Smuzhiyun 	.name  = MDPY_TYPE_3,
718*4882a593Smuzhiyun 	.attrs = mdev_types_attrs,
719*4882a593Smuzhiyun };
720*4882a593Smuzhiyun 
721*4882a593Smuzhiyun static struct attribute_group *mdev_type_groups[] = {
722*4882a593Smuzhiyun 	&mdev_type_group1,
723*4882a593Smuzhiyun 	&mdev_type_group2,
724*4882a593Smuzhiyun 	&mdev_type_group3,
725*4882a593Smuzhiyun 	NULL,
726*4882a593Smuzhiyun };
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun static const struct mdev_parent_ops mdev_fops = {
729*4882a593Smuzhiyun 	.owner			= THIS_MODULE,
730*4882a593Smuzhiyun 	.mdev_attr_groups	= mdev_dev_groups,
731*4882a593Smuzhiyun 	.supported_type_groups	= mdev_type_groups,
732*4882a593Smuzhiyun 	.create			= mdpy_create,
733*4882a593Smuzhiyun 	.remove			= mdpy_remove,
734*4882a593Smuzhiyun 	.open			= mdpy_open,
735*4882a593Smuzhiyun 	.release		= mdpy_close,
736*4882a593Smuzhiyun 	.read			= mdpy_read,
737*4882a593Smuzhiyun 	.write			= mdpy_write,
738*4882a593Smuzhiyun 	.ioctl			= mdpy_ioctl,
739*4882a593Smuzhiyun 	.mmap			= mdpy_mmap,
740*4882a593Smuzhiyun };
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun static const struct file_operations vd_fops = {
743*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
744*4882a593Smuzhiyun };
745*4882a593Smuzhiyun 
mdpy_device_release(struct device * dev)746*4882a593Smuzhiyun static void mdpy_device_release(struct device *dev)
747*4882a593Smuzhiyun {
748*4882a593Smuzhiyun 	/* nothing */
749*4882a593Smuzhiyun }
750*4882a593Smuzhiyun 
mdpy_dev_init(void)751*4882a593Smuzhiyun static int __init mdpy_dev_init(void)
752*4882a593Smuzhiyun {
753*4882a593Smuzhiyun 	int ret = 0;
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 	ret = alloc_chrdev_region(&mdpy_devt, 0, MINORMASK + 1, MDPY_NAME);
756*4882a593Smuzhiyun 	if (ret < 0) {
757*4882a593Smuzhiyun 		pr_err("Error: failed to register mdpy_dev, err: %d\n", ret);
758*4882a593Smuzhiyun 		return ret;
759*4882a593Smuzhiyun 	}
760*4882a593Smuzhiyun 	cdev_init(&mdpy_cdev, &vd_fops);
761*4882a593Smuzhiyun 	cdev_add(&mdpy_cdev, mdpy_devt, MINORMASK + 1);
762*4882a593Smuzhiyun 	pr_info("%s: major %d\n", __func__, MAJOR(mdpy_devt));
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 	mdpy_class = class_create(THIS_MODULE, MDPY_CLASS_NAME);
765*4882a593Smuzhiyun 	if (IS_ERR(mdpy_class)) {
766*4882a593Smuzhiyun 		pr_err("Error: failed to register mdpy_dev class\n");
767*4882a593Smuzhiyun 		ret = PTR_ERR(mdpy_class);
768*4882a593Smuzhiyun 		goto failed1;
769*4882a593Smuzhiyun 	}
770*4882a593Smuzhiyun 	mdpy_dev.class = mdpy_class;
771*4882a593Smuzhiyun 	mdpy_dev.release = mdpy_device_release;
772*4882a593Smuzhiyun 	dev_set_name(&mdpy_dev, "%s", MDPY_NAME);
773*4882a593Smuzhiyun 
774*4882a593Smuzhiyun 	ret = device_register(&mdpy_dev);
775*4882a593Smuzhiyun 	if (ret)
776*4882a593Smuzhiyun 		goto failed2;
777*4882a593Smuzhiyun 
778*4882a593Smuzhiyun 	ret = mdev_register_device(&mdpy_dev, &mdev_fops);
779*4882a593Smuzhiyun 	if (ret)
780*4882a593Smuzhiyun 		goto failed3;
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun 	return 0;
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun failed3:
785*4882a593Smuzhiyun 	device_unregister(&mdpy_dev);
786*4882a593Smuzhiyun failed2:
787*4882a593Smuzhiyun 	class_destroy(mdpy_class);
788*4882a593Smuzhiyun failed1:
789*4882a593Smuzhiyun 	cdev_del(&mdpy_cdev);
790*4882a593Smuzhiyun 	unregister_chrdev_region(mdpy_devt, MINORMASK + 1);
791*4882a593Smuzhiyun 	return ret;
792*4882a593Smuzhiyun }
793*4882a593Smuzhiyun 
mdpy_dev_exit(void)794*4882a593Smuzhiyun static void __exit mdpy_dev_exit(void)
795*4882a593Smuzhiyun {
796*4882a593Smuzhiyun 	mdpy_dev.bus = NULL;
797*4882a593Smuzhiyun 	mdev_unregister_device(&mdpy_dev);
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	device_unregister(&mdpy_dev);
800*4882a593Smuzhiyun 	cdev_del(&mdpy_cdev);
801*4882a593Smuzhiyun 	unregister_chrdev_region(mdpy_devt, MINORMASK + 1);
802*4882a593Smuzhiyun 	class_destroy(mdpy_class);
803*4882a593Smuzhiyun 	mdpy_class = NULL;
804*4882a593Smuzhiyun }
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun module_init(mdpy_dev_init)
807*4882a593Smuzhiyun module_exit(mdpy_dev_exit)
808