*
* Lock order (high->low)
* lock - hardware lock
- * open_sem - guard opens
+ * open_mutex - guard opens
* sem - guard dmabuf, write re-entry etc
*/
#include <linux/pm.h>
#include <linux/gameport.h>
#include <linux/kernel.h>
+#include <linux/mutex.h>
+
#include <asm/uaccess.h>
-#include <asm/hardirq.h>
#include <asm/io.h>
#include <asm/dma.h>
#define DRIVER_VERSION "0.14.10j-2.6"
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
/* magic numbers to protect our data structures */
#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */
#define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */
};
static struct pci_device_id trident_pci_tbl[] = {
- {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_DX},
- {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_NX},
- {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018},
- {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI_5451},
- {PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5050,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, CYBER5050},
+ {PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX),
+ PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TRIDENT_4D_DX},
+ {PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX),
+ 0, 0, TRIDENT_4D_NX},
+ {PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018), 0, 0, SIS_7018},
+ {PCI_DEVICE(PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451), 0, 0, ALI_5451},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5050),
+ 0, 0, CYBER5050},
{0,}
};
unsigned chans_num;
unsigned long fmt_flag;
/* Guard against mmap/write/read races */
- struct semaphore sem;
+ struct mutex sem;
};
struct trident_card *next;
/* single open lock mechanism, only used for recording */
- struct semaphore open_sem;
+ struct mutex open_mutex;
/* The trident has a certain amount of cross channel interaction
so we use a single per card lock */
struct timer_list timer;
/* Game port support */
- struct gameport gameport;
+ struct gameport *gameport;
};
enum dmabuf_mode {
static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card);
static void ali_restore_regs(struct trident_card *card);
static void ali_save_regs(struct trident_card *card);
-static int trident_suspend(struct pci_dev *dev, u32 unused);
+static int trident_suspend(struct pci_dev *dev, pm_message_t unused);
static int trident_resume(struct pci_dev *dev);
static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel);
static int ali_setup_multi_channels(struct trident_card *card, int chan_nums);
static void ali_disable_spdif_in(struct trident_card *card);
static void ali_disable_special_channel(struct trident_card *card, int ch);
static void ali_setup_spdif_out(struct trident_card *card, int flag);
-static int ali_write_5_1(struct trident_state *state, const char *buffer,
+static int ali_write_5_1(struct trident_state *state,
+ const char __user *buffer,
int cnt_for_multi_channel, unsigned int *copy_count,
unsigned int *state_cnt);
static int ali_allocate_other_states_resources(struct trident_state *state,
dmabuf->buforder = order;
/* now mark the pages as reserved; otherwise */
- /* remap_page_range doesn't do what we want */
+ /* remap_pfn_range doesn't do what we want */
pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);
for (page = virt_to_page(rawbuf); page <= pend; page++)
SetPageReserved(page);
/* to be copied to the user's buffer. it is filled by the dma machine and */
/* drained by this loop. */
static ssize_t
-trident_read(struct file *file, char *buffer, size_t count, loff_t * ppos)
+trident_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
{
struct trident_state *state = (struct trident_state *)file->private_data;
struct dmabuf *dmabuf = &state->dmabuf;
pr_debug("trident: trident_read called, count = %d\n", count);
VALIDATE_STATE(state);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (dmabuf->mapped)
return -ENXIO;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
- down(&state->sem);
+ mutex_lock(&state->sem);
if (!dmabuf->ready && (ret = prog_dmabuf_record(state)))
goto out;
goto out;
}
- up(&state->sem);
+ mutex_unlock(&state->sem);
/* No matter how much space left in the buffer, */
/* we have to wait until CSO == ESO/2 or CSO == ESO */
/* when address engine interrupts */
ret = -ERESTARTSYS;
goto out;
}
- down(&state->sem);
+ mutex_lock(&state->sem);
if (dmabuf->mapped) {
if (!ret)
ret = -ENXIO;
start_adc(state);
}
out:
- up(&state->sem);
+ mutex_unlock(&state->sem);
return ret;
}
the soundcard. it is drained by the dma machine and filled by this loop. */
static ssize_t
-trident_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
+trident_write(struct file *file, const char __user *buffer, size_t count, loff_t * ppos)
{
struct trident_state *state = (struct trident_state *)file->private_data;
struct dmabuf *dmabuf = &state->dmabuf;
pr_debug("trident: trident_write called, count = %d\n", count);
VALIDATE_STATE(state);
- if (ppos != &file->f_pos)
- return -ESPIPE;
/*
* Guard against an mmap or ioctl while writing
*/
- down(&state->sem);
+ mutex_lock(&state->sem);
if (dmabuf->mapped) {
ret = -ENXIO;
tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
tmo >>= sample_shift[dmabuf->fmt];
unlock_set_fmt(state);
- up(&state->sem);
+ mutex_unlock(&state->sem);
/* There are two situations when sleep_on_timeout */
/* returns, one is when the interrupt is serviced */
ret = -ERESTARTSYS;
goto out_nolock;
}
- down(&state->sem);
+ mutex_lock(&state->sem);
if (dmabuf->mapped) {
if (!ret)
ret = -ENXIO;
start_dac(state);
}
out:
- up(&state->sem);
+ mutex_unlock(&state->sem);
out_nolock:
return ret;
}
* prog_dmabuf events
*/
- down(&state->sem);
+ mutex_lock(&state->sem);
if (file->f_mode & FMODE_WRITE) {
if (!dmabuf->ready && prog_dmabuf_playback(state)) {
- up(&state->sem);
+ mutex_unlock(&state->sem);
return 0;
}
poll_wait(file, &dmabuf->wait, wait);
}
if (file->f_mode & FMODE_READ) {
if (!dmabuf->ready && prog_dmabuf_record(state)) {
- up(&state->sem);
+ mutex_unlock(&state->sem);
return 0;
}
poll_wait(file, &dmabuf->wait, wait);
}
- up(&state->sem);
+ mutex_unlock(&state->sem);
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
* a read or write against an mmap.
*/
- down(&state->sem);
+ mutex_lock(&state->sem);
if (vma->vm_flags & VM_WRITE) {
if ((ret = prog_dmabuf_playback(state)) != 0)
if (size > (PAGE_SIZE << dmabuf->buforder))
goto out;
ret = -EAGAIN;
- if (remap_page_range(vma, vma->vm_start, virt_to_phys(dmabuf->rawbuf),
+ if (remap_pfn_range(vma, vma->vm_start,
+ virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT,
size, vma->vm_page_prot))
goto out;
dmabuf->mapped = 1;
ret = 0;
out:
- up(&state->sem);
+ mutex_unlock(&state->sem);
return ret;
}
audio_buf_info abinfo;
count_info cinfo;
int val, mapped, ret = 0;
-
struct trident_card *card = state->card;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
VALIDATE_STATE(state);
mapped = ((file->f_mode & (FMODE_WRITE | FMODE_READ)) && dmabuf->mapped);
pr_debug("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",
- _IOC_NR(cmd), arg ? *(int *) arg : 0);
+ _IOC_NR(cmd), arg ? *p : 0);
switch (cmd) {
case OSS_GETVERSION:
- ret = put_user(SOUND_VERSION, (int *) arg);
+ ret = put_user(SOUND_VERSION, p);
break;
case SNDCTL_DSP_RESET:
break;
case SNDCTL_DSP_SPEED: /* set smaple rate */
- if (get_user(val, (int *) arg)) {
+ if (get_user(val, p)) {
ret = -EFAULT;
break;
}
spin_unlock_irqrestore(&state->card->lock, flags);
}
}
- ret = put_user(dmabuf->rate, (int *) arg);
+ ret = put_user(dmabuf->rate, p);
break;
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
- if (get_user(val, (int *) arg)) {
+ if (get_user(val, p)) {
ret = -EFAULT;
break;
}
if ((val = prog_dmabuf_playback(state)))
ret = val;
else
- ret = put_user(dmabuf->fragsize, (int *) arg);
+ ret = put_user(dmabuf->fragsize, p);
break;
}
if (file->f_mode & FMODE_READ) {
if ((val = prog_dmabuf_record(state)))
ret = val;
else
- ret = put_user(dmabuf->fragsize, (int *) arg);
+ ret = put_user(dmabuf->fragsize, p);
break;
}
/* neither READ nor WRITE? is this even possible? */
case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format */
ret = put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 |
- AFMT_U8, (int *) arg);
+ AFMT_U8, p);
break;
case SNDCTL_DSP_SETFMT: /* Select sample format */
- if (get_user(val, (int *) arg)) {
+ if (get_user(val, p)) {
ret = -EFAULT;
break;
}
}
unlock_set_fmt(state);
ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? AFMT_S16_LE :
- AFMT_U8, (int *) arg);
+ AFMT_U8, p);
break;
case SNDCTL_DSP_CHANNELS:
- if (get_user(val, (int *) arg)) {
+ if (get_user(val, p)) {
ret = -EFAULT;
break;
}
unlock_set_fmt(state);
break;
}
- down(&state->card->open_sem);
+ mutex_lock(&state->card->open_mutex);
ret = ali_allocate_other_states_resources(state, 6);
if (ret < 0) {
- up(&state->card->open_sem);
+ mutex_unlock(&state->card->open_mutex);
unlock_set_fmt(state);
break;
}
state->card->multi_channel_use_count++;
- up(&state->card->open_sem);
+ mutex_unlock(&state->card->open_mutex);
} else
val = 2; /*yield to 2-channels */
} else
}
unlock_set_fmt(state);
}
- ret = put_user(val, (int *) arg);
+ ret = put_user(val, p);
break;
case SNDCTL_DSP_POST:
ret = -EINVAL;
break;
}
- if (get_user(val, (int *) arg)) {
+ if (get_user(val, p)) {
ret = -EFAULT;
break;
}
break;
case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, (int *) arg)) {
+ if (get_user(val, p)) {
ret = -EFAULT;
break;
}
abinfo.fragstotal = dmabuf->numfrag;
abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
- ret = copy_to_user((void *) arg, &abinfo, sizeof (abinfo)) ?
+ ret = copy_to_user(argp, &abinfo, sizeof (abinfo)) ?
-EFAULT : 0;
break;
abinfo.fragstotal = dmabuf->numfrag;
abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
- ret = copy_to_user((void *) arg, &abinfo, sizeof (abinfo)) ?
+ ret = copy_to_user(argp, &abinfo, sizeof (abinfo)) ?
-EFAULT : 0;
break;
case SNDCTL_DSP_GETCAPS:
ret = put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER |
- DSP_CAP_MMAP | DSP_CAP_BIND, (int *) arg);
+ DSP_CAP_MMAP | DSP_CAP_BIND, p);
break;
case SNDCTL_DSP_GETTRIGGER:
val |= PCM_ENABLE_INPUT;
if ((file->f_mode & FMODE_WRITE) && dmabuf->enable)
val |= PCM_ENABLE_OUTPUT;
- ret = put_user(val, (int *) arg);
+ ret = put_user(val, p);
break;
case SNDCTL_DSP_SETTRIGGER:
- if (get_user(val, (int *) arg)) {
+ if (get_user(val, p)) {
ret = -EFAULT;
break;
}
if (dmabuf->mapped)
dmabuf->count &= dmabuf->fragsize - 1;
spin_unlock_irqrestore(&state->card->lock, flags);
- ret = copy_to_user((void *) arg, &cinfo, sizeof (cinfo)) ?
+ ret = copy_to_user(argp, &cinfo, sizeof (cinfo)) ?
-EFAULT : 0;
break;
if (dmabuf->mapped)
dmabuf->count &= dmabuf->fragsize - 1;
spin_unlock_irqrestore(&state->card->lock, flags);
- ret = copy_to_user((void *) arg, &cinfo, sizeof (cinfo)) ?
+ ret = copy_to_user(argp, &cinfo, sizeof (cinfo)) ?
-EFAULT : 0;
break;
trident_update_ptr(state);
val = dmabuf->count;
spin_unlock_irqrestore(&state->card->lock, flags);
- ret = put_user(val, (int *) arg);
+ ret = put_user(val, p);
break;
case SOUND_PCM_READ_RATE:
- ret = put_user(dmabuf->rate, (int *) arg);
+ ret = put_user(dmabuf->rate, p);
break;
case SOUND_PCM_READ_CHANNELS:
ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
- (int *) arg);
+ p);
break;
case SOUND_PCM_READ_BITS:
ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? AFMT_S16_LE :
- AFMT_U8, (int *) arg);
+ AFMT_U8, p);
break;
case SNDCTL_DSP_GETCHANNELMASK:
ret = put_user(DSP_BIND_FRONT | DSP_BIND_SURR |
- DSP_BIND_CENTER_LFE, (int *) arg);
+ DSP_BIND_CENTER_LFE, p);
break;
case SNDCTL_DSP_BIND_CHANNEL:
break;
}
- if (get_user(val, (int *) arg)) {
+ if (get_user(val, p)) {
ret = -EFAULT;
break;
}
SRC_ENABLE);
dmabuf->channel->attribute |= mask2attr[ffs(val)];
}
- ret = put_user(val, (int *) arg);
+ ret = put_user(val, p);
break;
case SNDCTL_DSP_MAPINBUF:
/* find an available virtual channel (instance of /dev/dsp) */
while (card != NULL) {
- down(&card->open_sem);
+ mutex_lock(&card->open_mutex);
if (file->f_mode & FMODE_READ) {
/* Skip opens on cards that are in 6 channel mode */
if (card->multi_channel_use_count > 0) {
- up(&card->open_sem);
+ mutex_unlock(&card->open_mutex);
card = card->next;
continue;
}
if (card->states[i] == NULL) {
state = card->states[i] = kmalloc(sizeof(*state), GFP_KERNEL);
if (state == NULL) {
- up(&card->open_sem);
+ mutex_unlock(&card->open_mutex);
return -ENOMEM;
}
memset(state, 0, sizeof(*state));
- init_MUTEX(&state->sem);
+ mutex_init(&state->sem);
dmabuf = &state->dmabuf;
goto found_virt;
}
}
- up(&card->open_sem);
+ mutex_unlock(&card->open_mutex);
card = card->next;
}
/* no more virtual channel avaiable */
}
state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- up(&card->open_sem);
+ mutex_unlock(&card->open_mutex);
pr_debug("trident: open virtual channel %d, hard channel %d\n",
state->virt, dmabuf->channel->num);
- return 0;
+ return nonseekable_open(inode, file);
}
static int
state->virt, dmabuf->channel->num);
/* stop DMA state machine and free DMA buffers/channels */
- down(&card->open_sem);
+ mutex_lock(&card->open_mutex);
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
card->states[state->virt] = NULL;
kfree(state);
- /* we're covered by the open_sem */
- up(&card->open_sem);
+ /* we're covered by the open_mutex */
+ mutex_unlock(&card->open_mutex);
return 0;
}
}
static int
-trident_suspend(struct pci_dev *dev, u32 unused)
+trident_suspend(struct pci_dev *dev, pm_message_t unused)
{
struct trident_card *card = pci_get_drvdata(dev);
state DMA is protected by a spinlock.
*/
static int
-ali_write_5_1(struct trident_state *state, const char *buf,
+ali_write_5_1(struct trident_state *state, const char __user *buf,
int cnt_for_multi_channel, unsigned int *copy_count,
unsigned int *state_cnt)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct dmabuf *dmabuf_temp;
- const char *buffer = buf;
+ const char __user *buffer = buf;
unsigned swptr, other_dma_nums, sample_s;
unsigned int i, loop;
}
}
-struct proc_dir_entry *res;
+static struct proc_dir_entry *res;
+
static int
-ali_write_proc(struct file *file, const char *buffer, unsigned long count, void *data)
+ali_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data)
{
struct trident_card *card = (struct trident_card *) data;
unsigned long flags;
match:
file->private_data = card->ac97_codec[i];
- return 0;
+ return nonseekable_open(inode, file);
}
static int
return num_ac97 + 1;
}
+#ifdef SUPPORT_JOYSTICK
/* Gameport functions for the cards ADC gameport */
-static unsigned char
-trident_game_read(struct gameport *gameport)
+static unsigned char trident_game_read(struct gameport *gameport)
{
- struct trident_card *card = gameport->driver;
+ struct trident_card *card = gameport->port_data;
+
return inb(TRID_REG(card, T4D_GAME_LEG));
}
-static void
-trident_game_trigger(struct gameport *gameport)
+static void trident_game_trigger(struct gameport *gameport)
{
- struct trident_card *card = gameport->driver;
+ struct trident_card *card = gameport->port_data;
+
outb(0xff, TRID_REG(card, T4D_GAME_LEG));
}
-static int
-trident_game_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+static int trident_game_cooked_read(struct gameport *gameport,
+ int *axes, int *buttons)
{
- struct trident_card *card = gameport->driver;
+ struct trident_card *card = gameport->port_data;
int i;
*buttons = (~inb(TRID_REG(card, T4D_GAME_LEG)) >> 4) & 0xf;
return 0;
}
-static int
-trident_game_open(struct gameport *gameport, int mode)
+static int trident_game_open(struct gameport *gameport, int mode)
{
- struct trident_card *card = gameport->driver;
+ struct trident_card *card = gameport->port_data;
switch (mode) {
case GAMEPORT_MODE_COOKED:
return 0;
}
+static int __devinit trident_register_gameport(struct trident_card *card)
+{
+ struct gameport *gp;
+
+ card->gameport = gp = gameport_allocate_port();
+ if (!gp) {
+ printk(KERN_ERR "trident: can not allocate memory for gameport\n");
+ return -ENOMEM;
+ }
+
+ gameport_set_name(gp, "Trident 4DWave");
+ gameport_set_phys(gp, "pci%s/gameport0", pci_name(card->pci_dev));
+ gp->read = trident_game_read;
+ gp->trigger = trident_game_trigger;
+ gp->cooked_read = trident_game_cooked_read;
+ gp->open = trident_game_open;
+ gp->fuzz = 64;
+ gp->port_data = card;
+
+ gameport_register_port(gp);
+
+ return 0;
+}
+
+static inline void trident_unregister_gameport(struct trident_card *card)
+{
+ if (card->gameport)
+ gameport_unregister_port(card->gameport);
+}
+
+#else
+static inline int trident_register_gameport(struct trident_card *card) { return -ENOSYS; }
+static inline void trident_unregister_gameport(struct trident_card *card) { }
+#endif /* SUPPORT_JOYSTICK */
+
/* install the driver, we do not allocate hardware channel nor DMA buffer */
/* now, they are defered until "ACCESS" time (in prog_dmabuf called by */
/* open/read/write/ioctl/mmap) */
card->banks[BANK_B].addresses = &bank_b_addrs;
card->banks[BANK_B].bitmap = 0UL;
- card->gameport.driver = card;
- card->gameport.fuzz = 64;
- card->gameport.read = trident_game_read;
- card->gameport.trigger = trident_game_trigger;
- card->gameport.cooked_read = trident_game_cooked_read;
- card->gameport.open = trident_game_open;
-
- init_MUTEX(&card->open_sem);
+ mutex_init(&card->open_mutex);
spin_lock_init(&card->lock);
init_timer(&card->timer);
trident_enable_loop_interrupts(card);
/* Register gameport */
- gameport_register_port(&card->gameport);
+ trident_register_gameport(card);
out:
return rc;
}
/* Unregister gameport */
- gameport_unregister_port(&card->gameport);
+ trident_unregister_gameport(card);
/* Kill interrupts, and SP/DIF */
trident_disable_loop_interrupts(card);
"5050 PCI Audio, version " DRIVER_VERSION ", " __TIME__ " "
__DATE__ "\n");
- if (!pci_register_driver(&trident_pci_driver)) {
- pci_unregister_driver(&trident_pci_driver);
- return -ENODEV;
- }
- return 0;
+ return pci_register_driver(&trident_pci_driver);
}
static void __exit