/* The fsl fifo writing tasklet. It loops through *all* FIFOs see if any are waiting to write, and does so until it blocks. if *any* FIFO blocks on writing, the tasklet is rescheduled. We could alternatively have one tasklet per FIFO , not sure which would be more efficient... */ void write_tasklet_func(unsigned long data) { struct fsl_fifo_t *fsl_fifo=(struct fsl_fifo_t *)data; int id; int do_reschedule=0; /* Loop through each fsl fifo*/ for(id=0;id<MAX_FSL_FIFO_COUNT;id++, fsl_fifo++) { unsigned flags; if(!fsl_fifo->exists) continue; save_flags_cli(flags); /* There must be at least "width" bytes in the buffer */ if(fsl_fifo->tx_cnt < fsl_fifo->width) { restore_flags(flags); continue; } while(fsl_fifo->tx_cnt >= fsl_fifo->width) { unsigned fsl_status; unsigned int value; void *ptr = &(fsl_fifo->tx_buf[fsl_fifo->tx_tail]); switch(fsl_fifo->width) { case 1: value = (u32) *((u8 *)ptr); break; case 2: value = (u32) *((u16 *)ptr); break; case 4: value = (u32) *((u32 *)ptr); break; default: /* errors in a tasklet? yuck! */ } fsl_nput(fsl_fifo->id, value, fsl_status); if(fsl_nodata(fsl_status)) { do_reschedule++; break; } else { /* Update the buffer data structure */ fsl_fifo->tx_cnt-=fsl_fifo->width; fsl_fifo->tx_tail+= fsl_fifo->width; fsl_fifo->tx_tail &= FSL_FIFO_BUF_SIZE-1; } } restore_flags(flags); } if(do_reschedule) tasklet_schedule(&fsl_write_tasklet); /* Wake any processes blocked on FIFO write() operations */ wake_up(&fsl_write_queue); } void read_tasklet_func(unsigned long data) { struct fsl_fifo_t *fsl_fifo=(struct fsl_fifo_t *)data; int id; int do_reschedule=0; /* Loop through each fsl fifo*/ for(id=0;id<MAX_FSL_FIFO_COUNT;id++, fsl_fifo++) { unsigned flags; if(!fsl_fifo->exists) continue; /* If rx_buf is full, don't even try */ save_flags_cli(flags); if((fsl_fifo->rx_cnt + fsl_fifo->width) >= FSL_FIFO_BUF_SIZE) { restore_flags(flags); continue; } restore_flags(flags); while(1) { unsigned val; unsigned fsl_status; void *ptr=&(fsl_fifo->rx_buf[fsl_fifo->rx_head]); fsl_nget(fsl_fifo->id, val, fsl_status); if(fsl_nodata(fsl_status)) { break; } save_flags_cli(flags); switch(fsl_fifo->width) { case 1: *((u8*)ptr) = val & 0xFF; break; case 2: *((u16*)ptr) = val & 0xFFFF; break; case 4: *((u32*)ptr) = val; break; default: /* bleugh, error in tasklet */ } fsl_fifo->rx_head += fsl_fifo->width; fsl_fifo->rx_head &= FSL_FIFO_BUF_SIZE-1; fsl_fifo->rx_cnt += fsl_fifo->width; if(fsl_fifo->rx_cnt<FSL_FIFO_BUF_SIZE) { restore_flags(flags); do_reschedule++; break; } restore_flags(flags); } } if(do_reschedule) { tasklet_schedule(&fsl_read_tasklet); } wake_up(&fsl_read_queue); } /* Seek doesn't make a lot of sense! */ static loff_t fsl_llseek(struct file *f, loff_t off, int whence) { return 0; } /* Put data into the tx_buffer and schedule write_tasklet */ static ssize_t fsl_write(struct file *f, const char *buf, size_t count, loff_t *pos) { struct fsl_fifo_t *fsl_fifo; int do_sched_tasklet=0; unsigned flags; int total=0; fsl_fifo = (struct fsl_fifo_t *)f->private_data; if(!fsl_fifo->exists) return -ENODEV; save_flags_cli(flags); /* Is the tx buffer full, and we block */ while(fsl_fifo->tx_cnt==FSL_FIFO_BUF_SIZE) { restore_flags(flags); tasklet_schedule(&fsl_write_tasklet); interruptible_sleep_on(&fsl_write_queue); if(current->sigpending) return -EINTR; save_flags_cli(flags); } /* The following logic stolen from mcfserial.c */ save_flags(flags); while(1) { int c; cli(); /* How much can we write into the buffer? */ c=MIN(count, MIN(FSL_FIFO_BUF_SIZE - fsl_fifo->tx_cnt-1, FSL_FIFO_BUF_SIZE - fsl_fifo->tx_head)); if(c<=0) { restore_flags(flags); break; } copy_from_user(fsl_fifo->tx_buf+fsl_fifo->tx_head, buf, c); fsl_fifo->tx_head=(fsl_fifo->tx_head+c) & (FSL_FIFO_BUF_SIZE-1); fsl_fifo->tx_cnt += c; restore_flags(flags); do_sched_tasklet++; buf+=c; count-=c; total+=c; } if(do_sched_tasklet) tasklet_schedule(&fsl_write_tasklet); return total; } static ssize_t fsl_read(struct file *f, char *buf, size_t count, loff_t *pos) { struct fsl_fifo_t *fsl_fifo=(struct fsl_fifo_t *)f->private_data; unsigned flags; int total=0; if(!fsl_fifo->exists) return -ENODEV; save_flags_cli(flags); /* Block until there's some data in the buffer */ while(!fsl_fifo->rx_cnt) { restore_flags(flags); tasklet_schedule(&fsl_read_tasklet); interruptible_sleep_on(&fsl_read_queue); if(current->sigpending) { return -EINTR; } save_flags_cli(flags); } while(1) { int c; cli(); c = MIN(count, MIN(fsl_fifo->rx_cnt, FSL_FIFO_BUF_SIZE - fsl_fifo->rx_tail)); if(c<=0) { restore_flags(flags); break; } copy_to_user(buf, fsl_fifo->rx_buf+fsl_fifo->rx_tail,c); /* Update the buffer data structure */ fsl_fifo->rx_cnt-=c; fsl_fifo->rx_tail+=c; fsl_fifo->rx_tail &= FSL_FIFO_BUF_SIZE-1; restore_flags(flags); buf += c; count -= c; total += c; } if(count) tasklet_schedule(&fsl_read_tasklet); return total; } int fsl_flush_buffers(int id) { unsigned flags; struct fsl_fifo_t *fsl_fifo=&fsl_fifo_table[id]; if(!fsl_fifo->exists) return 0; save_flags_cli(flags); fsl_fifo->rx_cnt=fsl_fifo->rx_head=fsl_fifo->rx_tail=0; fsl_fifo->tx_cnt=fsl_fifo->tx_head=fsl_fifo->tx_tail=0; restore_flags(flags); return 0; } static int fsl_ioctl(struct inode *inode, struct file *f, unsigned int cmd, unsigned long arg) { int fsl_id; struct fsl_fifo_t *fsl_fifo; unsigned flags, fsl_status; fsl_id=FSL_FIFO_ID(inode->i_rdev); if(fsl_id >= MAX_FSL_FIFO_COUNT) return -ENODEV; fsl_fifo=&fsl_fifo_table[fsl_id]; if(!fsl_fifo->exists) return -ENODEV; switch(cmd) { case FSLFIFO_IOCRESET: /* Reset */ save_flags_cli(flags); fsl_flush_buffers(fsl_id); restore_flags(flags); return 0; case FSLFIFO_IOCTCONTROL: /* Write control value */ /* Note this jumps the queue, and is blatted directly to the FSL port. It does not get queued in the main SW buffer */ save_flags_cli(flags); fsl_ncput(fsl_id, arg, fsl_status); restore_flags(flags); if(fsl_error(fsl_status)) return -EIO; else if(fsl_nodata(fsl_status)) return -EBUSY; return 0; case FSLFIFO_IOCQCONTROL: /* Read control value */ /* This bypasses the normal software buffers. It is very unlikely to work unless those buffers are empty, and the tasklets are idling */ save_flags_cli(flags); fsl_ncget(fsl_id, arg, fsl_status); restore_flags(flags); if(fsl_error(fsl_status)) /* Non-control value from FSL */ return -EIO; else if(fsl_nodata(fsl_status)) /* Nothing from FSL */ return -EBUSY; return arg; case FSLFIFO_IOCTWIDTH: /* set data width */ if(arg==1 || arg==2 || arg==4) { save_flags_cli(flags); fsl_flush_buffers(fsl_id); fsl_fifo->width=arg; return 0; } else return -EINVAL; case FSLFIFO_IOCQWIDTH: /* get data width */ return fsl_fifo->width; default: /* bleugh */ return -EINVAL; } return 0; }
static int fsl_ioctl(struct inode *inode, struct file *f, unsigned int cmd, unsigned long arg) { int fsl_id; struct fsl_fifo_t *fsl_fifo; unsigned flags, fsl_status; fsl_id=FSLFIFO_ID(inode->i_rdev); if(fsl_id >= MAX_FSLFIFO_COUNT) return -ENODEV; fsl_fifo=&fsl_fifo_table[fsl_id]; if(!fsl_fifo->exists) return -ENODEV; switch(cmd) { case FSLFIFO_IOCRESET: /* Reset */ save_flags_cli(flags); fsl_flush_buffers(fsl_id); restore_flags(flags); return 0; case FSLFIFO_IOCTCONTROL: /* Write control value */ /* Note this jumps the queue, and is blatted directly to the FSL port. It does not get queued in the main SW buffer */ save_flags_cli(flags); fsl_ncput(fsl_id, arg, fsl_status); restore_flags(flags); if(fsl_error(fsl_status)) return -EIO; else if(fsl_nodata(fsl_status)) return -EBUSY; return 0; case FSLFIFO_IOCQCONTROL: /* Read control value */ /* This bypasses the normal software buffers. It is very unlikely to work unless those buffers are empty, and the tasklets are idling */ save_flags_cli(flags); fsl_ncget(fsl_id, arg, fsl_status); restore_flags(flags); if(fsl_error(fsl_status)) /* Non-control value from FSL */ return -EIO; else if(fsl_nodata(fsl_status)) /* Nothing from FSL */ return -EBUSY; return arg; case FSLFIFO_IOCTWIDTH: /* set data width */ if(arg==1 || arg==2 || arg==4) { save_flags_cli(flags); fsl_flush_buffers(fsl_id); fsl_fifo->width=arg; return 0; } else return -EINVAL; case FSLFIFO_IOCQWIDTH: /* get data width */ return fsl_fifo->width; default: /* bleugh */ return -EINVAL; } return 0; }
/* The fsl fifo writing tasklet. It loops through *all* FIFOs see if any are waiting to write, and does so until it blocks. if *any* FIFO blocks on writing, the tasklet is rescheduled. We could alternatively have one tasklet per FIFO , not sure which would be more efficient... */ void write_tasklet_func(unsigned long data) { struct fsl_fifo_t *fsl_fifo=(struct fsl_fifo_t *)data; int id; int do_reschedule=0; /* Loop through each fsl fifo*/ for(id=0;id<MAX_FSLFIFO_COUNT;id++, fsl_fifo++) { unsigned flags; if(!fsl_fifo->exists) continue; save_flags_cli(flags); /* There must be at least "width" bytes in the buffer */ if(fsl_fifo->tx_cnt < fsl_fifo->width) { restore_flags(flags); continue; } while(fsl_fifo->tx_cnt >= fsl_fifo->width) { unsigned fsl_status; unsigned int value; void *ptr = &(fsl_fifo->tx_buf[fsl_fifo->tx_tail]); switch(fsl_fifo->width) { case 1: value = (u32) *((u8 *)ptr); break; case 2: value = (u32) *((u16 *)ptr); break; case 4: value = (u32) *((u32 *)ptr); break; default: ; /* errors in a tasklet? yuck! */ } fsl_nput(fsl_fifo->id, value, fsl_status); if(fsl_nodata(fsl_status)) { do_reschedule++; break; } else { /* Update the buffer data structure */ fsl_fifo->tx_cnt-=fsl_fifo->width; fsl_fifo->tx_tail+= fsl_fifo->width; fsl_fifo->tx_tail &= FSLFIFO_BUF_SIZE-1; } } restore_flags(flags); } if(do_reschedule) tasklet_schedule(&fsl_write_tasklet); /* Wake any processes blocked on FIFO write() operations */ wake_up(&fsl_write_queue); }
void read_tasklet_func(unsigned long data) { struct fsl_fifo_t *fsl_fifo=(struct fsl_fifo_t *)data; int id; int do_reschedule=0; /* Loop through each fsl fifo*/ for(id=0;id<MAX_FSLFIFO_COUNT;id++, fsl_fifo++) { unsigned flags; if(!fsl_fifo->exists) continue; /* If rx_buf is full, don't even try */ save_flags_cli(flags); if((fsl_fifo->rx_cnt + fsl_fifo->width) >= FSLFIFO_BUF_SIZE) { restore_flags(flags); continue; } restore_flags(flags); while(1) { unsigned val; unsigned fsl_status; void *ptr=&(fsl_fifo->rx_buf[fsl_fifo->rx_head]); fsl_nget(fsl_fifo->id, val, fsl_status); if(fsl_nodata(fsl_status)) { break; } save_flags_cli(flags); switch(fsl_fifo->width) { case 1: *((u8*)ptr) = val & 0xFF; break; case 2: *((u16*)ptr) = val & 0xFFFF; break; case 4: *((u32*)ptr) = val; break; default: ; /* bleugh, error in tasklet */ } fsl_fifo->rx_head += fsl_fifo->width; fsl_fifo->rx_head &= FSLFIFO_BUF_SIZE-1; fsl_fifo->rx_cnt += fsl_fifo->width; if(fsl_fifo->rx_cnt<FSLFIFO_BUF_SIZE) { restore_flags(flags); do_reschedule++; break; } restore_flags(flags); } } if(do_reschedule) { tasklet_schedule(&fsl_read_tasklet); } wake_up(&fsl_read_queue); }