📄 kllad.c
字号:
#if (EM86XX_CHIP==EM86XX_CHIPID_TANGO2)static inline int unlocked_regions(unsigned int index, unsigned int count){ unsigned int i,c; for (i=index,c=0 ; ((i<DIRECT_REGION_COUNT) && (c<count)) ; i++,c++) { if (R.region_info[i].direct_map) break; } return (count == c);}static inline int is_inside_region(struct gbus_lock_area *param, unsigned int index){ unsigned long offset; //1) same address? if (!(param->byte_address ==R.region_info[index].area_byte_address)) return 0; //2 same offset? //get address offset from page offset = param->byte_address & (KERNEL_PAGE_SIZE - 1); if (!(offset-R.region_info[index].area_offset==0)) { return 0; } //3 same size? if (!(param->size==R.region_info[index].area_size)) return 0;/* printk("is_inside_region: param->byte_address=%x, R.region_info[%i].area_byte_address=%x\n",param->byte_address,index,R.region_info[index].area_byte_address); *//* printk("is_inside_region: offset=%i, R.region_info[%i].area_offset=%i\n",offset,index,R.region_info[index].area_offset); *//* printk("is_inside_region: param->size=%u, R.region_info[%i].area_size=%u\n",param->size,index,R.region_info[index].area_size); */ return 1;} static int get_locked_area(struct gbus_lock_area *param){ unsigned long i; i = 0; while (i<DIRECT_REGION_COUNT) { if (R.region_info[i].direct_map == 0) { i++; continue; } if (!(is_inside_region(param, i))) { i++; continue; } param->region_index = i; param->offset = param->byte_address - R.region_info[i].direct_map; // warning: get_locked_area always return region_count=1. for real sys pages count, use ioctl with DIRECT_IOCTL_GBUS_GET_SYSTEM_PAGE_COUNT param->region_count = 1; //this way ruaunlock will only unlock 1 region, no neighbours are concerned. /* printk("get_locked_area(): region found: index=%i; offset=%i\n",param->region_index,param->offset); */ break; } return i;}#endif // EM86XX_CHIP/* Goal of the following ISR: we are declaring a IRQ handler for the hardware interrupts we will be using in the emhwlib. Unfortunately, the IRQ handler we will be inserting before the OS (re-route interrupt vector table) will "miss" some interrupts that will end up in the kernel. The default behavior of the kernel is to mask these "unused" interrupts. To prevent this, we right a semi-"dummy" interrupt handler that will disable the used IRQ until the emhwlib is loaded. */#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) static void dummy_isr(int irq, void *dev_id, struct pt_regs *regs) #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)static irqreturn_t dummy_isr(int irq, void *dev_id, struct pt_regs *regs) #elsestatic irqreturn_t dummy_isr(int irq, void *dev_id) #endif{ unsigned long flag; flag = gbus_read_uint32(pGBus, UCLINUX_LLAD_IRQHANDLER_HANDSHAKE); if ((flag & (1 << irq)) == 0) { /* Nobody is ready to handle it yet, disable the interrupt. If we don't disable the irq, the kernel will unmask it */ disable_irq(irq); } else { /* IRQ handler is ready to handle it the interrupt. If we don't re-enable the irq, the kernel will keep it masked */ enable_irq(irq); }#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) return IRQ_HANDLED;#endif}#if (EM86XX_CHIP==EM86XX_CHIPID_TANGO2)/* HACK ALTERT: We hook up an ISR with VSYNCx, the sole purpose for that is to * loop inside the ISR until XPU takes care of business (to reduce the load on GBUS * due to CPU operations as the loop is small enough to fit into the cache). */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) static void tango2_vsync_intr(int irq, void *devinfo, struct pt_regs *regs)#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)static irqreturn_t tango2_vsync_intr(int irq, void *devinfo, struct pt_regs *regs)#elsestatic irqreturn_t tango2_vsync_intr(int irq, void *devinfo)#endif{#if 0 // Per #4067 Comment #31, disable this portion, if no problem found, we can remove the // whole tango2_vsync_intr() stuff... volatile RMuint32 i; RMuint32 mask = 1UL << (irq - IRQ_CONTROLLER_IRQ_BASE); do { for (i = 0; i < loop_cnt; i++) /* Busy loop to be fitted into cache */ ; } while (gbus_read_uint32(pGBus, REG_BASE_irq_handler_block + CPU_edge_rawstat) & mask); /* XPU is done here */#endif#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) return IRQ_HANDLED;#endif}#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)static void tophalf(int irq, void *dev_id, struct pt_regs *regs)#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)static irqreturn_t tophalf(int irq, void *dev_id, struct pt_regs *regs)#elsestatic irqreturn_t tophalf(int irq, void *dev_id)#endif{ unsigned long status, mask; int i; status = gbus_read_uint32(pGBus, REG_BASE_cpu_block + CPU_irq_softset);/* printk("Received a llad software interrupt %lu\n", status); */ if ((status & SOFT_IRQ_ORIGIN_PT110) == 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) return;#else return IRQ_HANDLED;#endif gbus_mutex_lock(pGBus, PCI_IRQ_MUTEX); status = gbus_read_uint32(pGBus, HOST_INTERRUPT_STATUS); mask = status & ~(LLAD_WAIT_WRITE_COMPLETE | LLAD_SOFT_INTERRUPT); for( i=0; i<MAX_TASKLETS ; i++ ) mask = mask & ~(R.tasklet_mask[i]); gbus_write_uint32(pGBus, HOST_INTERRUPT_STATUS, mask); if (mask == 0) gbus_write_uint32(pGBus, REG_BASE_cpu_block + CPU_irq_softclr, SOFT_IRQ_ORIGIN_PT110); gbus_mutex_unlock(pGBus, PCI_IRQ_MUTEX);/* printk("HOST_INTERRUPT_STATUS=0x%08lx\n",status); */ if (status & LLAD_WAIT_WRITE_COMPLETE) { if (!test_and_set_bit(LOG2_LLAD_WAIT_WRITE_COMPLETE, &(R.irq_bits))) wake_up_interruptible(&(R.irq_queue)); } if (status & LLAD_SOFT_INTERRUPT) { if (!test_and_set_bit(LOG2_LLAD_SOFT_INTERRUPT, &(R.irq_bits))) wake_up_interruptible(&(R.irq_queue)); } for( i=0 ; i<MAX_TASKLETS ; i++ ) { if (status & R.tasklet_mask[i]) { if (R.tasklet[i]) { *(R.tasklet_irq_status[i]) |= status & R.tasklet_mask[i]; tasklet_schedule(R.tasklet[i]); } } }#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) return IRQ_HANDLED;#endif}EXPORT_SYMBOL(mumk_register_tasklet);void mumk_register_tasklet(struct gbus *PGBUS, struct kc_tasklet_struct *kctasklet, unsigned long *irq_status, unsigned long mask){ int i; for( i=0 ; i<MAX_TASKLETS ; i++ ) { if (!(R.tasklet[i])) { R.tasklet_irq_status[i] = irq_status; R.tasklet[i] = (struct tasklet_struct *)kctasklet; R.tasklet_mask[i] = mask; printk("mumk_register_tasklet: (%d) tasklet %p status @%p\n",i,R.tasklet[i],R.tasklet_irq_status[i]); return; } } printk("mumk_register_tasklet: cannot register\n");}EXPORT_SYMBOL(mumk_unregister_tasklet);void mumk_unregister_tasklet(struct gbus *PGBUS, struct kc_tasklet_struct *kctasklet){ int i; for( i=0 ; i<MAX_TASKLETS ; i++ ) { if (R.tasklet[i]==(struct tasklet_struct *)kctasklet) { R.tasklet_mask[i] = 0; R.tasklet[i] = (struct tasklet_struct *) NULL; R.tasklet_irq_status[i] = (unsigned long *) NULL; return; } } printk("mumk_unregister_tasklet: cannot unregister\n");}static void direct_wait(struct waitable *h){ long timeout_jiffies = US_TO_JIFFIES(h->timeout_microsecond); unsigned long targets; targets = h->mask & R.irq_bits; // printk("Wait for mask 0x%08x for %lu us and %lu jiffies\n", h->mask, h->timeout_microsecond, timeout_jiffies); while (1) { if (targets) { h->mask = targets; { int bitno=0; while (targets) { if (targets & 0x1) clear_bit(bitno,&(R.irq_bits)); targets >>= 1; bitno++; } } return; } // printk("no bit set so go to sleep\n"); timeout_jiffies = interruptible_sleep_on_timeout(&(R.irq_queue), timeout_jiffies); h->timeout_microsecond = JIFFIES_TO_US(timeout_jiffies); // handle signals gently (esp. Control-C...) if ((timeout_jiffies == 0) || signal_pending(current)) break; targets = h->mask & R.irq_bits; } if (timeout_jiffies != 0) printk("direct_wait: signal pending\n"); h->mask = 0;}static int minor_ioctl(struct inode *i_node, struct file *filp,unsigned int cmd, unsigned long arg){ int rc;/* printk("ioctl %lu %lu\n", cmd, arg); */ rc = 0; switch (cmd) { case DIRECT_IOCTL_WAIT: { struct waitable h; if (cfu(&h,(char *)arg,sizeof(struct waitable)) != 0) return -EFAULT; direct_wait(&h); rc = ctu((char *)arg,&h,sizeof(struct waitable)); if (rc != 0) rc = -EFAULT; } break; case DIRECT_IOCTL_ENABLE_INTERRUPT: { int flags; unsigned long enable; local_irq_save(flags); enable = gbus_read_uint32(pGBus, PCI_INTERRUPT_ENABLE); enable |= arg; gbus_write_uint32(pGBus, PCI_INTERRUPT_ENABLE, enable); local_irq_restore(flags); } break; case DIRECT_IOCTL_DISABLE_INTERRUPT: { int flags; unsigned long enable, status; local_irq_save(flags); enable = gbus_read_uint32(pGBus, PCI_INTERRUPT_ENABLE); enable &= ~arg; gbus_write_uint32(pGBus, PCI_INTERRUPT_ENABLE, enable); status = gbus_read_uint32(pGBus, HOST_INTERRUPT_STATUS); status &= enable; gbus_write_uint32(pGBus, HOST_INTERRUPT_STATUS, status); local_irq_restore(flags); } break; case DIRECT_IOCTL_LLAD_GET_CONFIG: { struct llad_get_config param; unsigned long size; static char llad_buf[256]; rc = -EINVAL; if (cfu(¶m, (char *)arg, sizeof(param)) != 0) break; size = param.ConfigNameSize; if (size > sizeof(llad_buf)) size = sizeof(llad_buf); llad_get_config(pLLAD, llad_buf, size); if (ctu(param.ConfigName, llad_buf, size) == 0) rc = 0; } break; case DIRECT_IOCTL_LLAD_GET_OPEN_COUNT: { unsigned long count; count = R.total_open_count; if (ctu((char *) arg, &count, sizeof(count)) != 0) return -EFAULT; } break; case DIRECT_IOCTL_DMAPOOL_OPEN: { struct kdmapool_dimension param; int i; if (cfu(¶m, (char *)arg, sizeof(param)) != 0) return -EFAULT; // find direct_id in the table for (i=0;i<MAX_OPENERS;i++) if (R.openers[i].direct_id==CURRENT_DIRECT_ID) break; if (i==MAX_OPENERS) { printk("minor_ioctl: invalid openers\n"); return -EINVAL; } rc = kdmapool_open((struct llad *) NULL, (char *) param.area, param.buffercount, param.buffersize); if (rc >= 0) { set_bit(rc, (void*)&(R.openers[i].dmapool_usage_mask)); param.dmapool_id = rc; rc = 0; if (ctu((char *) arg, ¶m, sizeof(param)) != 0) return -EFAULT; } } break; case DIRECT_IOCTL_DMAPOOL_CLOSE: { unsigned long dmapool_id; int i; if (cfu(&dmapool_id, (char *)arg, sizeof(unsigned long)) != 0) return -EFAULT; // find direct_id in the table for (i=0;i<MAX_OPENERS;i++) if (R.openers[i].direct_id==CURRENT_DIRECT_ID) break; if (i==MAX_OPENERS) { printk("minor_ioctl: invalid openers\n"); return -EINVAL; } rc = kdmapool_close((struct llad *) NULL, dmapool_id); clear_bit(dmapool_id, (void *)&(R.openers[i].dmapool_usage_mask)); } break; case DIRECT_IOCTL_DMAPOOL_RESET: { struct kdmapool_reset param; if (cfu(¶m, (char *)arg, sizeof(param)) != 0) return -EFAULT; param.acquired_count = kdmapool_reset((struct llad *) NULL, param.dmapool_id); if (ctu((char *) arg, ¶m, sizeof(param)) != 0) return -EFAULT; rc = 0; } break; case DIRECT_IOCTL_DMAPOOL_GET_BUFFER: { struct kdmapool_getbuffer param; if (cfu(¶m, (char *)arg, sizeof(param)) != 0) return -EFAULT; rc = kdmapool_check_valid((struct llad *) NULL,param.dmapool_id); if (rc ==0){ param.ptr = kdmapool_getbuffer((struct llad *) NULL, param.dmapool_id, &(param.timeout_microsecond)); if (ctu((char *) arg, ¶m, sizeof(param)) != 0) return -EFAULT; } } break; case DIRECT_IOCTL_DMAPOOL_GET_AVAILABLE_BUFFER_COUNT: { struct kdmapool_buffercount param; if (cfu(¶m, (char *)arg, sizeof(param)) != 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -