Commit 4967d53d authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

[media] si470x: Clean up, introduce the control framework

This cleans up the code and si470x now uses the proper v4l2 frameworks
and passes most of the v4l2-compliance tests.
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Acked-by: default avatarTobias Lorenz <tobias.lorenz@gmx.net>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 528f0f78
...@@ -196,9 +196,9 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) ...@@ -196,9 +196,9 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
} }
if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
dev_warn(&radio->videodev->dev, "tune does not complete\n"); dev_warn(&radio->videodev.dev, "tune does not complete\n");
if (timed_out) if (timed_out)
dev_warn(&radio->videodev->dev, dev_warn(&radio->videodev.dev,
"tune timed out after %u ms\n", tune_timeout); "tune timed out after %u ms\n", tune_timeout);
stop: stop:
...@@ -344,12 +344,12 @@ static int si470x_set_seek(struct si470x_device *radio, ...@@ -344,12 +344,12 @@ static int si470x_set_seek(struct si470x_device *radio,
} }
if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
dev_warn(&radio->videodev->dev, "seek does not complete\n"); dev_warn(&radio->videodev.dev, "seek does not complete\n");
if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
dev_warn(&radio->videodev->dev, dev_warn(&radio->videodev.dev,
"seek failed / band limit reached\n"); "seek failed / band limit reached\n");
if (timed_out) if (timed_out)
dev_warn(&radio->videodev->dev, dev_warn(&radio->videodev.dev,
"seek timed out after %u ms\n", seek_timeout); "seek timed out after %u ms\n", seek_timeout);
stop: stop:
...@@ -463,7 +463,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, ...@@ -463,7 +463,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf,
unsigned int block_count = 0; unsigned int block_count = 0;
/* switch on rds reception */ /* switch on rds reception */
mutex_lock(&radio->lock);
if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
si470x_rds_on(radio); si470x_rds_on(radio);
...@@ -505,7 +504,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, ...@@ -505,7 +504,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf,
} }
done: done:
mutex_unlock(&radio->lock);
return retval; return retval;
} }
...@@ -521,10 +519,8 @@ static unsigned int si470x_fops_poll(struct file *file, ...@@ -521,10 +519,8 @@ static unsigned int si470x_fops_poll(struct file *file,
/* switch on rds reception */ /* switch on rds reception */
mutex_lock(&radio->lock);
if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
si470x_rds_on(radio); si470x_rds_on(radio);
mutex_unlock(&radio->lock);
poll_wait(file, &radio->read_queue, pts); poll_wait(file, &radio->read_queue, pts);
...@@ -553,134 +549,27 @@ static const struct v4l2_file_operations si470x_fops = { ...@@ -553,134 +549,27 @@ static const struct v4l2_file_operations si470x_fops = {
* Video4Linux Interface * Video4Linux Interface
**************************************************************************/ **************************************************************************/
/*
* si470x_vidioc_queryctrl - enumerate control items
*/
static int si470x_vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qc)
{
struct si470x_device *radio = video_drvdata(file);
int retval = -EINVAL;
/* abort if qc->id is below V4L2_CID_BASE */
if (qc->id < V4L2_CID_BASE)
goto done;
/* search video control */
switch (qc->id) {
case V4L2_CID_AUDIO_VOLUME:
return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15);
case V4L2_CID_AUDIO_MUTE:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
}
/* disable unsupported base controls */ static int si470x_s_ctrl(struct v4l2_ctrl *ctrl)
/* to satisfy kradio and such apps */
if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) {
qc->flags = V4L2_CTRL_FLAG_DISABLED;
retval = 0;
}
done:
if (retval < 0)
dev_warn(&radio->videodev->dev,
"query controls failed with %d\n", retval);
return retval;
}
/*
* si470x_vidioc_g_ctrl - get the value of a control
*/
static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{ {
struct si470x_device *radio = video_drvdata(file); struct si470x_device *radio =
int retval = 0; container_of(ctrl->handler, struct si470x_device, hdl);
mutex_lock(&radio->lock);
/* safety checks */
retval = si470x_disconnect_check(radio);
if (retval)
goto done;
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = radio->registers[SYSCONFIG2] &
SYSCONFIG2_VOLUME;
break;
case V4L2_CID_AUDIO_MUTE:
ctrl->value = ((radio->registers[POWERCFG] &
POWERCFG_DMUTE) == 0) ? 1 : 0;
break;
default:
retval = -EINVAL;
}
done:
if (retval < 0)
dev_warn(&radio->videodev->dev,
"get control failed with %d\n", retval);
mutex_unlock(&radio->lock);
return retval;
}
/*
* si470x_vidioc_s_ctrl - set the value of a control
*/
static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct si470x_device *radio = video_drvdata(file);
int retval = 0;
mutex_lock(&radio->lock);
/* safety checks */
retval = si470x_disconnect_check(radio);
if (retval)
goto done;
switch (ctrl->id) { switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_VOLUME:
radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
radio->registers[SYSCONFIG2] |= ctrl->value; radio->registers[SYSCONFIG2] |= ctrl->val;
retval = si470x_set_register(radio, SYSCONFIG2); return si470x_set_register(radio, SYSCONFIG2);
break;
case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_MUTE:
if (ctrl->value == 1) if (ctrl->val)
radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
else else
radio->registers[POWERCFG] |= POWERCFG_DMUTE; radio->registers[POWERCFG] |= POWERCFG_DMUTE;
retval = si470x_set_register(radio, POWERCFG); return si470x_set_register(radio, POWERCFG);
break; break;
default: default:
retval = -EINVAL; return -EINVAL;
} }
done:
if (retval < 0)
dev_warn(&radio->videodev->dev,
"set control failed with %d\n", retval);
mutex_unlock(&radio->lock);
return retval;
}
/*
* si470x_vidioc_g_audio - get audio attributes
*/
static int si470x_vidioc_g_audio(struct file *file, void *priv,
struct v4l2_audio *audio)
{
/* driver constants */
audio->index = 0;
strcpy(audio->name, "Radio");
audio->capability = V4L2_AUDCAP_STEREO;
audio->mode = 0;
return 0;
} }
...@@ -693,12 +582,6 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, ...@@ -693,12 +582,6 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
struct si470x_device *radio = video_drvdata(file); struct si470x_device *radio = video_drvdata(file);
int retval = 0; int retval = 0;
mutex_lock(&radio->lock);
/* safety checks */
retval = si470x_disconnect_check(radio);
if (retval)
goto done;
if (tuner->index != 0) { if (tuner->index != 0) {
retval = -EINVAL; retval = -EINVAL;
goto done; goto done;
...@@ -737,7 +620,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, ...@@ -737,7 +620,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
tuner->rxsubchans = V4L2_TUNER_SUB_MONO; tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
else else
tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; tuner->rxsubchans = V4L2_TUNER_SUB_STEREO;
/* If there is a reliable method of detecting an RDS channel, /* If there is a reliable method of detecting an RDS channel,
then this code should check for that before setting this then this code should check for that before setting this
RDS subchannel. */ RDS subchannel. */
...@@ -761,9 +644,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, ...@@ -761,9 +644,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
done: done:
if (retval < 0) if (retval < 0)
dev_warn(&radio->videodev->dev, dev_warn(&radio->videodev.dev,
"get tuner failed with %d\n", retval); "get tuner failed with %d\n", retval);
mutex_unlock(&radio->lock);
return retval; return retval;
} }
...@@ -777,12 +659,6 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, ...@@ -777,12 +659,6 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv,
struct si470x_device *radio = video_drvdata(file); struct si470x_device *radio = video_drvdata(file);
int retval = 0; int retval = 0;
mutex_lock(&radio->lock);
/* safety checks */
retval = si470x_disconnect_check(radio);
if (retval)
goto done;
if (tuner->index != 0) if (tuner->index != 0)
goto done; goto done;
...@@ -802,9 +678,8 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, ...@@ -802,9 +678,8 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv,
done: done:
if (retval < 0) if (retval < 0)
dev_warn(&radio->videodev->dev, dev_warn(&radio->videodev.dev,
"set tuner failed with %d\n", retval); "set tuner failed with %d\n", retval);
mutex_unlock(&radio->lock);
return retval; return retval;
} }
...@@ -818,12 +693,6 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, ...@@ -818,12 +693,6 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv,
struct si470x_device *radio = video_drvdata(file); struct si470x_device *radio = video_drvdata(file);
int retval = 0; int retval = 0;
/* safety checks */
mutex_lock(&radio->lock);
retval = si470x_disconnect_check(radio);
if (retval)
goto done;
if (freq->tuner != 0) { if (freq->tuner != 0) {
retval = -EINVAL; retval = -EINVAL;
goto done; goto done;
...@@ -834,9 +703,8 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, ...@@ -834,9 +703,8 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv,
done: done:
if (retval < 0) if (retval < 0)
dev_warn(&radio->videodev->dev, dev_warn(&radio->videodev.dev,
"get frequency failed with %d\n", retval); "get frequency failed with %d\n", retval);
mutex_unlock(&radio->lock);
return retval; return retval;
} }
...@@ -850,12 +718,6 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, ...@@ -850,12 +718,6 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
struct si470x_device *radio = video_drvdata(file); struct si470x_device *radio = video_drvdata(file);
int retval = 0; int retval = 0;
mutex_lock(&radio->lock);
/* safety checks */
retval = si470x_disconnect_check(radio);
if (retval)
goto done;
if (freq->tuner != 0) { if (freq->tuner != 0) {
retval = -EINVAL; retval = -EINVAL;
goto done; goto done;
...@@ -865,9 +727,8 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, ...@@ -865,9 +727,8 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
done: done:
if (retval < 0) if (retval < 0)
dev_warn(&radio->videodev->dev, dev_warn(&radio->videodev.dev,
"set frequency failed with %d\n", retval); "set frequency failed with %d\n", retval);
mutex_unlock(&radio->lock);
return retval; return retval;
} }
...@@ -881,12 +742,6 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, ...@@ -881,12 +742,6 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
struct si470x_device *radio = video_drvdata(file); struct si470x_device *radio = video_drvdata(file);
int retval = 0; int retval = 0;
mutex_lock(&radio->lock);
/* safety checks */
retval = si470x_disconnect_check(radio);
if (retval)
goto done;
if (seek->tuner != 0) { if (seek->tuner != 0) {
retval = -EINVAL; retval = -EINVAL;
goto done; goto done;
...@@ -896,22 +751,20 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, ...@@ -896,22 +751,20 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
done: done:
if (retval < 0) if (retval < 0)
dev_warn(&radio->videodev->dev, dev_warn(&radio->videodev.dev,
"set hardware frequency seek failed with %d\n", retval); "set hardware frequency seek failed with %d\n", retval);
mutex_unlock(&radio->lock);
return retval; return retval;
} }
const struct v4l2_ctrl_ops si470x_ctrl_ops = {
.s_ctrl = si470x_s_ctrl,
};
/* /*
* si470x_ioctl_ops - video device ioctl operations * si470x_ioctl_ops - video device ioctl operations
*/ */
static const struct v4l2_ioctl_ops si470x_ioctl_ops = { static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
.vidioc_querycap = si470x_vidioc_querycap, .vidioc_querycap = si470x_vidioc_querycap,
.vidioc_queryctrl = si470x_vidioc_queryctrl,
.vidioc_g_ctrl = si470x_vidioc_g_ctrl,
.vidioc_s_ctrl = si470x_vidioc_s_ctrl,
.vidioc_g_audio = si470x_vidioc_g_audio,
.vidioc_g_tuner = si470x_vidioc_g_tuner, .vidioc_g_tuner = si470x_vidioc_g_tuner,
.vidioc_s_tuner = si470x_vidioc_s_tuner, .vidioc_s_tuner = si470x_vidioc_s_tuner,
.vidioc_g_frequency = si470x_vidioc_g_frequency, .vidioc_g_frequency = si470x_vidioc_g_frequency,
...@@ -926,6 +779,6 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = { ...@@ -926,6 +779,6 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
struct video_device si470x_viddev_template = { struct video_device si470x_viddev_template = {
.fops = &si470x_fops, .fops = &si470x_fops,
.name = DRIVER_NAME, .name = DRIVER_NAME,
.release = video_device_release, .release = video_device_release_empty,
.ioctl_ops = &si470x_ioctl_ops, .ioctl_ops = &si470x_ioctl_ops,
}; };
...@@ -161,20 +161,6 @@ static int si470x_get_all_registers(struct si470x_device *radio) ...@@ -161,20 +161,6 @@ static int si470x_get_all_registers(struct si470x_device *radio)
/**************************************************************************
* General Driver Functions - DISCONNECT_CHECK
**************************************************************************/
/*
* si470x_disconnect_check - check whether radio disconnects
*/
int si470x_disconnect_check(struct si470x_device *radio)
{
return 0;
}
/************************************************************************** /**************************************************************************
* File Operations Interface * File Operations Interface
**************************************************************************/ **************************************************************************/
...@@ -185,12 +171,12 @@ int si470x_disconnect_check(struct si470x_device *radio) ...@@ -185,12 +171,12 @@ int si470x_disconnect_check(struct si470x_device *radio)
int si470x_fops_open(struct file *file) int si470x_fops_open(struct file *file)
{ {
struct si470x_device *radio = video_drvdata(file); struct si470x_device *radio = video_drvdata(file);
int retval = 0; int retval = v4l2_fh_open(file);
mutex_lock(&radio->lock); if (retval)
radio->users++; return retval;
if (radio->users == 1) { if (v4l2_fh_is_singular_file(file)) {
/* start radio */ /* start radio */
retval = si470x_start(radio); retval = si470x_start(radio);
if (retval < 0) if (retval < 0)
...@@ -205,7 +191,8 @@ int si470x_fops_open(struct file *file) ...@@ -205,7 +191,8 @@ int si470x_fops_open(struct file *file)
} }
done: done:
mutex_unlock(&radio->lock); if (retval)
v4l2_fh_release(file);
return retval; return retval;
} }
...@@ -216,21 +203,12 @@ int si470x_fops_open(struct file *file) ...@@ -216,21 +203,12 @@ int si470x_fops_open(struct file *file)
int si470x_fops_release(struct file *file) int si470x_fops_release(struct file *file)
{ {
struct si470x_device *radio = video_drvdata(file); struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* safety check */
if (!radio)
return -ENODEV;
mutex_lock(&radio->lock); if (v4l2_fh_is_singular_file(file))
radio->users--;
if (radio->users == 0)
/* stop radio */ /* stop radio */
retval = si470x_stop(radio); si470x_stop(radio);
mutex_unlock(&radio->lock); return v4l2_fh_release(file);
return retval;
} }
...@@ -371,32 +349,25 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, ...@@ -371,32 +349,25 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
goto err_initial; goto err_initial;
} }
radio->users = 0;
radio->client = client; radio->client = client;
mutex_init(&radio->lock); mutex_init(&radio->lock);
/* video device allocation and initialization */ /* video device initialization */
radio->videodev = video_device_alloc(); radio->videodev = si470x_viddev_template;
if (!radio->videodev) { video_set_drvdata(&radio->videodev, radio);
retval = -ENOMEM;
goto err_radio;
}
memcpy(radio->videodev, &si470x_viddev_template,
sizeof(si470x_viddev_template));
video_set_drvdata(radio->videodev, radio);
/* power up : need 110ms */ /* power up : need 110ms */
radio->registers[POWERCFG] = POWERCFG_ENABLE; radio->registers[POWERCFG] = POWERCFG_ENABLE;
if (si470x_set_register(radio, POWERCFG) < 0) { if (si470x_set_register(radio, POWERCFG) < 0) {
retval = -EIO; retval = -EIO;
goto err_video; goto err_radio;
} }
msleep(110); msleep(110);
/* get device and chip versions */ /* get device and chip versions */
if (si470x_get_all_registers(radio) < 0) { if (si470x_get_all_registers(radio) < 0) {
retval = -EIO; retval = -EIO;
goto err_video; goto err_radio;
} }
dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
radio->registers[DEVICEID], radio->registers[CHIPID]); radio->registers[DEVICEID], radio->registers[CHIPID]);
...@@ -427,7 +398,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, ...@@ -427,7 +398,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
if (!radio->buffer) { if (!radio->buffer) {
retval = -EIO; retval = -EIO;
goto err_video; goto err_radio;
} }
/* rds buffer configuration */ /* rds buffer configuration */
...@@ -447,7 +418,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, ...@@ -447,7 +418,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
} }
/* register video device */ /* register video device */
retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
radio_nr); radio_nr);
if (retval) { if (retval) {
dev_warn(&client->dev, "Could not register video device\n"); dev_warn(&client->dev, "Could not register video device\n");
...@@ -460,8 +431,6 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, ...@@ -460,8 +431,6 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
free_irq(client->irq, radio); free_irq(client->irq, radio);
err_rds: err_rds:
kfree(radio->buffer); kfree(radio->buffer);
err_video:
video_device_release(radio->videodev);
err_radio: err_radio:
kfree(radio); kfree(radio);
err_initial: err_initial:
...@@ -477,7 +446,7 @@ static __devexit int si470x_i2c_remove(struct i2c_client *client) ...@@ -477,7 +446,7 @@ static __devexit int si470x_i2c_remove(struct i2c_client *client)
struct si470x_device *radio = i2c_get_clientdata(client); struct si470x_device *radio = i2c_get_clientdata(client);
free_irq(client->irq, radio); free_irq(client->irq, radio);
video_unregister_device(radio->videodev); video_unregister_device(&radio->videodev);
kfree(radio); kfree(radio);
return 0; return 0;
......
...@@ -366,23 +366,6 @@ static int si470x_get_scratch_page_versions(struct si470x_device *radio) ...@@ -366,23 +366,6 @@ static int si470x_get_scratch_page_versions(struct si470x_device *radio)
/**************************************************************************
* General Driver Functions - DISCONNECT_CHECK
**************************************************************************/
/*
* si470x_disconnect_check - check whether radio disconnects
*/
int si470x_disconnect_check(struct si470x_device *radio)
{
if (radio->disconnected)
return -EIO;
else
return 0;
}
/************************************************************************** /**************************************************************************
* RDS Driver Functions * RDS Driver Functions
**************************************************************************/ **************************************************************************/
...@@ -414,9 +397,6 @@ static void si470x_int_in_callback(struct urb *urb) ...@@ -414,9 +397,6 @@ static void si470x_int_in_callback(struct urb *urb)
} }
} }
/* safety checks */
if (radio->disconnected)
return;
if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
goto resubmit; goto resubmit;
...@@ -512,19 +492,16 @@ static void si470x_int_in_callback(struct urb *urb) ...@@ -512,19 +492,16 @@ static void si470x_int_in_callback(struct urb *urb)
int si470x_fops_open(struct file *file) int si470x_fops_open(struct file *file)
{ {
struct si470x_device *radio = video_drvdata(file); struct si470x_device *radio = video_drvdata(file);
int retval; int retval = v4l2_fh_open(file);
mutex_lock(&radio->lock); if (retval)
radio->users++; return retval;
retval = usb_autopm_get_interface(radio->intf); retval = usb_autopm_get_interface(radio->intf);
if (retval < 0) { if (retval < 0)
radio->users--;
retval = -EIO;
goto done; goto done;
}
if (radio->users == 1) { if (v4l2_fh_is_singular_file(file)) {
/* start radio */ /* start radio */
retval = si470x_start(radio); retval = si470x_start(radio);
if (retval < 0) { if (retval < 0) {
...@@ -555,7 +532,8 @@ int si470x_fops_open(struct file *file) ...@@ -555,7 +532,8 @@ int si470x_fops_open(struct file *file)
} }
done: done:
mutex_unlock(&radio->lock); if (retval)
v4l2_fh_release(file);
return retval; return retval;
} }
...@@ -566,45 +544,36 @@ int si470x_fops_open(struct file *file) ...@@ -566,45 +544,36 @@ int si470x_fops_open(struct file *file)
int si470x_fops_release(struct file *file) int si470x_fops_release(struct file *file)
{ {
struct si470x_device *radio = video_drvdata(file); struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* safety check */ if (v4l2_fh_is_singular_file(file)) {
if (!radio) {
retval = -ENODEV;
goto done;
}
mutex_lock(&radio->lock);
radio->users--;
if (radio->users == 0) {
/* shutdown interrupt handler */ /* shutdown interrupt handler */
if (radio->int_in_running) { if (radio->int_in_running) {
radio->int_in_running = 0; radio->int_in_running = 0;
if (radio->int_in_urb) if (radio->int_in_urb)
usb_kill_urb(radio->int_in_urb); usb_kill_urb(radio->int_in_urb);
}
if (radio->disconnected) {
video_unregister_device(radio->videodev);
kfree(radio->int_in_buffer);
kfree(radio->buffer);
mutex_unlock(&radio->lock);
kfree(radio);
goto done;
} }
/* cancel read processes */ /* cancel read processes */
wake_up_interruptible(&radio->read_queue); wake_up_interruptible(&radio->read_queue);
/* stop radio */ /* stop radio */
retval = si470x_stop(radio); si470x_stop(radio);
usb_autopm_put_interface(radio->intf); usb_autopm_put_interface(radio->intf);
} }
mutex_unlock(&radio->lock); return v4l2_fh_release(file);
done:
return retval;
} }
static void si470x_usb_release(struct video_device *vdev)
{
struct si470x_device *radio = video_get_drvdata(vdev);
usb_free_urb(radio->int_in_urb);
v4l2_ctrl_handler_free(&radio->hdl);
v4l2_device_unregister(&radio->v4l2_dev);
kfree(radio->int_in_buffer);
kfree(radio->buffer);
kfree(radio);
}
/************************************************************************** /**************************************************************************
...@@ -623,9 +592,9 @@ int si470x_vidioc_querycap(struct file *file, void *priv, ...@@ -623,9 +592,9 @@ int si470x_vidioc_querycap(struct file *file, void *priv,
strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
usb_make_path(radio->usbdev, capability->bus_info, usb_make_path(radio->usbdev, capability->bus_info,
sizeof(capability->bus_info)); sizeof(capability->bus_info));
capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | capability->device_caps = V4L2_CAP_HW_FREQ_SEEK |
V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0; return 0;
} }
...@@ -653,8 +622,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, ...@@ -653,8 +622,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
retval = -ENOMEM; retval = -ENOMEM;
goto err_initial; goto err_initial;
} }
radio->users = 0;
radio->disconnected = 0;
radio->usbdev = interface_to_usbdev(intf); radio->usbdev = interface_to_usbdev(intf);
radio->intf = intf; radio->intf = intf;
mutex_init(&radio->lock); mutex_init(&radio->lock);
...@@ -691,20 +658,34 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, ...@@ -691,20 +658,34 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
goto err_intbuffer; goto err_intbuffer;
} }
/* video device allocation and initialization */ retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
radio->videodev = video_device_alloc(); if (retval < 0) {
if (!radio->videodev) { dev_err(&intf->dev, "couldn't register v4l2_device\n");
retval = -ENOMEM;
goto err_urb; goto err_urb;
} }
memcpy(radio->videodev, &si470x_viddev_template,
sizeof(si470x_viddev_template)); v4l2_ctrl_handler_init(&radio->hdl, 2);
video_set_drvdata(radio->videodev, radio); v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops,
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 15);
if (radio->hdl.error) {
retval = radio->hdl.error;
dev_err(&intf->dev, "couldn't register control\n");
goto err_dev;
}
radio->videodev = si470x_viddev_template;
radio->videodev.ctrl_handler = &radio->hdl;
radio->videodev.lock = &radio->lock;
radio->videodev.v4l2_dev = &radio->v4l2_dev;
radio->videodev.release = si470x_usb_release;
set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags);
video_set_drvdata(&radio->videodev, radio);
/* get device and chip versions */ /* get device and chip versions */
if (si470x_get_all_registers(radio) < 0) { if (si470x_get_all_registers(radio) < 0) {
retval = -EIO; retval = -EIO;
goto err_video; goto err_ctrl;
} }
dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
radio->registers[DEVICEID], radio->registers[CHIPID]); radio->registers[DEVICEID], radio->registers[CHIPID]);
...@@ -721,7 +702,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, ...@@ -721,7 +702,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
/* get software and hardware versions */ /* get software and hardware versions */
if (si470x_get_scratch_page_versions(radio) < 0) { if (si470x_get_scratch_page_versions(radio) < 0) {
retval = -EIO; retval = -EIO;
goto err_video; goto err_ctrl;
} }
dev_info(&intf->dev, "software version %d, hardware version %d\n", dev_info(&intf->dev, "software version %d, hardware version %d\n",
radio->software_version, radio->hardware_version); radio->software_version, radio->hardware_version);
...@@ -764,28 +745,30 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, ...@@ -764,28 +745,30 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
if (!radio->buffer) { if (!radio->buffer) {
retval = -EIO; retval = -EIO;
goto err_video; goto err_ctrl;
} }
/* rds buffer configuration */ /* rds buffer configuration */
radio->wr_index = 0; radio->wr_index = 0;
radio->rd_index = 0; radio->rd_index = 0;
init_waitqueue_head(&radio->read_queue); init_waitqueue_head(&radio->read_queue);
usb_set_intfdata(intf, radio);
/* register video device */ /* register video device */
retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
radio_nr); radio_nr);
if (retval) { if (retval) {
dev_warn(&intf->dev, "Could not register video device\n"); dev_warn(&intf->dev, "Could not register video device\n");
goto err_all; goto err_all;
} }
usb_set_intfdata(intf, radio);
return 0; return 0;
err_all: err_all:
kfree(radio->buffer); kfree(radio->buffer);
err_video: err_ctrl:
video_device_release(radio->videodev); v4l2_ctrl_handler_free(&radio->hdl);
err_dev:
v4l2_device_unregister(&radio->v4l2_dev);
err_urb: err_urb:
usb_free_urb(radio->int_in_urb); usb_free_urb(radio->int_in_urb);
err_intbuffer: err_intbuffer:
...@@ -828,23 +811,10 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) ...@@ -828,23 +811,10 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf)
struct si470x_device *radio = usb_get_intfdata(intf); struct si470x_device *radio = usb_get_intfdata(intf);
mutex_lock(&radio->lock); mutex_lock(&radio->lock);
radio->disconnected = 1; v4l2_device_disconnect(&radio->v4l2_dev);
video_unregister_device(&radio->videodev);
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
if (radio->users == 0) { mutex_unlock(&radio->lock);
/* set led to disconnect state */
si470x_set_led_state(radio, BLINK_ORANGE_LED);
/* Free data structures. */
usb_free_urb(radio->int_in_urb);
kfree(radio->int_in_buffer);
video_unregister_device(radio->videodev);
kfree(radio->buffer);
mutex_unlock(&radio->lock);
kfree(radio);
} else {
mutex_unlock(&radio->lock);
}
} }
......
...@@ -36,6 +36,9 @@ ...@@ -36,6 +36,9 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <media/v4l2-common.h> #include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/v4l2-device.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
...@@ -141,10 +144,9 @@ ...@@ -141,10 +144,9 @@
* si470x_device - private data * si470x_device - private data
*/ */
struct si470x_device { struct si470x_device {
struct video_device *videodev; struct v4l2_device v4l2_dev;
struct video_device videodev;
/* driver management */ struct v4l2_ctrl_handler hdl;
unsigned int users;
/* Silabs internal registers (0..15) */ /* Silabs internal registers (0..15) */
unsigned short registers[RADIO_REGISTER_NUM]; unsigned short registers[RADIO_REGISTER_NUM];
...@@ -174,9 +176,6 @@ struct si470x_device { ...@@ -174,9 +176,6 @@ struct si470x_device {
/* scratch page */ /* scratch page */
unsigned char software_version; unsigned char software_version;
unsigned char hardware_version; unsigned char hardware_version;
/* driver management */
unsigned char disconnected;
#endif #endif
#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE) #if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE)
...@@ -213,6 +212,7 @@ struct si470x_device { ...@@ -213,6 +212,7 @@ struct si470x_device {
* Common Functions * Common Functions
**************************************************************************/ **************************************************************************/
extern struct video_device si470x_viddev_template; extern struct video_device si470x_viddev_template;
extern const struct v4l2_ctrl_ops si470x_ctrl_ops;
int si470x_get_register(struct si470x_device *radio, int regnr); int si470x_get_register(struct si470x_device *radio, int regnr);
int si470x_set_register(struct si470x_device *radio, int regnr); int si470x_set_register(struct si470x_device *radio, int regnr);
int si470x_disconnect_check(struct si470x_device *radio); int si470x_disconnect_check(struct si470x_device *radio);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment