1219820Sjeff/*-
2219820Sjeff * Copyright (c) 2010 Isilon Systems, Inc.
3219820Sjeff * Copyright (c) 2010 iX Systems, Inc.
4219820Sjeff * Copyright (c) 2010 Panasas, Inc.
5219820Sjeff * All rights reserved.
6219820Sjeff *
7219820Sjeff * Redistribution and use in source and binary forms, with or without
8219820Sjeff * modification, are permitted provided that the following conditions
9219820Sjeff * are met:
10219820Sjeff * 1. Redistributions of source code must retain the above copyright
11219820Sjeff *    notice unmodified, this list of conditions, and the following
12219820Sjeff *    disclaimer.
13219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
14219820Sjeff *    notice, this list of conditions and the following disclaimer in the
15219820Sjeff *    documentation and/or other materials provided with the distribution.
16219820Sjeff *
17219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18219820Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19219820Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20219820Sjeff * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21219820Sjeff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22219820Sjeff * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23219820Sjeff * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24219820Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25219820Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26219820Sjeff * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27219820Sjeff */
28219820Sjeff
29219820Sjeff#include <sys/param.h>
30219820Sjeff#include <sys/systm.h>
31219820Sjeff#include <sys/malloc.h>
32219820Sjeff#include <sys/kernel.h>
33219820Sjeff#include <sys/sysctl.h>
34219820Sjeff#include <sys/lock.h>
35219820Sjeff#include <sys/mutex.h>
36219820Sjeff#include <sys/bus.h>
37219820Sjeff#include <sys/fcntl.h>
38219820Sjeff#include <sys/file.h>
39219820Sjeff#include <sys/filio.h>
40219820Sjeff
41219820Sjeff#include <vm/vm.h>
42219820Sjeff#include <vm/pmap.h>
43219820Sjeff
44219820Sjeff#include <machine/stdarg.h>
45219820Sjeff#include <machine/pmap.h>
46219820Sjeff
47219820Sjeff#include <linux/kobject.h>
48219820Sjeff#include <linux/device.h>
49219820Sjeff#include <linux/slab.h>
50219820Sjeff#include <linux/module.h>
51219820Sjeff#include <linux/cdev.h>
52219820Sjeff#include <linux/file.h>
53219820Sjeff#include <linux/sysfs.h>
54219820Sjeff#include <linux/mm.h>
55219820Sjeff#include <linux/io.h>
56219820Sjeff#include <linux/vmalloc.h>
57219820Sjeff
58219820Sjeff#include <vm/vm_pager.h>
59219820Sjeff
60219820SjeffMALLOC_DEFINE(M_KMALLOC, "linux", "Linux kmalloc compat");
61219820Sjeff
62219820Sjeff#include <linux/rbtree.h>
63219820Sjeff/* Undo Linux compat changes. */
64219820Sjeff#undef RB_ROOT
65219820Sjeff#undef file
66219820Sjeff#undef cdev
67219820Sjeff#define	RB_ROOT(head)	(head)->rbh_root
68219820Sjeff#undef LIST_HEAD
69219820Sjeff/* From sys/queue.h */
70219820Sjeff#define LIST_HEAD(name, type)						\
71219820Sjeffstruct name {								\
72219820Sjeff	struct type *lh_first;	/* first element */			\
73219820Sjeff}
74219820Sjeff
75219820Sjeffstruct kobject class_root;
76219820Sjeffstruct device linux_rootdev;
77219820Sjeffstruct class miscclass;
78219820Sjeffstruct list_head pci_drivers;
79219820Sjeffstruct list_head pci_devices;
80219820Sjeffspinlock_t pci_lock;
81219820Sjeff
82219820Sjeffint
83219820Sjeffpanic_cmp(struct rb_node *one, struct rb_node *two)
84219820Sjeff{
85219820Sjeff	panic("no cmp");
86219820Sjeff}
87219820Sjeff
88219820SjeffRB_GENERATE(linux_root, rb_node, __entry, panic_cmp);
89219820Sjeff
90219820Sjeffint
91219820Sjeffkobject_set_name(struct kobject *kobj, const char *fmt, ...)
92219820Sjeff{
93219820Sjeff	va_list args;
94219820Sjeff	int error;
95219820Sjeff
96219820Sjeff	va_start(args, fmt);
97219820Sjeff	error = kobject_set_name_vargs(kobj, fmt, args);
98219820Sjeff	va_end(args);
99219820Sjeff
100219820Sjeff	return (error);
101219820Sjeff}
102219820Sjeff
103219820Sjeffstatic inline int
104219820Sjeffkobject_add_complete(struct kobject *kobj, struct kobject *parent)
105219820Sjeff{
106219820Sjeff	struct kobj_type *t;
107219820Sjeff	int error;
108219820Sjeff
109219820Sjeff	kobj->parent = kobject_get(parent);
110219820Sjeff	error = sysfs_create_dir(kobj);
111219820Sjeff	if (error == 0 && kobj->ktype && kobj->ktype->default_attrs) {
112219820Sjeff		struct attribute **attr;
113219820Sjeff		t = kobj->ktype;
114219820Sjeff
115219820Sjeff		for (attr = t->default_attrs; *attr != NULL; attr++) {
116219820Sjeff			error = sysfs_create_file(kobj, *attr);
117219820Sjeff			if (error)
118219820Sjeff				break;
119219820Sjeff		}
120219820Sjeff		if (error)
121219820Sjeff			sysfs_remove_dir(kobj);
122219820Sjeff
123219820Sjeff	}
124219820Sjeff	return (error);
125219820Sjeff}
126219820Sjeff
127219820Sjeffint
128219820Sjeffkobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)
129219820Sjeff{
130219820Sjeff	va_list args;
131219820Sjeff	int error;
132219820Sjeff
133219820Sjeff	va_start(args, fmt);
134219820Sjeff	error = kobject_set_name_vargs(kobj, fmt, args);
135219820Sjeff	va_end(args);
136219820Sjeff	if (error)
137219820Sjeff		return (error);
138219820Sjeff
139219820Sjeff	return kobject_add_complete(kobj, parent);
140219820Sjeff}
141219820Sjeff
142219820Sjeffvoid
143219820Sjeffkobject_release(struct kref *kref)
144219820Sjeff{
145219820Sjeff	struct kobject *kobj;
146219820Sjeff	char *name;
147219820Sjeff
148219820Sjeff	kobj = container_of(kref, struct kobject, kref);
149219820Sjeff	sysfs_remove_dir(kobj);
150219820Sjeff	if (kobj->parent)
151219820Sjeff		kobject_put(kobj->parent);
152219820Sjeff	kobj->parent = NULL;
153219820Sjeff	name = kobj->name;
154219820Sjeff	if (kobj->ktype && kobj->ktype->release)
155219820Sjeff		kobj->ktype->release(kobj);
156219820Sjeff	kfree(name);
157219820Sjeff}
158219820Sjeff
159219820Sjeffstatic void
160219820Sjeffkobject_kfree(struct kobject *kobj)
161219820Sjeff{
162219820Sjeff
163219820Sjeff	kfree(kobj);
164219820Sjeff}
165219820Sjeff
166219820Sjeffstruct kobj_type kfree_type = { .release = kobject_kfree };
167219820Sjeff
168219820Sjeffstruct device *
169219820Sjeffdevice_create(struct class *class, struct device *parent, dev_t devt,
170219820Sjeff    void *drvdata, const char *fmt, ...)
171219820Sjeff{
172219820Sjeff	struct device *dev;
173219820Sjeff	va_list args;
174219820Sjeff
175219820Sjeff	dev = kzalloc(sizeof(*dev), M_WAITOK);
176219820Sjeff	dev->parent = parent;
177219820Sjeff	dev->class = class;
178219820Sjeff	dev->devt = devt;
179219820Sjeff	dev->driver_data = drvdata;
180219820Sjeff	va_start(args, fmt);
181219820Sjeff	kobject_set_name_vargs(&dev->kobj, fmt, args);
182219820Sjeff	va_end(args);
183219820Sjeff	device_register(dev);
184219820Sjeff
185219820Sjeff	return (dev);
186219820Sjeff}
187219820Sjeff
188219820Sjeffint
189219820Sjeffkobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
190219820Sjeff    struct kobject *parent, const char *fmt, ...)
191219820Sjeff{
192219820Sjeff	va_list args;
193219820Sjeff	int error;
194219820Sjeff
195219820Sjeff	kobject_init(kobj, ktype);
196219820Sjeff	kobj->ktype = ktype;
197219820Sjeff	kobj->parent = parent;
198219820Sjeff	kobj->name = NULL;
199219820Sjeff
200219820Sjeff	va_start(args, fmt);
201219820Sjeff	error = kobject_set_name_vargs(kobj, fmt, args);
202219820Sjeff	va_end(args);
203219820Sjeff	if (error)
204219820Sjeff		return (error);
205219820Sjeff	return kobject_add_complete(kobj, parent);
206219820Sjeff}
207219820Sjeff
208219820Sjeffstatic void
209219820Sjefflinux_file_dtor(void *cdp)
210219820Sjeff{
211219820Sjeff	struct linux_file *filp;
212219820Sjeff
213219820Sjeff	filp = cdp;
214252041Sjhb	filp->f_op->release(filp->f_vnode, filp);
215252041Sjhb	vdrop(filp->f_vnode);
216219820Sjeff	kfree(filp);
217219820Sjeff}
218219820Sjeff
219219820Sjeffstatic int
220219820Sjefflinux_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
221219820Sjeff{
222219820Sjeff	struct linux_cdev *ldev;
223219820Sjeff	struct linux_file *filp;
224219820Sjeff	struct file *file;
225219820Sjeff	int error;
226219820Sjeff
227219820Sjeff	file = curthread->td_fpop;
228219820Sjeff	ldev = dev->si_drv1;
229219820Sjeff	if (ldev == NULL)
230219820Sjeff		return (ENODEV);
231219820Sjeff	filp = kzalloc(sizeof(*filp), GFP_KERNEL);
232219820Sjeff	filp->f_dentry = &filp->f_dentry_store;
233219820Sjeff	filp->f_op = ldev->ops;
234219820Sjeff	filp->f_flags = file->f_flag;
235252041Sjhb	vhold(file->f_vnode);
236252041Sjhb	filp->f_vnode = file->f_vnode;
237219820Sjeff	if (filp->f_op->open) {
238219820Sjeff		error = -filp->f_op->open(file->f_vnode, filp);
239219820Sjeff		if (error) {
240219820Sjeff			kfree(filp);
241219820Sjeff			return (error);
242219820Sjeff		}
243219820Sjeff	}
244219820Sjeff	error = devfs_set_cdevpriv(filp, linux_file_dtor);
245219820Sjeff	if (error) {
246219820Sjeff		filp->f_op->release(file->f_vnode, filp);
247219820Sjeff		kfree(filp);
248219820Sjeff		return (error);
249219820Sjeff	}
250219820Sjeff
251219820Sjeff	return 0;
252219820Sjeff}
253219820Sjeff
254219820Sjeffstatic int
255219820Sjefflinux_dev_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
256219820Sjeff{
257219820Sjeff	struct linux_cdev *ldev;
258219820Sjeff	struct linux_file *filp;
259219820Sjeff	struct file *file;
260219820Sjeff	int error;
261219820Sjeff
262219820Sjeff	file = curthread->td_fpop;
263219820Sjeff	ldev = dev->si_drv1;
264219820Sjeff	if (ldev == NULL)
265219820Sjeff		return (0);
266219820Sjeff	if ((error = devfs_get_cdevpriv((void **)&filp)) != 0)
267219820Sjeff		return (error);
268219820Sjeff	filp->f_flags = file->f_flag;
269219820Sjeff
270219820Sjeff	return (0);
271219820Sjeff}
272219820Sjeff
273219820Sjeffstatic int
274219820Sjefflinux_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
275219820Sjeff    struct thread *td)
276219820Sjeff{
277219820Sjeff	struct linux_cdev *ldev;
278219820Sjeff	struct linux_file *filp;
279219820Sjeff	struct file *file;
280219820Sjeff	int error;
281219820Sjeff
282219820Sjeff	file = curthread->td_fpop;
283219820Sjeff	ldev = dev->si_drv1;
284219820Sjeff	if (ldev == NULL)
285219820Sjeff		return (0);
286219820Sjeff	if ((error = devfs_get_cdevpriv((void **)&filp)) != 0)
287219820Sjeff		return (error);
288219820Sjeff	filp->f_flags = file->f_flag;
289219820Sjeff	/*
290219820Sjeff	 * Linux does not have a generic ioctl copyin/copyout layer.  All
291219820Sjeff	 * linux ioctls must be converted to void ioctls which pass a
292219820Sjeff	 * pointer to the address of the data.  We want the actual user
293219820Sjeff	 * address so we dereference here.
294219820Sjeff	 */
295219820Sjeff	data = *(void **)data;
296219820Sjeff	if (filp->f_op->unlocked_ioctl)
297219820Sjeff		error = -filp->f_op->unlocked_ioctl(filp, cmd, (u_long)data);
298219820Sjeff	else
299219820Sjeff		error = ENOTTY;
300219820Sjeff
301219820Sjeff	return (error);
302219820Sjeff}
303219820Sjeff
304219820Sjeffstatic int
305219820Sjefflinux_dev_read(struct cdev *dev, struct uio *uio, int ioflag)
306219820Sjeff{
307219820Sjeff	struct linux_cdev *ldev;
308219820Sjeff	struct linux_file *filp;
309219820Sjeff	struct file *file;
310219820Sjeff	ssize_t bytes;
311219820Sjeff	int error;
312219820Sjeff
313219820Sjeff	file = curthread->td_fpop;
314219820Sjeff	ldev = dev->si_drv1;
315219820Sjeff	if (ldev == NULL)
316219820Sjeff		return (0);
317219820Sjeff	if ((error = devfs_get_cdevpriv((void **)&filp)) != 0)
318219820Sjeff		return (error);
319219820Sjeff	filp->f_flags = file->f_flag;
320219820Sjeff	if (uio->uio_iovcnt != 1)
321219820Sjeff		panic("linux_dev_read: uio %p iovcnt %d",
322219820Sjeff		    uio, uio->uio_iovcnt);
323219820Sjeff	if (filp->f_op->read) {
324219820Sjeff		bytes = filp->f_op->read(filp, uio->uio_iov->iov_base,
325219820Sjeff		    uio->uio_iov->iov_len, &uio->uio_offset);
326219820Sjeff		if (bytes >= 0) {
327219820Sjeff			uio->uio_iov->iov_base += bytes;
328219820Sjeff			uio->uio_iov->iov_len -= bytes;
329219820Sjeff			uio->uio_resid -= bytes;
330219820Sjeff		} else
331219820Sjeff			error = -bytes;
332219820Sjeff	} else
333219820Sjeff		error = ENXIO;
334219820Sjeff
335219820Sjeff	return (error);
336219820Sjeff}
337219820Sjeff
338219820Sjeffstatic int
339219820Sjefflinux_dev_write(struct cdev *dev, struct uio *uio, int ioflag)
340219820Sjeff{
341219820Sjeff	struct linux_cdev *ldev;
342219820Sjeff	struct linux_file *filp;
343219820Sjeff	struct file *file;
344219820Sjeff	ssize_t bytes;
345219820Sjeff	int error;
346219820Sjeff
347219820Sjeff	file = curthread->td_fpop;
348219820Sjeff	ldev = dev->si_drv1;
349219820Sjeff	if (ldev == NULL)
350219820Sjeff		return (0);
351219820Sjeff	if ((error = devfs_get_cdevpriv((void **)&filp)) != 0)
352219820Sjeff		return (error);
353219820Sjeff	filp->f_flags = file->f_flag;
354219820Sjeff	if (uio->uio_iovcnt != 1)
355219820Sjeff		panic("linux_dev_write: uio %p iovcnt %d",
356219820Sjeff		    uio, uio->uio_iovcnt);
357219820Sjeff	if (filp->f_op->write) {
358219820Sjeff		bytes = filp->f_op->write(filp, uio->uio_iov->iov_base,
359219820Sjeff		    uio->uio_iov->iov_len, &uio->uio_offset);
360219820Sjeff		if (bytes >= 0) {
361219820Sjeff			uio->uio_iov->iov_base += bytes;
362219820Sjeff			uio->uio_iov->iov_len -= bytes;
363219820Sjeff			uio->uio_resid -= bytes;
364219820Sjeff		} else
365219820Sjeff			error = -bytes;
366219820Sjeff	} else
367219820Sjeff		error = ENXIO;
368219820Sjeff
369219820Sjeff	return (error);
370219820Sjeff}
371219820Sjeff
372219820Sjeffstatic int
373219820Sjefflinux_dev_poll(struct cdev *dev, int events, struct thread *td)
374219820Sjeff{
375219820Sjeff	struct linux_cdev *ldev;
376219820Sjeff	struct linux_file *filp;
377219820Sjeff	struct file *file;
378219820Sjeff	int revents;
379219820Sjeff	int error;
380219820Sjeff
381219820Sjeff	file = curthread->td_fpop;
382219820Sjeff	ldev = dev->si_drv1;
383219820Sjeff	if (ldev == NULL)
384219820Sjeff		return (0);
385219820Sjeff	if ((error = devfs_get_cdevpriv((void **)&filp)) != 0)
386219820Sjeff		return (error);
387219820Sjeff	filp->f_flags = file->f_flag;
388219820Sjeff	if (filp->f_op->poll)
389219820Sjeff		revents = filp->f_op->poll(filp, NULL) & events;
390219820Sjeff	else
391219820Sjeff		revents = 0;
392219820Sjeff
393219820Sjeff	return (revents);
394219820Sjeff}
395219820Sjeff
396219820Sjeffstatic int
397219820Sjefflinux_dev_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
398219820Sjeff    int nprot, vm_memattr_t *memattr)
399219820Sjeff{
400219820Sjeff
401219820Sjeff	/* XXX memattr not honored. */
402219820Sjeff	*paddr = offset;
403219820Sjeff	return (0);
404219820Sjeff}
405219820Sjeff
406219820Sjeffstatic int
407219820Sjefflinux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *offset,
408219820Sjeff    vm_size_t size, struct vm_object **object, int nprot)
409219820Sjeff{
410219820Sjeff	struct linux_cdev *ldev;
411219820Sjeff	struct linux_file *filp;
412219820Sjeff	struct file *file;
413219820Sjeff	struct vm_area_struct vma;
414219820Sjeff	vm_paddr_t paddr;
415219820Sjeff	vm_page_t m;
416219820Sjeff	int error;
417219820Sjeff
418219820Sjeff	file = curthread->td_fpop;
419219820Sjeff	ldev = dev->si_drv1;
420219820Sjeff	if (ldev == NULL)
421219820Sjeff		return (ENODEV);
422219820Sjeff	if (size != PAGE_SIZE)
423219820Sjeff		return (EINVAL);
424219820Sjeff	if ((error = devfs_get_cdevpriv((void **)&filp)) != 0)
425219820Sjeff		return (error);
426219820Sjeff	filp->f_flags = file->f_flag;
427219820Sjeff	vma.vm_start = 0;
428219820Sjeff	vma.vm_end = PAGE_SIZE;
429219820Sjeff	vma.vm_pgoff = *offset / PAGE_SIZE;
430219820Sjeff	vma.vm_pfn = 0;
431219820Sjeff	vma.vm_page_prot = 0;
432219820Sjeff	if (filp->f_op->mmap) {
433219820Sjeff		error = -filp->f_op->mmap(filp, &vma);
434219820Sjeff		if (error == 0) {
435219820Sjeff			paddr = (vm_paddr_t)vma.vm_pfn << PAGE_SHIFT;
436219820Sjeff			*offset = paddr;
437219820Sjeff			m = PHYS_TO_VM_PAGE(paddr);
438219820Sjeff			*object = vm_pager_allocate(OBJT_DEVICE, dev,
439219820Sjeff			    PAGE_SIZE, nprot, *offset, curthread->td_ucred);
440219820Sjeff		        if (*object == NULL)
441219820Sjeff               			 return (EINVAL);
442219820Sjeff			if (vma.vm_page_prot != VM_MEMATTR_DEFAULT)
443219820Sjeff				pmap_page_set_memattr(m, vma.vm_page_prot);
444219820Sjeff		}
445219820Sjeff	} else
446219820Sjeff		error = ENODEV;
447219820Sjeff
448219820Sjeff	return (error);
449219820Sjeff}
450219820Sjeff
451219820Sjeffstruct cdevsw linuxcdevsw = {
452219820Sjeff	.d_version = D_VERSION,
453219820Sjeff	.d_flags = D_TRACKCLOSE,
454219820Sjeff	.d_open = linux_dev_open,
455219820Sjeff	.d_close = linux_dev_close,
456219820Sjeff	.d_read = linux_dev_read,
457219820Sjeff	.d_write = linux_dev_write,
458219820Sjeff	.d_ioctl = linux_dev_ioctl,
459219820Sjeff	.d_mmap_single = linux_dev_mmap_single,
460219820Sjeff	.d_mmap = linux_dev_mmap,
461219820Sjeff	.d_poll = linux_dev_poll,
462219820Sjeff};
463219820Sjeff
464219820Sjeffstatic int
465219820Sjefflinux_file_read(struct file *file, struct uio *uio, struct ucred *active_cred,
466219820Sjeff    int flags, struct thread *td)
467219820Sjeff{
468219820Sjeff	struct linux_file *filp;
469219820Sjeff	ssize_t bytes;
470219820Sjeff	int error;
471219820Sjeff
472219820Sjeff	error = 0;
473219820Sjeff	filp = (struct linux_file *)file->f_data;
474219820Sjeff	filp->f_flags = file->f_flag;
475219820Sjeff	if (uio->uio_iovcnt != 1)
476219820Sjeff		panic("linux_file_read: uio %p iovcnt %d",
477219820Sjeff		    uio, uio->uio_iovcnt);
478219820Sjeff	if (filp->f_op->read) {
479219820Sjeff		bytes = filp->f_op->read(filp, uio->uio_iov->iov_base,
480219820Sjeff		    uio->uio_iov->iov_len, &uio->uio_offset);
481219820Sjeff		if (bytes >= 0) {
482219820Sjeff			uio->uio_iov->iov_base += bytes;
483219820Sjeff			uio->uio_iov->iov_len -= bytes;
484219820Sjeff			uio->uio_resid -= bytes;
485219820Sjeff		} else
486219820Sjeff			error = -bytes;
487219820Sjeff	} else
488219820Sjeff		error = ENXIO;
489219820Sjeff
490219820Sjeff	return (error);
491219820Sjeff}
492219820Sjeff
493219820Sjeffstatic int
494219820Sjefflinux_file_poll(struct file *file, int events, struct ucred *active_cred,
495219820Sjeff    struct thread *td)
496219820Sjeff{
497219820Sjeff	struct linux_file *filp;
498219820Sjeff	int revents;
499219820Sjeff
500219820Sjeff	filp = (struct linux_file *)file->f_data;
501219820Sjeff	filp->f_flags = file->f_flag;
502219820Sjeff	if (filp->f_op->poll)
503219820Sjeff		revents = filp->f_op->poll(filp, NULL) & events;
504219820Sjeff	else
505219820Sjeff		revents = 0;
506219820Sjeff
507219820Sjeff	return (0);
508219820Sjeff}
509219820Sjeff
510219820Sjeffstatic int
511219820Sjefflinux_file_close(struct file *file, struct thread *td)
512219820Sjeff{
513219820Sjeff	struct linux_file *filp;
514219820Sjeff	int error;
515219820Sjeff
516219820Sjeff	filp = (struct linux_file *)file->f_data;
517219820Sjeff	filp->f_flags = file->f_flag;
518219820Sjeff	error = -filp->f_op->release(NULL, filp);
519219820Sjeff	funsetown(&filp->f_sigio);
520219820Sjeff	kfree(filp);
521219820Sjeff
522219820Sjeff	return (error);
523219820Sjeff}
524219820Sjeff
525219820Sjeffstatic int
526219820Sjefflinux_file_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *cred,
527219820Sjeff    struct thread *td)
528219820Sjeff{
529219820Sjeff	struct linux_file *filp;
530219820Sjeff	int error;
531219820Sjeff
532219820Sjeff	filp = (struct linux_file *)fp->f_data;
533219820Sjeff	filp->f_flags = fp->f_flag;
534219820Sjeff	error = 0;
535219820Sjeff
536219820Sjeff	switch (cmd) {
537219820Sjeff	case FIONBIO:
538219820Sjeff		break;
539219820Sjeff	case FIOASYNC:
540219820Sjeff		if (filp->f_op->fasync == NULL)
541219820Sjeff			break;
542219820Sjeff		error = filp->f_op->fasync(0, filp, fp->f_flag & FASYNC);
543219820Sjeff		break;
544219820Sjeff	case FIOSETOWN:
545219820Sjeff		error = fsetown(*(int *)data, &filp->f_sigio);
546219820Sjeff		if (error == 0)
547219820Sjeff			error = filp->f_op->fasync(0, filp,
548219820Sjeff			    fp->f_flag & FASYNC);
549219820Sjeff		break;
550219820Sjeff	case FIOGETOWN:
551219820Sjeff		*(int *)data = fgetown(&filp->f_sigio);
552219820Sjeff		break;
553219820Sjeff	default:
554219820Sjeff		error = ENOTTY;
555219820Sjeff		break;
556219820Sjeff	}
557219820Sjeff	return (error);
558219820Sjeff}
559219820Sjeff
560219820Sjeffstruct fileops linuxfileops = {
561219820Sjeff	.fo_read = linux_file_read,
562219820Sjeff	.fo_poll = linux_file_poll,
563219820Sjeff	.fo_close = linux_file_close,
564224914Skib	.fo_ioctl = linux_file_ioctl,
565224914Skib	.fo_chmod = invfo_chmod,
566224914Skib	.fo_chown = invfo_chown,
567219820Sjeff};
568219820Sjeff
569219820Sjeff/*
570219820Sjeff * Hash of vmmap addresses.  This is infrequently accessed and does not
571219820Sjeff * need to be particularly large.  This is done because we must store the
572219820Sjeff * caller's idea of the map size to properly unmap.
573219820Sjeff */
574219820Sjeffstruct vmmap {
575219820Sjeff	LIST_ENTRY(vmmap)	vm_next;
576219820Sjeff	void 			*vm_addr;
577219820Sjeff	unsigned long		vm_size;
578219820Sjeff};
579219820Sjeff
580219820SjeffLIST_HEAD(vmmaphd, vmmap);
581219820Sjeff#define	VMMAP_HASH_SIZE	64
582219820Sjeff#define	VMMAP_HASH_MASK	(VMMAP_HASH_SIZE - 1)
583219820Sjeff#define	VM_HASH(addr)	((uintptr_t)(addr) >> PAGE_SHIFT) & VMMAP_HASH_MASK
584219820Sjeffstatic struct vmmaphd vmmaphead[VMMAP_HASH_SIZE];
585219820Sjeffstatic struct mtx vmmaplock;
586219820Sjeff
587219820Sjeffstatic void
588219820Sjeffvmmap_add(void *addr, unsigned long size)
589219820Sjeff{
590219820Sjeff	struct vmmap *vmmap;
591219820Sjeff
592219820Sjeff	vmmap = kmalloc(sizeof(*vmmap), GFP_KERNEL);
593219820Sjeff	mtx_lock(&vmmaplock);
594219820Sjeff	vmmap->vm_size = size;
595219820Sjeff	vmmap->vm_addr = addr;
596219820Sjeff	LIST_INSERT_HEAD(&vmmaphead[VM_HASH(addr)], vmmap, vm_next);
597219820Sjeff	mtx_unlock(&vmmaplock);
598219820Sjeff}
599219820Sjeff
600219820Sjeffstatic struct vmmap *
601219820Sjeffvmmap_remove(void *addr)
602219820Sjeff{
603219820Sjeff	struct vmmap *vmmap;
604219820Sjeff
605219820Sjeff	mtx_lock(&vmmaplock);
606219820Sjeff	LIST_FOREACH(vmmap, &vmmaphead[VM_HASH(addr)], vm_next)
607219820Sjeff		if (vmmap->vm_addr == addr)
608219820Sjeff			break;
609219820Sjeff	if (vmmap)
610219820Sjeff		LIST_REMOVE(vmmap, vm_next);
611219820Sjeff	mtx_unlock(&vmmaplock);
612219820Sjeff
613219820Sjeff	return (vmmap);
614219820Sjeff}
615219820Sjeff
616219820Sjeffvoid *
617219820Sjeff_ioremap_attr(vm_paddr_t phys_addr, unsigned long size, int attr)
618219820Sjeff{
619219820Sjeff	void *addr;
620219820Sjeff
621219820Sjeff	addr = pmap_mapdev_attr(phys_addr, size, attr);
622219820Sjeff	if (addr == NULL)
623219820Sjeff		return (NULL);
624219820Sjeff	vmmap_add(addr, size);
625219820Sjeff
626219820Sjeff	return (addr);
627219820Sjeff}
628219820Sjeff
629219820Sjeffvoid
630219820Sjeffiounmap(void *addr)
631219820Sjeff{
632219820Sjeff	struct vmmap *vmmap;
633219820Sjeff
634219820Sjeff	vmmap = vmmap_remove(addr);
635219820Sjeff	if (vmmap == NULL)
636219820Sjeff		return;
637219820Sjeff	pmap_unmapdev((vm_offset_t)addr, vmmap->vm_size);
638219820Sjeff	kfree(vmmap);
639219820Sjeff}
640219820Sjeff
641219820Sjeff
642219820Sjeffvoid *
643219820Sjeffvmap(struct page **pages, unsigned int count, unsigned long flags, int prot)
644219820Sjeff{
645219820Sjeff	vm_offset_t off;
646219820Sjeff	size_t size;
647219820Sjeff
648219820Sjeff	size = count * PAGE_SIZE;
649219820Sjeff	off = kmem_alloc_nofault(kernel_map, size);
650219820Sjeff	if (off == 0)
651219820Sjeff		return (NULL);
652219820Sjeff	vmmap_add((void *)off, size);
653219820Sjeff	pmap_qenter(off, pages, count);
654219820Sjeff
655219820Sjeff	return ((void *)off);
656219820Sjeff}
657219820Sjeff
658219820Sjeffvoid
659219820Sjeffvunmap(void *addr)
660219820Sjeff{
661219820Sjeff	struct vmmap *vmmap;
662219820Sjeff
663219820Sjeff	vmmap = vmmap_remove(addr);
664219820Sjeff	if (vmmap == NULL)
665219820Sjeff		return;
666219820Sjeff	pmap_qremove((vm_offset_t)addr, vmmap->vm_size / PAGE_SIZE);
667219820Sjeff	kmem_free(kernel_map, (vm_offset_t)addr, vmmap->vm_size);
668219820Sjeff	kfree(vmmap);
669219820Sjeff}
670219820Sjeff
671219820Sjeffstatic void
672219820Sjefflinux_compat_init(void)
673219820Sjeff{
674219820Sjeff	struct sysctl_oid *rootoid;
675219820Sjeff	int i;
676219820Sjeff
677219820Sjeff	rootoid = SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(),
678219820Sjeff	    OID_AUTO, "sys", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "sys");
679219820Sjeff	kobject_init(&class_root, &class_ktype);
680219820Sjeff	kobject_set_name(&class_root, "class");
681219820Sjeff	class_root.oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(rootoid),
682219820Sjeff	    OID_AUTO, "class", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "class");
683219820Sjeff	kobject_init(&linux_rootdev.kobj, &dev_ktype);
684219820Sjeff	kobject_set_name(&linux_rootdev.kobj, "device");
685219820Sjeff	linux_rootdev.kobj.oidp = SYSCTL_ADD_NODE(NULL,
686219820Sjeff	    SYSCTL_CHILDREN(rootoid), OID_AUTO, "device", CTLFLAG_RD, NULL,
687219820Sjeff	    "device");
688219820Sjeff	linux_rootdev.bsddev = root_bus;
689219820Sjeff	miscclass.name = "misc";
690219820Sjeff	class_register(&miscclass);
691219820Sjeff	INIT_LIST_HEAD(&pci_drivers);
692219820Sjeff	INIT_LIST_HEAD(&pci_devices);
693219820Sjeff	spin_lock_init(&pci_lock);
694219820Sjeff	mtx_init(&vmmaplock, "IO Map lock", NULL, MTX_DEF);
695219820Sjeff	for (i = 0; i < VMMAP_HASH_SIZE; i++)
696219820Sjeff		LIST_INIT(&vmmaphead[i]);
697219820Sjeff}
698219820Sjeff
699219820SjeffSYSINIT(linux_compat, SI_SUB_DRIVERS, SI_ORDER_SECOND, linux_compat_init, NULL);
700