📄 uchelper.c
字号:
// Copyright (c) 2004 by Istv醤 V醨adi// This file is part of dxr3Player, a DVD player written specifically // for the DXR3 (aka Hollywood+) decoder card, but now handles other// hardware as well. These files contain a (mostly) user-space driver // for the Unichrome board found on Via's EPIA motherboards.//// The information for implementing this driver has been gathered// from the following sources://// - The DirectFB Unichrome driver// Copyright (c) 2003 Andreas Robinson, All rights reserved.//// - Andreas Robinson's MPEG-2 decoder for the Unichrome board.// Copyright (c) 2003 Andreas Robinson, All rights reserved.//// - Via's Unichrome Framebuffer driver// Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.// Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA/*-----------------------------------------------------------------------------*/#include "uchelper.h"/*-----------------------------------------------------------------------------*/#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <asm/uaccess.h>/*-----------------------------------------------------------------------------*/#define MODULE_NAME "uchelper"#define MAJOR_NR 62/*-----------------------------------------------------------------------------*/#define PCI_DEVICE_ID_CLE266 0x3122#define UC_REG_IRQ (0x200>>2)#define UC_REG_V1_STARTADDR_Y0 (0x254>>2)#define UC_REG_V_COMPOSE_MODE (0x298>>2)#define UC_V1_COMMAND_FIRE 0x80000000#define UC_IRQ_GLOBAL 0x80000000#define UC_IRQ_VBI_ENABLE 0x00080000#define UC_IRQ_VBI_PENDING 0x00000008/*-----------------------------------------------------------------------------*/static struct pci_dev* pcidev = NULL;static u32 mmio_base_physical;static u32 mmio_size;static volatile u32* mmio;DECLARE_WAIT_QUEUE_HEAD(uchelper_wait_queue);static volatile unsigned field_count = 0;static volatile unsigned flip_field = 0;static volatile unsigned flip_offset = 0;/*-----------------------------------------------------------------------------*//*-----------------------------------------------------------------------------*/static int uchelper_ctl_flip(flip_param_t* flip_param){ unsigned offset; unsigned field; int displayed = 0; if (copy_from_user(&offset, &flip_param->offset, sizeof(offset)) || copy_from_user(&field, &flip_param->field, sizeof(field))) { return -EFAULT; } if (field!=0) { if (field<field_count) { printk(KERN_ALERT MODULE_NAME ": Field is late, skipping (%d, %d)\n", field, field_count); if (copy_to_user(&flip_param->displayed, &displayed, sizeof(displayed))) { return -EFAULT; } return 0; } else if (field>(field_count+100)) { printk(KERN_ALERT MODULE_NAME ": Too long time to field (%d, %d)\n", field, field_count); return -EINVAL; } } displayed = 1; DEFINE_WAIT(flip_wait); int retval = 0; flip_field = field; flip_offset = offset; while(flip_offset!=0 && retval==0) { prepare_to_wait(&uchelper_wait_queue, &flip_wait, TASK_INTERRUPTIBLE); if (flip_offset!=0) { if (signal_pending(current)) { flip_offset = 0; retval = -EAGAIN; } else { schedule(); } } finish_wait(&uchelper_wait_queue, &flip_wait); } if (copy_to_user(&flip_param->displayed, &displayed, sizeof(displayed))) { return -EFAULT; } return retval;}/*-----------------------------------------------------------------------------*/static int uchelper_ctl_get_count(unsigned* count){ unsigned c = field_count; if (copy_to_user(count, &c, sizeof(*count))) { return -EFAULT; } return 0;}/*-----------------------------------------------------------------------------*/static int uchelper_ioctl(struct inode* ino, struct file* filp, unsigned int cmd, unsigned long arg){ switch(cmd) { case UCHELPER_CTL_FLIP: return uchelper_ctl_flip((flip_param_t*)arg); case UCHELPER_CTL_GET_COUNT: return uchelper_ctl_get_count((unsigned*)arg); } return -EINVAL;}/*-----------------------------------------------------------------------------*/static irqreturn_t uchelper_irqhandler(int irq, void* dev_id, struct pt_regs* regs){ u32 status = mmio[UC_REG_IRQ]; if (status & UC_IRQ_VBI_PENDING) { mmio[UC_REG_IRQ] = status | UC_IRQ_VBI_PENDING; if (flip_offset!=0 && (flip_field==0 || flip_field<=field_count)) { if (flip_field==0) { field_count = 0; } else if (flip_field<field_count) { printk(KERN_ALERT MODULE_NAME ": Field is late by %u (%d<%d)\n", field_count-flip_field, flip_field, field_count); } mmio[UC_REG_V1_STARTADDR_Y0] = flip_offset; mmio[UC_REG_V_COMPOSE_MODE] = UC_V1_COMMAND_FIRE; flip_offset = 0; wake_up_interruptible(&uchelper_wait_queue); } ++field_count; } return IRQ_HANDLED;}/*-----------------------------------------------------------------------------*/static struct file_operations uchelper_operations = { .owner = THIS_MODULE, .ioctl = uchelper_ioctl};/*-----------------------------------------------------------------------------*/static int uchelper_init(void){ int retval; u8 revision; struct pci_dev* rev_dev; pcidev = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_CLE266, NULL); if (pcidev==NULL) { printk(KERN_ALERT MODULE_NAME ": No CLE266/Unichrome device is found.\n"); return -ENODEV; } if (pcidev->driver) { printk(KERN_ALERT MODULE_NAME ": Another driver is aleady controlling the device.\n"); return -EBUSY; } mmio_base_physical = pci_resource_start(pcidev, 1); mmio_size = pci_resource_len(pcidev, 1); if (!request_mem_region(mmio_base_physical, mmio_size, MODULE_NAME)) { printk(KERN_ALERT MODULE_NAME ": Cannot acquire MMIO memory region.\n"); return -EBUSY; } mmio = (volatile u32*)ioremap(mmio_base_physical, mmio_size); if (mmio==NULL) { printk(KERN_ALERT MODULE_NAME ": Cannot map MMIO memory region.\n"); release_mem_region(mmio_base_physical, mmio_size); return -EBUSY; } retval = request_irq(pcidev->irq, uchelper_irqhandler, SA_SHIRQ | SA_INTERRUPT, MODULE_NAME, (void*)&mmio); if (retval == -EINVAL) { printk(KERN_ALERT MODULE_NAME ": No IRQ handler installed, " "IRQ number %d is not valid.\n", pcidev->irq); } else if (retval == -EBUSY) { printk(KERN_ALERT MODULE_NAME ": No IRQ handler installed, " "IRQ %d is busy. Check BIOS.\n", pcidev->irq); } else if (retval < 0) { printk(KERN_ALERT MODULE_NAME ": No IRQ handler installed. " "Tried IRQ number %d.\n", pcidev->irq); } if (retval<0) { iounmap(mmio); release_mem_region(mmio_base_physical, mmio_size); return -EBUSY; } register_chrdev(MAJOR_NR, MODULE_NAME, &uchelper_operations); mmio[UC_REG_IRQ] |= UC_IRQ_GLOBAL | UC_IRQ_VBI_ENABLE | UC_IRQ_VBI_PENDING; outb(0x11, 0x3d4); outb(inb(0x3d5) | 0x30, 0x3d5); // Enable MMIO outb(0x10, 0x3c4); outb(0x01, 0x3c5); outb(0x1a, 0x3c4); rev_dev = pci_find_slot(0, 0); if (rev_dev!=NULL) { pci_read_config_byte(rev_dev, 0xf6, &revision); } else { revision = 255; } printk(KERN_ALERT MODULE_NAME " installed. CLE266/Unichrome rev %u detected.\n", (unsigned)revision); return 0;}/*-----------------------------------------------------------------------------*/static void uchelper_exit(void){ mmio[UC_REG_IRQ] = (mmio[UC_REG_IRQ] & ~UC_IRQ_VBI_ENABLE) | UC_IRQ_VBI_PENDING; unregister_chrdev(MAJOR_NR, MODULE_NAME); free_irq(pcidev->irq, (void*)&mmio); iounmap(mmio); release_mem_region(mmio_base_physical, mmio_size); printk(KERN_ALERT MODULE_NAME " removed\n");}/*-----------------------------------------------------------------------------*/module_init(uchelper_init);module_exit(uchelper_exit);/*-----------------------------------------------------------------------------*/MODULE_LICENSE("GPL");/*-----------------------------------------------------------------------------*//* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -