/* * linux/kernel/chr_drv/mem.c * * Copyright (C) 1991, 1992 Linus Torvalds */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_SOUND extern long soundcard_init(long mem_start); #endif static int read_ram(struct inode * inode, struct file * file,char * buf, int count) { return -EIO; } static int write_ram(struct inode * inode, struct file * file,char * buf, int count) { return -EIO; } static int read_mem(struct inode * inode, struct file * file,char * buf, int count) { unsigned long p = file->f_pos; int read; if (count < 0) return -EINVAL; if (p >= high_memory) return 0; if (count > high_memory - p) count = high_memory - p; read = 0; while (p < PAGE_SIZE && count > 0) { put_fs_byte(0,buf); buf++; p++; count--; read++; } memcpy_tofs(buf,(void *) p,count); read += count; file->f_pos += read; return read; } static int write_mem(struct inode * inode, struct file * file,char * buf, int count) { unsigned long p = file->f_pos; int written; if (count < 0) return -EINVAL; if (p >= high_memory) return 0; if (count > high_memory - p) count = high_memory - p; written = 0; while (p < PAGE_SIZE && count > 0) { /* Hmm. Do something? */ buf++; p++; count--; written++; } memcpy_fromfs((void *) p,buf,count); written += count; file->f_pos += written; return count; } static int mmap_mem(struct inode * inode, struct file * file, unsigned long addr, size_t len, int prot, unsigned long off) { struct vm_area_struct * mpnt; if (off & 0xfff || off + len < off) return -ENXIO; if (x86 > 3 && off >= high_memory) prot |= PAGE_PCD; if (remap_page_range(addr, off, len, prot)) return -EAGAIN; /* try to create a dummy vmm-structure so that the rest of the kernel knows we are here */ mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); if (!mpnt) return 0; mpnt->vm_task = current; mpnt->vm_start = addr; mpnt->vm_end = addr + len; mpnt->vm_page_prot = prot; mpnt->vm_share = NULL; mpnt->vm_inode = inode; inode->i_count++; mpnt->vm_offset = off; mpnt->vm_ops = NULL; insert_vm_struct(current, mpnt); merge_segments(current->mmap, NULL, NULL); return 0; } static int read_kmem(struct inode *inode, struct file *file, char *buf, int count) { int read1, read2; read1 = read_mem(inode, file, buf, count); if (read1 < 0) return read1; read2 = vread(buf + read1, (char *) file->f_pos, count - read1); if (read2 < 0) return read2; file->f_pos += read2; return read1 + read2; } static int read_port(struct inode * inode,struct file * file,char * buf, int count) { unsigned int i = file->f_pos; char * tmp = buf; while (count-- > 0 && i < 65536) { put_fs_byte(inb(i),tmp); i++; tmp++; } file->f_pos = i; return tmp-buf; } static int write_port(struct inode * inode,struct file * file,char * buf, int count) { unsigned int i = file->f_pos; char * tmp = buf; while (count-- > 0 && i < 65536) { outb(get_fs_byte(tmp),i); i++; tmp++; } file->f_pos = i; return tmp-buf; } static int read_null(struct inode * node,struct file * file,char * buf,int count) { return 0; } static int write_null(struct inode * inode,struct file * file,char * buf, int count) { return count; } static int read_zero(struct inode * node,struct file * file,char * buf,int count) { int left; for (left = count; left > 0; left--) { put_fs_byte(0,buf); buf++; } return count; } static int mmap_zero(struct inode * inode, struct file * file, unsigned long addr, size_t len, int prot, unsigned long off) { struct vm_area_struct *mpnt; if (prot & PAGE_RW) return -EINVAL; if (zeromap_page_range(addr, len, prot)) return -EAGAIN; /* * try to create a dummy vmm-structure so that the * rest of the kernel knows we are here */ mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); if (!mpnt) return 0; mpnt->vm_task = current; mpnt->vm_start = addr; mpnt->vm_end = addr + len; mpnt->vm_page_prot = prot; mpnt->vm_share = NULL; mpnt->vm_inode = NULL; mpnt->vm_offset = off; mpnt->vm_ops = NULL; insert_vm_struct(current, mpnt); merge_segments(current->mmap, ignoff_mergep, inode); return 0; } static int read_full(struct inode * node,struct file * file,char * buf,int count) { return count; } static int write_full(struct inode * inode,struct file * file,char * buf, int count) { return -ENOSPC; } /* * Special lseek() function for /dev/null and /dev/zero. Most notably, you can fopen() * both devices with "a" now. This was previously impossible. SRB. */ static int null_lseek(struct inode * inode, struct file * file, off_t offset, int orig) { return file->f_pos=0; } /* * The memory devices use the full 32 bits of the offset, and so we cannot * check against negative addresses: they are ok. The return value is weird, * though, in that case (0). * * also note that seeking relative to the "end of file" isn't supported: * it has no meaning, so it returns -EINVAL. */ static int memory_lseek(struct inode * inode, struct file * file, off_t offset, int orig) { switch (orig) { case 0: file->f_pos = offset; return file->f_pos; case 1: file->f_pos += offset; return file->f_pos; default: return -EINVAL; } if (file->f_pos < 0) return 0; return file->f_pos; } #define write_kmem write_mem #define mmap_kmem mmap_mem #define zero_lseek null_lseek #define write_zero write_null static struct file_operations ram_fops = { memory_lseek, read_ram, write_ram, NULL, /* ram_readdir */ NULL, /* ram_select */ NULL, /* ram_ioctl */ NULL, /* ram_mmap */ NULL, /* no special open code */ NULL, /* no special release code */ NULL /* fsync */ }; static struct file_operations mem_fops = { memory_lseek, read_mem, write_mem, NULL, /* mem_readdir */ NULL, /* mem_select */ NULL, /* mem_ioctl */ mmap_mem, NULL, /* no special open code */ NULL, /* no special release code */ NULL /* fsync */ }; static struct file_operations kmem_fops = { memory_lseek, read_kmem, write_kmem, NULL, /* kmem_readdir */ NULL, /* kmem_select */ NULL, /* kmem_ioctl */ mmap_kmem, NULL, /* no special open code */ NULL, /* no special release code */ NULL /* fsync */ }; static struct file_operations null_fops = { null_lseek, read_null, write_null, NULL, /* null_readdir */ NULL, /* null_select */ NULL, /* null_ioctl */ NULL, /* null_mmap */ NULL, /* no special open code */ NULL, /* no special release code */ NULL /* fsync */ }; static struct file_operations port_fops = { memory_lseek, read_port, write_port, NULL, /* port_readdir */ NULL, /* port_select */ NULL, /* port_ioctl */ NULL, /* port_mmap */ NULL, /* no special open code */ NULL, /* no special release code */ NULL /* fsync */ }; static struct file_operations zero_fops = { zero_lseek, read_zero, write_zero, NULL, /* zero_readdir */ NULL, /* zero_select */ NULL, /* zero_ioctl */ mmap_zero, NULL, /* no special open code */ NULL /* no special release code */ }; static struct file_operations full_fops = { memory_lseek, read_full, write_full, NULL, /* full_readdir */ NULL, /* full_select */ NULL, /* full_ioctl */ NULL, /* full_mmap */ NULL, /* no special open code */ NULL /* no special release code */ }; static int memory_open(struct inode * inode, struct file * filp) { switch (MINOR(inode->i_rdev)) { case 0: filp->f_op = &ram_fops; break; case 1: filp->f_op = &mem_fops; break; case 2: filp->f_op = &kmem_fops; break; case 3: filp->f_op = &null_fops; break; case 4: filp->f_op = &port_fops; break; case 5: filp->f_op = &zero_fops; break; case 7: filp->f_op = &full_fops; break; default: return -ENODEV; } if (filp->f_op && filp->f_op->open) return filp->f_op->open(inode,filp); return 0; } static struct file_operations memory_fops = { NULL, /* lseek */ NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* select */ NULL, /* ioctl */ NULL, /* mmap */ memory_open, /* just a selector for the real open */ NULL, /* release */ NULL /* fsync */ }; #ifdef CONFIG_FTAPE char* ftape_big_buffer; #endif long chr_dev_init(long mem_start, long mem_end) { if (register_chrdev(MEM_MAJOR,"mem",&memory_fops)) printk("unable to get major %d for memory devs\n", MEM_MAJOR); mem_start = tty_init(mem_start); #ifdef CONFIG_PRINTER mem_start = lp_init(mem_start); #endif #if defined (CONFIG_BUSMOUSE) || defined (CONFIG_82C710_MOUSE) || \ defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \ defined (CONFIG_ATIXL_BUSMOUSE) mem_start = mouse_init(mem_start); #endif #ifdef CONFIG_SOUND mem_start = soundcard_init(mem_start); #endif #if CONFIG_TAPE_QIC02 mem_start = tape_qic02_init(mem_start); #endif /* * Rude way to allocate kernel memory buffer for tape device */ #ifdef CONFIG_FTAPE /* allocate NR_FTAPE_BUFFERS 32Kb buffers at aligned address */ ftape_big_buffer= (char*) ((mem_start + 0x7fff) & ~0x7fff); printk( "ftape: allocated %d buffers alligned at: %p\n", NR_FTAPE_BUFFERS, ftape_big_buffer); mem_start = (long) ftape_big_buffer + NR_FTAPE_BUFFERS * 0x8000; #endif return mem_start; }