📄 dmasound_core.c
字号:
sq->locked = 1 ; /* don't think we have a race prob. here _check_ */ /* make sure that the parameters are set up This should have been done already... */ dmasound.mach.init(); /* OK. If the user has set fragment parameters explicitly, then we should leave them alone... as long as they are valid. Invalid user fragment params can occur if we allow the whole buffer to be used when the user requests the fragments sizes (with no soft x-lation) and then the user subsequently sets a soft x-lation that requires increased internal buffering. Othwerwise (if the user did not set them) OSS says that we should select frag params on the basis of 0.5 s output & 0.1 s input latency. (TODO. For now we will copy in the defaults.) */ if (sq->user_frags <= 0) { sq->max_count = sq->numBufs ; sq->max_active = sq->numBufs ; sq->block_size = sq->bufSize; /* set up the user info */ sq->user_frags = sq->numBufs ; sq->user_frag_size = sq->bufSize ; sq->user_frag_size *= (dmasound.soft.size * (dmasound.soft.stereo+1) ) ; sq->user_frag_size /= (dmasound.hard.size * (dmasound.hard.stereo+1) ) ; } else { /* work out requested block size */ sq->block_size = sq->user_frag_size ; sq->block_size *= (dmasound.hard.size * (dmasound.hard.stereo+1) ) ; sq->block_size /= (dmasound.soft.size * (dmasound.soft.stereo+1) ) ; /* the user wants to write frag-size chunks */ sq->block_size *= dmasound.hard.speed ; sq->block_size /= dmasound.soft.speed ; /* this only works for size values which are powers of 2 */ hard_frame = (dmasound.hard.size * (dmasound.hard.stereo+1))/8 ; sq->block_size += (hard_frame - 1) ; sq->block_size &= ~(hard_frame - 1) ; /* make sure we are aligned */ /* let's just check for obvious mistakes */ if ( sq->block_size <= 0 || sq->block_size > sq->bufSize) {#ifdef DEBUG_DMASOUNDprintk("dmasound_core: invalid frag size (user set %d)\n", sq->user_frag_size) ;#endif sq->block_size = sq->bufSize ; } if ( sq->user_frags <= sq->numBufs ) { sq->max_count = sq->user_frags ; /* if user has set max_active - then use it */ sq->max_active = (sq->max_active <= sq->max_count) ? sq->max_active : sq->max_count ; } else {#ifdef DEBUG_DMASOUNDprintk("dmasound_core: invalid frag count (user set %d)\n", sq->user_frags) ;#endif sq->max_count = sq->max_active = sq->numBufs ; } } sq->front = sq->count = sq->rear_size = 0; sq->syncing = 0; sq->active = 0; if (sq == &write_sq) { sq->rear = -1; setup_func = dmasound.mach.write_sq_setup; }#ifdef HAS_RECORD else { sq->rear = 0; setup_func = dmasound.mach.read_sq_setup; }#endif if (setup_func) return setup_func(); return 0 ;}static inline void sq_play(void){ dmasound.mach.play();}static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft, loff_t *ppos){ ssize_t uWritten = 0; u_char *dest; ssize_t uUsed = 0, bUsed, bLeft; unsigned long flags ; /* ++TeSche: Is something like this necessary? * Hey, that's an honest question! Or does any other part of the * filesystem already checks this situation? I really don't know. */ if (uLeft == 0) return 0; /* implement any changes we have made to the soft/hard params. this is not satisfactory really, all we have done up to now is to say what we would like - there hasn't been any real checking of capability */ if (shared_resources_initialised == 0) { dmasound.mach.init() ; shared_resources_initialised = 1 ; } /* set up the sq if it is not already done. This may seem a dumb place to do it - but it is what OSS requires. It means that write() can return memory allocation errors. To avoid this possibility use the GETBLKSIZE or GETOSPACE ioctls (after you've fiddled with all the params you want to change) - these ioctls also force the setup. */ if (write_sq.locked == 0) { if ((uWritten = sq_setup(&write_sq)) < 0) return uWritten ; uWritten = 0 ; }/* FIXME: I think that this may be the wrong behaviour when we get strapped for time and the cpu is close to being (or actually) behind in sending data. - because we've lost the time that the N samples, already in the buffer, would have given us to get here with the next lot from the user.*/ /* The interrupt doesn't start to play the last, incomplete frame. * Thus we can append to it without disabling the interrupts! (Note * also that write_sq.rear isn't affected by the interrupt.) */ /* as of 1.6 this behaviour changes if SNDCTL_DSP_POST has been issued: this will mimic the behaviour of syncing and allow the sq_play() to queue a partial fragment. Since sq_play() may/will be called from the IRQ handler - at least on Pmac we have to deal with it. The strategy - possibly not optimum - is to kill _POST status if we get here. This seems, at least, reasonable - in the sense that POST is supposed to indicate that we might not write before the queue is drained - and if we get here in time then it does not apply. */ spin_lock_irqsave(&dmasound.lock, flags); write_sq.syncing &= ~2 ; /* take out POST status */ spin_unlock_irqrestore(&dmasound.lock, flags); if (write_sq.count > 0 && (bLeft = write_sq.block_size-write_sq.rear_size) > 0) { dest = write_sq.buffers[write_sq.rear]; bUsed = write_sq.rear_size; uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, dest, &bUsed, bLeft); if (uUsed <= 0) return uUsed; src += uUsed; uWritten += uUsed; uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */ write_sq.rear_size = bUsed; } while (uLeft) { while (write_sq.count >= write_sq.max_active) { sq_play(); if (write_sq.open_mode & O_NONBLOCK) return uWritten > 0 ? uWritten : -EAGAIN; SLEEP(write_sq.action_queue); if (signal_pending(current)) return uWritten > 0 ? uWritten : -EINTR; } /* Here, we can avoid disabling the interrupt by first * copying and translating the data, and then updating * the write_sq variables. Until this is done, the interrupt * won't see the new frame and we can work on it * undisturbed. */ dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count]; bUsed = 0; bLeft = write_sq.block_size; uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, dest, &bUsed, bLeft); if (uUsed <= 0) break; src += uUsed; uWritten += uUsed; uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */ if (bUsed) { write_sq.rear = (write_sq.rear+1) % write_sq.max_count; write_sq.rear_size = bUsed; write_sq.count++; } } /* uUsed may have been 0 */ sq_play(); return uUsed < 0? uUsed: uWritten;}static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait){ unsigned int mask = 0; int retVal; if (write_sq.locked == 0) { if ((retVal = sq_setup(&write_sq)) < 0) return retVal; return 0; } if (file->f_mode & FMODE_WRITE ) poll_wait(file, &write_sq.action_queue, wait);#ifdef HAS_RECORD if (file->f_mode & FMODE_READ) poll_wait(file, &read_sq.action_queue, wait); if (file->f_mode & FMODE_READ) if (read_sq.block_size - read_sq.rear_size > 0) mask |= POLLIN | POLLRDNORM;#endif if (file->f_mode & FMODE_WRITE) if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0) mask |= POLLOUT | POLLWRNORM; return mask;}#ifdef HAS_RECORD /* * Here is how the values are used for reading. * The value 'active' simply indicates the DMA is running. This is done * so the driver semantics are DMA starts when the first read is posted. * The value 'front' indicates the buffer we should next send to the user. * The value 'rear' indicates the buffer the DMA is currently filling. * When 'front' == 'rear' the buffer "ring" is empty (we always have an * empty available). The 'rear_size' is used to track partial offsets * into the buffer we are currently returning to the user. * This level (> [1.5]) doesn't care what strategy the LL driver uses with * DMA on over-run. It can leave it running (and keep active == 1) or it * can kill it and set active == 0 in which case this routine will spot * it and restart the DMA. */static ssize_t sq_read(struct file *file, char __user *dst, size_t uLeft, loff_t *ppos){ ssize_t uRead, bLeft, bUsed, uUsed; if (uLeft == 0) return 0; /* cater for the compatibility mode - record compiled in but no LL */ if (dmasound.mach.record == NULL) return -EINVAL ; /* see comment in sq_write() */ if( shared_resources_initialised == 0) { dmasound.mach.init() ; shared_resources_initialised = 1 ; } /* set up the sq if it is not already done. see comments in sq_write(). */ if (read_sq.locked == 0) { if ((uRead = sq_setup(&read_sq)) < 0) return uRead ; } uRead = 0; /* Move what the user requests, depending upon other options. */ while (uLeft > 0) { /* we happened to get behind and the LL driver killed DMA then we should set it going again. This also sets it going the first time through. */ if ( !read_sq.active ) dmasound.mach.record(); /* When front == rear, the DMA is not done yet. */ while (read_sq.front == read_sq.rear) { if (read_sq.open_mode & O_NONBLOCK) { return uRead > 0 ? uRead : -EAGAIN; } SLEEP(read_sq.action_queue); if (signal_pending(current)) return uRead > 0 ? uRead : -EINTR; } /* The amount we move is either what is left in the * current buffer or what the user wants. */ bLeft = read_sq.block_size - read_sq.rear_size; bUsed = read_sq.rear_size; uUsed = sound_copy_translate(dmasound.trans_read, dst, uLeft, read_sq.buffers[read_sq.front], &bUsed, bLeft); if (uUsed <= 0) return uUsed; dst += uUsed; uRead += uUsed; uLeft -= uUsed; read_sq.rear_size += bUsed; if (read_sq.rear_size >= read_sq.block_size) { read_sq.rear_size = 0; read_sq.front++; if (read_sq.front >= read_sq.max_active) read_sq.front = 0; } } return uRead;}#endif /* HAS_RECORD */static inline void sq_init_waitqueue(struct sound_queue *sq){ init_waitqueue_head(&sq->action_queue); init_waitqueue_head(&sq->open_queue); init_waitqueue_head(&sq->sync_queue); sq->busy = 0;}#if 0 /* blocking open() */static inline void sq_wake_up(struct sound_queue *sq, struct file *file, mode_t mode){ if (file->f_mode & mode) { sq->busy = 0; /* CHECK: IS THIS OK??? */ WAKE_UP(sq->open_queue); }}#endifstatic int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode, int numbufs, int bufsize){ int rc = 0; if (file->f_mode & mode) { if (sq->busy) {#if 0 /* blocking open() */ rc = -EBUSY; if (file->f_flags & O_NONBLOCK) return rc; rc = -EINTR; while (sq->busy) { SLEEP(sq->open_queue); if (signal_pending(current)) return rc; } rc = 0;#else /* OSS manual says we will return EBUSY regardless of O_NOBLOCK. */ return -EBUSY ;#endif } sq->busy = 1; /* Let's play spot-the-race-condition */ /* allocate the default number & size of buffers. (i.e. specified in _setup() or as module params) can't be changed at the moment - but _could_ be perhaps in the setfragments ioctl. */ if (( rc = sq_allocate_buffers(sq, numbufs, bufsize))) {#if 0 /* blocking open() */ sq_wake_up(sq, file, mode);#else sq->busy = 0 ;#endif return rc; } sq->open_mode = file->f_mode; } return rc;}#define write_sq_init_waitqueue() sq_init_waitqueue(&write_sq)#if 0 /* blocking open() */#define write_sq_wake_up(file) sq_wake_up(&write_sq, file, FMODE_WRITE)#endif#define write_sq_release_buffers() sq_release_buffers(&write_sq)#define write_sq_open(file) \ sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize )#ifdef HAS_RECORD#define read_sq_init_waitqueue() sq_init_waitqueue(&read_sq)#if 0 /* blocking open() */#define read_sq_wake_up(file) sq_wake_up(&read_sq, file, FMODE_READ)#endif#define read_sq_release_buffers() sq_release_buffers(&read_sq)#define read_sq_open(file) \ sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize )#else#define read_sq_init_waitqueue() do {} while (0)#if 0 /* blocking open() */#define read_sq_wake_up(file) do {} while (0)#endif#define read_sq_release_buffers() do {} while (0)#define sq_reset_input() do {} while (0)#endifstatic int sq_open(struct inode *inode, struct file *file){ int rc; if (!try_module_get(dmasound.mach.owner)) return -ENODEV; rc = write_sq_open(file); /* checks the f_mode */ if (rc) goto out;#ifdef HAS_RECORD if (dmasound.mach.record) { rc = read_sq_open(file); /* checks the f_mode */ if (rc) goto out; } else { /* no record function installed; in compat mode */ if (file->f_mode & FMODE_READ) { /* TODO: if O_RDWR, release any resources grabbed by write part */ rc = -ENXIO; goto out; } }#else /* !HAS_RECORD */ if (file->f_mode & FMODE_READ) { /* TODO: if O_RDWR, release any resources grabbed by write part */ rc = -ENXIO ; /* I think this is what is required by open(2) */ goto out; }#endif /* HAS_RECORD */ if (dmasound.mach.sq_open) dmasound.mach.sq_open(file->f_mode); /* CHECK whether this is sensible - in the case that dsp0 could be opened O_RDONLY and dsp1 could be opened O_WRONLY */ dmasound.minDev = iminor(inode) & 0x0f; /* OK. - we should make some attempt at consistency. At least the H'ware options should be set with a valid mode. We will make it that the LL driver must supply defaults for hard & soft params. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -