drivers/media/radio/radio-mr800.c

Source file repositories/reference/linux-study-clean/drivers/media/radio/radio-mr800.c

File Facts

System
Linux kernel
Corpus path
drivers/media/radio/radio-mr800.c
Extension
.c
Size
16777 bytes
Lines
600
Domain
Driver Families
Bucket
drivers/media
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct amradio_device {
	/* reference to USB and video device */
	struct usb_device *usbdev;
	struct usb_interface *intf;
	struct video_device vdev;
	struct v4l2_device v4l2_dev;
	struct v4l2_ctrl_handler hdl;

	u8 *buffer;
	struct mutex lock;	/* buffer locking */
	int curfreq;
	int stereo;
	int muted;
};

static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev)
{
	return container_of(v4l2_dev, struct amradio_device, v4l2_dev);
}

static int amradio_send_cmd(struct amradio_device *radio, u8 cmd, u8 arg,
		u8 *extra, u8 extralen, bool reply)
{
	int retval;
	int size;

	radio->buffer[0] = 0x00;
	radio->buffer[1] = 0x55;
	radio->buffer[2] = 0xaa;
	radio->buffer[3] = extralen;
	radio->buffer[4] = cmd;
	radio->buffer[5] = arg;
	radio->buffer[6] = 0x00;
	radio->buffer[7] = extra || reply ? 8 : 0;

	retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
		radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT);

	if (retval < 0 || size != BUFFER_LENGTH) {
		if (video_is_registered(&radio->vdev))
			amradio_dev_warn(&radio->vdev.dev,
					"cmd %02x failed\n", cmd);
		return retval ? retval : -EIO;
	}
	if (!extra && !reply)
		return 0;

	if (extra) {
		memcpy(radio->buffer, extra, extralen);
		memset(radio->buffer + extralen, 0, 8 - extralen);
		retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
			radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT);
	} else {
		memset(radio->buffer, 0, 8);
		retval = usb_bulk_msg(radio->usbdev, usb_rcvbulkpipe(radio->usbdev, 0x81),
			radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT);
	}
	if (retval == 0 && size == BUFFER_LENGTH)
		return 0;
	if (video_is_registered(&radio->vdev) && cmd != AMRADIO_GET_READY_FLAG)
		amradio_dev_warn(&radio->vdev.dev, "follow-up to cmd %02x failed\n", cmd);
	return retval ? retval : -EIO;
}

/* switch on/off the radio. Send 8 bytes to device */
static int amradio_set_mute(struct amradio_device *radio, bool mute)
{
	int ret = amradio_send_cmd(radio,
			AMRADIO_SET_MUTE, mute, NULL, 0, false);

	if (!ret)
		radio->muted = mute;
	return ret;
}

/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
static int amradio_set_freq(struct amradio_device *radio, int freq)
{
	unsigned short freq_send;
	u8 buf[3];
	int retval;

	/* we need to be sure that frequency isn't out of range */
	freq = clamp_t(unsigned, freq, FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL);
	freq_send = 0x10 + (freq >> 3) / 25;

	/* frequency is calculated from freq_send and placed in first 2 bytes */
	buf[0] = (freq_send >> 8) & 0xff;
	buf[1] = freq_send & 0xff;
	buf[2] = 0x01;

Annotation

Implementation Notes