📄 msp3400.c
字号:
static struct msp3400c *mspmix = NULL; /* ugly hack, should do something more sensible */static int mixer_num;static int mixer_modcnt = 0;static struct semaphore mixer_sem = MUTEX;static int mix_to_v4l(int i){ int r; r = ((i & 0xff) * 65536 + 50) / 100; if (r > 65535) r = 65535; if (r < 0) r = 0; return r;}static int v4l_to_mix(int i){ int r; r = (i * 100 + 32768) / 65536; if (r > 100) r = 100; if (r < 0) r = 0; return r | (r << 8);}static int v4l_to_mix2(int l, int r){ r = (r * 100 + 32768) / 65536; if (r > 100) r = 100; if (r < 0) r = 0; l = (l * 100 + 32768) / 65536; if (l > 100) l = 100; if (l < 0) l = 0; return (r << 8) | l;}static intmsp3400c_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int ret,val = 0; LOCK_FLAGS; if (cmd == SOUND_MIXER_INFO) { mixer_info info; strncpy(info.id, "MSP3400", sizeof(info.id)); strncpy(info.name, "MSP 3400", sizeof(info.name)); info.modify_counter = mixer_modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; strncpy(info.id, "MSP3400", sizeof(info.id)); strncpy(info.name, "MSP 3400", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == OSS_GETVERSION) return put_user(SOUND_VERSION, (int *)arg); if (_SIOC_DIR(cmd) & _SIOC_WRITE) if (get_user(val, (int *)arg)) return -EFAULT; down(&mixer_sem); if (!mspmix) { up(&mixer_sem); return -ENODEV; } switch (cmd) { case MIXER_READ(SOUND_MIXER_RECMASK): case MIXER_READ(SOUND_MIXER_CAPS): case MIXER_READ(SOUND_MIXER_RECSRC): case MIXER_WRITE(SOUND_MIXER_RECSRC): ret = 0; break; case MIXER_READ(SOUND_MIXER_STEREODEVS): ret = SOUND_MASK_VOLUME; break; case MIXER_READ(SOUND_MIXER_DEVMASK): ret = SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE; break; case MIXER_WRITE(SOUND_MIXER_VOLUME): mspmix->left = mix_to_v4l(val); mspmix->right = mix_to_v4l(val >> 8); LOCK_I2C_BUS(mspmix->bus); msp3400c_setvolume(mspmix->bus,mspmix->left,mspmix->right); UNLOCK_I2C_BUS(mspmix->bus); mixer_modcnt++; /* fall */ case MIXER_READ(SOUND_MIXER_VOLUME): ret = v4l_to_mix2(mspmix->left, mspmix->right); break; case MIXER_WRITE(SOUND_MIXER_BASS): mspmix->bass = mix_to_v4l(val); LOCK_I2C_BUS(mspmix->bus); msp3400c_setbass(mspmix->bus,mspmix->bass); UNLOCK_I2C_BUS(mspmix->bus); mixer_modcnt++; /* fall */ case MIXER_READ(SOUND_MIXER_BASS): ret = v4l_to_mix(mspmix->bass); break; case MIXER_WRITE(SOUND_MIXER_TREBLE): mspmix->treble = mix_to_v4l(val); LOCK_I2C_BUS(mspmix->bus); msp3400c_settreble(mspmix->bus,mspmix->treble); UNLOCK_I2C_BUS(mspmix->bus); mixer_modcnt++; /* fall */ case MIXER_READ(SOUND_MIXER_TREBLE): ret = v4l_to_mix(mspmix->treble); break; default: up(&mixer_sem); return -EINVAL; } up(&mixer_sem); if (put_user(ret, (int *)arg)) return -EFAULT; return 0;}static intmsp3400c_mixer_open(struct inode *inode, struct file *file){ MOD_INC_USE_COUNT; return 0;}static intmsp3400c_mixer_release(struct inode *inode, struct file *file){ MOD_DEC_USE_COUNT; return 0;}static loff_tmsp3400c_mixer_llseek(struct file *file, loff_t offset, int origin){ return -ESPIPE;}static /*const*/ struct file_operations msp3400c_mixer_fops = { &msp3400c_mixer_llseek, NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* poll */ &msp3400c_mixer_ioctl, NULL, /* mmap */ &msp3400c_mixer_open, NULL, &msp3400c_mixer_release, NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL, /* revalidate */ NULL, /* lock */};#endif/* ----------------------------------------------------------------------- */static int msp3400c_attach(struct i2c_device *device){ struct semaphore sem = MUTEX_LOCKED; struct msp3400c *msp; int rev1,rev2; LOCK_FLAGS; device->data = msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL); if (NULL == msp) return -ENOMEM; memset(msp,0,sizeof(struct msp3400c)); msp->bus = device->bus; msp->left = 65535; msp->right = 65535; msp->bass = 32768; msp->treble = 32768; LOCK_I2C_BUS(msp->bus); if (-1 == msp3400c_reset(msp->bus)) { UNLOCK_I2C_BUS(msp->bus); kfree(msp); dprintk("msp3400: no chip found\n"); return -1; } rev1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1e); rev2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1f); if (0 == rev1 && 0 == rev2) { UNLOCK_I2C_BUS(msp->bus); kfree(msp); printk("msp3400: error while reading chip version\n"); return -1; } msp3400c_setmode(msp, MSP_MODE_FM_TERRA); msp3400c_setvolume(msp->bus, msp->left, msp->right); msp3400c_setbass(msp->bus, msp->bass); msp3400c_settreble(msp->bus, msp->treble); #if 0 /* this will turn on a 1kHz beep - might be useful for debugging... */ msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0014, 0x1040);#endif UNLOCK_I2C_BUS(msp->bus); sprintf(device->name,"MSP34%02d%c-%c%d", (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0; /* timer for stereo checking */ msp->wake_stereo.function = msp3400c_stereo_wake; msp->wake_stereo.data = (unsigned long)msp; /* startup control thread */ MOD_INC_USE_COUNT; msp->wq = NULL; msp->notify = &sem; kernel_thread(msp3400c_thread, (void *)msp, 0); down(&sem); msp->notify = NULL; wake_up_interruptible(&msp->wq); printk(KERN_INFO "msp3400: init: chip=%s",device->name); if (msp->nicam) printk(", has NICAM support");#ifdef REGISTER_MIXER down(&mixer_sem); mspmix = msp; up(&mixer_sem);#endif printk("\n"); return 0;}static int msp3400c_detach(struct i2c_device *device){ struct semaphore sem = MUTEX_LOCKED; struct msp3400c *msp = (struct msp3400c*)device->data; LOCK_FLAGS; #ifdef REGISTER_MIXER down(&mixer_sem); mspmix = NULL; up(&mixer_sem);#endif /* shutdown control thread */ del_timer(&msp->wake_stereo); if (msp->thread) { msp->notify = &sem; msp->rmmod = 1; wake_up_interruptible(&msp->wq); down(&sem); msp->notify = NULL; } LOCK_I2C_BUS(msp->bus); msp3400c_reset(msp->bus); UNLOCK_I2C_BUS(msp->bus); kfree(msp); MOD_DEC_USE_COUNT; return 0;}static int msp3400c_command(struct i2c_device *device, unsigned int cmd, void *arg){ struct msp3400c *msp = (struct msp3400c*)device->data; int *iarg = (int*)arg; __u16 *sarg = arg; LOCK_FLAGS; switch (cmd) { case MSP_SET_RADIO: msp->norm = VIDEO_MODE_RADIO; msp->watch_stereo=0; del_timer(&msp->wake_stereo); LOCK_I2C_BUS(msp->bus); msp3400c_setmode(msp,MSP_MODE_FM_RADIO); msp3400c_setcarrier(msp->bus, MSP_CARRIER(10.7),MSP_CARRIER(10.7)); msp3400c_setvolume(msp->bus,msp->left, msp->right); UNLOCK_I2C_BUS(msp->bus); break; case MSP_SET_TVNORM: msp->norm = *iarg; break; case MSP_SWITCH_MUTE: /* channels switching step one -- mute */ msp->watch_stereo=0; del_timer(&msp->wake_stereo); LOCK_I2C_BUS(msp->bus); msp3400c_setvolume(msp->bus,0,0); UNLOCK_I2C_BUS(msp->bus); break; case MSP_NEWCHANNEL: /* channels switching step two -- trigger sound carrier scan */ msp->watch_stereo=0; del_timer(&msp->wake_stereo); if (msp->active) msp->restart = 1; wake_up_interruptible(&msp->wq); break; case MSP_GET_VOLUME: *sarg = (msp->left > msp->right) ? msp->left : msp->right; break; case MSP_SET_VOLUME: msp->left = msp->right = *sarg; LOCK_I2C_BUS(msp->bus); msp3400c_setvolume(msp->bus,msp->left, msp->right); UNLOCK_I2C_BUS(msp->bus); break; case MSP_GET_BASS: *sarg = msp->bass; break; case MSP_SET_BASS: msp->bass = *sarg; LOCK_I2C_BUS(msp->bus); msp3400c_setbass(msp->bus,msp->bass); UNLOCK_I2C_BUS(msp->bus); break; case MSP_GET_TREBLE: *sarg = msp->treble; break; case MSP_SET_TREBLE: msp->treble = *sarg; LOCK_I2C_BUS(msp->bus); msp3400c_settreble(msp->bus,msp->treble); UNLOCK_I2C_BUS(msp->bus); break; case MSP_GET_STEREO: *sarg = msp->stereo; break; case MSP_SET_STEREO: if (*sarg) { msp->watch_stereo=0; del_timer(&msp->wake_stereo); LOCK_I2C_BUS(msp->bus); msp3400c_setstereo(msp,*sarg); UNLOCK_I2C_BUS(msp->bus); } break; case MSP_GET_DC: LOCK_I2C_BUS(msp->bus); *sarg = ((int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) + (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c)); UNLOCK_I2C_BUS(msp->bus); break; default: return -EINVAL; } return 0;}/* ----------------------------------------------------------------------- */struct i2c_driver i2c_driver_msp = { "msp3400", /* name */ I2C_DRIVERID_MSP3400, /* ID */ I2C_MSP3400C, I2C_MSP3400C, /* addr range */ msp3400c_attach, msp3400c_detach, msp3400c_command};#ifdef MODULEint init_module(void)#else int msp3400c_init(void)#endif{ i2c_register_driver(&i2c_driver_msp);#ifdef REGISTER_MIXER if ((mixer_num = register_sound_mixer(&msp3400c_mixer_fops, -1)) < 0) printk(KERN_ERR "msp3400c: cannot allocate mixer device\n");#endif return 0;}#ifdef MODULEvoid cleanup_module(void){ i2c_unregister_driver(&i2c_driver_msp);#ifdef REGISTER_MIXER if (mixer_num >= 0) unregister_sound_mixer(mixer_num);#endif}#endif/* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- * Local variables: * c-basic-offset: 8 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -