mirror of
				https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
				synced 2025-10-31 08:08:51 +01:00 
			
		
		
		
	Fixed MTP to work with TWRP
This commit is contained in:
		
						commit
						f6dfaef42e
					
				
					 50820 changed files with 20846062 additions and 0 deletions
				
			
		
							
								
								
									
										65
									
								
								drivers/media/usb/pvrusb2/Kconfig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								drivers/media/usb/pvrusb2/Kconfig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | |||
| config VIDEO_PVRUSB2 | ||||
| 	tristate "Hauppauge WinTV-PVR USB2 support" | ||||
| 	depends on VIDEO_V4L2 && I2C | ||||
| 	select VIDEO_TUNER | ||||
| 	select VIDEO_TVEEPROM | ||||
| 	select VIDEO_CX2341X | ||||
| 	select VIDEO_SAA711X | ||||
| 	select VIDEO_CX25840 | ||||
| 	select VIDEO_MSP3400 | ||||
| 	select VIDEO_WM8775 | ||||
| 	select VIDEO_CS53L32A | ||||
| 	---help--- | ||||
| 	  This is a video4linux driver for Conexant 23416 based | ||||
| 	  usb2 personal video recorder devices. | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here: the | ||||
| 	  module will be called pvrusb2 | ||||
| 
 | ||||
| config VIDEO_PVRUSB2_SYSFS | ||||
| 	bool "pvrusb2 sysfs support" | ||||
| 	default y | ||||
| 	depends on VIDEO_PVRUSB2 && SYSFS | ||||
| 	---help--- | ||||
| 	  This option enables the operation of a sysfs based | ||||
| 	  interface for query and control of the pvrusb2 driver. | ||||
| 
 | ||||
| 	  This is not generally needed for v4l applications, | ||||
| 	  although certain applications are optimized to take | ||||
| 	  advantage of this feature. | ||||
| 
 | ||||
| 	  If you are in doubt, say Y. | ||||
| 
 | ||||
| 	  Note: This feature is experimental and subject to change. | ||||
| 
 | ||||
| config VIDEO_PVRUSB2_DVB | ||||
| 	bool "pvrusb2 ATSC/DVB support" | ||||
| 	default y | ||||
| 	depends on VIDEO_PVRUSB2 && DVB_CORE | ||||
| 	select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT | ||||
| 	select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT | ||||
| 	select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT | ||||
| 	select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT | ||||
| 	select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT | ||||
| 	select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT | ||||
| 	select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT | ||||
| 	---help--- | ||||
| 
 | ||||
| 	  This option enables a DVB interface for the pvrusb2 driver. | ||||
| 	  If your device does not support digital television, this | ||||
| 	  feature will have no affect on the driver's operation. | ||||
| 
 | ||||
| 	  If you are in doubt, say Y. | ||||
| 
 | ||||
| config VIDEO_PVRUSB2_DEBUGIFC | ||||
| 	bool "pvrusb2 debug interface" | ||||
| 	depends on VIDEO_PVRUSB2_SYSFS | ||||
| 	---help--- | ||||
| 	  This option enables the inclusion of a debug interface | ||||
| 	  in the pvrusb2 driver, hosted through sysfs. | ||||
| 
 | ||||
| 	  You do not need to select this option unless you plan | ||||
| 	  on debugging the driver or performing a manual firmware | ||||
| 	  extraction. | ||||
| 
 | ||||
| 	  If you are in doubt, say N. | ||||
							
								
								
									
										22
									
								
								drivers/media/usb/pvrusb2/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								drivers/media/usb/pvrusb2/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o | ||||
| obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o | ||||
| obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o | ||||
| 
 | ||||
| pvrusb2-objs	:= pvrusb2-i2c-core.o \
 | ||||
| 		   pvrusb2-audio.o \
 | ||||
| 		   pvrusb2-encoder.o pvrusb2-video-v4l.o \
 | ||||
| 		   pvrusb2-eeprom.o \
 | ||||
| 		   pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \
 | ||||
| 		   pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \
 | ||||
| 		   pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
 | ||||
| 		   pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \
 | ||||
| 		   pvrusb2-cs53l32a.o \
 | ||||
| 		   $(obj-pvrusb2-dvb-y) \
 | ||||
| 		   $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) | ||||
| 
 | ||||
| obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o | ||||
| 
 | ||||
| ccflags-y += -Idrivers/media/i2c | ||||
| ccflags-y += -Idrivers/media/tuners | ||||
| ccflags-y += -Idrivers/media/dvb-core | ||||
| ccflags-y += -Idrivers/media/dvb-frontends | ||||
							
								
								
									
										96
									
								
								drivers/media/usb/pvrusb2/pvrusb2-audio.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								drivers/media/usb/pvrusb2/pvrusb2-audio.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,96 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "pvrusb2-audio.h" | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include <linux/videodev2.h> | ||||
| #include <media/msp3400.h> | ||||
| #include <media/v4l2-common.h> | ||||
| 
 | ||||
| 
 | ||||
| struct routing_scheme { | ||||
| 	const int *def; | ||||
| 	unsigned int cnt; | ||||
| }; | ||||
| 
 | ||||
| static const int routing_scheme0[] = { | ||||
| 	[PVR2_CVAL_INPUT_TV]        = MSP_INPUT_DEFAULT, | ||||
| 	[PVR2_CVAL_INPUT_RADIO]     = MSP_INPUT(MSP_IN_SCART2, | ||||
| 						MSP_IN_TUNER1, | ||||
| 						MSP_DSP_IN_SCART, | ||||
| 						MSP_DSP_IN_SCART), | ||||
| 	[PVR2_CVAL_INPUT_COMPOSITE] = MSP_INPUT(MSP_IN_SCART1, | ||||
| 						MSP_IN_TUNER1, | ||||
| 						MSP_DSP_IN_SCART, | ||||
| 						MSP_DSP_IN_SCART), | ||||
| 	[PVR2_CVAL_INPUT_SVIDEO]    = MSP_INPUT(MSP_IN_SCART1, | ||||
| 						MSP_IN_TUNER1, | ||||
| 						MSP_DSP_IN_SCART, | ||||
| 						MSP_DSP_IN_SCART), | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme routing_def0 = { | ||||
| 	.def = routing_scheme0, | ||||
| 	.cnt = ARRAY_SIZE(routing_scheme0), | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme *routing_schemes[] = { | ||||
| 	[PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, | ||||
| }; | ||||
| 
 | ||||
| void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) | ||||
| { | ||||
| 	if (hdw->input_dirty || hdw->force_dirty) { | ||||
| 		const struct routing_scheme *sp; | ||||
| 		unsigned int sid = hdw->hdw_desc->signal_routing_scheme; | ||||
| 		u32 input; | ||||
| 
 | ||||
| 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); | ||||
| 		sp = (sid < ARRAY_SIZE(routing_schemes)) ? | ||||
| 			routing_schemes[sid] : NULL; | ||||
| 
 | ||||
| 		if ((sp != NULL) && | ||||
| 		    (hdw->input_val >= 0) && | ||||
| 		    (hdw->input_val < sp->cnt)) { | ||||
| 			input = sp->def[hdw->input_val]; | ||||
| 		} else { | ||||
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 				   "*** WARNING *** subdev msp3400 set_input:" | ||||
| 				   " Invalid routing scheme (%u)" | ||||
| 				   " and/or input (%d)", | ||||
| 				   sid, hdw->input_val); | ||||
| 			return; | ||||
| 		} | ||||
| 		sd->ops->audio->s_routing(sd, input, | ||||
| 			MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										37
									
								
								drivers/media/usb/pvrusb2/pvrusb2-audio.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								drivers/media/usb/pvrusb2/pvrusb2-audio.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __PVRUSB2_AUDIO_H | ||||
| #define __PVRUSB2_AUDIO_H | ||||
| 
 | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); | ||||
| #endif /* __PVRUSB2_AUDIO_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										431
									
								
								drivers/media/usb/pvrusb2/pvrusb2-context.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										431
									
								
								drivers/media/usb/pvrusb2/pvrusb2-context.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,431 @@ | |||
| /*
 | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "pvrusb2-context.h" | ||||
| #include "pvrusb2-io.h" | ||||
| #include "pvrusb2-ioread.h" | ||||
| #include "pvrusb2-hdw.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include <linux/wait.h> | ||||
| #include <linux/kthread.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| static struct pvr2_context *pvr2_context_exist_first; | ||||
| static struct pvr2_context *pvr2_context_exist_last; | ||||
| static struct pvr2_context *pvr2_context_notify_first; | ||||
| static struct pvr2_context *pvr2_context_notify_last; | ||||
| static DEFINE_MUTEX(pvr2_context_mutex); | ||||
| static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data); | ||||
| static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data); | ||||
| static int pvr2_context_cleanup_flag; | ||||
| static int pvr2_context_cleaned_flag; | ||||
| static struct task_struct *pvr2_context_thread_ptr; | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_context_set_notify(struct pvr2_context *mp, int fl) | ||||
| { | ||||
| 	int signal_flag = 0; | ||||
| 	mutex_lock(&pvr2_context_mutex); | ||||
| 	if (fl) { | ||||
| 		if (!mp->notify_flag) { | ||||
| 			signal_flag = (pvr2_context_notify_first == NULL); | ||||
| 			mp->notify_prev = pvr2_context_notify_last; | ||||
| 			mp->notify_next = NULL; | ||||
| 			pvr2_context_notify_last = mp; | ||||
| 			if (mp->notify_prev) { | ||||
| 				mp->notify_prev->notify_next = mp; | ||||
| 			} else { | ||||
| 				pvr2_context_notify_first = mp; | ||||
| 			} | ||||
| 			mp->notify_flag = !0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (mp->notify_flag) { | ||||
| 			mp->notify_flag = 0; | ||||
| 			if (mp->notify_next) { | ||||
| 				mp->notify_next->notify_prev = mp->notify_prev; | ||||
| 			} else { | ||||
| 				pvr2_context_notify_last = mp->notify_prev; | ||||
| 			} | ||||
| 			if (mp->notify_prev) { | ||||
| 				mp->notify_prev->notify_next = mp->notify_next; | ||||
| 			} else { | ||||
| 				pvr2_context_notify_first = mp->notify_next; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	mutex_unlock(&pvr2_context_mutex); | ||||
| 	if (signal_flag) wake_up(&pvr2_context_sync_data); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_context_destroy(struct pvr2_context *mp) | ||||
| { | ||||
| 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp); | ||||
| 	if (mp->hdw) pvr2_hdw_destroy(mp->hdw); | ||||
| 	pvr2_context_set_notify(mp, 0); | ||||
| 	mutex_lock(&pvr2_context_mutex); | ||||
| 	if (mp->exist_next) { | ||||
| 		mp->exist_next->exist_prev = mp->exist_prev; | ||||
| 	} else { | ||||
| 		pvr2_context_exist_last = mp->exist_prev; | ||||
| 	} | ||||
| 	if (mp->exist_prev) { | ||||
| 		mp->exist_prev->exist_next = mp->exist_next; | ||||
| 	} else { | ||||
| 		pvr2_context_exist_first = mp->exist_next; | ||||
| 	} | ||||
| 	if (!pvr2_context_exist_first) { | ||||
| 		/* Trigger wakeup on control thread in case it is waiting
 | ||||
| 		   for an exit condition. */ | ||||
| 		wake_up(&pvr2_context_sync_data); | ||||
| 	} | ||||
| 	mutex_unlock(&pvr2_context_mutex); | ||||
| 	kfree(mp); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_context_notify(struct pvr2_context *mp) | ||||
| { | ||||
| 	pvr2_context_set_notify(mp,!0); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_context_check(struct pvr2_context *mp) | ||||
| { | ||||
| 	struct pvr2_channel *ch1, *ch2; | ||||
| 	pvr2_trace(PVR2_TRACE_CTXT, | ||||
| 		   "pvr2_context %p (notify)", mp); | ||||
| 	if (!mp->initialized_flag && !mp->disconnect_flag) { | ||||
| 		mp->initialized_flag = !0; | ||||
| 		pvr2_trace(PVR2_TRACE_CTXT, | ||||
| 			   "pvr2_context %p (initialize)", mp); | ||||
| 		/* Finish hardware initialization */ | ||||
| 		if (pvr2_hdw_initialize(mp->hdw, | ||||
| 					(void (*)(void *))pvr2_context_notify, | ||||
| 					mp)) { | ||||
| 			mp->video_stream.stream = | ||||
| 				pvr2_hdw_get_video_stream(mp->hdw); | ||||
| 			/* Trigger interface initialization.  By doing this
 | ||||
| 			   here initialization runs in our own safe and | ||||
| 			   cozy thread context. */ | ||||
| 			if (mp->setup_func) mp->setup_func(mp); | ||||
| 		} else { | ||||
| 			pvr2_trace(PVR2_TRACE_CTXT, | ||||
| 				   "pvr2_context %p (thread skipping setup)", | ||||
| 				   mp); | ||||
| 			/* Even though initialization did not succeed,
 | ||||
| 			   we're still going to continue anyway.  We need | ||||
| 			   to do this in order to await the expected | ||||
| 			   disconnect (which we will detect in the normal | ||||
| 			   course of operation). */ | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (ch1 = mp->mc_first; ch1; ch1 = ch2) { | ||||
| 		ch2 = ch1->mc_next; | ||||
| 		if (ch1->check_func) ch1->check_func(ch1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (mp->disconnect_flag && !mp->mc_first) { | ||||
| 		/* Go away... */ | ||||
| 		pvr2_context_destroy(mp); | ||||
| 		return; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int pvr2_context_shutok(void) | ||||
| { | ||||
| 	return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int pvr2_context_thread_func(void *foo) | ||||
| { | ||||
| 	struct pvr2_context *mp; | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start"); | ||||
| 
 | ||||
| 	do { | ||||
| 		while ((mp = pvr2_context_notify_first) != NULL) { | ||||
| 			pvr2_context_set_notify(mp, 0); | ||||
| 			pvr2_context_check(mp); | ||||
| 		} | ||||
| 		wait_event_interruptible( | ||||
| 			pvr2_context_sync_data, | ||||
| 			((pvr2_context_notify_first != NULL) || | ||||
| 			 pvr2_context_shutok())); | ||||
| 	} while (!pvr2_context_shutok()); | ||||
| 
 | ||||
| 	pvr2_context_cleaned_flag = !0; | ||||
| 	wake_up(&pvr2_context_cleanup_data); | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up"); | ||||
| 
 | ||||
| 	wait_event_interruptible( | ||||
| 		pvr2_context_sync_data, | ||||
| 		kthread_should_stop()); | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end"); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int pvr2_context_global_init(void) | ||||
| { | ||||
| 	pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func, | ||||
| 					      NULL, | ||||
| 					      "pvrusb2-context"); | ||||
| 	return (pvr2_context_thread_ptr ? 0 : -ENOMEM); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void pvr2_context_global_done(void) | ||||
| { | ||||
| 	pvr2_context_cleanup_flag = !0; | ||||
| 	wake_up(&pvr2_context_sync_data); | ||||
| 	wait_event_interruptible( | ||||
| 		pvr2_context_cleanup_data, | ||||
| 		pvr2_context_cleaned_flag); | ||||
| 	kthread_stop(pvr2_context_thread_ptr); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| struct pvr2_context *pvr2_context_create( | ||||
| 	struct usb_interface *intf, | ||||
| 	const struct usb_device_id *devid, | ||||
| 	void (*setup_func)(struct pvr2_context *)) | ||||
| { | ||||
| 	struct pvr2_context *mp = NULL; | ||||
| 	mp = kzalloc(sizeof(*mp),GFP_KERNEL); | ||||
| 	if (!mp) goto done; | ||||
| 	pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp); | ||||
| 	mp->setup_func = setup_func; | ||||
| 	mutex_init(&mp->mutex); | ||||
| 	mutex_lock(&pvr2_context_mutex); | ||||
| 	mp->exist_prev = pvr2_context_exist_last; | ||||
| 	mp->exist_next = NULL; | ||||
| 	pvr2_context_exist_last = mp; | ||||
| 	if (mp->exist_prev) { | ||||
| 		mp->exist_prev->exist_next = mp; | ||||
| 	} else { | ||||
| 		pvr2_context_exist_first = mp; | ||||
| 	} | ||||
| 	mutex_unlock(&pvr2_context_mutex); | ||||
| 	mp->hdw = pvr2_hdw_create(intf,devid); | ||||
| 	if (!mp->hdw) { | ||||
| 		pvr2_context_destroy(mp); | ||||
| 		mp = NULL; | ||||
| 		goto done; | ||||
| 	} | ||||
| 	pvr2_context_set_notify(mp, !0); | ||||
|  done: | ||||
| 	return mp; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_context_reset_input_limits(struct pvr2_context *mp) | ||||
| { | ||||
| 	unsigned int tmsk,mmsk; | ||||
| 	struct pvr2_channel *cp; | ||||
| 	struct pvr2_hdw *hdw = mp->hdw; | ||||
| 	mmsk = pvr2_hdw_get_input_available(hdw); | ||||
| 	tmsk = mmsk; | ||||
| 	for (cp = mp->mc_first; cp; cp = cp->mc_next) { | ||||
| 		if (!cp->input_mask) continue; | ||||
| 		tmsk &= cp->input_mask; | ||||
| 	} | ||||
| 	pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk); | ||||
| 	pvr2_hdw_commit_ctl(hdw); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_context_enter(struct pvr2_context *mp) | ||||
| { | ||||
| 	mutex_lock(&mp->mutex); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_context_exit(struct pvr2_context *mp) | ||||
| { | ||||
| 	int destroy_flag = 0; | ||||
| 	if (!(mp->mc_first || !mp->disconnect_flag)) { | ||||
| 		destroy_flag = !0; | ||||
| 	} | ||||
| 	mutex_unlock(&mp->mutex); | ||||
| 	if (destroy_flag) pvr2_context_notify(mp); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void pvr2_context_disconnect(struct pvr2_context *mp) | ||||
| { | ||||
| 	pvr2_hdw_disconnect(mp->hdw); | ||||
| 	mp->disconnect_flag = !0; | ||||
| 	pvr2_context_notify(mp); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) | ||||
| { | ||||
| 	pvr2_context_enter(mp); | ||||
| 	cp->hdw = mp->hdw; | ||||
| 	cp->mc_head = mp; | ||||
| 	cp->mc_next = NULL; | ||||
| 	cp->mc_prev = mp->mc_last; | ||||
| 	if (mp->mc_last) { | ||||
| 		mp->mc_last->mc_next = cp; | ||||
| 	} else { | ||||
| 		mp->mc_first = cp; | ||||
| 	} | ||||
| 	mp->mc_last = cp; | ||||
| 	pvr2_context_exit(mp); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp) | ||||
| { | ||||
| 	if (!cp->stream) return; | ||||
| 	pvr2_stream_kill(cp->stream->stream); | ||||
| 	cp->stream->user = NULL; | ||||
| 	cp->stream = NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void pvr2_channel_done(struct pvr2_channel *cp) | ||||
| { | ||||
| 	struct pvr2_context *mp = cp->mc_head; | ||||
| 	pvr2_context_enter(mp); | ||||
| 	cp->input_mask = 0; | ||||
| 	pvr2_channel_disclaim_stream(cp); | ||||
| 	pvr2_context_reset_input_limits(mp); | ||||
| 	if (cp->mc_next) { | ||||
| 		cp->mc_next->mc_prev = cp->mc_prev; | ||||
| 	} else { | ||||
| 		mp->mc_last = cp->mc_prev; | ||||
| 	} | ||||
| 	if (cp->mc_prev) { | ||||
| 		cp->mc_prev->mc_next = cp->mc_next; | ||||
| 	} else { | ||||
| 		mp->mc_first = cp->mc_next; | ||||
| 	} | ||||
| 	cp->hdw = NULL; | ||||
| 	pvr2_context_exit(mp); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk) | ||||
| { | ||||
| 	unsigned int tmsk,mmsk; | ||||
| 	int ret = 0; | ||||
| 	struct pvr2_channel *p2; | ||||
| 	struct pvr2_hdw *hdw = cp->hdw; | ||||
| 
 | ||||
| 	mmsk = pvr2_hdw_get_input_available(hdw); | ||||
| 	cmsk &= mmsk; | ||||
| 	if (cmsk == cp->input_mask) { | ||||
| 		/* No change; nothing to do */ | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	pvr2_context_enter(cp->mc_head); | ||||
| 	do { | ||||
| 		if (!cmsk) { | ||||
| 			cp->input_mask = 0; | ||||
| 			pvr2_context_reset_input_limits(cp->mc_head); | ||||
| 			break; | ||||
| 		} | ||||
| 		tmsk = mmsk; | ||||
| 		for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) { | ||||
| 			if (p2 == cp) continue; | ||||
| 			if (!p2->input_mask) continue; | ||||
| 			tmsk &= p2->input_mask; | ||||
| 		} | ||||
| 		if (!(tmsk & cmsk)) { | ||||
| 			ret = -EPERM; | ||||
| 			break; | ||||
| 		} | ||||
| 		tmsk &= cmsk; | ||||
| 		if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) { | ||||
| 			/* Internal failure changing allowed list; probably
 | ||||
| 			   should not happen, but react if it does. */ | ||||
| 			break; | ||||
| 		} | ||||
| 		cp->input_mask = cmsk; | ||||
| 		pvr2_hdw_commit_ctl(hdw); | ||||
| 	} while (0); | ||||
| 	pvr2_context_exit(cp->mc_head); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp) | ||||
| { | ||||
| 	return cp->input_mask; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int pvr2_channel_claim_stream(struct pvr2_channel *cp, | ||||
| 			      struct pvr2_context_stream *sp) | ||||
| { | ||||
| 	int code = 0; | ||||
| 	pvr2_context_enter(cp->mc_head); do { | ||||
| 		if (sp == cp->stream) break; | ||||
| 		if (sp && sp->user) { | ||||
| 			code = -EBUSY; | ||||
| 			break; | ||||
| 		} | ||||
| 		pvr2_channel_disclaim_stream(cp); | ||||
| 		if (!sp) break; | ||||
| 		sp->user = cp; | ||||
| 		cp->stream = sp; | ||||
| 	} while (0); pvr2_context_exit(cp->mc_head); | ||||
| 	return code; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // This is the marker for the real beginning of a legitimate mpeg2 stream.
 | ||||
| static char stream_sync_key[] = { | ||||
| 	0x00, 0x00, 0x01, 0xba, | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_ioread *pvr2_channel_create_mpeg_stream( | ||||
| 	struct pvr2_context_stream *sp) | ||||
| { | ||||
| 	struct pvr2_ioread *cp; | ||||
| 	cp = pvr2_ioread_create(); | ||||
| 	if (!cp) return NULL; | ||||
| 	pvr2_ioread_setup(cp,sp->stream); | ||||
| 	pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key)); | ||||
| 	return cp; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										94
									
								
								drivers/media/usb/pvrusb2/pvrusb2-context.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								drivers/media/usb/pvrusb2/pvrusb2-context.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | |||
| /*
 | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_CONTEXT_H | ||||
| #define __PVRUSB2_CONTEXT_H | ||||
| 
 | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/usb.h> | ||||
| #include <linux/workqueue.h> | ||||
| 
 | ||||
| struct pvr2_hdw;     /* hardware interface - defined elsewhere */ | ||||
| struct pvr2_stream;  /* stream interface - defined elsewhere */ | ||||
| 
 | ||||
| struct pvr2_context;        /* All central state */ | ||||
| struct pvr2_channel;        /* One I/O pathway to a user */ | ||||
| struct pvr2_context_stream; /* Wrapper for a stream */ | ||||
| struct pvr2_ioread;         /* Low level stream structure */ | ||||
| 
 | ||||
| struct pvr2_context_stream { | ||||
| 	struct pvr2_channel *user; | ||||
| 	struct pvr2_stream *stream; | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_context { | ||||
| 	struct pvr2_channel *mc_first; | ||||
| 	struct pvr2_channel *mc_last; | ||||
| 	struct pvr2_context *exist_next; | ||||
| 	struct pvr2_context *exist_prev; | ||||
| 	struct pvr2_context *notify_next; | ||||
| 	struct pvr2_context *notify_prev; | ||||
| 	struct pvr2_hdw *hdw; | ||||
| 	struct pvr2_context_stream video_stream; | ||||
| 	struct mutex mutex; | ||||
| 	int notify_flag; | ||||
| 	int initialized_flag; | ||||
| 	int disconnect_flag; | ||||
| 
 | ||||
| 	/* Called after pvr2_context initialization is complete */ | ||||
| 	void (*setup_func)(struct pvr2_context *); | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_channel { | ||||
| 	struct pvr2_context *mc_head; | ||||
| 	struct pvr2_channel *mc_next; | ||||
| 	struct pvr2_channel *mc_prev; | ||||
| 	struct pvr2_context_stream *stream; | ||||
| 	struct pvr2_hdw *hdw; | ||||
| 	unsigned int input_mask; | ||||
| 	void (*check_func)(struct pvr2_channel *); | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_context *pvr2_context_create(struct usb_interface *intf, | ||||
| 					 const struct usb_device_id *devid, | ||||
| 					 void (*setup_func)(struct pvr2_context *)); | ||||
| void pvr2_context_disconnect(struct pvr2_context *); | ||||
| 
 | ||||
| void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *); | ||||
| void pvr2_channel_done(struct pvr2_channel *); | ||||
| int pvr2_channel_limit_inputs(struct pvr2_channel *,unsigned int); | ||||
| unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *); | ||||
| int pvr2_channel_claim_stream(struct pvr2_channel *, | ||||
| 			      struct pvr2_context_stream *); | ||||
| struct pvr2_ioread *pvr2_channel_create_mpeg_stream( | ||||
| 	struct pvr2_context_stream *); | ||||
| 
 | ||||
| int pvr2_context_global_init(void); | ||||
| void pvr2_context_global_done(void); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_CONTEXT_H */ | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										95
									
								
								drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,95 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|    This source file is specifically designed to interface with the | ||||
|    v4l-dvb cs53l32a module. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #include "pvrusb2-cs53l32a.h" | ||||
| 
 | ||||
| 
 | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include <linux/videodev2.h> | ||||
| #include <media/v4l2-common.h> | ||||
| #include <linux/errno.h> | ||||
| 
 | ||||
| struct routing_scheme { | ||||
| 	const int *def; | ||||
| 	unsigned int cnt; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static const int routing_scheme1[] = { | ||||
| 	[PVR2_CVAL_INPUT_TV] = 2,  /* 1 or 2 seems to work here */ | ||||
| 	[PVR2_CVAL_INPUT_RADIO] = 2, | ||||
| 	[PVR2_CVAL_INPUT_COMPOSITE] = 0, | ||||
| 	[PVR2_CVAL_INPUT_SVIDEO] =  0, | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme routing_def1 = { | ||||
| 	.def = routing_scheme1, | ||||
| 	.cnt = ARRAY_SIZE(routing_scheme1), | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme *routing_schemes[] = { | ||||
| 	[PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) | ||||
| { | ||||
| 	if (hdw->input_dirty || hdw->force_dirty) { | ||||
| 		const struct routing_scheme *sp; | ||||
| 		unsigned int sid = hdw->hdw_desc->signal_routing_scheme; | ||||
| 		u32 input; | ||||
| 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", | ||||
| 			   hdw->input_val); | ||||
| 		sp = (sid < ARRAY_SIZE(routing_schemes)) ? | ||||
| 			routing_schemes[sid] : NULL; | ||||
| 		if ((sp == NULL) || | ||||
| 		    (hdw->input_val < 0) || | ||||
| 		    (hdw->input_val >= sp->cnt)) { | ||||
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 				   "*** WARNING *** subdev v4l2 set_input:" | ||||
| 				   " Invalid routing scheme (%u)" | ||||
| 				   " and/or input (%d)", | ||||
| 				   sid, hdw->input_val); | ||||
| 			return; | ||||
| 		} | ||||
| 		input = sp->def[hdw->input_val]; | ||||
| 		sd->ops->audio->s_routing(sd, input, 0, 0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										48
									
								
								drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __PVRUSB2_CS53L32A_H | ||||
| #define __PVRUSB2_CS53L32A_H | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|    This module connects the pvrusb2 driver to the I2C chip level | ||||
|    driver which handles device video processing.  This interface is | ||||
|    used internally by the driver; higher level code should only | ||||
|    interact through the interface provided by pvrusb2-hdw.h. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_AUDIO_CS53L32A_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										609
									
								
								drivers/media/usb/pvrusb2/pvrusb2-ctrl.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										609
									
								
								drivers/media/usb/pvrusb2/pvrusb2-ctrl.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,609 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "pvrusb2-ctrl.h" | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include <linux/errno.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/mutex.h> | ||||
| 
 | ||||
| 
 | ||||
| static int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val) | ||||
| { | ||||
| 	if (cptr->info->check_value) { | ||||
| 		if (!cptr->info->check_value(cptr,val)) return -ERANGE; | ||||
| 	} else if (cptr->info->type == pvr2_ctl_enum) { | ||||
| 		if (val < 0) return -ERANGE; | ||||
| 		if (val >= cptr->info->def.type_enum.count) return -ERANGE; | ||||
| 	} else { | ||||
| 		int lim; | ||||
| 		lim = cptr->info->def.type_int.min_value; | ||||
| 		if (cptr->info->get_min_value) { | ||||
| 			cptr->info->get_min_value(cptr,&lim); | ||||
| 		} | ||||
| 		if (val < lim) return -ERANGE; | ||||
| 		lim = cptr->info->def.type_int.max_value; | ||||
| 		if (cptr->info->get_max_value) { | ||||
| 			cptr->info->get_max_value(cptr,&lim); | ||||
| 		} | ||||
| 		if (val > lim) return -ERANGE; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Set the given control. */ | ||||
| int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val) | ||||
| { | ||||
| 	return pvr2_ctrl_set_mask_value(cptr,~0,val); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Set/clear specific bits of the given control. */ | ||||
| int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	if (!cptr) return -EINVAL; | ||||
| 	LOCK_TAKE(cptr->hdw->big_lock); do { | ||||
| 		if (cptr->info->set_value) { | ||||
| 			if (cptr->info->type == pvr2_ctl_bitmask) { | ||||
| 				mask &= cptr->info->def.type_bitmask.valid_bits; | ||||
| 			} else if ((cptr->info->type == pvr2_ctl_int)|| | ||||
| 				   (cptr->info->type == pvr2_ctl_enum)) { | ||||
| 				ret = pvr2_ctrl_range_check(cptr,val); | ||||
| 				if (ret < 0) break; | ||||
| 			} else if (cptr->info->type != pvr2_ctl_bool) { | ||||
| 				break; | ||||
| 			} | ||||
| 			ret = cptr->info->set_value(cptr,mask,val); | ||||
| 		} else { | ||||
| 			ret = -EPERM; | ||||
| 		} | ||||
| 	} while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Get the current value of the given control. */ | ||||
| int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	if (!cptr) return -EINVAL; | ||||
| 	LOCK_TAKE(cptr->hdw->big_lock); do { | ||||
| 		ret = cptr->info->get_value(cptr,valptr); | ||||
| 	} while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Retrieve control's type */ | ||||
| enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	if (!cptr) return pvr2_ctl_int; | ||||
| 	return cptr->info->type; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Retrieve control's maximum value (int type) */ | ||||
| int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	if (!cptr) return 0; | ||||
| 	LOCK_TAKE(cptr->hdw->big_lock); do { | ||||
| 		if (cptr->info->get_max_value) { | ||||
| 			cptr->info->get_max_value(cptr,&ret); | ||||
| 		} else if (cptr->info->type == pvr2_ctl_int) { | ||||
| 			ret = cptr->info->def.type_int.max_value; | ||||
| 		} | ||||
| 	} while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Retrieve control's minimum value (int type) */ | ||||
| int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	if (!cptr) return 0; | ||||
| 	LOCK_TAKE(cptr->hdw->big_lock); do { | ||||
| 		if (cptr->info->get_min_value) { | ||||
| 			cptr->info->get_min_value(cptr,&ret); | ||||
| 		} else if (cptr->info->type == pvr2_ctl_int) { | ||||
| 			ret = cptr->info->def.type_int.min_value; | ||||
| 		} | ||||
| 	} while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Retrieve control's default value (any type) */ | ||||
| int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr, int *valptr) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	if (!cptr) return -EINVAL; | ||||
| 	LOCK_TAKE(cptr->hdw->big_lock); do { | ||||
| 		if (cptr->info->get_def_value) { | ||||
| 			ret = cptr->info->get_def_value(cptr, valptr); | ||||
| 		} else { | ||||
| 			*valptr = cptr->info->default_value; | ||||
| 		} | ||||
| 	} while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Retrieve control's enumeration count (enum only) */ | ||||
| int pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	if (!cptr) return 0; | ||||
| 	LOCK_TAKE(cptr->hdw->big_lock); do { | ||||
| 		if (cptr->info->type == pvr2_ctl_enum) { | ||||
| 			ret = cptr->info->def.type_enum.count; | ||||
| 		} | ||||
| 	} while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Retrieve control's valid mask bits (bit mask only) */ | ||||
| int pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	if (!cptr) return 0; | ||||
| 	LOCK_TAKE(cptr->hdw->big_lock); do { | ||||
| 		if (cptr->info->type == pvr2_ctl_bitmask) { | ||||
| 			ret = cptr->info->def.type_bitmask.valid_bits; | ||||
| 		} | ||||
| 	} while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Retrieve the control's name */ | ||||
| const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	if (!cptr) return NULL; | ||||
| 	return cptr->info->name; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Retrieve the control's desc */ | ||||
| const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	if (!cptr) return NULL; | ||||
| 	return cptr->info->desc; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Retrieve a control enumeration or bit mask value */ | ||||
| int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val, | ||||
| 			  char *bptr,unsigned int bmax, | ||||
| 			  unsigned int *blen) | ||||
| { | ||||
| 	int ret = -EINVAL; | ||||
| 	if (!cptr) return 0; | ||||
| 	*blen = 0; | ||||
| 	LOCK_TAKE(cptr->hdw->big_lock); do { | ||||
| 		if (cptr->info->type == pvr2_ctl_enum) { | ||||
| 			const char * const *names; | ||||
| 			names = cptr->info->def.type_enum.value_names; | ||||
| 			if (pvr2_ctrl_range_check(cptr,val) == 0) { | ||||
| 				if (names[val]) { | ||||
| 					*blen = scnprintf( | ||||
| 						bptr,bmax,"%s", | ||||
| 						names[val]); | ||||
| 				} else { | ||||
| 					*blen = 0; | ||||
| 				} | ||||
| 				ret = 0; | ||||
| 			} | ||||
| 		} else if (cptr->info->type == pvr2_ctl_bitmask) { | ||||
| 			const char **names; | ||||
| 			unsigned int idx; | ||||
| 			int msk; | ||||
| 			names = cptr->info->def.type_bitmask.bit_names; | ||||
| 			val &= cptr->info->def.type_bitmask.valid_bits; | ||||
| 			for (idx = 0, msk = 1; val; idx++, msk <<= 1) { | ||||
| 				if (val & msk) { | ||||
| 					*blen = scnprintf(bptr,bmax,"%s", | ||||
| 							  names[idx]); | ||||
| 					ret = 0; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Return V4L ID for this control or zero if none */ | ||||
| int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	if (!cptr) return 0; | ||||
| 	return cptr->info->v4l_id; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	unsigned int flags = 0; | ||||
| 
 | ||||
| 	if (cptr->info->get_v4lflags) { | ||||
| 		flags = cptr->info->get_v4lflags(cptr); | ||||
| 	} | ||||
| 
 | ||||
| 	if (cptr->info->set_value) { | ||||
| 		flags &= ~V4L2_CTRL_FLAG_READ_ONLY; | ||||
| 	} else { | ||||
| 		flags |= V4L2_CTRL_FLAG_READ_ONLY; | ||||
| 	} | ||||
| 
 | ||||
| 	return flags; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Return true if control is writable */ | ||||
| int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	if (!cptr) return 0; | ||||
| 	return cptr->info->set_value != NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Return true if control has custom symbolic representation */ | ||||
| int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr) | ||||
| { | ||||
| 	if (!cptr) return 0; | ||||
| 	if (!cptr->info->val_to_sym) return 0; | ||||
| 	if (!cptr->info->sym_to_val) return 0; | ||||
| 	return !0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Convert a given mask/val to a custom symbolic value */ | ||||
| int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr, | ||||
| 				  int mask,int val, | ||||
| 				  char *buf,unsigned int maxlen, | ||||
| 				  unsigned int *len) | ||||
| { | ||||
| 	if (!cptr) return -EINVAL; | ||||
| 	if (!cptr->info->val_to_sym) return -EINVAL; | ||||
| 	return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Convert a symbolic value to a mask/value pair */ | ||||
| int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr, | ||||
| 				  const char *buf,unsigned int len, | ||||
| 				  int *maskptr,int *valptr) | ||||
| { | ||||
| 	if (!cptr) return -EINVAL; | ||||
| 	if (!cptr->info->sym_to_val) return -EINVAL; | ||||
| 	return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static unsigned int gen_bitmask_string(int msk,int val,int msk_only, | ||||
| 				       const char **names, | ||||
| 				       char *ptr,unsigned int len) | ||||
| { | ||||
| 	unsigned int idx; | ||||
| 	long sm,um; | ||||
| 	int spcFl; | ||||
| 	unsigned int uc,cnt; | ||||
| 	const char *idStr; | ||||
| 
 | ||||
| 	spcFl = 0; | ||||
| 	uc = 0; | ||||
| 	um = 0; | ||||
| 	for (idx = 0, sm = 1; msk; idx++, sm <<= 1) { | ||||
| 		if (sm & msk) { | ||||
| 			msk &= ~sm; | ||||
| 			idStr = names[idx]; | ||||
| 			if (idStr) { | ||||
| 				cnt = scnprintf(ptr,len,"%s%s%s", | ||||
| 						(spcFl ? " " : ""), | ||||
| 						(msk_only ? "" : | ||||
| 						 ((val & sm) ? "+" : "-")), | ||||
| 						idStr); | ||||
| 				ptr += cnt; len -= cnt; uc += cnt; | ||||
| 				spcFl = !0; | ||||
| 			} else { | ||||
| 				um |= sm; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if (um) { | ||||
| 		if (msk_only) { | ||||
| 			cnt = scnprintf(ptr,len,"%s0x%lx", | ||||
| 					(spcFl ? " " : ""), | ||||
| 					um); | ||||
| 			ptr += cnt; len -= cnt; uc += cnt; | ||||
| 			spcFl = !0; | ||||
| 		} else if (um & val) { | ||||
| 			cnt = scnprintf(ptr,len,"%s+0x%lx", | ||||
| 					(spcFl ? " " : ""), | ||||
| 					um & val); | ||||
| 			ptr += cnt; len -= cnt; uc += cnt; | ||||
| 			spcFl = !0; | ||||
| 		} else if (um & ~val) { | ||||
| 			cnt = scnprintf(ptr,len,"%s+0x%lx", | ||||
| 					(spcFl ? " " : ""), | ||||
| 					um & ~val); | ||||
| 			ptr += cnt; len -= cnt; uc += cnt; | ||||
| 			spcFl = !0; | ||||
| 		} | ||||
| 	} | ||||
| 	return uc; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static const char *boolNames[] = { | ||||
| 	"false", | ||||
| 	"true", | ||||
| 	"no", | ||||
| 	"yes", | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static int parse_token(const char *ptr,unsigned int len, | ||||
| 		       int *valptr, | ||||
| 		       const char * const *names, unsigned int namecnt) | ||||
| { | ||||
| 	char buf[33]; | ||||
| 	unsigned int slen; | ||||
| 	unsigned int idx; | ||||
| 	int negfl; | ||||
| 	char *p2; | ||||
| 	*valptr = 0; | ||||
| 	if (!names) namecnt = 0; | ||||
| 	for (idx = 0; idx < namecnt; idx++) { | ||||
| 		if (!names[idx]) continue; | ||||
| 		slen = strlen(names[idx]); | ||||
| 		if (slen != len) continue; | ||||
| 		if (memcmp(names[idx],ptr,slen)) continue; | ||||
| 		*valptr = idx; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	negfl = 0; | ||||
| 	if ((*ptr == '-') || (*ptr == '+')) { | ||||
| 		negfl = (*ptr == '-'); | ||||
| 		ptr++; len--; | ||||
| 	} | ||||
| 	if (len >= sizeof(buf)) return -EINVAL; | ||||
| 	memcpy(buf,ptr,len); | ||||
| 	buf[len] = 0; | ||||
| 	*valptr = simple_strtol(buf,&p2,0); | ||||
| 	if (negfl) *valptr = -(*valptr); | ||||
| 	if (*p2) return -EINVAL; | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int parse_mtoken(const char *ptr,unsigned int len, | ||||
| 			int *valptr, | ||||
| 			const char **names,int valid_bits) | ||||
| { | ||||
| 	char buf[33]; | ||||
| 	unsigned int slen; | ||||
| 	unsigned int idx; | ||||
| 	char *p2; | ||||
| 	int msk; | ||||
| 	*valptr = 0; | ||||
| 	for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) { | ||||
| 		if (!(msk & valid_bits)) continue; | ||||
| 		valid_bits &= ~msk; | ||||
| 		if (!names[idx]) continue; | ||||
| 		slen = strlen(names[idx]); | ||||
| 		if (slen != len) continue; | ||||
| 		if (memcmp(names[idx],ptr,slen)) continue; | ||||
| 		*valptr = msk; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	if (len >= sizeof(buf)) return -EINVAL; | ||||
| 	memcpy(buf,ptr,len); | ||||
| 	buf[len] = 0; | ||||
| 	*valptr = simple_strtol(buf,&p2,0); | ||||
| 	if (*p2) return -EINVAL; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int parse_tlist(const char *ptr,unsigned int len, | ||||
| 		       int *maskptr,int *valptr, | ||||
| 		       const char **names,int valid_bits) | ||||
| { | ||||
| 	unsigned int cnt; | ||||
| 	int mask,val,kv,mode,ret; | ||||
| 	mask = 0; | ||||
| 	val = 0; | ||||
| 	ret = 0; | ||||
| 	while (len) { | ||||
| 		cnt = 0; | ||||
| 		while ((cnt < len) && | ||||
| 		       ((ptr[cnt] <= 32) || | ||||
| 			(ptr[cnt] >= 127))) cnt++; | ||||
| 		ptr += cnt; | ||||
| 		len -= cnt; | ||||
| 		mode = 0; | ||||
| 		if ((*ptr == '-') || (*ptr == '+')) { | ||||
| 			mode = (*ptr == '-') ? -1 : 1; | ||||
| 			ptr++; | ||||
| 			len--; | ||||
| 		} | ||||
| 		cnt = 0; | ||||
| 		while (cnt < len) { | ||||
| 			if (ptr[cnt] <= 32) break; | ||||
| 			if (ptr[cnt] >= 127) break; | ||||
| 			cnt++; | ||||
| 		} | ||||
| 		if (!cnt) break; | ||||
| 		if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) { | ||||
| 			ret = -EINVAL; | ||||
| 			break; | ||||
| 		} | ||||
| 		ptr += cnt; | ||||
| 		len -= cnt; | ||||
| 		switch (mode) { | ||||
| 		case 0: | ||||
| 			mask = valid_bits; | ||||
| 			val |= kv; | ||||
| 			break; | ||||
| 		case -1: | ||||
| 			mask |= kv; | ||||
| 			val &= ~kv; | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			mask |= kv; | ||||
| 			val |= kv; | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	*maskptr = mask; | ||||
| 	*valptr = val; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Convert a symbolic value to a mask/value pair */ | ||||
| int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr, | ||||
| 			   const char *ptr,unsigned int len, | ||||
| 			   int *maskptr,int *valptr) | ||||
| { | ||||
| 	int ret = -EINVAL; | ||||
| 	unsigned int cnt; | ||||
| 
 | ||||
| 	*maskptr = 0; | ||||
| 	*valptr = 0; | ||||
| 
 | ||||
| 	cnt = 0; | ||||
| 	while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++; | ||||
| 	len -= cnt; ptr += cnt; | ||||
| 	cnt = 0; | ||||
| 	while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) || | ||||
| 			       (ptr[len-(cnt+1)] >= 127))) cnt++; | ||||
| 	len -= cnt; | ||||
| 
 | ||||
| 	if (!len) return -EINVAL; | ||||
| 
 | ||||
| 	LOCK_TAKE(cptr->hdw->big_lock); do { | ||||
| 		if (cptr->info->type == pvr2_ctl_int) { | ||||
| 			ret = parse_token(ptr,len,valptr,NULL,0); | ||||
| 			if (ret >= 0) { | ||||
| 				ret = pvr2_ctrl_range_check(cptr,*valptr); | ||||
| 			} | ||||
| 			*maskptr = ~0; | ||||
| 		} else if (cptr->info->type == pvr2_ctl_bool) { | ||||
| 			ret = parse_token(ptr,len,valptr,boolNames, | ||||
| 					  ARRAY_SIZE(boolNames)); | ||||
| 			if (ret == 1) { | ||||
| 				*valptr = *valptr ? !0 : 0; | ||||
| 			} else if (ret == 0) { | ||||
| 				*valptr = (*valptr & 1) ? !0 : 0; | ||||
| 			} | ||||
| 			*maskptr = 1; | ||||
| 		} else if (cptr->info->type == pvr2_ctl_enum) { | ||||
| 			ret = parse_token( | ||||
| 				ptr,len,valptr, | ||||
| 				cptr->info->def.type_enum.value_names, | ||||
| 				cptr->info->def.type_enum.count); | ||||
| 			if (ret >= 0) { | ||||
| 				ret = pvr2_ctrl_range_check(cptr,*valptr); | ||||
| 			} | ||||
| 			*maskptr = ~0; | ||||
| 		} else if (cptr->info->type == pvr2_ctl_bitmask) { | ||||
| 			ret = parse_tlist( | ||||
| 				ptr,len,maskptr,valptr, | ||||
| 				cptr->info->def.type_bitmask.bit_names, | ||||
| 				cptr->info->def.type_bitmask.valid_bits); | ||||
| 		} | ||||
| 	} while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Convert a given mask/val to a symbolic value */ | ||||
| int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr, | ||||
| 				    int mask,int val, | ||||
| 				    char *buf,unsigned int maxlen, | ||||
| 				    unsigned int *len) | ||||
| { | ||||
| 	int ret = -EINVAL; | ||||
| 
 | ||||
| 	*len = 0; | ||||
| 	if (cptr->info->type == pvr2_ctl_int) { | ||||
| 		*len = scnprintf(buf,maxlen,"%d",val); | ||||
| 		ret = 0; | ||||
| 	} else if (cptr->info->type == pvr2_ctl_bool) { | ||||
| 		*len = scnprintf(buf,maxlen,"%s",val ? "true" : "false"); | ||||
| 		ret = 0; | ||||
| 	} else if (cptr->info->type == pvr2_ctl_enum) { | ||||
| 		const char * const *names; | ||||
| 		names = cptr->info->def.type_enum.value_names; | ||||
| 		if ((val >= 0) && | ||||
| 		    (val < cptr->info->def.type_enum.count)) { | ||||
| 			if (names[val]) { | ||||
| 				*len = scnprintf( | ||||
| 					buf,maxlen,"%s", | ||||
| 					names[val]); | ||||
| 			} else { | ||||
| 				*len = 0; | ||||
| 			} | ||||
| 			ret = 0; | ||||
| 		} | ||||
| 	} else if (cptr->info->type == pvr2_ctl_bitmask) { | ||||
| 		*len = gen_bitmask_string( | ||||
| 			val & mask & cptr->info->def.type_bitmask.valid_bits, | ||||
| 			~0,!0, | ||||
| 			cptr->info->def.type_bitmask.bit_names, | ||||
| 			buf,maxlen); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Convert a given mask/val to a symbolic value */ | ||||
| int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr, | ||||
| 			   int mask,int val, | ||||
| 			   char *buf,unsigned int maxlen, | ||||
| 			   unsigned int *len) | ||||
| { | ||||
| 	int ret; | ||||
| 	LOCK_TAKE(cptr->hdw->big_lock); do { | ||||
| 		ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val, | ||||
| 						      buf,maxlen,len); | ||||
| 	} while(0); LOCK_GIVE(cptr->hdw->big_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										122
									
								
								drivers/media/usb/pvrusb2/pvrusb2-ctrl.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								drivers/media/usb/pvrusb2/pvrusb2-ctrl.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,122 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_CTRL_H | ||||
| #define __PVRUSB2_CTRL_H | ||||
| 
 | ||||
| struct pvr2_ctrl; | ||||
| 
 | ||||
| enum pvr2_ctl_type { | ||||
| 	pvr2_ctl_int = 0, | ||||
| 	pvr2_ctl_enum = 1, | ||||
| 	pvr2_ctl_bitmask = 2, | ||||
| 	pvr2_ctl_bool = 3, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* Set the given control. */ | ||||
| int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val); | ||||
| 
 | ||||
| /* Set/clear specific bits of the given control. */ | ||||
| int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *,int mask,int val); | ||||
| 
 | ||||
| /* Get the current value of the given control. */ | ||||
| int pvr2_ctrl_get_value(struct pvr2_ctrl *,int *valptr); | ||||
| 
 | ||||
| /* Retrieve control's type */ | ||||
| enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Retrieve control's maximum value (int type) */ | ||||
| int pvr2_ctrl_get_max(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Retrieve control's minimum value (int type) */ | ||||
| int pvr2_ctrl_get_min(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Retrieve control's default value (any type) */ | ||||
| int pvr2_ctrl_get_def(struct pvr2_ctrl *, int *valptr); | ||||
| 
 | ||||
| /* Retrieve control's enumeration count (enum only) */ | ||||
| int pvr2_ctrl_get_cnt(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Retrieve control's valid mask bits (bit mask only) */ | ||||
| int pvr2_ctrl_get_mask(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Retrieve the control's name */ | ||||
| const char *pvr2_ctrl_get_name(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Retrieve the control's desc */ | ||||
| const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Retrieve a control enumeration or bit mask value */ | ||||
| int pvr2_ctrl_get_valname(struct pvr2_ctrl *,int,char *,unsigned int, | ||||
| 			  unsigned int *); | ||||
| 
 | ||||
| /* Return true if control is writable */ | ||||
| int pvr2_ctrl_is_writable(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Return V4L flags value for control (or zero if there is no v4l control
 | ||||
|    actually under this control) */ | ||||
| unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Return V4L ID for this control or zero if none */ | ||||
| int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Return true if control has custom symbolic representation */ | ||||
| int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* Convert a given mask/val to a custom symbolic value */ | ||||
| int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *, | ||||
| 				  int mask,int val, | ||||
| 				  char *buf,unsigned int maxlen, | ||||
| 				  unsigned int *len); | ||||
| 
 | ||||
| /* Convert a symbolic value to a mask/value pair */ | ||||
| int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *, | ||||
| 				  const char *buf,unsigned int len, | ||||
| 				  int *maskptr,int *valptr); | ||||
| 
 | ||||
| /* Convert a given mask/val to a symbolic value */ | ||||
| int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *, | ||||
| 			   int mask,int val, | ||||
| 			   char *buf,unsigned int maxlen, | ||||
| 			   unsigned int *len); | ||||
| 
 | ||||
| /* Convert a symbolic value to a mask/value pair */ | ||||
| int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *, | ||||
| 			   const char *buf,unsigned int len, | ||||
| 			   int *maskptr,int *valptr); | ||||
| 
 | ||||
| /* Convert a given mask/val to a symbolic value - must already be
 | ||||
|    inside of critical region. */ | ||||
| int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *, | ||||
| 			   int mask,int val, | ||||
| 			   char *buf,unsigned int maxlen, | ||||
| 			   unsigned int *len); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_CTRL_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										166
									
								
								drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,166 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|    This source file is specifically designed to interface with the | ||||
|    cx2584x, in kernels 2.6.16 or newer. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #include "pvrusb2-cx2584x-v4l.h" | ||||
| #include "pvrusb2-video-v4l.h" | ||||
| 
 | ||||
| 
 | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include <media/cx25840.h> | ||||
| #include <linux/videodev2.h> | ||||
| #include <media/v4l2-common.h> | ||||
| #include <linux/errno.h> | ||||
| 
 | ||||
| 
 | ||||
| struct routing_scheme_item { | ||||
| 	int vid; | ||||
| 	int aud; | ||||
| }; | ||||
| 
 | ||||
| struct routing_scheme { | ||||
| 	const struct routing_scheme_item *def; | ||||
| 	unsigned int cnt; | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme_item routing_scheme0[] = { | ||||
| 	[PVR2_CVAL_INPUT_TV] = { | ||||
| 		.vid = CX25840_COMPOSITE7, | ||||
| 		.aud = CX25840_AUDIO8, | ||||
| 	}, | ||||
| 	[PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */ | ||||
| 		.vid = CX25840_COMPOSITE3, | ||||
| 		.aud = CX25840_AUDIO_SERIAL, | ||||
| 	}, | ||||
| 	[PVR2_CVAL_INPUT_COMPOSITE] = { | ||||
| 		.vid = CX25840_COMPOSITE3, | ||||
| 		.aud = CX25840_AUDIO_SERIAL, | ||||
| 	}, | ||||
| 	[PVR2_CVAL_INPUT_SVIDEO] = { | ||||
| 		.vid = CX25840_SVIDEO1, | ||||
| 		.aud = CX25840_AUDIO_SERIAL, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme routing_def0 = { | ||||
| 	.def = routing_scheme0, | ||||
| 	.cnt = ARRAY_SIZE(routing_scheme0), | ||||
| }; | ||||
| 
 | ||||
| /* Specific to gotview device */ | ||||
| static const struct routing_scheme_item routing_schemegv[] = { | ||||
| 	[PVR2_CVAL_INPUT_TV] = { | ||||
| 		.vid = CX25840_COMPOSITE2, | ||||
| 		.aud = CX25840_AUDIO5, | ||||
| 	}, | ||||
| 	[PVR2_CVAL_INPUT_RADIO] = { | ||||
| 		/* line-in is used for radio and composite.  A GPIO is
 | ||||
| 		   used to switch between the two choices. */ | ||||
| 		.vid = CX25840_COMPOSITE1, | ||||
| 		.aud = CX25840_AUDIO_SERIAL, | ||||
| 	}, | ||||
| 	[PVR2_CVAL_INPUT_COMPOSITE] = { | ||||
| 		.vid = CX25840_COMPOSITE1, | ||||
| 		.aud = CX25840_AUDIO_SERIAL, | ||||
| 	}, | ||||
| 	[PVR2_CVAL_INPUT_SVIDEO] = { | ||||
| 		.vid = (CX25840_SVIDEO_LUMA3|CX25840_SVIDEO_CHROMA4), | ||||
| 		.aud = CX25840_AUDIO_SERIAL, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme routing_defgv = { | ||||
| 	.def = routing_schemegv, | ||||
| 	.cnt = ARRAY_SIZE(routing_schemegv), | ||||
| }; | ||||
| 
 | ||||
| /* Specific to grabster av400 device */ | ||||
| static const struct routing_scheme_item routing_schemeav400[] = { | ||||
| 	[PVR2_CVAL_INPUT_COMPOSITE] = { | ||||
| 		.vid = CX25840_COMPOSITE1, | ||||
| 		.aud = CX25840_AUDIO_SERIAL, | ||||
| 	}, | ||||
| 	[PVR2_CVAL_INPUT_SVIDEO] = { | ||||
| 		.vid = (CX25840_SVIDEO_LUMA2|CX25840_SVIDEO_CHROMA4), | ||||
| 		.aud = CX25840_AUDIO_SERIAL, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme routing_defav400 = { | ||||
| 	.def = routing_schemeav400, | ||||
| 	.cnt = ARRAY_SIZE(routing_schemeav400), | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme *routing_schemes[] = { | ||||
| 	[PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, | ||||
| 	[PVR2_ROUTING_SCHEME_GOTVIEW] = &routing_defgv, | ||||
| 	[PVR2_ROUTING_SCHEME_AV400] = &routing_defav400, | ||||
| }; | ||||
| 
 | ||||
| void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) | ||||
| { | ||||
| 	pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x update..."); | ||||
| 	if (hdw->input_dirty || hdw->force_dirty) { | ||||
| 		enum cx25840_video_input vid_input; | ||||
| 		enum cx25840_audio_input aud_input; | ||||
| 		const struct routing_scheme *sp; | ||||
| 		unsigned int sid = hdw->hdw_desc->signal_routing_scheme; | ||||
| 
 | ||||
| 		sp = (sid < ARRAY_SIZE(routing_schemes)) ? | ||||
| 			routing_schemes[sid] : NULL; | ||||
| 		if ((sp == NULL) || | ||||
| 		    (hdw->input_val < 0) || | ||||
| 		    (hdw->input_val >= sp->cnt)) { | ||||
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 				   "*** WARNING *** subdev cx2584x set_input:" | ||||
| 				   " Invalid routing scheme (%u)" | ||||
| 				   " and/or input (%d)", | ||||
| 				   sid, hdw->input_val); | ||||
| 			return; | ||||
| 		} | ||||
| 		vid_input = sp->def[hdw->input_val].vid; | ||||
| 		aud_input = sp->def[hdw->input_val].aud; | ||||
| 		pvr2_trace(PVR2_TRACE_CHIPS, | ||||
| 			   "subdev cx2584x set_input vid=0x%x aud=0x%x", | ||||
| 			   vid_input, aud_input); | ||||
| 		sd->ops->video->s_routing(sd, (u32)vid_input, 0, 0); | ||||
| 		sd->ops->audio->s_routing(sd, (u32)aud_input, 0, 0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										52
									
								
								drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __PVRUSB2_CX2584X_V4L_H | ||||
| #define __PVRUSB2_CX2584X_V4L_H | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|    This module connects the pvrusb2 driver to the I2C chip level | ||||
|    driver which handles combined device audio & video processing. | ||||
|    This interface is used internally by the driver; higher level code | ||||
|    should only interact through the interface provided by | ||||
|    pvrusb2-hdw.h. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| 
 | ||||
| void pvr2_cx25840_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); | ||||
| 
 | ||||
| 
 | ||||
| #endif /* __PVRUSB2_CX2584X_V4L_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										69
									
								
								drivers/media/usb/pvrusb2/pvrusb2-debug.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								drivers/media/usb/pvrusb2/pvrusb2-debug.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | |||
| /*
 | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_DEBUG_H | ||||
| #define __PVRUSB2_DEBUG_H | ||||
| 
 | ||||
| extern int pvrusb2_debug; | ||||
| 
 | ||||
| #define pvr2_trace(msk, fmt, arg...) do {if(msk & pvrusb2_debug) printk(KERN_INFO "pvrusb2: " fmt "\n", ##arg); } while (0) | ||||
| 
 | ||||
| /* These are listed in *rough* order of decreasing usefulness and
 | ||||
|    increasing noise level. */ | ||||
| #define PVR2_TRACE_INFO       (1 <<  0) /* Normal messages */ | ||||
| #define PVR2_TRACE_ERROR_LEGS (1 <<  1) /* error messages */ | ||||
| #define PVR2_TRACE_TOLERANCE  (1 <<  2) /* track tolerance-affected errors */ | ||||
| #define PVR2_TRACE_TRAP       (1 <<  3) /* Trap & report app misbehavior */ | ||||
| #define PVR2_TRACE_STD        (1 <<  4) /* Log video standard stuff */ | ||||
| #define PVR2_TRACE_INIT       (1 <<  5) /* misc initialization steps */ | ||||
| #define PVR2_TRACE_START_STOP (1 <<  6) /* Streaming start / stop */ | ||||
| #define PVR2_TRACE_CTL        (1 <<  7) /* commit of control changes */ | ||||
| #define PVR2_TRACE_STATE      (1 <<  8) /* Device state changes */ | ||||
| #define PVR2_TRACE_STBITS     (1 <<  9) /* Individual bit state changes */ | ||||
| #define PVR2_TRACE_EEPROM     (1 << 10) /* eeprom parsing / report */ | ||||
| #define PVR2_TRACE_STRUCT     (1 << 11) /* internal struct creation */ | ||||
| #define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */ | ||||
| #define PVR2_TRACE_CTXT       (1 << 13) /* Main context tracking */ | ||||
| #define PVR2_TRACE_SYSFS      (1 << 14) /* Sysfs driven I/O */ | ||||
| #define PVR2_TRACE_FIRMWARE   (1 << 15) /* firmware upload actions */ | ||||
| #define PVR2_TRACE_CHIPS      (1 << 16) /* chip broadcast operation */ | ||||
| #define PVR2_TRACE_I2C        (1 << 17) /* I2C related stuff */ | ||||
| #define PVR2_TRACE_I2C_CMD    (1 << 18) /* Software commands to I2C modules */ | ||||
| #define PVR2_TRACE_I2C_CORE   (1 << 19) /* I2C core debugging */ | ||||
| #define PVR2_TRACE_I2C_TRAF   (1 << 20) /* I2C traffic through the adapter */ | ||||
| #define PVR2_TRACE_V4LIOCTL   (1 << 21) /* v4l ioctl details */ | ||||
| #define PVR2_TRACE_ENCODER    (1 << 22) /* mpeg2 encoder operation */ | ||||
| #define PVR2_TRACE_BUF_POOL   (1 << 23) /* Track buffer pool management */ | ||||
| #define PVR2_TRACE_BUF_FLOW   (1 << 24) /* Track buffer flow in system */ | ||||
| #define PVR2_TRACE_DATA_FLOW  (1 << 25) /* Track data flow */ | ||||
| #define PVR2_TRACE_DEBUGIFC   (1 << 26) /* Debug interface actions */ | ||||
| #define PVR2_TRACE_GPIO       (1 << 27) /* GPIO state bit changes */ | ||||
| #define PVR2_TRACE_DVB_FEED   (1 << 28) /* DVB transport feed debug */ | ||||
| 
 | ||||
| 
 | ||||
| #endif /* __PVRUSB2_HDW_INTERNAL_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										335
									
								
								drivers/media/usb/pvrusb2/pvrusb2-debugifc.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								drivers/media/usb/pvrusb2/pvrusb2-debugifc.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,335 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/string.h> | ||||
| #include "pvrusb2-debugifc.h" | ||||
| #include "pvrusb2-hdw.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| 
 | ||||
| struct debugifc_mask_item { | ||||
| 	const char *name; | ||||
| 	unsigned long msk; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static unsigned int debugifc_count_whitespace(const char *buf, | ||||
| 					      unsigned int count) | ||||
| { | ||||
| 	unsigned int scnt; | ||||
| 	char ch; | ||||
| 
 | ||||
| 	for (scnt = 0; scnt < count; scnt++) { | ||||
| 		ch = buf[scnt]; | ||||
| 		if (ch == ' ') continue; | ||||
| 		if (ch == '\t') continue; | ||||
| 		if (ch == '\n') continue; | ||||
| 		break; | ||||
| 	} | ||||
| 	return scnt; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static unsigned int debugifc_count_nonwhitespace(const char *buf, | ||||
| 						 unsigned int count) | ||||
| { | ||||
| 	unsigned int scnt; | ||||
| 	char ch; | ||||
| 
 | ||||
| 	for (scnt = 0; scnt < count; scnt++) { | ||||
| 		ch = buf[scnt]; | ||||
| 		if (ch == ' ') break; | ||||
| 		if (ch == '\t') break; | ||||
| 		if (ch == '\n') break; | ||||
| 	} | ||||
| 	return scnt; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static unsigned int debugifc_isolate_word(const char *buf,unsigned int count, | ||||
| 					  const char **wstrPtr, | ||||
| 					  unsigned int *wlenPtr) | ||||
| { | ||||
| 	const char *wptr; | ||||
| 	unsigned int consume_cnt = 0; | ||||
| 	unsigned int wlen; | ||||
| 	unsigned int scnt; | ||||
| 
 | ||||
| 	wptr = NULL; | ||||
| 	wlen = 0; | ||||
| 	scnt = debugifc_count_whitespace(buf,count); | ||||
| 	consume_cnt += scnt; count -= scnt; buf += scnt; | ||||
| 	if (!count) goto done; | ||||
| 
 | ||||
| 	scnt = debugifc_count_nonwhitespace(buf,count); | ||||
| 	if (!scnt) goto done; | ||||
| 	wptr = buf; | ||||
| 	wlen = scnt; | ||||
| 	consume_cnt += scnt; count -= scnt; buf += scnt; | ||||
| 
 | ||||
|  done: | ||||
| 	*wstrPtr = wptr; | ||||
| 	*wlenPtr = wlen; | ||||
| 	return consume_cnt; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int debugifc_parse_unsigned_number(const char *buf,unsigned int count, | ||||
| 					  u32 *num_ptr) | ||||
| { | ||||
| 	u32 result = 0; | ||||
| 	int radix = 10; | ||||
| 	if ((count >= 2) && (buf[0] == '0') && | ||||
| 	    ((buf[1] == 'x') || (buf[1] == 'X'))) { | ||||
| 		radix = 16; | ||||
| 		count -= 2; | ||||
| 		buf += 2; | ||||
| 	} else if ((count >= 1) && (buf[0] == '0')) { | ||||
| 		radix = 8; | ||||
| 	} | ||||
| 
 | ||||
| 	while (count--) { | ||||
| 		int val = hex_to_bin(*buf++); | ||||
| 		if (val < 0 || val >= radix) | ||||
| 			return -EINVAL; | ||||
| 		result *= radix; | ||||
| 		result += val; | ||||
| 	} | ||||
| 	*num_ptr = result; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int debugifc_match_keyword(const char *buf,unsigned int count, | ||||
| 				  const char *keyword) | ||||
| { | ||||
| 	unsigned int kl; | ||||
| 	if (!keyword) return 0; | ||||
| 	kl = strlen(keyword); | ||||
| 	if (kl != count) return 0; | ||||
| 	return !memcmp(buf,keyword,kl); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) | ||||
| { | ||||
| 	int bcnt = 0; | ||||
| 	int ccnt; | ||||
| 	ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n", | ||||
| 			 pvr2_hdw_get_desc(hdw)); | ||||
| 	bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||||
| 	ccnt = scnprintf(buf,acnt,"Driver state info:\n"); | ||||
| 	bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||||
| 	ccnt = pvr2_hdw_state_report(hdw,buf,acnt); | ||||
| 	bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||||
| 
 | ||||
| 	return bcnt; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, | ||||
| 			       char *buf,unsigned int acnt) | ||||
| { | ||||
| 	int bcnt = 0; | ||||
| 	int ccnt; | ||||
| 	int ret; | ||||
| 	u32 gpio_dir,gpio_in,gpio_out; | ||||
| 	struct pvr2_stream_stats stats; | ||||
| 	struct pvr2_stream *sp; | ||||
| 
 | ||||
| 	ret = pvr2_hdw_is_hsm(hdw); | ||||
| 	ccnt = scnprintf(buf,acnt,"USB link speed: %s\n", | ||||
| 			 (ret < 0 ? "FAIL" : (ret ? "high" : "full"))); | ||||
| 	bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||||
| 
 | ||||
| 	gpio_dir = 0; gpio_in = 0; gpio_out = 0; | ||||
| 	pvr2_hdw_gpio_get_dir(hdw,&gpio_dir); | ||||
| 	pvr2_hdw_gpio_get_out(hdw,&gpio_out); | ||||
| 	pvr2_hdw_gpio_get_in(hdw,&gpio_in); | ||||
| 	ccnt = scnprintf(buf,acnt,"GPIO state: dir=0x%x in=0x%x out=0x%x\n", | ||||
| 			 gpio_dir,gpio_in,gpio_out); | ||||
| 	bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||||
| 
 | ||||
| 	ccnt = scnprintf(buf,acnt,"Streaming is %s\n", | ||||
| 			 pvr2_hdw_get_streaming(hdw) ? "on" : "off"); | ||||
| 	bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||||
| 
 | ||||
| 
 | ||||
| 	sp = pvr2_hdw_get_video_stream(hdw); | ||||
| 	if (sp) { | ||||
| 		pvr2_stream_get_stats(sp, &stats, 0); | ||||
| 		ccnt = scnprintf( | ||||
| 			buf,acnt, | ||||
| 			"Bytes streamed=%u" | ||||
| 			" URBs: queued=%u idle=%u ready=%u" | ||||
| 			" processed=%u failed=%u\n", | ||||
| 			stats.bytes_processed, | ||||
| 			stats.buffers_in_queue, | ||||
| 			stats.buffers_in_idle, | ||||
| 			stats.buffers_in_ready, | ||||
| 			stats.buffers_processed, | ||||
| 			stats.buffers_failed); | ||||
| 		bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||||
| 	} | ||||
| 
 | ||||
| 	return bcnt; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, | ||||
| 				unsigned int count) | ||||
| { | ||||
| 	const char *wptr; | ||||
| 	unsigned int wlen; | ||||
| 	unsigned int scnt; | ||||
| 
 | ||||
| 	scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||||
| 	if (!scnt) return 0; | ||||
| 	count -= scnt; buf += scnt; | ||||
| 	if (!wptr) return 0; | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_DEBUGIFC,"debugifc cmd: \"%.*s\"",wlen,wptr); | ||||
| 	if (debugifc_match_keyword(wptr,wlen,"reset")) { | ||||
| 		scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||||
| 		if (!scnt) return -EINVAL; | ||||
| 		count -= scnt; buf += scnt; | ||||
| 		if (!wptr) return -EINVAL; | ||||
| 		if (debugifc_match_keyword(wptr,wlen,"cpu")) { | ||||
| 			pvr2_hdw_cpureset_assert(hdw,!0); | ||||
| 			pvr2_hdw_cpureset_assert(hdw,0); | ||||
| 			return 0; | ||||
| 		} else if (debugifc_match_keyword(wptr,wlen,"bus")) { | ||||
| 			pvr2_hdw_device_reset(hdw); | ||||
| 		} else if (debugifc_match_keyword(wptr,wlen,"soft")) { | ||||
| 			return pvr2_hdw_cmd_powerup(hdw); | ||||
| 		} else if (debugifc_match_keyword(wptr,wlen,"deep")) { | ||||
| 			return pvr2_hdw_cmd_deep_reset(hdw); | ||||
| 		} else if (debugifc_match_keyword(wptr,wlen,"firmware")) { | ||||
| 			return pvr2_upload_firmware2(hdw); | ||||
| 		} else if (debugifc_match_keyword(wptr,wlen,"decoder")) { | ||||
| 			return pvr2_hdw_cmd_decoder_reset(hdw); | ||||
| 		} else if (debugifc_match_keyword(wptr,wlen,"worker")) { | ||||
| 			return pvr2_hdw_untrip(hdw); | ||||
| 		} else if (debugifc_match_keyword(wptr,wlen,"usbstats")) { | ||||
| 			pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw), | ||||
| 					      NULL, !0); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		return -EINVAL; | ||||
| 	} else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { | ||||
| 		scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||||
| 		if (!scnt) return -EINVAL; | ||||
| 		count -= scnt; buf += scnt; | ||||
| 		if (!wptr) return -EINVAL; | ||||
| 		if (debugifc_match_keyword(wptr,wlen,"fetch")) { | ||||
| 			scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||||
| 			if (scnt && wptr) { | ||||
| 				count -= scnt; buf += scnt; | ||||
| 				if (debugifc_match_keyword(wptr, wlen, | ||||
| 							   "prom")) { | ||||
| 					pvr2_hdw_cpufw_set_enabled(hdw, 2, !0); | ||||
| 				} else if (debugifc_match_keyword(wptr, wlen, | ||||
| 								  "ram8k")) { | ||||
| 					pvr2_hdw_cpufw_set_enabled(hdw, 0, !0); | ||||
| 				} else if (debugifc_match_keyword(wptr, wlen, | ||||
| 								  "ram16k")) { | ||||
| 					pvr2_hdw_cpufw_set_enabled(hdw, 1, !0); | ||||
| 				} else { | ||||
| 					return -EINVAL; | ||||
| 				} | ||||
| 			} | ||||
| 			pvr2_hdw_cpufw_set_enabled(hdw,0,!0); | ||||
| 			return 0; | ||||
| 		} else if (debugifc_match_keyword(wptr,wlen,"done")) { | ||||
| 			pvr2_hdw_cpufw_set_enabled(hdw,0,0); | ||||
| 			return 0; | ||||
| 		} else { | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} else if (debugifc_match_keyword(wptr,wlen,"gpio")) { | ||||
| 		int dir_fl = 0; | ||||
| 		int ret; | ||||
| 		u32 msk,val; | ||||
| 		scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||||
| 		if (!scnt) return -EINVAL; | ||||
| 		count -= scnt; buf += scnt; | ||||
| 		if (!wptr) return -EINVAL; | ||||
| 		if (debugifc_match_keyword(wptr,wlen,"dir")) { | ||||
| 			dir_fl = !0; | ||||
| 		} else if (!debugifc_match_keyword(wptr,wlen,"out")) { | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||||
| 		if (!scnt) return -EINVAL; | ||||
| 		count -= scnt; buf += scnt; | ||||
| 		if (!wptr) return -EINVAL; | ||||
| 		ret = debugifc_parse_unsigned_number(wptr,wlen,&msk); | ||||
| 		if (ret) return ret; | ||||
| 		scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||||
| 		if (wptr) { | ||||
| 			ret = debugifc_parse_unsigned_number(wptr,wlen,&val); | ||||
| 			if (ret) return ret; | ||||
| 		} else { | ||||
| 			val = msk; | ||||
| 			msk = 0xffffffff; | ||||
| 		} | ||||
| 		if (dir_fl) { | ||||
| 			ret = pvr2_hdw_gpio_chg_dir(hdw,msk,val); | ||||
| 		} else { | ||||
| 			ret = pvr2_hdw_gpio_chg_out(hdw,msk,val); | ||||
| 		} | ||||
| 		return ret; | ||||
| 	} | ||||
| 	pvr2_trace(PVR2_TRACE_DEBUGIFC, | ||||
| 		   "debugifc failed to recognize cmd: \"%.*s\"",wlen,wptr); | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf, | ||||
| 			unsigned int count) | ||||
| { | ||||
| 	unsigned int bcnt = 0; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	while (count) { | ||||
| 		for (bcnt = 0; bcnt < count; bcnt++) { | ||||
| 			if (buf[bcnt] == '\n') break; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = pvr2_debugifc_do1cmd(hdw,buf,bcnt); | ||||
| 		if (ret < 0) return ret; | ||||
| 		if (bcnt < count) bcnt++; | ||||
| 		buf += bcnt; | ||||
| 		count -= bcnt; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										52
									
								
								drivers/media/usb/pvrusb2/pvrusb2-debugifc.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								drivers/media/usb/pvrusb2/pvrusb2-debugifc.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_DEBUGIFC_H | ||||
| #define __PVRUSB2_DEBUGIFC_H | ||||
| 
 | ||||
| struct pvr2_hdw; | ||||
| 
 | ||||
| /* Print general status of driver.  This will also trigger a probe of
 | ||||
|    the USB link.  Unlike print_info(), this one synchronizes with the | ||||
|    driver so the information should be self-consistent (but it will | ||||
|    hang if the driver is wedged). */ | ||||
| int pvr2_debugifc_print_info(struct pvr2_hdw *, | ||||
| 			     char *buf_ptr, unsigned int buf_size); | ||||
| 
 | ||||
| /* Non-intrusively print some useful debugging info from inside the
 | ||||
|    driver.  This should work even if the driver appears to be | ||||
|    wedged. */ | ||||
| int pvr2_debugifc_print_status(struct pvr2_hdw *, | ||||
| 			       char *buf_ptr,unsigned int buf_size); | ||||
| 
 | ||||
| /* Parse a string command into a driver action. */ | ||||
| int pvr2_debugifc_docmd(struct pvr2_hdw *, | ||||
| 			const char *buf_ptr,unsigned int buf_size); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_DEBUGIFC_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										576
									
								
								drivers/media/usb/pvrusb2/pvrusb2-devattr.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										576
									
								
								drivers/media/usb/pvrusb2/pvrusb2-devattr.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,576 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2007 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
| This source file should encompass ALL per-device type information for the | ||||
| driver.  To define a new device, add elements to the pvr2_device_table and | ||||
| pvr2_device_desc structures. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #include "pvrusb2-devattr.h" | ||||
| #include <linux/usb.h> | ||||
| #include <linux/module.h> | ||||
| /* This is needed in order to pull in tuner type ids... */ | ||||
| #include <linux/i2c.h> | ||||
| #include <media/tuner.h> | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include "lgdt330x.h" | ||||
| #include "s5h1409.h" | ||||
| #include "s5h1411.h" | ||||
| #include "tda10048.h" | ||||
| #include "tda18271.h" | ||||
| #include "tda8290.h" | ||||
| #include "tuner-simple.h" | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| /*------------------------------------------------------------------------*/ | ||||
| /* Hauppauge PVR-USB2 Model 29xxx */ | ||||
| 
 | ||||
| static const struct pvr2_device_client_desc pvr2_cli_29xxx[] = { | ||||
| 	{ .module_id = PVR2_CLIENT_ID_SAA7115 }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_MSP3400 }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_TUNER }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_DEMOD }, | ||||
| }; | ||||
| 
 | ||||
| #define PVR2_FIRMWARE_29xxx "v4l-pvrusb2-29xxx-01.fw" | ||||
| static const char *pvr2_fw1_names_29xxx[] = { | ||||
| 		PVR2_FIRMWARE_29xxx, | ||||
| }; | ||||
| 
 | ||||
| static const struct pvr2_device_desc pvr2_device_29xxx = { | ||||
| 		.description = "WinTV PVR USB2 Model 29xxx", | ||||
| 		.shortname = "29xxx", | ||||
| 		.client_table.lst = pvr2_cli_29xxx, | ||||
| 		.client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx), | ||||
| 		.fx2_firmware.lst = pvr2_fw1_names_29xxx, | ||||
| 		.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), | ||||
| 		.flag_has_hauppauge_rom = !0, | ||||
| 		.flag_has_analogtuner = !0, | ||||
| 		.flag_has_fmradio = !0, | ||||
| 		.flag_has_composite = !0, | ||||
| 		.flag_has_svideo = !0, | ||||
| 		.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, | ||||
| 		.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, | ||||
| 		.ir_scheme = PVR2_IR_SCHEME_29XXX, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*------------------------------------------------------------------------*/ | ||||
| /* Hauppauge PVR-USB2 Model 24xxx */ | ||||
| 
 | ||||
| static const struct pvr2_device_client_desc pvr2_cli_24xxx[] = { | ||||
| 	{ .module_id = PVR2_CLIENT_ID_CX25840 }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_TUNER }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_WM8775 }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_DEMOD }, | ||||
| }; | ||||
| 
 | ||||
| #define PVR2_FIRMWARE_24xxx "v4l-pvrusb2-24xxx-01.fw" | ||||
| static const char *pvr2_fw1_names_24xxx[] = { | ||||
| 		PVR2_FIRMWARE_24xxx, | ||||
| }; | ||||
| 
 | ||||
| static const struct pvr2_device_desc pvr2_device_24xxx = { | ||||
| 		.description = "WinTV PVR USB2 Model 24xxx", | ||||
| 		.shortname = "24xxx", | ||||
| 		.client_table.lst = pvr2_cli_24xxx, | ||||
| 		.client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx), | ||||
| 		.fx2_firmware.lst = pvr2_fw1_names_24xxx, | ||||
| 		.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx), | ||||
| 		.flag_has_cx25840 = !0, | ||||
| 		.flag_has_wm8775 = !0, | ||||
| 		.flag_has_hauppauge_rom = !0, | ||||
| 		.flag_has_analogtuner = !0, | ||||
| 		.flag_has_fmradio = !0, | ||||
| 		.flag_has_composite = !0, | ||||
| 		.flag_has_svideo = !0, | ||||
| 		.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, | ||||
| 		.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, | ||||
| 		.ir_scheme = PVR2_IR_SCHEME_24XXX, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*------------------------------------------------------------------------*/ | ||||
| /* GOTVIEW USB2.0 DVD2 */ | ||||
| 
 | ||||
| static const struct pvr2_device_client_desc pvr2_cli_gotview_2[] = { | ||||
| 	{ .module_id = PVR2_CLIENT_ID_CX25840 }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_TUNER }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_DEMOD }, | ||||
| }; | ||||
| 
 | ||||
| static const struct pvr2_device_desc pvr2_device_gotview_2 = { | ||||
| 		.description = "Gotview USB 2.0 DVD 2", | ||||
| 		.shortname = "gv2", | ||||
| 		.client_table.lst = pvr2_cli_gotview_2, | ||||
| 		.client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2), | ||||
| 		.flag_has_cx25840 = !0, | ||||
| 		.default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, | ||||
| 		.flag_has_analogtuner = !0, | ||||
| 		.flag_has_fmradio = !0, | ||||
| 		.flag_has_composite = !0, | ||||
| 		.flag_has_svideo = !0, | ||||
| 		.signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*------------------------------------------------------------------------*/ | ||||
| /* GOTVIEW USB2.0 DVD Deluxe */ | ||||
| 
 | ||||
| /* (same module list as gotview_2) */ | ||||
| 
 | ||||
| static const struct pvr2_device_desc pvr2_device_gotview_2d = { | ||||
| 		.description = "Gotview USB 2.0 DVD Deluxe", | ||||
| 		.shortname = "gv2d", | ||||
| 		.client_table.lst = pvr2_cli_gotview_2, | ||||
| 		.client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2), | ||||
| 		.flag_has_cx25840 = !0, | ||||
| 		.default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, | ||||
| 		.flag_has_analogtuner = !0, | ||||
| 		.flag_has_composite = !0, | ||||
| 		.flag_has_svideo = !0, | ||||
| 		.signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*------------------------------------------------------------------------*/ | ||||
| /* Terratec Grabster AV400 */ | ||||
| 
 | ||||
| static const struct pvr2_device_client_desc pvr2_cli_av400[] = { | ||||
| 	{ .module_id = PVR2_CLIENT_ID_CX25840 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct pvr2_device_desc pvr2_device_av400 = { | ||||
| 		.description = "Terratec Grabster AV400", | ||||
| 		.shortname = "av400", | ||||
| 		.flag_is_experimental = 1, | ||||
| 		.client_table.lst = pvr2_cli_av400, | ||||
| 		.client_table.cnt = ARRAY_SIZE(pvr2_cli_av400), | ||||
| 		.flag_has_cx25840 = !0, | ||||
| 		.flag_has_analogtuner = 0, | ||||
| 		.flag_has_composite = !0, | ||||
| 		.flag_has_svideo = !0, | ||||
| 		.signal_routing_scheme = PVR2_ROUTING_SCHEME_AV400, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*------------------------------------------------------------------------*/ | ||||
| /* OnAir Creator */ | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| static struct lgdt330x_config pvr2_lgdt3303_config = { | ||||
| 	.demod_address       = 0x0e, | ||||
| 	.demod_chip          = LGDT3303, | ||||
| 	.clock_polarity_flip = 1, | ||||
| }; | ||||
| 
 | ||||
| static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config, | ||||
| 			      &adap->channel.hdw->i2c_adap); | ||||
| 	if (adap->fe) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return -EIO; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	dvb_attach(simple_tuner_attach, adap->fe, | ||||
| 		   &adap->channel.hdw->i2c_adap, 0x61, | ||||
| 		   TUNER_LG_TDVS_H06XF); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct pvr2_dvb_props pvr2_onair_creator_fe_props = { | ||||
| 	.frontend_attach = pvr2_lgdt3303_attach, | ||||
| 	.tuner_attach    = pvr2_lgh06xf_attach, | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| static const struct pvr2_device_client_desc pvr2_cli_onair_creator[] = { | ||||
| 	{ .module_id = PVR2_CLIENT_ID_SAA7115 }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_CS53L32A }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_TUNER }, | ||||
| }; | ||||
| 
 | ||||
| static const struct pvr2_device_desc pvr2_device_onair_creator = { | ||||
| 		.description = "OnAir Creator Hybrid USB tuner", | ||||
| 		.shortname = "oac", | ||||
| 		.client_table.lst = pvr2_cli_onair_creator, | ||||
| 		.client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_creator), | ||||
| 		.default_tuner_type = TUNER_LG_TDVS_H06XF, | ||||
| 		.flag_has_analogtuner = !0, | ||||
| 		.flag_has_composite = !0, | ||||
| 		.flag_has_svideo = !0, | ||||
| 		.flag_digital_requires_cx23416 = !0, | ||||
| 		.signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR, | ||||
| 		.digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, | ||||
| 		.default_std_mask = V4L2_STD_NTSC_M, | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| 		.dvb_props = &pvr2_onair_creator_fe_props, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*------------------------------------------------------------------------*/ | ||||
| /* OnAir USB 2.0 */ | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| static struct lgdt330x_config pvr2_lgdt3302_config = { | ||||
| 	.demod_address       = 0x0e, | ||||
| 	.demod_chip          = LGDT3302, | ||||
| }; | ||||
| 
 | ||||
| static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config, | ||||
| 			      &adap->channel.hdw->i2c_adap); | ||||
| 	if (adap->fe) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return -EIO; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	dvb_attach(simple_tuner_attach, adap->fe, | ||||
| 		   &adap->channel.hdw->i2c_adap, 0x61, | ||||
| 		   TUNER_PHILIPS_FCV1236D); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { | ||||
| 	.frontend_attach = pvr2_lgdt3302_attach, | ||||
| 	.tuner_attach    = pvr2_fcv1236d_attach, | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| static const struct pvr2_device_client_desc pvr2_cli_onair_usb2[] = { | ||||
| 	{ .module_id = PVR2_CLIENT_ID_SAA7115 }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_CS53L32A }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_TUNER }, | ||||
| }; | ||||
| 
 | ||||
| static const struct pvr2_device_desc pvr2_device_onair_usb2 = { | ||||
| 		.description = "OnAir USB2 Hybrid USB tuner", | ||||
| 		.shortname = "oa2", | ||||
| 		.client_table.lst = pvr2_cli_onair_usb2, | ||||
| 		.client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_usb2), | ||||
| 		.default_tuner_type = TUNER_PHILIPS_FCV1236D, | ||||
| 		.flag_has_analogtuner = !0, | ||||
| 		.flag_has_composite = !0, | ||||
| 		.flag_has_svideo = !0, | ||||
| 		.flag_digital_requires_cx23416 = !0, | ||||
| 		.signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR, | ||||
| 		.digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, | ||||
| 		.default_std_mask = V4L2_STD_NTSC_M, | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| 		.dvb_props = &pvr2_onair_usb2_fe_props, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*------------------------------------------------------------------------*/ | ||||
| /* Hauppauge PVR-USB2 Model 73xxx */ | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| static struct tda10048_config hauppauge_tda10048_config = { | ||||
| 	.demod_address  = 0x10 >> 1, | ||||
| 	.output_mode    = TDA10048_PARALLEL_OUTPUT, | ||||
| 	.fwbulkwritelen = TDA10048_BULKWRITE_50, | ||||
| 	.inversion      = TDA10048_INVERSION_ON, | ||||
| 	.dtv6_if_freq_khz = TDA10048_IF_3300, | ||||
| 	.dtv7_if_freq_khz = TDA10048_IF_3800, | ||||
| 	.dtv8_if_freq_khz = TDA10048_IF_4300, | ||||
| 	.clk_freq_khz   = TDA10048_CLK_16000, | ||||
| 	.disable_gate_access = 1, | ||||
| }; | ||||
| 
 | ||||
| static struct tda829x_config tda829x_no_probe = { | ||||
| 	.probe_tuner = TDA829X_DONT_PROBE, | ||||
| }; | ||||
| 
 | ||||
| static struct tda18271_std_map hauppauge_tda18271_dvbt_std_map = { | ||||
|         .dvbt_6   = { .if_freq = 3300, .agc_mode = 3, .std = 4, | ||||
|                       .if_lvl = 1, .rfagc_top = 0x37, }, | ||||
|         .dvbt_7   = { .if_freq = 3800, .agc_mode = 3, .std = 5, | ||||
|                       .if_lvl = 1, .rfagc_top = 0x37, }, | ||||
|         .dvbt_8   = { .if_freq = 4300, .agc_mode = 3, .std = 6, | ||||
|                       .if_lvl = 1, .rfagc_top = 0x37, }, | ||||
| }; | ||||
| 
 | ||||
| static struct tda18271_config hauppauge_tda18271_dvb_config = { | ||||
| 	.std_map = &hauppauge_tda18271_dvbt_std_map, | ||||
| 	.gate    = TDA18271_GATE_ANALOG, | ||||
| 	.output_opt = TDA18271_OUTPUT_LT_OFF, | ||||
| }; | ||||
| 
 | ||||
| static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config, | ||||
| 			      &adap->channel.hdw->i2c_adap); | ||||
| 	if (adap->fe) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return -EIO; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	dvb_attach(tda829x_attach, adap->fe, | ||||
| 		   &adap->channel.hdw->i2c_adap, 0x42, | ||||
| 		   &tda829x_no_probe); | ||||
| 	dvb_attach(tda18271_attach, adap->fe, 0x60, | ||||
| 		   &adap->channel.hdw->i2c_adap, | ||||
| 		   &hauppauge_tda18271_dvb_config); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct pvr2_dvb_props pvr2_73xxx_dvb_props = { | ||||
| 	.frontend_attach = pvr2_tda10048_attach, | ||||
| 	.tuner_attach    = pvr2_73xxx_tda18271_8295_attach, | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| static const struct pvr2_device_client_desc pvr2_cli_73xxx[] = { | ||||
| 	{ .module_id = PVR2_CLIENT_ID_CX25840 }, | ||||
| 	{ .module_id = PVR2_CLIENT_ID_TUNER, | ||||
| 	  .i2c_address_list = "\x42"}, | ||||
| }; | ||||
| 
 | ||||
| #define PVR2_FIRMWARE_73xxx "v4l-pvrusb2-73xxx-01.fw" | ||||
| static const char *pvr2_fw1_names_73xxx[] = { | ||||
| 		PVR2_FIRMWARE_73xxx, | ||||
| }; | ||||
| 
 | ||||
| static const struct pvr2_device_desc pvr2_device_73xxx = { | ||||
| 		.description = "WinTV HVR-1900 Model 73xxx", | ||||
| 		.shortname = "73xxx", | ||||
| 		.client_table.lst = pvr2_cli_73xxx, | ||||
| 		.client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), | ||||
| 		.fx2_firmware.lst = pvr2_fw1_names_73xxx, | ||||
| 		.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx), | ||||
| 		.flag_has_cx25840 = !0, | ||||
| 		.flag_has_hauppauge_rom = !0, | ||||
| 		.flag_has_analogtuner = !0, | ||||
| 		.flag_has_composite = !0, | ||||
| 		.flag_has_svideo = !0, | ||||
| 		.flag_fx2_16kb = !0, | ||||
| 		.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, | ||||
| 		.digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, | ||||
| 		.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, | ||||
| 		.ir_scheme = PVR2_IR_SCHEME_ZILOG, | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| 		.dvb_props = &pvr2_73xxx_dvb_props, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*------------------------------------------------------------------------*/ | ||||
| /* Hauppauge PVR-USB2 Model 75xxx */ | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| static struct s5h1409_config pvr2_s5h1409_config = { | ||||
| 	.demod_address = 0x32 >> 1, | ||||
| 	.output_mode   = S5H1409_PARALLEL_OUTPUT, | ||||
| 	.gpio          = S5H1409_GPIO_OFF, | ||||
| 	.qam_if        = 4000, | ||||
| 	.inversion     = S5H1409_INVERSION_ON, | ||||
| 	.status_mode   = S5H1409_DEMODLOCKING, | ||||
| }; | ||||
| 
 | ||||
| static struct s5h1411_config pvr2_s5h1411_config = { | ||||
| 	.output_mode   = S5H1411_PARALLEL_OUTPUT, | ||||
| 	.gpio          = S5H1411_GPIO_OFF, | ||||
| 	.vsb_if        = S5H1411_IF_44000, | ||||
| 	.qam_if        = S5H1411_IF_4000, | ||||
| 	.inversion     = S5H1411_INVERSION_ON, | ||||
| 	.status_mode   = S5H1411_DEMODLOCKING, | ||||
| }; | ||||
| 
 | ||||
| static struct tda18271_std_map hauppauge_tda18271_std_map = { | ||||
| 	.atsc_6   = { .if_freq = 5380, .agc_mode = 3, .std = 3, | ||||
| 		      .if_lvl = 6, .rfagc_top = 0x37, }, | ||||
| 	.qam_6    = { .if_freq = 4000, .agc_mode = 3, .std = 0, | ||||
| 		      .if_lvl = 6, .rfagc_top = 0x37, }, | ||||
| }; | ||||
| 
 | ||||
| static struct tda18271_config hauppauge_tda18271_config = { | ||||
| 	.std_map = &hauppauge_tda18271_std_map, | ||||
| 	.gate    = TDA18271_GATE_ANALOG, | ||||
| 	.output_opt = TDA18271_OUTPUT_LT_OFF, | ||||
| }; | ||||
| 
 | ||||
| static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config, | ||||
| 			      &adap->channel.hdw->i2c_adap); | ||||
| 	if (adap->fe) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return -EIO; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_s5h1411_attach(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	adap->fe = dvb_attach(s5h1411_attach, &pvr2_s5h1411_config, | ||||
| 			      &adap->channel.hdw->i2c_adap); | ||||
| 	if (adap->fe) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return -EIO; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	dvb_attach(tda829x_attach, adap->fe, | ||||
| 		   &adap->channel.hdw->i2c_adap, 0x42, | ||||
| 		   &tda829x_no_probe); | ||||
| 	dvb_attach(tda18271_attach, adap->fe, 0x60, | ||||
| 		   &adap->channel.hdw->i2c_adap, | ||||
| 		   &hauppauge_tda18271_config); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct pvr2_dvb_props pvr2_750xx_dvb_props = { | ||||
| 	.frontend_attach = pvr2_s5h1409_attach, | ||||
| 	.tuner_attach    = pvr2_tda18271_8295_attach, | ||||
| }; | ||||
| 
 | ||||
| static const struct pvr2_dvb_props pvr2_751xx_dvb_props = { | ||||
| 	.frontend_attach = pvr2_s5h1411_attach, | ||||
| 	.tuner_attach    = pvr2_tda18271_8295_attach, | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| #define PVR2_FIRMWARE_75xxx "v4l-pvrusb2-73xxx-01.fw" | ||||
| static const char *pvr2_fw1_names_75xxx[] = { | ||||
| 		PVR2_FIRMWARE_75xxx, | ||||
| }; | ||||
| 
 | ||||
| static const struct pvr2_device_desc pvr2_device_750xx = { | ||||
| 		.description = "WinTV HVR-1950 Model 750xx", | ||||
| 		.shortname = "750xx", | ||||
| 		.client_table.lst = pvr2_cli_73xxx, | ||||
| 		.client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), | ||||
| 		.fx2_firmware.lst = pvr2_fw1_names_75xxx, | ||||
| 		.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), | ||||
| 		.flag_has_cx25840 = !0, | ||||
| 		.flag_has_hauppauge_rom = !0, | ||||
| 		.flag_has_analogtuner = !0, | ||||
| 		.flag_has_composite = !0, | ||||
| 		.flag_has_svideo = !0, | ||||
| 		.flag_fx2_16kb = !0, | ||||
| 		.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, | ||||
| 		.digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, | ||||
| 		.default_std_mask = V4L2_STD_NTSC_M, | ||||
| 		.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, | ||||
| 		.ir_scheme = PVR2_IR_SCHEME_ZILOG, | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| 		.dvb_props = &pvr2_750xx_dvb_props, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static const struct pvr2_device_desc pvr2_device_751xx = { | ||||
| 		.description = "WinTV HVR-1950 Model 751xx", | ||||
| 		.shortname = "751xx", | ||||
| 		.client_table.lst = pvr2_cli_73xxx, | ||||
| 		.client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), | ||||
| 		.fx2_firmware.lst = pvr2_fw1_names_75xxx, | ||||
| 		.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), | ||||
| 		.flag_has_cx25840 = !0, | ||||
| 		.flag_has_hauppauge_rom = !0, | ||||
| 		.flag_has_analogtuner = !0, | ||||
| 		.flag_has_composite = !0, | ||||
| 		.flag_has_svideo = !0, | ||||
| 		.flag_fx2_16kb = !0, | ||||
| 		.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, | ||||
| 		.digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, | ||||
| 		.default_std_mask = V4L2_STD_NTSC_M, | ||||
| 		.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, | ||||
| 		.ir_scheme = PVR2_IR_SCHEME_ZILOG, | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| 		.dvb_props = &pvr2_751xx_dvb_props, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*------------------------------------------------------------------------*/ | ||||
| 
 | ||||
| struct usb_device_id pvr2_device_table[] = { | ||||
| 	{ USB_DEVICE(0x2040, 0x2900), | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, | ||||
| 	{ USB_DEVICE(0x2040, 0x2950), /* Logically identical to 2900 */ | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, | ||||
| 	{ USB_DEVICE(0x2040, 0x2400), | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_24xxx}, | ||||
| 	{ USB_DEVICE(0x1164, 0x0622), | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2}, | ||||
| 	{ USB_DEVICE(0x1164, 0x0602), | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d}, | ||||
| 	{ USB_DEVICE(0x11ba, 0x1003), | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator}, | ||||
| 	{ USB_DEVICE(0x11ba, 0x1001), | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2}, | ||||
| 	{ USB_DEVICE(0x2040, 0x7300), | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_73xxx}, | ||||
| 	{ USB_DEVICE(0x2040, 0x7500), | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_750xx}, | ||||
| 	{ USB_DEVICE(0x2040, 0x7501), | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_751xx}, | ||||
| 	{ USB_DEVICE(0x0ccd, 0x0039), | ||||
| 	  .driver_info = (kernel_ulong_t)&pvr2_device_av400}, | ||||
| 	{ } | ||||
| }; | ||||
| 
 | ||||
| MODULE_DEVICE_TABLE(usb, pvr2_device_table); | ||||
| MODULE_FIRMWARE(PVR2_FIRMWARE_29xxx); | ||||
| MODULE_FIRMWARE(PVR2_FIRMWARE_24xxx); | ||||
| MODULE_FIRMWARE(PVR2_FIRMWARE_73xxx); | ||||
| MODULE_FIRMWARE(PVR2_FIRMWARE_75xxx); | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										199
									
								
								drivers/media/usb/pvrusb2/pvrusb2-devattr.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								drivers/media/usb/pvrusb2/pvrusb2-devattr.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,199 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_DEVATTR_H | ||||
| #define __PVRUSB2_DEVATTR_H | ||||
| 
 | ||||
| #include <linux/mod_devicetable.h> | ||||
| #include <linux/videodev2.h> | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| #include "pvrusb2-dvb.h" | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|   This header defines structures used to describe attributes of a device. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #define PVR2_CLIENT_ID_NULL 0 | ||||
| #define PVR2_CLIENT_ID_MSP3400 1 | ||||
| #define PVR2_CLIENT_ID_CX25840 2 | ||||
| #define PVR2_CLIENT_ID_SAA7115 3 | ||||
| #define PVR2_CLIENT_ID_TUNER 4 | ||||
| #define PVR2_CLIENT_ID_CS53L32A 5 | ||||
| #define PVR2_CLIENT_ID_WM8775 6 | ||||
| #define PVR2_CLIENT_ID_DEMOD 7 | ||||
| 
 | ||||
| struct pvr2_device_client_desc { | ||||
| 	/* One ovr PVR2_CLIENT_ID_xxxx */ | ||||
| 	unsigned char module_id; | ||||
| 
 | ||||
| 	/* Null-terminated array of I2C addresses to try in order
 | ||||
| 	   initialize the module.  It's safe to make this null terminated | ||||
| 	   since we're never going to encounter an i2c device with an | ||||
| 	   address of zero.  If this is a null pointer or zero-length, | ||||
| 	   then no I2C addresses have been specified, in which case we'll | ||||
| 	   try some compiled in defaults for now. */ | ||||
| 	unsigned char *i2c_address_list; | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_device_client_table { | ||||
| 	const struct pvr2_device_client_desc *lst; | ||||
| 	unsigned char cnt; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| struct pvr2_string_table { | ||||
| 	const char **lst; | ||||
| 	unsigned int cnt; | ||||
| }; | ||||
| 
 | ||||
| #define PVR2_ROUTING_SCHEME_HAUPPAUGE 0 | ||||
| #define PVR2_ROUTING_SCHEME_GOTVIEW 1 | ||||
| #define PVR2_ROUTING_SCHEME_ONAIR 2 | ||||
| #define PVR2_ROUTING_SCHEME_AV400 3 | ||||
| 
 | ||||
| #define PVR2_DIGITAL_SCHEME_NONE 0 | ||||
| #define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1 | ||||
| #define PVR2_DIGITAL_SCHEME_ONAIR 2 | ||||
| 
 | ||||
| #define PVR2_LED_SCHEME_NONE 0 | ||||
| #define PVR2_LED_SCHEME_HAUPPAUGE 1 | ||||
| 
 | ||||
| #define PVR2_IR_SCHEME_NONE 0 | ||||
| #define PVR2_IR_SCHEME_24XXX 1 /* FX2-controlled IR */ | ||||
| #define PVR2_IR_SCHEME_ZILOG 2 /* HVR-1950 style (must be taken out of reset) */ | ||||
| #define PVR2_IR_SCHEME_24XXX_MCE 3 /* 24xxx MCE device */ | ||||
| #define PVR2_IR_SCHEME_29XXX 4 /* Original 29xxx device */ | ||||
| 
 | ||||
| /* This describes a particular hardware type (except for the USB device ID
 | ||||
|    which must live in a separate structure due to environmental | ||||
|    constraints).  See the top of pvrusb2-hdw.c for where this is | ||||
|    instantiated. */ | ||||
| struct pvr2_device_desc { | ||||
| 	/* Single line text description of hardware */ | ||||
| 	const char *description; | ||||
| 
 | ||||
| 	/* Single token identifier for hardware */ | ||||
| 	const char *shortname; | ||||
| 
 | ||||
| 	/* List of additional client modules we need to load */ | ||||
| 	struct pvr2_string_table client_modules; | ||||
| 
 | ||||
| 	/* List of defined client modules we need to load */ | ||||
| 	struct pvr2_device_client_table client_table; | ||||
| 
 | ||||
| 	/* List of FX2 firmware file names we should search; if empty then
 | ||||
| 	   FX2 firmware check / load is skipped and we assume the device | ||||
| 	   was initialized from internal ROM. */ | ||||
| 	struct pvr2_string_table fx2_firmware; | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| 	/* callback functions to handle attachment of digital tuner & demod */ | ||||
| 	const struct pvr2_dvb_props *dvb_props; | ||||
| 
 | ||||
| #endif | ||||
| 	/* Initial standard bits to use for this device, if not zero.
 | ||||
| 	   Anything set here is also implied as an available standard. | ||||
| 	   Note: This is ignored if overridden on the module load line via | ||||
| 	   the video_std module option. */ | ||||
| 	v4l2_std_id default_std_mask; | ||||
| 
 | ||||
| 	/* V4L tuner type ID to use with this device (only used if the
 | ||||
| 	   driver could not discover the type any other way). */ | ||||
| 	int default_tuner_type; | ||||
| 
 | ||||
| 	/* Signal routing scheme used by device, contains one of
 | ||||
| 	   PVR2_ROUTING_SCHEME_XXX.  Schemes have to be defined as we | ||||
| 	   encounter them.  This is an arbitrary integer scheme id; its | ||||
| 	   meaning is contained entirely within the driver and is | ||||
| 	   interpreted by logic which must send commands to the chip-level | ||||
| 	   drivers (search for things which touch this field). */ | ||||
| 	unsigned char signal_routing_scheme; | ||||
| 
 | ||||
| 	/* Indicates scheme for controlling device's LED (if any).  The
 | ||||
| 	   driver will turn on the LED when streaming is underway.  This | ||||
| 	   contains one of PVR2_LED_SCHEME_XXX. */ | ||||
| 	unsigned char led_scheme; | ||||
| 
 | ||||
| 	/* Control scheme to use if there is a digital tuner.  This
 | ||||
| 	   contains one of PVR2_DIGITAL_SCHEME_XXX.  This is an arbitrary | ||||
| 	   integer scheme id; its meaning is contained entirely within the | ||||
| 	   driver and is interpreted by logic which must control the | ||||
| 	   streaming pathway (search for things which touch this field). */ | ||||
| 	unsigned char digital_control_scheme; | ||||
| 
 | ||||
| 	/* If set, we don't bother trying to load cx23416 firmware. */ | ||||
| 	unsigned int flag_skip_cx23416_firmware:1; | ||||
| 
 | ||||
| 	/* If set, the encoder must be healthy in order for digital mode to
 | ||||
| 	   work (otherwise we assume that digital streaming will work even | ||||
| 	   if we fail to locate firmware for the encoder).  If the device | ||||
| 	   doesn't support digital streaming then this flag has no | ||||
| 	   effect. */ | ||||
| 	unsigned int flag_digital_requires_cx23416:1; | ||||
| 
 | ||||
| 	/* Device has a hauppauge eeprom which we can interrogate. */ | ||||
| 	unsigned int flag_has_hauppauge_rom:1; | ||||
| 
 | ||||
| 	/* Device does not require a powerup command to be issued. */ | ||||
| 	unsigned int flag_no_powerup:1; | ||||
| 
 | ||||
| 	/* Device has a cx25840 - this enables special additional logic to
 | ||||
| 	   handle it. */ | ||||
| 	unsigned int flag_has_cx25840:1; | ||||
| 
 | ||||
| 	/* Device has a wm8775 - this enables special additional logic to
 | ||||
| 	   ensure that it is found. */ | ||||
| 	unsigned int flag_has_wm8775:1; | ||||
| 
 | ||||
| 	/* Indicate IR scheme of hardware.  If not set, then it is assumed
 | ||||
| 	   that IR can work without any help from the driver. */ | ||||
| 	unsigned int ir_scheme:3; | ||||
| 
 | ||||
| 	/* These bits define which kinds of sources the device can handle.
 | ||||
| 	   Note: Digital tuner presence is inferred by the | ||||
| 	   digital_control_scheme enumeration. */ | ||||
| 	unsigned int flag_has_fmradio:1;       /* Has FM radio receiver */ | ||||
| 	unsigned int flag_has_analogtuner:1;   /* Has analog tuner */ | ||||
| 	unsigned int flag_has_composite:1;     /* Has composite input */ | ||||
| 	unsigned int flag_has_svideo:1;        /* Has s-video input */ | ||||
| 	unsigned int flag_fx2_16kb:1;          /* 16KB FX2 firmware OK here */ | ||||
| 
 | ||||
| 	/* If this driver is considered experimental, i.e. not all aspects
 | ||||
| 	   are working correctly and/or it is untested, mark that fact | ||||
| 	   with this flag. */ | ||||
| 	unsigned int flag_is_experimental:1; | ||||
| }; | ||||
| 
 | ||||
| extern struct usb_device_id pvr2_device_table[]; | ||||
| 
 | ||||
| #endif /* __PVRUSB2_HDW_INTERNAL_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										435
									
								
								drivers/media/usb/pvrusb2/pvrusb2-dvb.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										435
									
								
								drivers/media/usb/pvrusb2/pvrusb2-dvb.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,435 @@ | |||
| /*
 | ||||
|  *  pvrusb2-dvb.c - linux-dvb api interface to the pvrusb2 driver. | ||||
|  * | ||||
|  *  Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kthread.h> | ||||
| #include <linux/freezer.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/mm.h> | ||||
| #include "dvbdev.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include "pvrusb2-hdw.h" | ||||
| #include "pvrusb2-io.h" | ||||
| #include "pvrusb2-dvb.h" | ||||
| 
 | ||||
| DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | ||||
| 
 | ||||
| static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	int ret; | ||||
| 	unsigned int count; | ||||
| 	struct pvr2_buffer *bp; | ||||
| 	struct pvr2_stream *stream; | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread started"); | ||||
| 	set_freezable(); | ||||
| 
 | ||||
| 	stream = adap->channel.stream->stream; | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		if (kthread_should_stop()) break; | ||||
| 
 | ||||
| 		/* Not sure about this... */ | ||||
| 		try_to_freeze(); | ||||
| 
 | ||||
| 		bp = pvr2_stream_get_ready_buffer(stream); | ||||
| 		if (bp != NULL) { | ||||
| 			count = pvr2_buffer_get_count(bp); | ||||
| 			if (count) { | ||||
| 				dvb_dmx_swfilter( | ||||
| 					&adap->demux, | ||||
| 					adap->buffer_storage[ | ||||
| 					    pvr2_buffer_get_id(bp)], | ||||
| 					count); | ||||
| 			} else { | ||||
| 				ret = pvr2_buffer_get_status(bp); | ||||
| 				if (ret < 0) break; | ||||
| 			} | ||||
| 			ret = pvr2_buffer_queue(bp); | ||||
| 			if (ret < 0) break; | ||||
| 
 | ||||
| 			/* Since we know we did something to a buffer,
 | ||||
| 			   just go back and try again.  No point in | ||||
| 			   blocking unless we really ran out of | ||||
| 			   buffers to process. */ | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 
 | ||||
| 		/* Wait until more buffers become available or we're
 | ||||
| 		   told not to wait any longer. */ | ||||
| 		ret = wait_event_interruptible( | ||||
| 		    adap->buffer_wait_data, | ||||
| 		    (pvr2_stream_get_ready_count(stream) > 0) || | ||||
| 		    kthread_should_stop()); | ||||
| 		if (ret < 0) break; | ||||
| 	} | ||||
| 
 | ||||
| 	/* If we get here and ret is < 0, then an error has occurred.
 | ||||
| 	   Probably would be a good idea to communicate that to DVB core... */ | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread stopped"); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_feed_thread(void *data) | ||||
| { | ||||
| 	int stat = pvr2_dvb_feed_func(data); | ||||
| 	/* from videobuf-dvb.c: */ | ||||
| 	while (!kthread_should_stop()) { | ||||
| 		set_current_state(TASK_INTERRUPTIBLE); | ||||
| 		schedule(); | ||||
| 	} | ||||
| 	return stat; | ||||
| } | ||||
| 
 | ||||
| static void pvr2_dvb_notify(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	wake_up(&adap->buffer_wait_data); | ||||
| } | ||||
| 
 | ||||
| static void pvr2_dvb_stream_end(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	unsigned int idx; | ||||
| 	struct pvr2_stream *stream; | ||||
| 
 | ||||
| 	if (adap->thread) { | ||||
| 		kthread_stop(adap->thread); | ||||
| 		adap->thread = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (adap->channel.stream) { | ||||
| 		stream = adap->channel.stream->stream; | ||||
| 	} else { | ||||
| 		stream = NULL; | ||||
| 	} | ||||
| 	if (stream) { | ||||
| 		pvr2_hdw_set_streaming(adap->channel.hdw, 0); | ||||
| 		pvr2_stream_set_callback(stream, NULL, NULL); | ||||
| 		pvr2_stream_kill(stream); | ||||
| 		pvr2_stream_set_buffer_count(stream, 0); | ||||
| 		pvr2_channel_claim_stream(&adap->channel, NULL); | ||||
| 	} | ||||
| 
 | ||||
| 	if (adap->stream_run) { | ||||
| 		for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { | ||||
| 			if (!(adap->buffer_storage[idx])) continue; | ||||
| 			kfree(adap->buffer_storage[idx]); | ||||
| 			adap->buffer_storage[idx] = NULL; | ||||
| 		} | ||||
| 		adap->stream_run = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_stream_do_start(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	struct pvr2_context *pvr = adap->channel.mc_head; | ||||
| 	unsigned int idx; | ||||
| 	int ret; | ||||
| 	struct pvr2_buffer *bp; | ||||
| 	struct pvr2_stream *stream = NULL; | ||||
| 
 | ||||
| 	if (adap->stream_run) return -EIO; | ||||
| 
 | ||||
| 	ret = pvr2_channel_claim_stream(&adap->channel, &pvr->video_stream); | ||||
| 	/* somebody else already has the stream */ | ||||
| 	if (ret < 0) return ret; | ||||
| 
 | ||||
| 	stream = adap->channel.stream->stream; | ||||
| 
 | ||||
| 	for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { | ||||
| 		adap->buffer_storage[idx] = kmalloc(PVR2_DVB_BUFFER_SIZE, | ||||
| 						    GFP_KERNEL); | ||||
| 		if (!(adap->buffer_storage[idx])) return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	pvr2_stream_set_callback(pvr->video_stream.stream, | ||||
| 				 (pvr2_stream_callback) pvr2_dvb_notify, adap); | ||||
| 
 | ||||
| 	ret = pvr2_stream_set_buffer_count(stream, PVR2_DVB_BUFFER_COUNT); | ||||
| 	if (ret < 0) return ret; | ||||
| 
 | ||||
| 	for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { | ||||
| 		bp = pvr2_stream_get_buffer(stream, idx); | ||||
| 		pvr2_buffer_set_buffer(bp, | ||||
| 				       adap->buffer_storage[idx], | ||||
| 				       PVR2_DVB_BUFFER_SIZE); | ||||
| 	} | ||||
| 
 | ||||
| 	ret = pvr2_hdw_set_streaming(adap->channel.hdw, 1); | ||||
| 	if (ret < 0) return ret; | ||||
| 
 | ||||
| 	while ((bp = pvr2_stream_get_idle_buffer(stream)) != NULL) { | ||||
| 		ret = pvr2_buffer_queue(bp); | ||||
| 		if (ret < 0) return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	adap->thread = kthread_run(pvr2_dvb_feed_thread, adap, "pvrusb2-dvb"); | ||||
| 
 | ||||
| 	if (IS_ERR(adap->thread)) { | ||||
| 		ret = PTR_ERR(adap->thread); | ||||
| 		adap->thread = NULL; | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	adap->stream_run = !0; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_stream_start(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	int ret = pvr2_dvb_stream_do_start(adap); | ||||
| 	if (ret < 0) pvr2_dvb_stream_end(adap); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) | ||||
| { | ||||
| 	struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (adap == NULL) return -ENODEV; | ||||
| 
 | ||||
| 	mutex_lock(&adap->lock); | ||||
| 	do { | ||||
| 		if (onoff) { | ||||
| 			if (!adap->feedcount) { | ||||
| 				pvr2_trace(PVR2_TRACE_DVB_FEED, | ||||
| 					   "start feeding demux"); | ||||
| 				ret = pvr2_dvb_stream_start(adap); | ||||
| 				if (ret < 0) break; | ||||
| 			} | ||||
| 			(adap->feedcount)++; | ||||
| 		} else if (adap->feedcount > 0) { | ||||
| 			(adap->feedcount)--; | ||||
| 			if (!adap->feedcount) { | ||||
| 				pvr2_trace(PVR2_TRACE_DVB_FEED, | ||||
| 					   "stop feeding demux"); | ||||
| 				pvr2_dvb_stream_end(adap); | ||||
| 			} | ||||
| 		} | ||||
| 	} while (0); | ||||
| 	mutex_unlock(&adap->lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) | ||||
| { | ||||
| 	pvr2_trace(PVR2_TRACE_DVB_FEED, "start pid: 0x%04x", dvbdmxfeed->pid); | ||||
| 	return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1); | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) | ||||
| { | ||||
| 	pvr2_trace(PVR2_TRACE_DVB_FEED, "stop pid: 0x%04x", dvbdmxfeed->pid); | ||||
| 	return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0); | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) | ||||
| { | ||||
| 	struct pvr2_dvb_adapter *adap = fe->dvb->priv; | ||||
| 	return pvr2_channel_limit_inputs( | ||||
| 	    &adap->channel, | ||||
| 	    (acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0)); | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = dvb_register_adapter(&adap->dvb_adap, "pvrusb2-dvb", | ||||
| 				   THIS_MODULE/*&hdw->usb_dev->owner*/, | ||||
| 				   &adap->channel.hdw->usb_dev->dev, | ||||
| 				   adapter_nr); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "dvb_register_adapter failed: error %d", ret); | ||||
| 		goto err; | ||||
| 	} | ||||
| 	adap->dvb_adap.priv = adap; | ||||
| 
 | ||||
| 	adap->demux.dmx.capabilities = DMX_TS_FILTERING | | ||||
| 				       DMX_SECTION_FILTERING | | ||||
| 				       DMX_MEMORY_BASED_FILTERING; | ||||
| 	adap->demux.priv             = adap; | ||||
| 	adap->demux.filternum        = 256; | ||||
| 	adap->demux.feednum          = 256; | ||||
| 	adap->demux.start_feed       = pvr2_dvb_start_feed; | ||||
| 	adap->demux.stop_feed        = pvr2_dvb_stop_feed; | ||||
| 	adap->demux.write_to_decoder = NULL; | ||||
| 
 | ||||
| 	ret = dvb_dmx_init(&adap->demux); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "dvb_dmx_init failed: error %d", ret); | ||||
| 		goto err_dmx; | ||||
| 	} | ||||
| 
 | ||||
| 	adap->dmxdev.filternum       = adap->demux.filternum; | ||||
| 	adap->dmxdev.demux           = &adap->demux.dmx; | ||||
| 	adap->dmxdev.capabilities    = 0; | ||||
| 
 | ||||
| 	ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "dvb_dmxdev_init failed: error %d", ret); | ||||
| 		goto err_dmx_dev; | ||||
| 	} | ||||
| 
 | ||||
| 	dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_dmx_dev: | ||||
| 	dvb_dmx_release(&adap->demux); | ||||
| err_dmx: | ||||
| 	dvb_unregister_adapter(&adap->dvb_adap); | ||||
| err: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	pvr2_trace(PVR2_TRACE_INFO, "unregistering DVB devices"); | ||||
| 	dvb_net_release(&adap->dvb_net); | ||||
| 	adap->demux.dmx.close(&adap->demux.dmx); | ||||
| 	dvb_dmxdev_release(&adap->dmxdev); | ||||
| 	dvb_dmx_release(&adap->demux); | ||||
| 	dvb_unregister_adapter(&adap->dvb_adap); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	struct pvr2_hdw *hdw = adap->channel.hdw; | ||||
| 	const struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (dvb_props == NULL) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, "fe_props not defined!"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = pvr2_channel_limit_inputs( | ||||
| 	    &adap->channel, | ||||
| 	    (1 << PVR2_CVAL_INPUT_DTV)); | ||||
| 	if (ret) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "failed to grab control of dtv input (code=%d)", | ||||
| 		    ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dvb_props->frontend_attach == NULL) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "frontend_attach not defined!"); | ||||
| 		ret = -EINVAL; | ||||
| 		goto done; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) { | ||||
| 
 | ||||
| 		if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) { | ||||
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 				   "frontend registration failed!"); | ||||
| 			dvb_frontend_detach(adap->fe); | ||||
| 			adap->fe = NULL; | ||||
| 			ret = -ENODEV; | ||||
| 			goto done; | ||||
| 		} | ||||
| 
 | ||||
| 		if (dvb_props->tuner_attach) | ||||
| 			dvb_props->tuner_attach(adap); | ||||
| 
 | ||||
| 		if (adap->fe->ops.analog_ops.standby) | ||||
| 			adap->fe->ops.analog_ops.standby(adap->fe); | ||||
| 
 | ||||
| 		/* Ensure all frontends negotiate bus access */ | ||||
| 		adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl; | ||||
| 
 | ||||
| 	} else { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "no frontend was attached!"); | ||||
| 		ret = -ENODEV; | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
|  done: | ||||
| 	pvr2_channel_limit_inputs(&adap->channel, 0); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	if (adap->fe != NULL) { | ||||
| 		dvb_unregister_frontend(adap->fe); | ||||
| 		dvb_frontend_detach(adap->fe); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void pvr2_dvb_destroy(struct pvr2_dvb_adapter *adap) | ||||
| { | ||||
| 	pvr2_dvb_stream_end(adap); | ||||
| 	pvr2_dvb_frontend_exit(adap); | ||||
| 	pvr2_dvb_adapter_exit(adap); | ||||
| 	pvr2_channel_done(&adap->channel); | ||||
| 	kfree(adap); | ||||
| } | ||||
| 
 | ||||
| static void pvr2_dvb_internal_check(struct pvr2_channel *chp) | ||||
| { | ||||
| 	struct pvr2_dvb_adapter *adap; | ||||
| 	adap = container_of(chp, struct pvr2_dvb_adapter, channel); | ||||
| 	if (!adap->channel.mc_head->disconnect_flag) return; | ||||
| 	pvr2_dvb_destroy(adap); | ||||
| } | ||||
| 
 | ||||
| struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	struct pvr2_dvb_adapter *adap; | ||||
| 	if (!pvr->hdw->hdw_desc->dvb_props) { | ||||
| 		/* Device lacks a digital interface so don't set up
 | ||||
| 		   the DVB side of the driver either.  For now. */ | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	adap = kzalloc(sizeof(*adap), GFP_KERNEL); | ||||
| 	if (!adap) return adap; | ||||
| 	pvr2_channel_init(&adap->channel, pvr); | ||||
| 	adap->channel.check_func = pvr2_dvb_internal_check; | ||||
| 	init_waitqueue_head(&adap->buffer_wait_data); | ||||
| 	mutex_init(&adap->lock); | ||||
| 	ret = pvr2_dvb_adapter_init(adap); | ||||
| 	if (ret < 0) goto fail1; | ||||
| 	ret = pvr2_dvb_frontend_init(adap); | ||||
| 	if (ret < 0) goto fail2; | ||||
| 	return adap; | ||||
| 
 | ||||
| fail2: | ||||
| 	pvr2_dvb_adapter_exit(adap); | ||||
| fail1: | ||||
| 	pvr2_channel_done(&adap->channel); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										41
									
								
								drivers/media/usb/pvrusb2/pvrusb2-dvb.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								drivers/media/usb/pvrusb2/pvrusb2-dvb.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| #ifndef __PVRUSB2_DVB_H__ | ||||
| #define __PVRUSB2_DVB_H__ | ||||
| 
 | ||||
| #include "dvb_frontend.h" | ||||
| #include "dvb_demux.h" | ||||
| #include "dvb_net.h" | ||||
| #include "dmxdev.h" | ||||
| #include "pvrusb2-context.h" | ||||
| 
 | ||||
| #define PVR2_DVB_BUFFER_COUNT 32 | ||||
| #define PVR2_DVB_BUFFER_SIZE PAGE_ALIGN(0x4000) | ||||
| 
 | ||||
| struct pvr2_dvb_adapter { | ||||
| 	struct pvr2_channel	channel; | ||||
| 
 | ||||
| 	struct dvb_adapter	dvb_adap; | ||||
| 	struct dmxdev		dmxdev; | ||||
| 	struct dvb_demux	demux; | ||||
| 	struct dvb_net		dvb_net; | ||||
| 	struct dvb_frontend	*fe; | ||||
| 
 | ||||
| 	int			feedcount; | ||||
| 	int			max_feed_count; | ||||
| 
 | ||||
| 	struct task_struct	*thread; | ||||
| 	struct mutex		lock; | ||||
| 
 | ||||
| 	unsigned int		stream_run:1; | ||||
| 
 | ||||
| 	wait_queue_head_t	buffer_wait_data; | ||||
| 	char			*buffer_storage[PVR2_DVB_BUFFER_COUNT]; | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_dvb_props { | ||||
| 	int (*frontend_attach) (struct pvr2_dvb_adapter *); | ||||
| 	int (*tuner_attach) (struct pvr2_dvb_adapter *); | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_DVB_H__ */ | ||||
							
								
								
									
										164
									
								
								drivers/media/usb/pvrusb2/pvrusb2-eeprom.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								drivers/media/usb/pvrusb2/pvrusb2-eeprom.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,164 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/slab.h> | ||||
| #include "pvrusb2-eeprom.h" | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| 
 | ||||
| #define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|    Read and analyze data in the eeprom.  Use tveeprom to figure out | ||||
|    the packet structure, since this is another Hauppauge device and | ||||
|    internally it has a family resemblance to ivtv-type devices | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #include <media/tveeprom.h> | ||||
| 
 | ||||
| /* We seem to only be interested in the last 128 bytes of the EEPROM */ | ||||
| #define EEPROM_SIZE 128 | ||||
| 
 | ||||
| /* Grab EEPROM contents, needed for direct method. */ | ||||
| static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	struct i2c_msg msg[2]; | ||||
| 	u8 *eeprom; | ||||
| 	u8 iadd[2]; | ||||
| 	u8 addr; | ||||
| 	u16 eepromSize; | ||||
| 	unsigned int offs; | ||||
| 	int ret; | ||||
| 	int mode16 = 0; | ||||
| 	unsigned pcnt,tcnt; | ||||
| 	eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL); | ||||
| 	if (!eeprom) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "Failed to allocate memory" | ||||
| 			   " required to read eeprom"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	trace_eeprom("Value for eeprom addr from controller was 0x%x", | ||||
| 		     hdw->eeprom_addr); | ||||
| 	addr = hdw->eeprom_addr; | ||||
| 	/* Seems that if the high bit is set, then the *real* eeprom
 | ||||
| 	   address is shifted right now bit position (noticed this in | ||||
| 	   newer PVR USB2 hardware) */ | ||||
| 	if (addr & 0x80) addr >>= 1; | ||||
| 
 | ||||
| 	/* FX2 documentation states that a 16bit-addressed eeprom is
 | ||||
| 	   expected if the I2C address is an odd number (yeah, this is | ||||
| 	   strange but it's what they do) */ | ||||
| 	mode16 = (addr & 1); | ||||
| 	eepromSize = (mode16 ? 4096 : 256); | ||||
| 	trace_eeprom("Examining %d byte eeprom at location 0x%x" | ||||
| 		     " using %d bit addressing",eepromSize,addr, | ||||
| 		     mode16 ? 16 : 8); | ||||
| 
 | ||||
| 	msg[0].addr = addr; | ||||
| 	msg[0].flags = 0; | ||||
| 	msg[0].len = mode16 ? 2 : 1; | ||||
| 	msg[0].buf = iadd; | ||||
| 	msg[1].addr = addr; | ||||
| 	msg[1].flags = I2C_M_RD; | ||||
| 
 | ||||
| 	/* We have to do the actual eeprom data fetch ourselves, because
 | ||||
| 	   (1) we're only fetching part of the eeprom, and (2) if we were | ||||
| 	   getting the whole thing our I2C driver can't grab it in one | ||||
| 	   pass - which is what tveeprom is otherwise going to attempt */ | ||||
| 	memset(eeprom,0,EEPROM_SIZE); | ||||
| 	for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) { | ||||
| 		pcnt = 16; | ||||
| 		if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt; | ||||
| 		offs = tcnt + (eepromSize - EEPROM_SIZE); | ||||
| 		if (mode16) { | ||||
| 			iadd[0] = offs >> 8; | ||||
| 			iadd[1] = offs; | ||||
| 		} else { | ||||
| 			iadd[0] = offs; | ||||
| 		} | ||||
| 		msg[1].len = pcnt; | ||||
| 		msg[1].buf = eeprom+tcnt; | ||||
| 		if ((ret = i2c_transfer(&hdw->i2c_adap, | ||||
| 					msg,ARRAY_SIZE(msg))) != 2) { | ||||
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 				   "eeprom fetch set offs err=%d",ret); | ||||
| 			kfree(eeprom); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	} | ||||
| 	return eeprom; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Directly call eeprom analysis function within tveeprom. */ | ||||
| int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	u8 *eeprom; | ||||
| 	struct tveeprom tvdata; | ||||
| 
 | ||||
| 	memset(&tvdata,0,sizeof(tvdata)); | ||||
| 
 | ||||
| 	eeprom = pvr2_eeprom_fetch(hdw); | ||||
| 	if (!eeprom) return -EINVAL; | ||||
| 
 | ||||
| 	{ | ||||
| 		struct i2c_client fake_client; | ||||
| 		/* Newer version expects a useless client interface */ | ||||
| 		fake_client.addr = hdw->eeprom_addr; | ||||
| 		fake_client.adapter = &hdw->i2c_adap; | ||||
| 		tveeprom_hauppauge_analog(&fake_client,&tvdata,eeprom); | ||||
| 	} | ||||
| 
 | ||||
| 	trace_eeprom("eeprom assumed v4l tveeprom module"); | ||||
| 	trace_eeprom("eeprom direct call results:"); | ||||
| 	trace_eeprom("has_radio=%d",tvdata.has_radio); | ||||
| 	trace_eeprom("tuner_type=%d",tvdata.tuner_type); | ||||
| 	trace_eeprom("tuner_formats=0x%x",tvdata.tuner_formats); | ||||
| 	trace_eeprom("audio_processor=%d",tvdata.audio_processor); | ||||
| 	trace_eeprom("model=%d",tvdata.model); | ||||
| 	trace_eeprom("revision=%d",tvdata.revision); | ||||
| 	trace_eeprom("serial_number=%d",tvdata.serial_number); | ||||
| 	trace_eeprom("rev_str=%s",tvdata.rev_str); | ||||
| 	hdw->tuner_type = tvdata.tuner_type; | ||||
| 	hdw->tuner_updated = !0; | ||||
| 	hdw->serial_number = tvdata.serial_number; | ||||
| 	hdw->std_mask_eeprom = tvdata.tuner_formats; | ||||
| 
 | ||||
| 	kfree(eeprom); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										39
									
								
								drivers/media/usb/pvrusb2/pvrusb2-eeprom.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								drivers/media/usb/pvrusb2/pvrusb2-eeprom.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __PVRUSB2_EEPROM_H | ||||
| #define __PVRUSB2_EEPROM_H | ||||
| 
 | ||||
| struct pvr2_hdw; | ||||
| 
 | ||||
| int pvr2_eeprom_analyze(struct pvr2_hdw *); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_EEPROM_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										551
									
								
								drivers/media/usb/pvrusb2/pvrusb2-encoder.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										551
									
								
								drivers/media/usb/pvrusb2/pvrusb2-encoder.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,551 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/device.h>   // for linux/firmware.h | ||||
| #include <linux/firmware.h> | ||||
| #include "pvrusb2-util.h" | ||||
| #include "pvrusb2-encoder.h" | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include "pvrusb2-fx2-cmd.h" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* Firmware mailbox flags - definitions found from ivtv */ | ||||
| #define IVTV_MBOX_FIRMWARE_DONE 0x00000004 | ||||
| #define IVTV_MBOX_DRIVER_DONE 0x00000002 | ||||
| #define IVTV_MBOX_DRIVER_BUSY 0x00000001 | ||||
| 
 | ||||
| #define MBOX_BASE 0x44 | ||||
| 
 | ||||
| 
 | ||||
| static int pvr2_encoder_write_words(struct pvr2_hdw *hdw, | ||||
| 				    unsigned int offs, | ||||
| 				    const u32 *data, unsigned int dlen) | ||||
| { | ||||
| 	unsigned int idx,addr; | ||||
| 	unsigned int bAddr; | ||||
| 	int ret; | ||||
| 	unsigned int chunkCnt; | ||||
| 
 | ||||
| 	/*
 | ||||
| 
 | ||||
| 	Format: First byte must be 0x01.  Remaining 32 bit words are | ||||
| 	spread out into chunks of 7 bytes each, with the first 4 bytes | ||||
| 	being the data word (little endian), and the next 3 bytes | ||||
| 	being the address where that data word is to be written (big | ||||
| 	endian).  Repeat request for additional words, with offset | ||||
| 	adjusted accordingly. | ||||
| 
 | ||||
| 	*/ | ||||
| 	while (dlen) { | ||||
| 		chunkCnt = 8; | ||||
| 		if (chunkCnt > dlen) chunkCnt = dlen; | ||||
| 		memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); | ||||
| 		bAddr = 0; | ||||
| 		hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD; | ||||
| 		for (idx = 0; idx < chunkCnt; idx++) { | ||||
| 			addr = idx + offs; | ||||
| 			hdw->cmd_buffer[bAddr+6] = (addr & 0xffu); | ||||
| 			hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu); | ||||
| 			hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu); | ||||
| 			PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]); | ||||
| 			bAddr += 7; | ||||
| 		} | ||||
| 		ret = pvr2_send_request(hdw, | ||||
| 					hdw->cmd_buffer,1+(chunkCnt*7), | ||||
| 					NULL,0); | ||||
| 		if (ret) return ret; | ||||
| 		data += chunkCnt; | ||||
| 		dlen -= chunkCnt; | ||||
| 		offs += chunkCnt; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int pvr2_encoder_read_words(struct pvr2_hdw *hdw, | ||||
| 				   unsigned int offs, | ||||
| 				   u32 *data, unsigned int dlen) | ||||
| { | ||||
| 	unsigned int idx; | ||||
| 	int ret; | ||||
| 	unsigned int chunkCnt; | ||||
| 
 | ||||
| 	/*
 | ||||
| 
 | ||||
| 	Format: First byte must be 0x02 (status check) or 0x28 (read | ||||
| 	back block of 32 bit words).  Next 6 bytes must be zero, | ||||
| 	followed by a single byte of MBOX_BASE+offset for portion to | ||||
| 	be read.  Returned data is packed set of 32 bits words that | ||||
| 	were read. | ||||
| 
 | ||||
| 	*/ | ||||
| 
 | ||||
| 	while (dlen) { | ||||
| 		chunkCnt = 16; | ||||
| 		if (chunkCnt > dlen) chunkCnt = dlen; | ||||
| 		if (chunkCnt < 16) chunkCnt = 1; | ||||
| 		hdw->cmd_buffer[0] = | ||||
| 			((chunkCnt == 1) ? | ||||
| 			 FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES); | ||||
| 		hdw->cmd_buffer[1] = 0; | ||||
| 		hdw->cmd_buffer[2] = 0; | ||||
| 		hdw->cmd_buffer[3] = 0; | ||||
| 		hdw->cmd_buffer[4] = 0; | ||||
| 		hdw->cmd_buffer[5] = ((offs>>16) & 0xffu); | ||||
| 		hdw->cmd_buffer[6] = ((offs>>8) & 0xffu); | ||||
| 		hdw->cmd_buffer[7] = (offs & 0xffu); | ||||
| 		ret = pvr2_send_request(hdw, | ||||
| 					hdw->cmd_buffer,8, | ||||
| 					hdw->cmd_buffer, | ||||
| 					(chunkCnt == 1 ? 4 : 16 * 4)); | ||||
| 		if (ret) return ret; | ||||
| 
 | ||||
| 		for (idx = 0; idx < chunkCnt; idx++) { | ||||
| 			data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4); | ||||
| 		} | ||||
| 		data += chunkCnt; | ||||
| 		dlen -= chunkCnt; | ||||
| 		offs += chunkCnt; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* This prototype is set up to be compatible with the
 | ||||
|    cx2341x_mbox_func prototype in cx2341x.h, which should be in | ||||
|    kernels 2.6.18 or later.  We do this so that we can enable | ||||
|    cx2341x.ko to write to our encoder (by handing it a pointer to this | ||||
|    function).  For earlier kernels this doesn't really matter. */ | ||||
| static int pvr2_encoder_cmd(void *ctxt, | ||||
| 			    u32 cmd, | ||||
| 			    int arg_cnt_send, | ||||
| 			    int arg_cnt_recv, | ||||
| 			    u32 *argp) | ||||
| { | ||||
| 	unsigned int poll_count; | ||||
| 	unsigned int try_count = 0; | ||||
| 	int retry_flag; | ||||
| 	int ret = 0; | ||||
| 	unsigned int idx; | ||||
| 	/* These sizes look to be limited by the FX2 firmware implementation */ | ||||
| 	u32 wrData[16]; | ||||
| 	u32 rdData[16]; | ||||
| 	struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt; | ||||
| 
 | ||||
| 
 | ||||
| 	/*
 | ||||
| 
 | ||||
| 	The encoder seems to speak entirely using blocks 32 bit words. | ||||
| 	In ivtv driver terms, this is a mailbox at MBOX_BASE which we | ||||
| 	populate with data and watch what the hardware does with it. | ||||
| 	The first word is a set of flags used to control the | ||||
| 	transaction, the second word is the command to execute, the | ||||
| 	third byte is zero (ivtv driver suggests that this is some | ||||
| 	kind of return value), and the fourth byte is a specified | ||||
| 	timeout (windows driver always uses 0x00060000 except for one | ||||
| 	case when it is zero).  All successive words are the argument | ||||
| 	words for the command. | ||||
| 
 | ||||
| 	First, write out the entire set of words, with the first word | ||||
| 	being zero. | ||||
| 
 | ||||
| 	Next, write out just the first word again, but set it to | ||||
| 	IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which | ||||
| 	probably means "go"). | ||||
| 
 | ||||
| 	Next, read back the return count words.  Check the first word, | ||||
| 	which should have IVTV_MBOX_FIRMWARE_DONE set.  If however | ||||
| 	that bit is not set, then the command isn't done so repeat the | ||||
| 	read until it is set. | ||||
| 
 | ||||
| 	Finally, write out just the first word again, but set it to | ||||
| 	0x0 this time (which probably means "idle"). | ||||
| 
 | ||||
| 	*/ | ||||
| 
 | ||||
| 	if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) { | ||||
| 		pvr2_trace( | ||||
| 			PVR2_TRACE_ERROR_LEGS, | ||||
| 			"Failed to write cx23416 command" | ||||
| 			" - too many input arguments" | ||||
| 			" (was given %u limit %lu)", | ||||
| 			arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) { | ||||
| 		pvr2_trace( | ||||
| 			PVR2_TRACE_ERROR_LEGS, | ||||
| 			"Failed to write cx23416 command" | ||||
| 			" - too many return arguments" | ||||
| 			" (was given %u limit %lu)", | ||||
| 			arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	LOCK_TAKE(hdw->ctl_lock); do { | ||||
| 
 | ||||
| 		if (!hdw->state_encoder_ok) { | ||||
| 			ret = -EIO; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		retry_flag = 0; | ||||
| 		try_count++; | ||||
| 		ret = 0; | ||||
| 		wrData[0] = 0; | ||||
| 		wrData[1] = cmd; | ||||
| 		wrData[2] = 0; | ||||
| 		wrData[3] = 0x00060000; | ||||
| 		for (idx = 0; idx < arg_cnt_send; idx++) { | ||||
| 			wrData[idx+4] = argp[idx]; | ||||
| 		} | ||||
| 		for (; idx < ARRAY_SIZE(wrData) - 4; idx++) { | ||||
| 			wrData[idx+4] = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx); | ||||
| 		if (ret) break; | ||||
| 		wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY; | ||||
| 		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); | ||||
| 		if (ret) break; | ||||
| 		poll_count = 0; | ||||
| 		while (1) { | ||||
| 			poll_count++; | ||||
| 			ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData, | ||||
| 						      arg_cnt_recv+4); | ||||
| 			if (ret) { | ||||
| 				break; | ||||
| 			} | ||||
| 			if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) { | ||||
| 				break; | ||||
| 			} | ||||
| 			if (rdData[0] && (poll_count < 1000)) continue; | ||||
| 			if (!rdData[0]) { | ||||
| 				retry_flag = !0; | ||||
| 				pvr2_trace( | ||||
| 					PVR2_TRACE_ERROR_LEGS, | ||||
| 					"Encoder timed out waiting for us" | ||||
| 					"; arranging to retry"); | ||||
| 			} else { | ||||
| 				pvr2_trace( | ||||
| 					PVR2_TRACE_ERROR_LEGS, | ||||
| 					"***WARNING*** device's encoder" | ||||
| 					" appears to be stuck" | ||||
| 					" (status=0x%08x)",rdData[0]); | ||||
| 			} | ||||
| 			pvr2_trace( | ||||
| 				PVR2_TRACE_ERROR_LEGS, | ||||
| 				"Encoder command: 0x%02x",cmd); | ||||
| 			for (idx = 4; idx < arg_cnt_send; idx++) { | ||||
| 				pvr2_trace( | ||||
| 					PVR2_TRACE_ERROR_LEGS, | ||||
| 					"Encoder arg%d: 0x%08x", | ||||
| 					idx-3,wrData[idx]); | ||||
| 			} | ||||
| 			ret = -EBUSY; | ||||
| 			break; | ||||
| 		} | ||||
| 		if (retry_flag) { | ||||
| 			if (try_count < 20) continue; | ||||
| 			pvr2_trace( | ||||
| 				PVR2_TRACE_ERROR_LEGS, | ||||
| 				"Too many retries..."); | ||||
| 			ret = -EBUSY; | ||||
| 		} | ||||
| 		if (ret) { | ||||
| 			del_timer_sync(&hdw->encoder_run_timer); | ||||
| 			hdw->state_encoder_ok = 0; | ||||
| 			pvr2_trace(PVR2_TRACE_STBITS, | ||||
| 				   "State bit %s <-- %s", | ||||
| 				   "state_encoder_ok", | ||||
| 				   (hdw->state_encoder_ok ? "true" : "false")); | ||||
| 			if (hdw->state_encoder_runok) { | ||||
| 				hdw->state_encoder_runok = 0; | ||||
| 				pvr2_trace(PVR2_TRACE_STBITS, | ||||
| 				   "State bit %s <-- %s", | ||||
| 					   "state_encoder_runok", | ||||
| 					   (hdw->state_encoder_runok ? | ||||
| 					    "true" : "false")); | ||||
| 			} | ||||
| 			pvr2_trace( | ||||
| 				PVR2_TRACE_ERROR_LEGS, | ||||
| 				"Giving up on command." | ||||
| 				"  This is normally recovered via a firmware" | ||||
| 				" reload and re-initialization; concern" | ||||
| 				" is only warranted if this happens repeatedly" | ||||
| 				" and rapidly."); | ||||
| 			break; | ||||
| 		} | ||||
| 		wrData[0] = 0x7; | ||||
| 		for (idx = 0; idx < arg_cnt_recv; idx++) { | ||||
| 			argp[idx] = rdData[idx+4]; | ||||
| 		} | ||||
| 
 | ||||
| 		wrData[0] = 0x0; | ||||
| 		ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); | ||||
| 		if (ret) break; | ||||
| 
 | ||||
| 	} while(0); LOCK_GIVE(hdw->ctl_lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, | ||||
| 			     int args, ...) | ||||
| { | ||||
| 	va_list vl; | ||||
| 	unsigned int idx; | ||||
| 	u32 data[12]; | ||||
| 
 | ||||
| 	if (args > ARRAY_SIZE(data)) { | ||||
| 		pvr2_trace( | ||||
| 			PVR2_TRACE_ERROR_LEGS, | ||||
| 			"Failed to write cx23416 command" | ||||
| 			" - too many arguments" | ||||
| 			" (was given %u limit %lu)", | ||||
| 			args, (long unsigned) ARRAY_SIZE(data)); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	va_start(vl, args); | ||||
| 	for (idx = 0; idx < args; idx++) { | ||||
| 		data[idx] = va_arg(vl, u32); | ||||
| 	} | ||||
| 	va_end(vl); | ||||
| 
 | ||||
| 	return pvr2_encoder_cmd(hdw,cmd,args,0,data); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* This implements some extra setup for the encoder that seems to be
 | ||||
|    specific to the PVR USB2 hardware. */ | ||||
| static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	int encMisc3Arg = 0; | ||||
| 
 | ||||
| #if 0 | ||||
| 	/* This inexplicable bit happens in the Hauppauge windows
 | ||||
| 	   driver (for both 24xxx and 29xxx devices).  However I | ||||
| 	   currently see no difference in behavior with or without | ||||
| 	   this stuff.  Leave this here as a note of its existence, | ||||
| 	   but don't use it. */ | ||||
| 	LOCK_TAKE(hdw->ctl_lock); do { | ||||
| 		u32 dat[1]; | ||||
| 		dat[0] = 0x80000640; | ||||
| 		pvr2_encoder_write_words(hdw,0x01fe,dat,1); | ||||
| 		pvr2_encoder_write_words(hdw,0x023e,dat,1); | ||||
| 	} while(0); LOCK_GIVE(hdw->ctl_lock); | ||||
| #endif | ||||
| 
 | ||||
| 	/* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver
 | ||||
| 	   sends the following list of ENC_MISC commands (for both | ||||
| 	   24xxx and 29xxx devices).  Meanings are not entirely clear, | ||||
| 	   however without the ENC_MISC(3,1) command then we risk | ||||
| 	   random perpetual video corruption whenever the video input | ||||
| 	   breaks up for a moment (like when switching channels). */ | ||||
| 
 | ||||
| 
 | ||||
| #if 0 | ||||
| 	/* This ENC_MISC(5,0) command seems to hurt 29xxx sync
 | ||||
| 	   performance on channel changes, but is not a problem on | ||||
| 	   24xxx devices. */ | ||||
| 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); | ||||
| #endif | ||||
| 
 | ||||
| 	/* This ENC_MISC(3,encMisc3Arg) command is critical - without
 | ||||
| 	   it there will eventually be video corruption.  Also, the | ||||
| 	   saa7115 case is strange - the Windows driver is passing 1 | ||||
| 	   regardless of device type but if we have 1 for saa7115 | ||||
| 	   devices the video turns sluggish.  */ | ||||
| 	if (hdw->hdw_desc->flag_has_cx25840) { | ||||
| 		encMisc3Arg = 1; | ||||
| 	} else { | ||||
| 		encMisc3Arg = 0; | ||||
| 	} | ||||
| 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3, | ||||
| 				 encMisc3Arg,0,0); | ||||
| 
 | ||||
| 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); | ||||
| 
 | ||||
| #if 0 | ||||
| 	/* This ENC_MISC(4,1) command is poisonous, so it is commented
 | ||||
| 	   out.  But I'm leaving it here anyway to document its | ||||
| 	   existence in the Windows driver.  The effect of this | ||||
| 	   command is that apps displaying the stream become sluggish | ||||
| 	   with stuttering video. */ | ||||
| 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); | ||||
| #endif | ||||
| 
 | ||||
| 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); | ||||
| 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); | ||||
| 
 | ||||
| 	/* prevent the PTSs from slowly drifting away in the generated
 | ||||
| 	   MPEG stream */ | ||||
| 	ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int pvr2_encoder_adjust(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	int ret; | ||||
| 	ret = cx2341x_update(hdw,pvr2_encoder_cmd, | ||||
| 			     (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), | ||||
| 			     &hdw->enc_ctl_state); | ||||
| 	if (ret) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "Error from cx2341x module code=%d",ret); | ||||
| 	} else { | ||||
| 		hdw->enc_cur_state = hdw->enc_ctl_state; | ||||
| 		hdw->enc_cur_valid = !0; | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int pvr2_encoder_configure(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	int ret; | ||||
| 	int val; | ||||
| 	pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure" | ||||
| 		   " (cx2341x module)"); | ||||
| 	hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING; | ||||
| 	hdw->enc_ctl_state.width = hdw->res_hor_val; | ||||
| 	hdw->enc_ctl_state.height = hdw->res_ver_val; | ||||
| 	hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ? | ||||
| 				      0 : 1); | ||||
| 
 | ||||
| 	ret = 0; | ||||
| 
 | ||||
| 	ret |= pvr2_encoder_prep_config(hdw); | ||||
| 
 | ||||
| 	/* saa7115: 0xf0 */ | ||||
| 	val = 0xf0; | ||||
| 	if (hdw->hdw_desc->flag_has_cx25840) { | ||||
| 		/* ivtv cx25840: 0x140 */ | ||||
| 		val = 0x140; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!ret) ret = pvr2_encoder_vcmd( | ||||
| 		hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, | ||||
| 		val, val); | ||||
| 
 | ||||
| 	/* setup firmware to notify us about some events (don't know why...) */ | ||||
| 	if (!ret) ret = pvr2_encoder_vcmd( | ||||
| 		hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, | ||||
| 		0, 0, 0x10000000, 0xffffffff); | ||||
| 
 | ||||
| 	if (!ret) ret = pvr2_encoder_vcmd( | ||||
| 		hdw,CX2341X_ENC_SET_VBI_LINE, 5, | ||||
| 		0xffffffff,0,0,0,0); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "Failed to configure cx23416"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = pvr2_encoder_adjust(hdw); | ||||
| 	if (ret) return ret; | ||||
| 
 | ||||
| 	ret = pvr2_encoder_vcmd( | ||||
| 		hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "Failed to initialize cx23416 video input"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int pvr2_encoder_start(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	int status; | ||||
| 
 | ||||
| 	/* unmask some interrupts */ | ||||
| 	pvr2_write_register(hdw, 0x0048, 0xbfffffff); | ||||
| 
 | ||||
| 	pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, | ||||
| 			  hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); | ||||
| 
 | ||||
| 	switch (hdw->active_stream_type) { | ||||
| 	case pvr2_config_vbi: | ||||
| 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, | ||||
| 					   0x01,0x14); | ||||
| 		break; | ||||
| 	case pvr2_config_mpeg: | ||||
| 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, | ||||
| 					   0,0x13); | ||||
| 		break; | ||||
| 	default: /* Unhandled cases for now */ | ||||
| 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, | ||||
| 					   0,0x13); | ||||
| 		break; | ||||
| 	} | ||||
| 	return status; | ||||
| } | ||||
| 
 | ||||
| int pvr2_encoder_stop(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	int status; | ||||
| 
 | ||||
| 	/* mask all interrupts */ | ||||
| 	pvr2_write_register(hdw, 0x0048, 0xffffffff); | ||||
| 
 | ||||
| 	switch (hdw->active_stream_type) { | ||||
| 	case pvr2_config_vbi: | ||||
| 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, | ||||
| 					   0x01,0x01,0x14); | ||||
| 		break; | ||||
| 	case pvr2_config_mpeg: | ||||
| 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, | ||||
| 					   0x01,0,0x13); | ||||
| 		break; | ||||
| 	default: /* Unhandled cases for now */ | ||||
| 		status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, | ||||
| 					   0x01,0,0x13); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return status; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										42
									
								
								drivers/media/usb/pvrusb2/pvrusb2-encoder.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								drivers/media/usb/pvrusb2/pvrusb2-encoder.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __PVRUSB2_ENCODER_H | ||||
| #define __PVRUSB2_ENCODER_H | ||||
| 
 | ||||
| struct pvr2_hdw; | ||||
| 
 | ||||
| int pvr2_encoder_adjust(struct pvr2_hdw *); | ||||
| int pvr2_encoder_configure(struct pvr2_hdw *); | ||||
| int pvr2_encoder_start(struct pvr2_hdw *); | ||||
| int pvr2_encoder_stop(struct pvr2_hdw *); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_ENCODER_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										72
									
								
								drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,72 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2007 Michael Krufky <mkrufky@linuxtv.org> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _PVRUSB2_FX2_CMD_H_ | ||||
| #define _PVRUSB2_FX2_CMD_H_ | ||||
| 
 | ||||
| #define FX2CMD_MEM_WRITE_DWORD  0x01u | ||||
| #define FX2CMD_MEM_READ_DWORD   0x02u | ||||
| 
 | ||||
| #define FX2CMD_HCW_ZILOG_RESET  0x10u /* 1=reset 0=release */ | ||||
| 
 | ||||
| #define FX2CMD_MEM_READ_64BYTES 0x28u | ||||
| 
 | ||||
| #define FX2CMD_REG_WRITE        0x04u | ||||
| #define FX2CMD_REG_READ         0x05u | ||||
| #define FX2CMD_MEMSEL           0x06u | ||||
| 
 | ||||
| #define FX2CMD_I2C_WRITE        0x08u | ||||
| #define FX2CMD_I2C_READ         0x09u | ||||
| 
 | ||||
| #define FX2CMD_GET_USB_SPEED    0x0bu | ||||
| 
 | ||||
| #define FX2CMD_STREAMING_ON     0x36u | ||||
| #define FX2CMD_STREAMING_OFF    0x37u | ||||
| 
 | ||||
| #define FX2CMD_FWPOST1          0x52u | ||||
| 
 | ||||
| #define FX2CMD_POWER_OFF        0xdcu | ||||
| #define FX2CMD_POWER_ON         0xdeu | ||||
| 
 | ||||
| #define FX2CMD_DEEP_RESET       0xddu | ||||
| 
 | ||||
| #define FX2CMD_GET_EEPROM_ADDR  0xebu | ||||
| #define FX2CMD_GET_IR_CODE      0xecu | ||||
| 
 | ||||
| #define FX2CMD_HCW_DEMOD_RESETIN       0xf0u | ||||
| #define FX2CMD_HCW_DTV_STREAMING_ON    0xf1u | ||||
| #define FX2CMD_HCW_DTV_STREAMING_OFF   0xf2u | ||||
| 
 | ||||
| #define FX2CMD_ONAIR_DTV_STREAMING_ON  0xa0u | ||||
| #define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1u | ||||
| #define FX2CMD_ONAIR_DTV_POWER_ON      0xa2u | ||||
| #define FX2CMD_ONAIR_DTV_POWER_OFF     0xa3u | ||||
| 
 | ||||
| #endif /* _PVRUSB2_FX2_CMD_H_ */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										406
									
								
								drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										406
									
								
								drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,406 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_HDW_INTERNAL_H | ||||
| #define __PVRUSB2_HDW_INTERNAL_H | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|   This header sets up all the internal structures and definitions needed to | ||||
|   track and coordinate the driver's interaction with the hardware.  ONLY | ||||
|   source files which actually implement part of that whole circus should be | ||||
|   including this header.  Higher levels, like the external layers to the | ||||
|   various public APIs (V4L, sysfs, etc) should NOT ever include this | ||||
|   private, internal header.  This means that pvrusb2-hdw, pvrusb2-encoder, | ||||
|   etc will include this, but pvrusb2-v4l should not. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #include <linux/videodev2.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/workqueue.h> | ||||
| #include <linux/mutex.h> | ||||
| #include "pvrusb2-hdw.h" | ||||
| #include "pvrusb2-io.h" | ||||
| #include <media/v4l2-device.h> | ||||
| #include <media/cx2341x.h> | ||||
| #include <media/ir-kbd-i2c.h> | ||||
| #include "pvrusb2-devattr.h" | ||||
| 
 | ||||
| /* Legal values for PVR2_CID_HSM */ | ||||
| #define PVR2_CVAL_HSM_FAIL 0 | ||||
| #define PVR2_CVAL_HSM_FULL 1 | ||||
| #define PVR2_CVAL_HSM_HIGH 2 | ||||
| 
 | ||||
| #define PVR2_VID_ENDPOINT        0x84 | ||||
| #define PVR2_UNK_ENDPOINT        0x86    /* maybe raw yuv ? */ | ||||
| #define PVR2_VBI_ENDPOINT        0x88 | ||||
| 
 | ||||
| #define PVR2_CTL_BUFFSIZE        64 | ||||
| 
 | ||||
| #define FREQTABLE_SIZE 500 | ||||
| 
 | ||||
| #define LOCK_TAKE(x) do { mutex_lock(&x##_mutex); x##_held = !0; } while (0) | ||||
| #define LOCK_GIVE(x) do { x##_held = 0; mutex_unlock(&x##_mutex); } while (0) | ||||
| 
 | ||||
| typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *); | ||||
| typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *); | ||||
| typedef int (*pvr2_ctlf_check_value)(struct pvr2_ctrl *,int); | ||||
| typedef int (*pvr2_ctlf_get_value)(struct pvr2_ctrl *,int *); | ||||
| typedef int (*pvr2_ctlf_set_value)(struct pvr2_ctrl *,int msk,int val); | ||||
| typedef int (*pvr2_ctlf_val_to_sym)(struct pvr2_ctrl *,int msk,int val, | ||||
| 				    char *,unsigned int,unsigned int *); | ||||
| typedef int (*pvr2_ctlf_sym_to_val)(struct pvr2_ctrl *, | ||||
| 				    const char *,unsigned int, | ||||
| 				    int *mskp,int *valp); | ||||
| typedef unsigned int (*pvr2_ctlf_get_v4lflags)(struct pvr2_ctrl *); | ||||
| 
 | ||||
| /* This structure describes a specific control.  A table of these is set up
 | ||||
|    in pvrusb2-hdw.c. */ | ||||
| struct pvr2_ctl_info { | ||||
| 	/* Control's name suitable for use as an identifier */ | ||||
| 	const char *name; | ||||
| 
 | ||||
| 	/* Short description of control */ | ||||
| 	const char *desc; | ||||
| 
 | ||||
| 	/* Control's implementation */ | ||||
| 	pvr2_ctlf_get_value get_value;      /* Get its value */ | ||||
| 	pvr2_ctlf_get_value get_def_value;  /* Get its default value */ | ||||
| 	pvr2_ctlf_get_value get_min_value;  /* Get minimum allowed value */ | ||||
| 	pvr2_ctlf_get_value get_max_value;  /* Get maximum allowed value */ | ||||
| 	pvr2_ctlf_set_value set_value;      /* Set its value */ | ||||
| 	pvr2_ctlf_check_value check_value;  /* Check that value is valid */ | ||||
| 	pvr2_ctlf_val_to_sym val_to_sym;    /* Custom convert value->symbol */ | ||||
| 	pvr2_ctlf_sym_to_val sym_to_val;    /* Custom convert symbol->value */ | ||||
| 	pvr2_ctlf_is_dirty is_dirty;        /* Return true if dirty */ | ||||
| 	pvr2_ctlf_clear_dirty clear_dirty;  /* Clear dirty state */ | ||||
| 	pvr2_ctlf_get_v4lflags get_v4lflags;/* Retrieve v4l flags */ | ||||
| 
 | ||||
| 	/* Control's type (int, enum, bitmask) */ | ||||
| 	enum pvr2_ctl_type type; | ||||
| 
 | ||||
| 	/* Associated V4L control ID, if any */ | ||||
| 	int v4l_id; | ||||
| 
 | ||||
| 	/* Associated driver internal ID, if any */ | ||||
| 	int internal_id; | ||||
| 
 | ||||
| 	/* Don't implicitly initialize this control's value */ | ||||
| 	int skip_init; | ||||
| 
 | ||||
| 	/* Starting value for this control */ | ||||
| 	int default_value; | ||||
| 
 | ||||
| 	/* Type-specific control information */ | ||||
| 	union { | ||||
| 		struct { /* Integer control */ | ||||
| 			long min_value; /* lower limit */ | ||||
| 			long max_value; /* upper limit */ | ||||
| 		} type_int; | ||||
| 		struct { /* enumerated control */ | ||||
| 			unsigned int count;       /* enum value count */ | ||||
| 			const char * const *value_names; /* symbol names */ | ||||
| 		} type_enum; | ||||
| 		struct { /* bitmask control */ | ||||
| 			unsigned int valid_bits; /* bits in use */ | ||||
| 			const char **bit_names;  /* symbol name/bit */ | ||||
| 		} type_bitmask; | ||||
| 	} def; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* Same as pvr2_ctl_info, but includes storage for the control description */ | ||||
| #define PVR2_CTLD_INFO_DESC_SIZE 32 | ||||
| struct pvr2_ctld_info { | ||||
| 	struct pvr2_ctl_info info; | ||||
| 	char desc[PVR2_CTLD_INFO_DESC_SIZE]; | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_ctrl { | ||||
| 	const struct pvr2_ctl_info *info; | ||||
| 	struct pvr2_hdw *hdw; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* Disposition of firmware1 loading situation */ | ||||
| #define FW1_STATE_UNKNOWN 0 | ||||
| #define FW1_STATE_MISSING 1 | ||||
| #define FW1_STATE_FAILED 2 | ||||
| #define FW1_STATE_RELOAD 3 | ||||
| #define FW1_STATE_OK 4 | ||||
| 
 | ||||
| /* What state the device is in if it is a hybrid */ | ||||
| #define PVR2_PATHWAY_UNKNOWN 0 | ||||
| #define PVR2_PATHWAY_ANALOG 1 | ||||
| #define PVR2_PATHWAY_DIGITAL 2 | ||||
| 
 | ||||
| typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); | ||||
| #define PVR2_I2C_FUNC_CNT 128 | ||||
| 
 | ||||
| /* This structure contains all state data directly needed to
 | ||||
|    manipulate the hardware (as opposed to complying with a kernel | ||||
|    interface) */ | ||||
| struct pvr2_hdw { | ||||
| 	/* Underlying USB device handle */ | ||||
| 	struct usb_device *usb_dev; | ||||
| 	struct usb_interface *usb_intf; | ||||
| 
 | ||||
| 	/* Our handle into the v4l2 sub-device architecture */ | ||||
| 	struct v4l2_device v4l2_dev; | ||||
| 	/* Device description, anything that must adjust behavior based on
 | ||||
| 	   device specific info will use information held here. */ | ||||
| 	const struct pvr2_device_desc *hdw_desc; | ||||
| 
 | ||||
| 	/* Kernel worker thread handling */ | ||||
| 	struct workqueue_struct *workqueue; | ||||
| 	struct work_struct workpoll;     /* Update driver state */ | ||||
| 
 | ||||
| 	/* Video spigot */ | ||||
| 	struct pvr2_stream *vid_stream; | ||||
| 
 | ||||
| 	/* Mutex for all hardware state control */ | ||||
| 	struct mutex big_lock_mutex; | ||||
| 	int big_lock_held;  /* For debugging */ | ||||
| 
 | ||||
| 	/* This is a simple string which identifies the instance of this
 | ||||
| 	   driver.  It is unique within the set of existing devices, but | ||||
| 	   there is no attempt to keep the name consistent with the same | ||||
| 	   physical device each time. */ | ||||
| 	char name[32]; | ||||
| 
 | ||||
| 	/* This is a simple string which identifies the physical device
 | ||||
| 	   instance itself - if possible.  (If not possible, then it is | ||||
| 	   based on the specific driver instance, similar to name above.) | ||||
| 	   The idea here is that userspace might hopefully be able to use | ||||
| 	   this recognize specific tuners.  It will encode a serial number, | ||||
| 	   if available. */ | ||||
| 	char identifier[32]; | ||||
| 
 | ||||
| 	/* I2C stuff */ | ||||
| 	struct i2c_adapter i2c_adap; | ||||
| 	struct i2c_algorithm i2c_algo; | ||||
| 	pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT]; | ||||
| 	int i2c_cx25840_hack_state; | ||||
| 	int i2c_linked; | ||||
| 
 | ||||
| 	/* IR related */ | ||||
| 	unsigned int ir_scheme_active; /* IR scheme as seen from the outside */ | ||||
| 	struct IR_i2c_init_data ir_init_data; /* params passed to IR modules */ | ||||
| 
 | ||||
| 	/* Frequency table */ | ||||
| 	unsigned int freqTable[FREQTABLE_SIZE]; | ||||
| 	unsigned int freqProgSlot; | ||||
| 
 | ||||
| 	/* Stuff for handling low level control interaction with device */ | ||||
| 	struct mutex ctl_lock_mutex; | ||||
| 	int ctl_lock_held;  /* For debugging */ | ||||
| 	struct urb *ctl_write_urb; | ||||
| 	struct urb *ctl_read_urb; | ||||
| 	unsigned char *ctl_write_buffer; | ||||
| 	unsigned char *ctl_read_buffer; | ||||
| 	int ctl_write_pend_flag; | ||||
| 	int ctl_read_pend_flag; | ||||
| 	int ctl_timeout_flag; | ||||
| 	struct completion ctl_done; | ||||
| 	unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE]; | ||||
| 	int cmd_debug_state;               // Low level command debugging info
 | ||||
| 	unsigned char cmd_debug_code;      //
 | ||||
| 	unsigned int cmd_debug_write_len;  //
 | ||||
| 	unsigned int cmd_debug_read_len;   //
 | ||||
| 
 | ||||
| 	/* Bits of state that describe what is going on with various parts
 | ||||
| 	   of the driver. */ | ||||
| 	int state_pathway_ok;         /* Pathway config is ok */ | ||||
| 	int state_encoder_ok;         /* Encoder is operational */ | ||||
| 	int state_encoder_run;        /* Encoder is running */ | ||||
| 	int state_encoder_config;     /* Encoder is configured */ | ||||
| 	int state_encoder_waitok;     /* Encoder pre-wait done */ | ||||
| 	int state_encoder_runok;      /* Encoder has run for >= .25 sec */ | ||||
| 	int state_decoder_run;        /* Decoder is running */ | ||||
| 	int state_decoder_ready;      /* Decoder is stabilized & streamable */ | ||||
| 	int state_usbstream_run;      /* FX2 is streaming */ | ||||
| 	int state_decoder_quiescent;  /* Decoder idle for minimal interval */ | ||||
| 	int state_pipeline_config;    /* Pipeline is configured */ | ||||
| 	int state_pipeline_req;       /* Somebody wants to stream */ | ||||
| 	int state_pipeline_pause;     /* Pipeline must be paused */ | ||||
| 	int state_pipeline_idle;      /* Pipeline not running */ | ||||
| 
 | ||||
| 	/* This is the master state of the driver.  It is the combined
 | ||||
| 	   result of other bits of state.  Examining this will indicate the | ||||
| 	   overall state of the driver.  Values here are one of | ||||
| 	   PVR2_STATE_xxxx */ | ||||
| 	unsigned int master_state; | ||||
| 
 | ||||
| 	/* True if device led is currently on */ | ||||
| 	int led_on; | ||||
| 
 | ||||
| 	/* True if states must be re-evaluated */ | ||||
| 	int state_stale; | ||||
| 
 | ||||
| 	void (*state_func)(void *); | ||||
| 	void *state_data; | ||||
| 
 | ||||
| 	/* Timer for measuring required decoder settling time before we're
 | ||||
| 	   allowed to fire it up again. */ | ||||
| 	struct timer_list quiescent_timer; | ||||
| 
 | ||||
| 	/* Timer for measuring decoder stabilization time, which is the
 | ||||
| 	   amount of time we need to let the decoder run before we can | ||||
| 	   trust its output (otherwise the encoder might see garbage and | ||||
| 	   then fail to start correctly). */ | ||||
| 	struct timer_list decoder_stabilization_timer; | ||||
| 
 | ||||
| 	/* Timer for measuring encoder pre-wait time */ | ||||
| 	struct timer_list encoder_wait_timer; | ||||
| 
 | ||||
| 	/* Timer for measuring encoder minimum run time */ | ||||
| 	struct timer_list encoder_run_timer; | ||||
| 
 | ||||
| 	/* Place to block while waiting for state changes */ | ||||
| 	wait_queue_head_t state_wait_data; | ||||
| 
 | ||||
| 
 | ||||
| 	int force_dirty;        /* consider all controls dirty if true */ | ||||
| 	int flag_ok;            /* device in known good state */ | ||||
| 	int flag_modulefail;    /* true if at least one module failed to load */ | ||||
| 	int flag_disconnected;  /* flag_ok == 0 due to disconnect */ | ||||
| 	int flag_init_ok;       /* true if structure is fully initialized */ | ||||
| 	int fw1_state;          /* current situation with fw1 */ | ||||
| 	int pathway_state;      /* one of PVR2_PATHWAY_xxx */ | ||||
| 	int flag_decoder_missed;/* We've noticed missing decoder */ | ||||
| 	int flag_tripped;       /* Indicates overall failure to start */ | ||||
| 
 | ||||
| 	unsigned int decoder_client_id; | ||||
| 
 | ||||
| 	// CPU firmware info (used to help find / save firmware data)
 | ||||
| 	char *fw_buffer; | ||||
| 	unsigned int fw_size; | ||||
| 	int fw_cpu_flag; /* True if we are dealing with the CPU */ | ||||
| 
 | ||||
| 	/* Tuner / frequency control stuff */ | ||||
| 	unsigned int tuner_type; | ||||
| 	int tuner_updated; | ||||
| 	unsigned int freqValTelevision;  /* Current freq for tv mode */ | ||||
| 	unsigned int freqValRadio;       /* Current freq for radio mode */ | ||||
| 	unsigned int freqSlotTelevision; /* Current slot for tv mode */ | ||||
| 	unsigned int freqSlotRadio;      /* Current slot for radio mode */ | ||||
| 	unsigned int freqSelector;       /* 0=radio 1=television */ | ||||
| 	int freqDirty; | ||||
| 
 | ||||
| 	/* Current tuner info - this information is polled from the I2C bus */ | ||||
| 	struct v4l2_tuner tuner_signal_info; | ||||
| 	int tuner_signal_stale; | ||||
| 
 | ||||
| 	/* Cropping capability info */ | ||||
| 	struct v4l2_cropcap cropcap_info; | ||||
| 	int cropcap_stale; | ||||
| 
 | ||||
| 	/* Video standard handling */ | ||||
| 	v4l2_std_id std_mask_eeprom; // Hardware supported selections
 | ||||
| 	v4l2_std_id std_mask_avail;  // Which standards we may select from
 | ||||
| 	v4l2_std_id std_mask_cur;    // Currently selected standard(s)
 | ||||
| 	int std_enum_cur;            // selected standard enumeration value
 | ||||
| 	int std_dirty;               // True if std_mask_cur has changed
 | ||||
| 	struct pvr2_ctl_info std_info_enum; | ||||
| 	struct pvr2_ctl_info std_info_avail; | ||||
| 	struct pvr2_ctl_info std_info_cur; | ||||
| 	struct pvr2_ctl_info std_info_detect; | ||||
| 
 | ||||
| 	// Generated string names, one per actual V4L2 standard
 | ||||
| 	const char *std_mask_ptrs[32]; | ||||
| 	char std_mask_names[32][16]; | ||||
| 
 | ||||
| 	int unit_number;             /* ID for driver instance */ | ||||
| 	unsigned long serial_number; /* ID for hardware itself */ | ||||
| 
 | ||||
| 	char bus_info[32]; /* Bus location info */ | ||||
| 
 | ||||
| 	/* Minor numbers used by v4l logic (yes, this is a hack, as there
 | ||||
| 	   should be no v4l junk here).  Probably a better way to do this. */ | ||||
| 	int v4l_minor_number_video; | ||||
| 	int v4l_minor_number_vbi; | ||||
| 	int v4l_minor_number_radio; | ||||
| 
 | ||||
| 	/* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */ | ||||
| 	unsigned int input_avail_mask; | ||||
| 	/* Bit mask of PVR2_CVAL_INPUT choices which are currently allowed */ | ||||
| 	unsigned int input_allowed_mask; | ||||
| 
 | ||||
| 	/* Location of eeprom or a negative number if none */ | ||||
| 	int eeprom_addr; | ||||
| 
 | ||||
| 	enum pvr2_config active_stream_type; | ||||
| 	enum pvr2_config desired_stream_type; | ||||
| 
 | ||||
| 	/* Control state needed for cx2341x module */ | ||||
| 	struct cx2341x_mpeg_params enc_cur_state; | ||||
| 	struct cx2341x_mpeg_params enc_ctl_state; | ||||
| 	/* True if an encoder attribute has changed */ | ||||
| 	int enc_stale; | ||||
| 	/* True if an unsafe encoder attribute has changed */ | ||||
| 	int enc_unsafe_stale; | ||||
| 	/* True if enc_cur_state is valid */ | ||||
| 	int enc_cur_valid; | ||||
| 
 | ||||
| 	/* Control state */ | ||||
| #define VCREATE_DATA(lab) int lab##_val; int lab##_dirty | ||||
| 	VCREATE_DATA(brightness); | ||||
| 	VCREATE_DATA(contrast); | ||||
| 	VCREATE_DATA(saturation); | ||||
| 	VCREATE_DATA(hue); | ||||
| 	VCREATE_DATA(volume); | ||||
| 	VCREATE_DATA(balance); | ||||
| 	VCREATE_DATA(bass); | ||||
| 	VCREATE_DATA(treble); | ||||
| 	VCREATE_DATA(mute); | ||||
| 	VCREATE_DATA(cropl); | ||||
| 	VCREATE_DATA(cropt); | ||||
| 	VCREATE_DATA(cropw); | ||||
| 	VCREATE_DATA(croph); | ||||
| 	VCREATE_DATA(input); | ||||
| 	VCREATE_DATA(audiomode); | ||||
| 	VCREATE_DATA(res_hor); | ||||
| 	VCREATE_DATA(res_ver); | ||||
| 	VCREATE_DATA(srate); | ||||
| #undef VCREATE_DATA | ||||
| 
 | ||||
| 	struct pvr2_ctld_info *mpeg_ctrl_info; | ||||
| 
 | ||||
| 	struct pvr2_ctrl *controls; | ||||
| 	unsigned int control_cnt; | ||||
| }; | ||||
| 
 | ||||
| /* This function gets the current frequency */ | ||||
| unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); | ||||
| 
 | ||||
| void pvr2_hdw_status_poll(struct pvr2_hdw *); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_HDW_INTERNAL_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										5168
									
								
								drivers/media/usb/pvrusb2/pvrusb2-hdw.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5168
									
								
								drivers/media/usb/pvrusb2/pvrusb2-hdw.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										355
									
								
								drivers/media/usb/pvrusb2/pvrusb2-hdw.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										355
									
								
								drivers/media/usb/pvrusb2/pvrusb2-hdw.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,355 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_HDW_H | ||||
| #define __PVRUSB2_HDW_H | ||||
| 
 | ||||
| #include <linux/usb.h> | ||||
| #include <linux/videodev2.h> | ||||
| #include <media/v4l2-dev.h> | ||||
| #include "pvrusb2-io.h" | ||||
| #include "pvrusb2-ctrl.h" | ||||
| 
 | ||||
| 
 | ||||
| /* Private internal control ids, look these up with
 | ||||
|    pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */ | ||||
| #define PVR2_CID_STDCUR 2 | ||||
| #define PVR2_CID_STDAVAIL 3 | ||||
| #define PVR2_CID_INPUT 4 | ||||
| #define PVR2_CID_AUDIOMODE 5 | ||||
| #define PVR2_CID_FREQUENCY 6 | ||||
| #define PVR2_CID_HRES 7 | ||||
| #define PVR2_CID_VRES 8 | ||||
| #define PVR2_CID_CROPL 9 | ||||
| #define PVR2_CID_CROPT 10 | ||||
| #define PVR2_CID_CROPW 11 | ||||
| #define PVR2_CID_CROPH 12 | ||||
| #define PVR2_CID_CROPCAPPAN 13 | ||||
| #define PVR2_CID_CROPCAPPAD 14 | ||||
| #define PVR2_CID_CROPCAPBL 15 | ||||
| #define PVR2_CID_CROPCAPBT 16 | ||||
| #define PVR2_CID_CROPCAPBW 17 | ||||
| #define PVR2_CID_CROPCAPBH 18 | ||||
| #define PVR2_CID_STDDETECT 19 | ||||
| 
 | ||||
| /* Legal values for the INPUT state variable */ | ||||
| #define PVR2_CVAL_INPUT_TV 0 | ||||
| #define PVR2_CVAL_INPUT_DTV 1 | ||||
| #define PVR2_CVAL_INPUT_COMPOSITE 2 | ||||
| #define PVR2_CVAL_INPUT_SVIDEO 3 | ||||
| #define PVR2_CVAL_INPUT_RADIO 4 | ||||
| 
 | ||||
| enum pvr2_config { | ||||
| 	pvr2_config_empty,    /* No configuration */ | ||||
| 	pvr2_config_mpeg,     /* Encoded / compressed video */ | ||||
| 	pvr2_config_vbi,      /* Standard vbi info */ | ||||
| 	pvr2_config_pcm,      /* Audio raw pcm stream */ | ||||
| 	pvr2_config_rawvideo, /* Video raw frames */ | ||||
| }; | ||||
| 
 | ||||
| enum pvr2_v4l_type { | ||||
| 	pvr2_v4l_type_video, | ||||
| 	pvr2_v4l_type_vbi, | ||||
| 	pvr2_v4l_type_radio, | ||||
| }; | ||||
| 
 | ||||
| /* Major states that we can be in:
 | ||||
|  * | ||||
|  *  DEAD - Device is in an unusable state and cannot be recovered.  This | ||||
|  *  can happen if we completely lose the ability to communicate with it | ||||
|  *  (but it might still on the bus).  In this state there's nothing we can | ||||
|  *  do; it must be replugged in order to recover. | ||||
|  * | ||||
|  *  COLD - Device is in an unusable state, needs microcontroller firmware. | ||||
|  * | ||||
|  *  WARM - We can communicate with the device and the proper | ||||
|  *  microcontroller firmware is running, but other device initialization is | ||||
|  *  still needed (e.g. encoder firmware). | ||||
|  * | ||||
|  *  ERROR - A problem prevents capture operation (e.g. encoder firmware | ||||
|  *  missing). | ||||
|  * | ||||
|  *  READY - Device is operational, but not streaming. | ||||
|  * | ||||
|  *  RUN - Device is streaming. | ||||
|  * | ||||
|  */ | ||||
| #define PVR2_STATE_NONE 0 | ||||
| #define PVR2_STATE_DEAD 1 | ||||
| #define PVR2_STATE_COLD 2 | ||||
| #define PVR2_STATE_WARM 3 | ||||
| #define PVR2_STATE_ERROR 4 | ||||
| #define PVR2_STATE_READY 5 | ||||
| #define PVR2_STATE_RUN 6 | ||||
| 
 | ||||
| /* Translate configuration enum to a string label */ | ||||
| const char *pvr2_config_get_name(enum pvr2_config); | ||||
| 
 | ||||
| struct pvr2_hdw; | ||||
| 
 | ||||
| /* Create and return a structure for interacting with the underlying
 | ||||
|    hardware */ | ||||
| struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, | ||||
| 				 const struct usb_device_id *devid); | ||||
| 
 | ||||
| /* Perform second stage initialization, passing in a notification callback
 | ||||
|    for when the master state changes. */ | ||||
| int pvr2_hdw_initialize(struct pvr2_hdw *, | ||||
| 			void (*callback_func)(void *), | ||||
| 			void *callback_data); | ||||
| 
 | ||||
| /* Destroy hardware interaction structure */ | ||||
| void pvr2_hdw_destroy(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Return true if in the ready (normal) state */ | ||||
| int pvr2_hdw_dev_ok(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Return small integer number [1..N] for logical instance number of this
 | ||||
|    device.  This is useful for indexing array-valued module parameters. */ | ||||
| int pvr2_hdw_get_unit_number(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Get pointer to underlying USB device */ | ||||
| struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Retrieve serial number of device */ | ||||
| unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Retrieve bus location info of device */ | ||||
| const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Retrieve per-instance string identifier for this specific device */ | ||||
| const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Called when hardware has been unplugged */ | ||||
| void pvr2_hdw_disconnect(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Sets v4l2_dev of a video_device struct */ | ||||
| void pvr2_hdw_set_v4l2_dev(struct pvr2_hdw *, struct video_device *); | ||||
| 
 | ||||
| /* Get the number of defined controls */ | ||||
| unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Retrieve a control handle given its index (0..count-1) */ | ||||
| struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *,unsigned int); | ||||
| 
 | ||||
| /* Retrieve a control handle given its internal ID (if any) */ | ||||
| struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *,unsigned int); | ||||
| 
 | ||||
| /* Retrieve a control handle given its V4L ID (if any) */ | ||||
| struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *,unsigned int ctl_id); | ||||
| 
 | ||||
| /* Retrieve a control handle given its immediate predecessor V4L ID (if any) */ | ||||
| struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *, | ||||
| 					    unsigned int ctl_id); | ||||
| 
 | ||||
| /* Commit all control changes made up to this point */ | ||||
| int pvr2_hdw_commit_ctl(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Return a bit mask of valid input selections for this device.  Mask bits
 | ||||
|  * will be according to PVR_CVAL_INPUT_xxxx definitions. */ | ||||
| unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Return a bit mask of allowed input selections for this device.  Mask bits
 | ||||
|  * will be according to PVR_CVAL_INPUT_xxxx definitions. */ | ||||
| unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Change the set of allowed input selections for this device.  Both
 | ||||
|    change_mask and change_valu are mask bits according to | ||||
|    PVR_CVAL_INPUT_xxxx definitions.  The change_mask parameter indicate | ||||
|    which settings are being changed and the change_val parameter indicates | ||||
|    whether corresponding settings are being set or cleared. */ | ||||
| int pvr2_hdw_set_input_allowed(struct pvr2_hdw *, | ||||
| 			       unsigned int change_mask, | ||||
| 			       unsigned int change_val); | ||||
| 
 | ||||
| /* Return name for this driver instance */ | ||||
| const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Mark tuner status stale so that it will be re-fetched */ | ||||
| void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Return information about the tuner */ | ||||
| int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *); | ||||
| 
 | ||||
| /* Return information about cropping capabilities */ | ||||
| int pvr2_hdw_get_cropcap(struct pvr2_hdw *, struct v4l2_cropcap *); | ||||
| 
 | ||||
| /* Query device and see if it thinks it is on a high-speed USB link */ | ||||
| int pvr2_hdw_is_hsm(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Return a string token representative of the hardware type */ | ||||
| const char *pvr2_hdw_get_type(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Return a single line description of the hardware type */ | ||||
| const char *pvr2_hdw_get_desc(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Turn streaming on/off */ | ||||
| int pvr2_hdw_set_streaming(struct pvr2_hdw *,int); | ||||
| 
 | ||||
| /* Find out if streaming is on */ | ||||
| int pvr2_hdw_get_streaming(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Retrieve driver overall state */ | ||||
| int pvr2_hdw_get_state(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Configure the type of stream to generate */ | ||||
| int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); | ||||
| 
 | ||||
| /* Get handle to video output stream */ | ||||
| struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Enable / disable retrieval of CPU firmware or prom contents.  This must
 | ||||
|    be enabled before pvr2_hdw_cpufw_get() will function.  Note that doing | ||||
|    this may prevent the device from running (and leaving this mode may | ||||
|    imply a device reset). */ | ||||
| void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *, | ||||
| 				int mode, /* 0=8KB FX2, 1=16KB FX2, 2=PROM */ | ||||
| 				int enable_flag); | ||||
| 
 | ||||
| /* Return true if we're in a mode for retrieval CPU firmware */ | ||||
| int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Retrieve a piece of the CPU's firmware at the given offset.  Return
 | ||||
|    value is the number of bytes retrieved or zero if we're past the end or | ||||
|    an error otherwise (e.g. if firmware retrieval is not enabled). */ | ||||
| int pvr2_hdw_cpufw_get(struct pvr2_hdw *,unsigned int offs, | ||||
| 		       char *buf,unsigned int cnt); | ||||
| 
 | ||||
| /* Retrieve a previously stored v4l minor device number */ | ||||
| int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *,enum pvr2_v4l_type index); | ||||
| 
 | ||||
| /* Store a v4l minor device number */ | ||||
| void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *, | ||||
| 				     enum pvr2_v4l_type index,int); | ||||
| 
 | ||||
| /* The following entry points are all lower level things you normally don't
 | ||||
|    want to worry about. */ | ||||
| 
 | ||||
| /* Issue a command and get a response from the device.  LOTS of higher
 | ||||
|    level stuff is built on this. */ | ||||
| int pvr2_send_request(struct pvr2_hdw *, | ||||
| 		      void *write_ptr,unsigned int write_len, | ||||
| 		      void *read_ptr,unsigned int read_len); | ||||
| 
 | ||||
| /* Slightly higher level device communication functions. */ | ||||
| int pvr2_write_register(struct pvr2_hdw *, u16, u32); | ||||
| 
 | ||||
| /* Call if for any reason we can't talk to the hardware anymore - this will
 | ||||
|    cause the driver to stop flailing on the device. */ | ||||
| void pvr2_hdw_render_useless(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Set / clear 8051's reset bit */ | ||||
| void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int); | ||||
| 
 | ||||
| /* Execute a USB-commanded device reset */ | ||||
| void pvr2_hdw_device_reset(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Reset worker's error trapping circuit breaker */ | ||||
| int pvr2_hdw_untrip(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Execute hard reset command (after this point it's likely that the
 | ||||
|    encoder will have to be reconfigured).  This also clears the "useless" | ||||
|    state. */ | ||||
| int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Execute simple reset command */ | ||||
| int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* suspend */ | ||||
| int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Order decoder to reset */ | ||||
| int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *); | ||||
| 
 | ||||
| /* Direct manipulation of GPIO bits */ | ||||
| int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *); | ||||
| int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *); | ||||
| int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *); | ||||
| int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val); | ||||
| int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val); | ||||
| 
 | ||||
| /* This data structure is specifically for the next function... */ | ||||
| struct pvr2_hdw_debug_info { | ||||
| 	int big_lock_held; | ||||
| 	int ctl_lock_held; | ||||
| 	int flag_disconnected; | ||||
| 	int flag_init_ok; | ||||
| 	int flag_ok; | ||||
| 	int fw1_state; | ||||
| 	int flag_decoder_missed; | ||||
| 	int flag_tripped; | ||||
| 	int state_encoder_ok; | ||||
| 	int state_encoder_run; | ||||
| 	int state_decoder_run; | ||||
| 	int state_decoder_ready; | ||||
| 	int state_usbstream_run; | ||||
| 	int state_decoder_quiescent; | ||||
| 	int state_pipeline_config; | ||||
| 	int state_pipeline_req; | ||||
| 	int state_pipeline_pause; | ||||
| 	int state_pipeline_idle; | ||||
| 	int cmd_debug_state; | ||||
| 	int cmd_debug_write_len; | ||||
| 	int cmd_debug_read_len; | ||||
| 	int cmd_debug_write_pend; | ||||
| 	int cmd_debug_read_pend; | ||||
| 	int cmd_debug_timeout; | ||||
| 	int cmd_debug_rstatus; | ||||
| 	int cmd_debug_wstatus; | ||||
| 	unsigned char cmd_code; | ||||
| }; | ||||
| 
 | ||||
| /* Non-intrusively retrieve internal state info - this is useful for
 | ||||
|    diagnosing lockups.  Note that this operation is completed without any | ||||
|    kind of locking and so it is not atomic and may yield inconsistent | ||||
|    results.  This is *purely* a debugging aid. */ | ||||
| void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw, | ||||
| 				      struct pvr2_hdw_debug_info *); | ||||
| 
 | ||||
| /* Intrusively retrieve internal state info - this is useful for
 | ||||
|    diagnosing overall driver state.  This operation synchronizes against | ||||
|    the overall driver mutex - so if there are locking problems this will | ||||
|    likely hang!  This is *purely* a debugging aid. */ | ||||
| void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw, | ||||
| 				    struct pvr2_hdw_debug_info *); | ||||
| 
 | ||||
| /* Report out several lines of text that describes driver internal state.
 | ||||
|    Results are written into the passed-in buffer. */ | ||||
| unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, | ||||
| 				   char *buf_ptr,unsigned int buf_size); | ||||
| 
 | ||||
| /* Cause modules to log their state once */ | ||||
| void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw); | ||||
| 
 | ||||
| /* Cause encoder firmware to be uploaded into the device.  This is normally
 | ||||
|    done autonomously, but the interface is exported here because it is also | ||||
|    a debugging aid. */ | ||||
| int pvr2_upload_firmware2(struct pvr2_hdw *hdw); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_HDW_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										698
									
								
								drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										698
									
								
								drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,698 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/module.h> | ||||
| #include <media/ir-kbd-i2c.h> | ||||
| #include "pvrusb2-i2c-core.h" | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include "pvrusb2-fx2-cmd.h" | ||||
| #include "pvrusb2.h" | ||||
| 
 | ||||
| #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|   This module attempts to implement a compliant I2C adapter for the pvrusb2 | ||||
|   device. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| static unsigned int i2c_scan; | ||||
| module_param(i2c_scan, int, S_IRUGO|S_IWUSR); | ||||
| MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); | ||||
| 
 | ||||
| static int ir_mode[PVR_NUM] = { [0 ... PVR_NUM-1] = 1 }; | ||||
| module_param_array(ir_mode, int, NULL, 0444); | ||||
| MODULE_PARM_DESC(ir_mode,"specify: 0=disable IR reception, 1=normal IR"); | ||||
| 
 | ||||
| static int pvr2_disable_ir_video; | ||||
| module_param_named(disable_autoload_ir_video, pvr2_disable_ir_video, | ||||
| 		   int, S_IRUGO|S_IWUSR); | ||||
| MODULE_PARM_DESC(disable_autoload_ir_video, | ||||
| 		 "1=do not try to autoload ir_video IR receiver"); | ||||
| 
 | ||||
| static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ | ||||
| 			  u8 i2c_addr,      /* I2C address we're talking to */ | ||||
| 			  u8 *data,         /* Data to write */ | ||||
| 			  u16 length)       /* Size of data to write */ | ||||
| { | ||||
| 	/* Return value - default 0 means success */ | ||||
| 	int ret; | ||||
| 
 | ||||
| 
 | ||||
| 	if (!data) length = 0; | ||||
| 	if (length > (sizeof(hdw->cmd_buffer) - 3)) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "Killing an I2C write to %u that is too large" | ||||
| 			   " (desired=%u limit=%u)", | ||||
| 			   i2c_addr, | ||||
| 			   length,(unsigned int)(sizeof(hdw->cmd_buffer) - 3)); | ||||
| 		return -ENOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
| 	LOCK_TAKE(hdw->ctl_lock); | ||||
| 
 | ||||
| 	/* Clear the command buffer (likely to be paranoia) */ | ||||
| 	memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); | ||||
| 
 | ||||
| 	/* Set up command buffer for an I2C write */ | ||||
| 	hdw->cmd_buffer[0] = FX2CMD_I2C_WRITE;      /* write prefix */ | ||||
| 	hdw->cmd_buffer[1] = i2c_addr;  /* i2c addr of chip */ | ||||
| 	hdw->cmd_buffer[2] = length;    /* length of what follows */ | ||||
| 	if (length) memcpy(hdw->cmd_buffer + 3, data, length); | ||||
| 
 | ||||
| 	/* Do the operation */ | ||||
| 	ret = pvr2_send_request(hdw, | ||||
| 				hdw->cmd_buffer, | ||||
| 				length + 3, | ||||
| 				hdw->cmd_buffer, | ||||
| 				1); | ||||
| 	if (!ret) { | ||||
| 		if (hdw->cmd_buffer[0] != 8) { | ||||
| 			ret = -EIO; | ||||
| 			if (hdw->cmd_buffer[0] != 7) { | ||||
| 				trace_i2c("unexpected status" | ||||
| 					  " from i2_write[%d]: %d", | ||||
| 					  i2c_addr,hdw->cmd_buffer[0]); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	LOCK_GIVE(hdw->ctl_lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ | ||||
| 			 u8 i2c_addr,       /* I2C address we're talking to */ | ||||
| 			 u8 *data,          /* Data to write */ | ||||
| 			 u16 dlen,          /* Size of data to write */ | ||||
| 			 u8 *res,           /* Where to put data we read */ | ||||
| 			 u16 rlen)          /* Amount of data to read */ | ||||
| { | ||||
| 	/* Return value - default 0 means success */ | ||||
| 	int ret; | ||||
| 
 | ||||
| 
 | ||||
| 	if (!data) dlen = 0; | ||||
| 	if (dlen > (sizeof(hdw->cmd_buffer) - 4)) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "Killing an I2C read to %u that has wlen too large" | ||||
| 			   " (desired=%u limit=%u)", | ||||
| 			   i2c_addr, | ||||
| 			   dlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 4)); | ||||
| 		return -ENOTSUPP; | ||||
| 	} | ||||
| 	if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "Killing an I2C read to %u that has rlen too large" | ||||
| 			   " (desired=%u limit=%u)", | ||||
| 			   i2c_addr, | ||||
| 			   rlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 1)); | ||||
| 		return -ENOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
| 	LOCK_TAKE(hdw->ctl_lock); | ||||
| 
 | ||||
| 	/* Clear the command buffer (likely to be paranoia) */ | ||||
| 	memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); | ||||
| 
 | ||||
| 	/* Set up command buffer for an I2C write followed by a read */ | ||||
| 	hdw->cmd_buffer[0] = FX2CMD_I2C_READ;  /* read prefix */ | ||||
| 	hdw->cmd_buffer[1] = dlen;  /* arg length */ | ||||
| 	hdw->cmd_buffer[2] = rlen;  /* answer length. Device will send one
 | ||||
| 				       more byte (status). */ | ||||
| 	hdw->cmd_buffer[3] = i2c_addr;  /* i2c addr of chip */ | ||||
| 	if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen); | ||||
| 
 | ||||
| 	/* Do the operation */ | ||||
| 	ret = pvr2_send_request(hdw, | ||||
| 				hdw->cmd_buffer, | ||||
| 				4 + dlen, | ||||
| 				hdw->cmd_buffer, | ||||
| 				rlen + 1); | ||||
| 	if (!ret) { | ||||
| 		if (hdw->cmd_buffer[0] != 8) { | ||||
| 			ret = -EIO; | ||||
| 			if (hdw->cmd_buffer[0] != 7) { | ||||
| 				trace_i2c("unexpected status" | ||||
| 					  " from i2_read[%d]: %d", | ||||
| 					  i2c_addr,hdw->cmd_buffer[0]); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Copy back the result */ | ||||
| 	if (res && rlen) { | ||||
| 		if (ret) { | ||||
| 			/* Error, just blank out the return buffer */ | ||||
| 			memset(res, 0, rlen); | ||||
| 		} else { | ||||
| 			memcpy(res, hdw->cmd_buffer + 1, rlen); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	LOCK_GIVE(hdw->ctl_lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /* This is the common low level entry point for doing I2C operations to the
 | ||||
|    hardware. */ | ||||
| static int pvr2_i2c_basic_op(struct pvr2_hdw *hdw, | ||||
| 			     u8 i2c_addr, | ||||
| 			     u8 *wdata, | ||||
| 			     u16 wlen, | ||||
| 			     u8 *rdata, | ||||
| 			     u16 rlen) | ||||
| { | ||||
| 	if (!rdata) rlen = 0; | ||||
| 	if (!wdata) wlen = 0; | ||||
| 	if (rlen || !wlen) { | ||||
| 		return pvr2_i2c_read(hdw,i2c_addr,wdata,wlen,rdata,rlen); | ||||
| 	} else { | ||||
| 		return pvr2_i2c_write(hdw,i2c_addr,wdata,wlen); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* This is a special entry point for cases of I2C transaction attempts to
 | ||||
|    the IR receiver.  The implementation here simulates the IR receiver by | ||||
|    issuing a command to the FX2 firmware and using that response to return | ||||
|    what the real I2C receiver would have returned.  We use this for 24xxx | ||||
|    devices, where the IR receiver chip has been removed and replaced with | ||||
|    FX2 related logic. */ | ||||
| static int i2c_24xxx_ir(struct pvr2_hdw *hdw, | ||||
| 			u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) | ||||
| { | ||||
| 	u8 dat[4]; | ||||
| 	unsigned int stat; | ||||
| 
 | ||||
| 	if (!(rlen || wlen)) { | ||||
| 		/* This is a probe attempt.  Just let it succeed. */ | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* We don't understand this kind of transaction */ | ||||
| 	if ((wlen != 0) || (rlen == 0)) return -EIO; | ||||
| 
 | ||||
| 	if (rlen < 3) { | ||||
| 		/* Mike Isely <isely@pobox.com> Appears to be a probe
 | ||||
| 		   attempt from lirc.  Just fill in zeroes and return.  If | ||||
| 		   we try instead to do the full transaction here, then bad | ||||
| 		   things seem to happen within the lirc driver module | ||||
| 		   (version 0.8.0-7 sources from Debian, when run under | ||||
| 		   vanilla 2.6.17.6 kernel) - and I don't have the patience | ||||
| 		   to chase it down. */ | ||||
| 		if (rlen > 0) rdata[0] = 0; | ||||
| 		if (rlen > 1) rdata[1] = 0; | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Issue a command to the FX2 to read the IR receiver. */ | ||||
| 	LOCK_TAKE(hdw->ctl_lock); do { | ||||
| 		hdw->cmd_buffer[0] = FX2CMD_GET_IR_CODE; | ||||
| 		stat = pvr2_send_request(hdw, | ||||
| 					 hdw->cmd_buffer,1, | ||||
| 					 hdw->cmd_buffer,4); | ||||
| 		dat[0] = hdw->cmd_buffer[0]; | ||||
| 		dat[1] = hdw->cmd_buffer[1]; | ||||
| 		dat[2] = hdw->cmd_buffer[2]; | ||||
| 		dat[3] = hdw->cmd_buffer[3]; | ||||
| 	} while (0); LOCK_GIVE(hdw->ctl_lock); | ||||
| 
 | ||||
| 	/* Give up if that operation failed. */ | ||||
| 	if (stat != 0) return stat; | ||||
| 
 | ||||
| 	/* Mangle the results into something that looks like the real IR
 | ||||
| 	   receiver. */ | ||||
| 	rdata[2] = 0xc1; | ||||
| 	if (dat[0] != 1) { | ||||
| 		/* No code received. */ | ||||
| 		rdata[0] = 0; | ||||
| 		rdata[1] = 0; | ||||
| 	} else { | ||||
| 		u16 val; | ||||
| 		/* Mash the FX2 firmware-provided IR code into something
 | ||||
| 		   that the normal i2c chip-level driver expects. */ | ||||
| 		val = dat[1]; | ||||
| 		val <<= 8; | ||||
| 		val |= dat[2]; | ||||
| 		val >>= 1; | ||||
| 		val &= ~0x0003; | ||||
| 		val |= 0x8000; | ||||
| 		rdata[0] = (val >> 8) & 0xffu; | ||||
| 		rdata[1] = val & 0xffu; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* This is a special entry point that is entered if an I2C operation is
 | ||||
|    attempted to a wm8775 chip on model 24xxx hardware.  Autodetect of this | ||||
|    part doesn't work, but we know it is really there.  So let's look for | ||||
|    the autodetect attempt and just return success if we see that. */ | ||||
| static int i2c_hack_wm8775(struct pvr2_hdw *hdw, | ||||
| 			   u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) | ||||
| { | ||||
| 	if (!(rlen || wlen)) { | ||||
| 		// This is a probe attempt.  Just let it succeed.
 | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); | ||||
| } | ||||
| 
 | ||||
| /* This is an entry point designed to always fail any attempt to perform a
 | ||||
|    transfer.  We use this to cause certain I2C addresses to not be | ||||
|    probed. */ | ||||
| static int i2c_black_hole(struct pvr2_hdw *hdw, | ||||
| 			   u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) | ||||
| { | ||||
| 	return -EIO; | ||||
| } | ||||
| 
 | ||||
| /* This is a special entry point that is entered if an I2C operation is
 | ||||
|    attempted to a cx25840 chip on model 24xxx hardware.  This chip can | ||||
|    sometimes wedge itself.  Worse still, when this happens msp3400 can | ||||
|    falsely detect this part and then the system gets hosed up after msp3400 | ||||
|    gets confused and dies.  What we want to do here is try to keep msp3400 | ||||
|    away and also try to notice if the chip is wedged and send a warning to | ||||
|    the system log. */ | ||||
| static int i2c_hack_cx25840(struct pvr2_hdw *hdw, | ||||
| 			    u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) | ||||
| { | ||||
| 	int ret; | ||||
| 	unsigned int subaddr; | ||||
| 	u8 wbuf[2]; | ||||
| 	int state = hdw->i2c_cx25840_hack_state; | ||||
| 
 | ||||
| 	if (!(rlen || wlen)) { | ||||
| 		// Probe attempt - always just succeed and don't bother the
 | ||||
| 		// hardware (this helps to make the state machine further
 | ||||
| 		// down somewhat easier).
 | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (state == 3) { | ||||
| 		return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); | ||||
| 	} | ||||
| 
 | ||||
| 	/* We're looking for the exact pattern where the revision register
 | ||||
| 	   is being read.  The cx25840 module will always look at the | ||||
| 	   revision register first.  Any other pattern of access therefore | ||||
| 	   has to be a probe attempt from somebody else so we'll reject it. | ||||
| 	   Normally we could just let each client just probe the part | ||||
| 	   anyway, but when the cx25840 is wedged, msp3400 will get a false | ||||
| 	   positive and that just screws things up... */ | ||||
| 
 | ||||
| 	if (wlen == 0) { | ||||
| 		switch (state) { | ||||
| 		case 1: subaddr = 0x0100; break; | ||||
| 		case 2: subaddr = 0x0101; break; | ||||
| 		default: goto fail; | ||||
| 		} | ||||
| 	} else if (wlen == 2) { | ||||
| 		subaddr = (wdata[0] << 8) | wdata[1]; | ||||
| 		switch (subaddr) { | ||||
| 		case 0x0100: state = 1; break; | ||||
| 		case 0x0101: state = 2; break; | ||||
| 		default: goto fail; | ||||
| 		} | ||||
| 	} else { | ||||
| 		goto fail; | ||||
| 	} | ||||
| 	if (!rlen) goto success; | ||||
| 	state = 0; | ||||
| 	if (rlen != 1) goto fail; | ||||
| 
 | ||||
| 	/* If we get to here then we have a legitimate read for one of the
 | ||||
| 	   two revision bytes, so pass it through. */ | ||||
| 	wbuf[0] = subaddr >> 8; | ||||
| 	wbuf[1] = subaddr; | ||||
| 	ret = pvr2_i2c_basic_op(hdw,i2c_addr,wbuf,2,rdata,rlen); | ||||
| 
 | ||||
| 	if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "WARNING: Detected a wedged cx25840 chip;" | ||||
| 			   " the device will not work."); | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "WARNING: Try power cycling the pvrusb2 device."); | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "WARNING: Disabling further access to the device" | ||||
| 			   " to prevent other foul-ups."); | ||||
| 		// This blocks all further communication with the part.
 | ||||
| 		hdw->i2c_func[0x44] = NULL; | ||||
| 		pvr2_hdw_render_useless(hdw); | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Success! */ | ||||
| 	pvr2_trace(PVR2_TRACE_CHIPS,"cx25840 appears to be OK."); | ||||
| 	state = 3; | ||||
| 
 | ||||
|  success: | ||||
| 	hdw->i2c_cx25840_hack_state = state; | ||||
| 	return 0; | ||||
| 
 | ||||
|  fail: | ||||
| 	hdw->i2c_cx25840_hack_state = state; | ||||
| 	return -EIO; | ||||
| } | ||||
| 
 | ||||
| /* This is a very, very limited I2C adapter implementation.  We can only
 | ||||
|    support what we actually know will work on the device... */ | ||||
| static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, | ||||
| 			 struct i2c_msg msgs[], | ||||
| 			 int num) | ||||
| { | ||||
| 	int ret = -ENOTSUPP; | ||||
| 	pvr2_i2c_func funcp = NULL; | ||||
| 	struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data); | ||||
| 
 | ||||
| 	if (!num) { | ||||
| 		ret = -EINVAL; | ||||
| 		goto done; | ||||
| 	} | ||||
| 	if (msgs[0].addr < PVR2_I2C_FUNC_CNT) { | ||||
| 		funcp = hdw->i2c_func[msgs[0].addr]; | ||||
| 	} | ||||
| 	if (!funcp) { | ||||
| 		ret = -EIO; | ||||
| 		goto done; | ||||
| 	} | ||||
| 
 | ||||
| 	if (num == 1) { | ||||
| 		if (msgs[0].flags & I2C_M_RD) { | ||||
| 			/* Simple read */ | ||||
| 			u16 tcnt,bcnt,offs; | ||||
| 			if (!msgs[0].len) { | ||||
| 				/* Length == 0 read.  This is a probe. */ | ||||
| 				if (funcp(hdw,msgs[0].addr,NULL,0,NULL,0)) { | ||||
| 					ret = -EIO; | ||||
| 					goto done; | ||||
| 				} | ||||
| 				ret = 1; | ||||
| 				goto done; | ||||
| 			} | ||||
| 			/* If the read is short enough we'll do the whole
 | ||||
| 			   thing atomically.  Otherwise we have no choice | ||||
| 			   but to break apart the reads. */ | ||||
| 			tcnt = msgs[0].len; | ||||
| 			offs = 0; | ||||
| 			while (tcnt) { | ||||
| 				bcnt = tcnt; | ||||
| 				if (bcnt > sizeof(hdw->cmd_buffer)-1) { | ||||
| 					bcnt = sizeof(hdw->cmd_buffer)-1; | ||||
| 				} | ||||
| 				if (funcp(hdw,msgs[0].addr,NULL,0, | ||||
| 					  msgs[0].buf+offs,bcnt)) { | ||||
| 					ret = -EIO; | ||||
| 					goto done; | ||||
| 				} | ||||
| 				offs += bcnt; | ||||
| 				tcnt -= bcnt; | ||||
| 			} | ||||
| 			ret = 1; | ||||
| 			goto done; | ||||
| 		} else { | ||||
| 			/* Simple write */ | ||||
| 			ret = 1; | ||||
| 			if (funcp(hdw,msgs[0].addr, | ||||
| 				  msgs[0].buf,msgs[0].len,NULL,0)) { | ||||
| 				ret = -EIO; | ||||
| 			} | ||||
| 			goto done; | ||||
| 		} | ||||
| 	} else if (num == 2) { | ||||
| 		if (msgs[0].addr != msgs[1].addr) { | ||||
| 			trace_i2c("i2c refusing 2 phase transfer with" | ||||
| 				  " conflicting target addresses"); | ||||
| 			ret = -ENOTSUPP; | ||||
| 			goto done; | ||||
| 		} | ||||
| 		if ((!((msgs[0].flags & I2C_M_RD))) && | ||||
| 		    (msgs[1].flags & I2C_M_RD)) { | ||||
| 			u16 tcnt,bcnt,wcnt,offs; | ||||
| 			/* Write followed by atomic read.  If the read
 | ||||
| 			   portion is short enough we'll do the whole thing | ||||
| 			   atomically.  Otherwise we have no choice but to | ||||
| 			   break apart the reads. */ | ||||
| 			tcnt = msgs[1].len; | ||||
| 			wcnt = msgs[0].len; | ||||
| 			offs = 0; | ||||
| 			while (tcnt || wcnt) { | ||||
| 				bcnt = tcnt; | ||||
| 				if (bcnt > sizeof(hdw->cmd_buffer)-1) { | ||||
| 					bcnt = sizeof(hdw->cmd_buffer)-1; | ||||
| 				} | ||||
| 				if (funcp(hdw,msgs[0].addr, | ||||
| 					  msgs[0].buf,wcnt, | ||||
| 					  msgs[1].buf+offs,bcnt)) { | ||||
| 					ret = -EIO; | ||||
| 					goto done; | ||||
| 				} | ||||
| 				offs += bcnt; | ||||
| 				tcnt -= bcnt; | ||||
| 				wcnt = 0; | ||||
| 			} | ||||
| 			ret = 2; | ||||
| 			goto done; | ||||
| 		} else { | ||||
| 			trace_i2c("i2c refusing complex transfer" | ||||
| 				  " read0=%d read1=%d", | ||||
| 				  (msgs[0].flags & I2C_M_RD), | ||||
| 				  (msgs[1].flags & I2C_M_RD)); | ||||
| 		} | ||||
| 	} else { | ||||
| 		trace_i2c("i2c refusing %d phase transfer",num); | ||||
| 	} | ||||
| 
 | ||||
|  done: | ||||
| 	if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) { | ||||
| 		unsigned int idx,offs,cnt; | ||||
| 		for (idx = 0; idx < num; idx++) { | ||||
| 			cnt = msgs[idx].len; | ||||
| 			printk(KERN_INFO | ||||
| 			       "pvrusb2 i2c xfer %u/%u:" | ||||
| 			       " addr=0x%x len=%d %s", | ||||
| 			       idx+1,num, | ||||
| 			       msgs[idx].addr, | ||||
| 			       cnt, | ||||
| 			       (msgs[idx].flags & I2C_M_RD ? | ||||
| 				"read" : "write")); | ||||
| 			if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) { | ||||
| 				if (cnt > 8) cnt = 8; | ||||
| 				printk(" ["); | ||||
| 				for (offs = 0; offs < (cnt>8?8:cnt); offs++) { | ||||
| 					if (offs) printk(" "); | ||||
| 					printk("%02x",msgs[idx].buf[offs]); | ||||
| 				} | ||||
| 				if (offs < cnt) printk(" ..."); | ||||
| 				printk("]"); | ||||
| 			} | ||||
| 			if (idx+1 == num) { | ||||
| 				printk(" result=%d",ret); | ||||
| 			} | ||||
| 			printk("\n"); | ||||
| 		} | ||||
| 		if (!num) { | ||||
| 			printk(KERN_INFO | ||||
| 			       "pvrusb2 i2c xfer null transfer result=%d\n", | ||||
| 			       ret); | ||||
| 		} | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) | ||||
| { | ||||
| 	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; | ||||
| } | ||||
| 
 | ||||
| static struct i2c_algorithm pvr2_i2c_algo_template = { | ||||
| 	.master_xfer   = pvr2_i2c_xfer, | ||||
| 	.functionality = pvr2_i2c_functionality, | ||||
| }; | ||||
| 
 | ||||
| static struct i2c_adapter pvr2_i2c_adap_template = { | ||||
| 	.owner         = THIS_MODULE, | ||||
| 	.class	       = 0, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* Return true if device exists at given address */ | ||||
| static int do_i2c_probe(struct pvr2_hdw *hdw, int addr) | ||||
| { | ||||
| 	struct i2c_msg msg[1]; | ||||
| 	int rc; | ||||
| 	msg[0].addr = 0; | ||||
| 	msg[0].flags = I2C_M_RD; | ||||
| 	msg[0].len = 0; | ||||
| 	msg[0].buf = NULL; | ||||
| 	msg[0].addr = addr; | ||||
| 	rc = i2c_transfer(&hdw->i2c_adap, msg, ARRAY_SIZE(msg)); | ||||
| 	return rc == 1; | ||||
| } | ||||
| 
 | ||||
| static void do_i2c_scan(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	int i; | ||||
| 	printk(KERN_INFO "%s: i2c scan beginning\n", hdw->name); | ||||
| 	for (i = 0; i < 128; i++) { | ||||
| 		if (do_i2c_probe(hdw, i)) { | ||||
| 			printk(KERN_INFO "%s: i2c scan: found device @ 0x%x\n", | ||||
| 			       hdw->name, i); | ||||
| 		} | ||||
| 	} | ||||
| 	printk(KERN_INFO "%s: i2c scan done.\n", hdw->name); | ||||
| } | ||||
| 
 | ||||
| static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	struct i2c_board_info info; | ||||
| 	struct IR_i2c_init_data *init_data = &hdw->ir_init_data; | ||||
| 	if (pvr2_disable_ir_video) { | ||||
| 		pvr2_trace(PVR2_TRACE_INFO, | ||||
| 			   "Automatic binding of ir_video has been disabled."); | ||||
| 		return; | ||||
| 	} | ||||
| 	memset(&info, 0, sizeof(struct i2c_board_info)); | ||||
| 	switch (hdw->ir_scheme_active) { | ||||
| 	case PVR2_IR_SCHEME_24XXX: /* FX2-controlled IR */ | ||||
| 	case PVR2_IR_SCHEME_29XXX: /* Original 29xxx device */ | ||||
| 		init_data->ir_codes              = RC_MAP_HAUPPAUGE; | ||||
| 		init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; | ||||
| 		init_data->type                  = RC_BIT_RC5; | ||||
| 		init_data->name                  = hdw->hdw_desc->description; | ||||
| 		init_data->polling_interval      = 100; /* ms From ir-kbd-i2c */ | ||||
| 		/* IR Receiver */ | ||||
| 		info.addr          = 0x18; | ||||
| 		info.platform_data = init_data; | ||||
| 		strlcpy(info.type, "ir_video", I2C_NAME_SIZE); | ||||
| 		pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", | ||||
| 			   info.type, info.addr); | ||||
| 		i2c_new_device(&hdw->i2c_adap, &info); | ||||
| 		break; | ||||
| 	case PVR2_IR_SCHEME_ZILOG:     /* HVR-1950 style */ | ||||
| 	case PVR2_IR_SCHEME_24XXX_MCE: /* 24xxx MCE device */ | ||||
| 		init_data->ir_codes              = RC_MAP_HAUPPAUGE; | ||||
| 		init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; | ||||
| 		init_data->type                  = RC_BIT_RC5; | ||||
| 		init_data->name                  = hdw->hdw_desc->description; | ||||
| 		/* IR Receiver */ | ||||
| 		info.addr          = 0x71; | ||||
| 		info.platform_data = init_data; | ||||
| 		strlcpy(info.type, "ir_rx_z8f0811_haup", I2C_NAME_SIZE); | ||||
| 		pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", | ||||
| 			   info.type, info.addr); | ||||
| 		i2c_new_device(&hdw->i2c_adap, &info); | ||||
| 		/* IR Trasmitter */ | ||||
| 		info.addr          = 0x70; | ||||
| 		info.platform_data = init_data; | ||||
| 		strlcpy(info.type, "ir_tx_z8f0811_haup", I2C_NAME_SIZE); | ||||
| 		pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", | ||||
| 			   info.type, info.addr); | ||||
| 		i2c_new_device(&hdw->i2c_adap, &info); | ||||
| 		break; | ||||
| 	default: | ||||
| 		/* The device either doesn't support I2C-based IR or we
 | ||||
| 		   don't know (yet) how to operate IR on the device. */ | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void pvr2_i2c_core_init(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	unsigned int idx; | ||||
| 
 | ||||
| 	/* The default action for all possible I2C addresses is just to do
 | ||||
| 	   the transfer normally. */ | ||||
| 	for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) { | ||||
| 		hdw->i2c_func[idx] = pvr2_i2c_basic_op; | ||||
| 	} | ||||
| 
 | ||||
| 	/* However, deal with various special cases for 24xxx hardware. */ | ||||
| 	if (ir_mode[hdw->unit_number] == 0) { | ||||
| 		printk(KERN_INFO "%s: IR disabled\n",hdw->name); | ||||
| 		hdw->i2c_func[0x18] = i2c_black_hole; | ||||
| 	} else if (ir_mode[hdw->unit_number] == 1) { | ||||
| 		if (hdw->ir_scheme_active == PVR2_IR_SCHEME_24XXX) { | ||||
| 			/* Set up translation so that our IR looks like a
 | ||||
| 			   29xxx device */ | ||||
| 			hdw->i2c_func[0x18] = i2c_24xxx_ir; | ||||
| 		} | ||||
| 	} | ||||
| 	if (hdw->hdw_desc->flag_has_cx25840) { | ||||
| 		hdw->i2c_func[0x44] = i2c_hack_cx25840; | ||||
| 	} | ||||
| 	if (hdw->hdw_desc->flag_has_wm8775) { | ||||
| 		hdw->i2c_func[0x1b] = i2c_hack_wm8775; | ||||
| 	} | ||||
| 
 | ||||
| 	// Configure the adapter and set up everything else related to it.
 | ||||
| 	hdw->i2c_adap = pvr2_i2c_adap_template; | ||||
| 	hdw->i2c_algo = pvr2_i2c_algo_template; | ||||
| 	strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name)); | ||||
| 	hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev; | ||||
| 	hdw->i2c_adap.algo = &hdw->i2c_algo; | ||||
| 	hdw->i2c_adap.algo_data = hdw; | ||||
| 	hdw->i2c_linked = !0; | ||||
| 	i2c_set_adapdata(&hdw->i2c_adap, &hdw->v4l2_dev); | ||||
| 	i2c_add_adapter(&hdw->i2c_adap); | ||||
| 	if (hdw->i2c_func[0x18] == i2c_24xxx_ir) { | ||||
| 		/* Probe for a different type of IR receiver on this
 | ||||
| 		   device.  This is really the only way to differentiate | ||||
| 		   older 24xxx devices from 24xxx variants that include an | ||||
| 		   IR blaster.  If the IR blaster is present, the IR | ||||
| 		   receiver is part of that chip and thus we must disable | ||||
| 		   the emulated IR receiver. */ | ||||
| 		if (do_i2c_probe(hdw, 0x71)) { | ||||
| 			pvr2_trace(PVR2_TRACE_INFO, | ||||
| 				   "Device has newer IR hardware;" | ||||
| 				   " disabling unneeded virtual IR device"); | ||||
| 			hdw->i2c_func[0x18] = NULL; | ||||
| 			/* Remember that this is a different device... */ | ||||
| 			hdw->ir_scheme_active = PVR2_IR_SCHEME_24XXX_MCE; | ||||
| 		} | ||||
| 	} | ||||
| 	if (i2c_scan) do_i2c_scan(hdw); | ||||
| 
 | ||||
| 	pvr2_i2c_register_ir(hdw); | ||||
| } | ||||
| 
 | ||||
| void pvr2_i2c_core_done(struct pvr2_hdw *hdw) | ||||
| { | ||||
| 	if (hdw->i2c_linked) { | ||||
| 		i2c_del_adapter(&hdw->i2c_adap); | ||||
| 		hdw->i2c_linked = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										40
									
								
								drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_I2C_CORE_H | ||||
| #define __PVRUSB2_I2C_CORE_H | ||||
| 
 | ||||
| struct pvr2_hdw; | ||||
| 
 | ||||
| void pvr2_i2c_core_init(struct pvr2_hdw *); | ||||
| void pvr2_i2c_core_done(struct pvr2_hdw *); | ||||
| 
 | ||||
| 
 | ||||
| #endif /* __PVRUSB2_I2C_ADAPTER_H */ | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										695
									
								
								drivers/media/usb/pvrusb2/pvrusb2-io.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										695
									
								
								drivers/media/usb/pvrusb2/pvrusb2-io.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,695 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "pvrusb2-io.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include <linux/errno.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/mutex.h> | ||||
| 
 | ||||
| static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state); | ||||
| 
 | ||||
| #define BUFFER_SIG 0x47653271 | ||||
| 
 | ||||
| // #define SANITY_CHECK_BUFFERS
 | ||||
| 
 | ||||
| 
 | ||||
| #ifdef SANITY_CHECK_BUFFERS | ||||
| #define BUFFER_CHECK(bp) do { \ | ||||
| 	if ((bp)->signature != BUFFER_SIG) { \ | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, \ | ||||
| 		"Buffer %p is bad at %s:%d", \ | ||||
| 		(bp),__FILE__,__LINE__); \ | ||||
| 		pvr2_buffer_describe(bp,"BadSig"); \ | ||||
| 		BUG(); \ | ||||
| 	} \ | ||||
| } while (0) | ||||
| #else | ||||
| #define BUFFER_CHECK(bp) do {} while(0) | ||||
| #endif | ||||
| 
 | ||||
| struct pvr2_stream { | ||||
| 	/* Buffers queued for reading */ | ||||
| 	struct list_head queued_list; | ||||
| 	unsigned int q_count; | ||||
| 	unsigned int q_bcount; | ||||
| 	/* Buffers with retrieved data */ | ||||
| 	struct list_head ready_list; | ||||
| 	unsigned int r_count; | ||||
| 	unsigned int r_bcount; | ||||
| 	/* Buffers available for use */ | ||||
| 	struct list_head idle_list; | ||||
| 	unsigned int i_count; | ||||
| 	unsigned int i_bcount; | ||||
| 	/* Pointers to all buffers */ | ||||
| 	struct pvr2_buffer **buffers; | ||||
| 	/* Array size of buffers */ | ||||
| 	unsigned int buffer_slot_count; | ||||
| 	/* Total buffers actually in circulation */ | ||||
| 	unsigned int buffer_total_count; | ||||
| 	/* Designed number of buffers to be in circulation */ | ||||
| 	unsigned int buffer_target_count; | ||||
| 	/* Executed when ready list become non-empty */ | ||||
| 	pvr2_stream_callback callback_func; | ||||
| 	void *callback_data; | ||||
| 	/* Context for transfer endpoint */ | ||||
| 	struct usb_device *dev; | ||||
| 	int endpoint; | ||||
| 	/* Overhead for mutex enforcement */ | ||||
| 	spinlock_t list_lock; | ||||
| 	struct mutex mutex; | ||||
| 	/* Tracking state for tolerating errors */ | ||||
| 	unsigned int fail_count; | ||||
| 	unsigned int fail_tolerance; | ||||
| 
 | ||||
| 	unsigned int buffers_processed; | ||||
| 	unsigned int buffers_failed; | ||||
| 	unsigned int bytes_processed; | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_buffer { | ||||
| 	int id; | ||||
| 	int signature; | ||||
| 	enum pvr2_buffer_state state; | ||||
| 	void *ptr;               /* Pointer to storage area */ | ||||
| 	unsigned int max_count;  /* Size of storage area */ | ||||
| 	unsigned int used_count; /* Amount of valid data in storage area */ | ||||
| 	int status;              /* Transfer result status */ | ||||
| 	struct pvr2_stream *stream; | ||||
| 	struct list_head list_overhead; | ||||
| 	struct urb *purb; | ||||
| }; | ||||
| 
 | ||||
| static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st) | ||||
| { | ||||
| 	switch (st) { | ||||
| 	case pvr2_buffer_state_none: return "none"; | ||||
| 	case pvr2_buffer_state_idle: return "idle"; | ||||
| 	case pvr2_buffer_state_queued: return "queued"; | ||||
| 	case pvr2_buffer_state_ready: return "ready"; | ||||
| 	} | ||||
| 	return "unknown"; | ||||
| } | ||||
| 
 | ||||
| #ifdef SANITY_CHECK_BUFFERS | ||||
| static void pvr2_buffer_describe(struct pvr2_buffer *bp,const char *msg) | ||||
| { | ||||
| 	pvr2_trace(PVR2_TRACE_INFO, | ||||
| 		   "buffer%s%s %p state=%s id=%d status=%d" | ||||
| 		   " stream=%p purb=%p sig=0x%x", | ||||
| 		   (msg ? " " : ""), | ||||
| 		   (msg ? msg : ""), | ||||
| 		   bp, | ||||
| 		   (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"), | ||||
| 		   (bp ? bp->id : 0), | ||||
| 		   (bp ? bp->status : 0), | ||||
| 		   (bp ? bp->stream : NULL), | ||||
| 		   (bp ? bp->purb : NULL), | ||||
| 		   (bp ? bp->signature : 0)); | ||||
| } | ||||
| #endif  /*  SANITY_CHECK_BUFFERS  */ | ||||
| 
 | ||||
| static void pvr2_buffer_remove(struct pvr2_buffer *bp) | ||||
| { | ||||
| 	unsigned int *cnt; | ||||
| 	unsigned int *bcnt; | ||||
| 	unsigned int ccnt; | ||||
| 	struct pvr2_stream *sp = bp->stream; | ||||
| 	switch (bp->state) { | ||||
| 	case pvr2_buffer_state_idle: | ||||
| 		cnt = &sp->i_count; | ||||
| 		bcnt = &sp->i_bcount; | ||||
| 		ccnt = bp->max_count; | ||||
| 		break; | ||||
| 	case pvr2_buffer_state_queued: | ||||
| 		cnt = &sp->q_count; | ||||
| 		bcnt = &sp->q_bcount; | ||||
| 		ccnt = bp->max_count; | ||||
| 		break; | ||||
| 	case pvr2_buffer_state_ready: | ||||
| 		cnt = &sp->r_count; | ||||
| 		bcnt = &sp->r_bcount; | ||||
| 		ccnt = bp->used_count; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return; | ||||
| 	} | ||||
| 	list_del_init(&bp->list_overhead); | ||||
| 	(*cnt)--; | ||||
| 	(*bcnt) -= ccnt; | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||||
| 		   "/*---TRACE_FLOW---*/" | ||||
| 		   " bufferPool     %8s dec cap=%07d cnt=%02d", | ||||
| 		   pvr2_buffer_state_decode(bp->state),*bcnt,*cnt); | ||||
| 	bp->state = pvr2_buffer_state_none; | ||||
| } | ||||
| 
 | ||||
| static void pvr2_buffer_set_none(struct pvr2_buffer *bp) | ||||
| { | ||||
| 	unsigned long irq_flags; | ||||
| 	struct pvr2_stream *sp; | ||||
| 	BUFFER_CHECK(bp); | ||||
| 	sp = bp->stream; | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||||
| 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s", | ||||
| 		   bp, | ||||
| 		   pvr2_buffer_state_decode(bp->state), | ||||
| 		   pvr2_buffer_state_decode(pvr2_buffer_state_none)); | ||||
| 	spin_lock_irqsave(&sp->list_lock,irq_flags); | ||||
| 	pvr2_buffer_remove(bp); | ||||
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||||
| } | ||||
| 
 | ||||
| static int pvr2_buffer_set_ready(struct pvr2_buffer *bp) | ||||
| { | ||||
| 	int fl; | ||||
| 	unsigned long irq_flags; | ||||
| 	struct pvr2_stream *sp; | ||||
| 	BUFFER_CHECK(bp); | ||||
| 	sp = bp->stream; | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||||
| 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s", | ||||
| 		   bp, | ||||
| 		   pvr2_buffer_state_decode(bp->state), | ||||
| 		   pvr2_buffer_state_decode(pvr2_buffer_state_ready)); | ||||
| 	spin_lock_irqsave(&sp->list_lock,irq_flags); | ||||
| 	fl = (sp->r_count == 0); | ||||
| 	pvr2_buffer_remove(bp); | ||||
| 	list_add_tail(&bp->list_overhead,&sp->ready_list); | ||||
| 	bp->state = pvr2_buffer_state_ready; | ||||
| 	(sp->r_count)++; | ||||
| 	sp->r_bcount += bp->used_count; | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||||
| 		   "/*---TRACE_FLOW---*/" | ||||
| 		   " bufferPool     %8s inc cap=%07d cnt=%02d", | ||||
| 		   pvr2_buffer_state_decode(bp->state), | ||||
| 		   sp->r_bcount,sp->r_count); | ||||
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||||
| 	return fl; | ||||
| } | ||||
| 
 | ||||
| static void pvr2_buffer_set_idle(struct pvr2_buffer *bp) | ||||
| { | ||||
| 	unsigned long irq_flags; | ||||
| 	struct pvr2_stream *sp; | ||||
| 	BUFFER_CHECK(bp); | ||||
| 	sp = bp->stream; | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||||
| 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s", | ||||
| 		   bp, | ||||
| 		   pvr2_buffer_state_decode(bp->state), | ||||
| 		   pvr2_buffer_state_decode(pvr2_buffer_state_idle)); | ||||
| 	spin_lock_irqsave(&sp->list_lock,irq_flags); | ||||
| 	pvr2_buffer_remove(bp); | ||||
| 	list_add_tail(&bp->list_overhead,&sp->idle_list); | ||||
| 	bp->state = pvr2_buffer_state_idle; | ||||
| 	(sp->i_count)++; | ||||
| 	sp->i_bcount += bp->max_count; | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||||
| 		   "/*---TRACE_FLOW---*/" | ||||
| 		   " bufferPool     %8s inc cap=%07d cnt=%02d", | ||||
| 		   pvr2_buffer_state_decode(bp->state), | ||||
| 		   sp->i_bcount,sp->i_count); | ||||
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||||
| } | ||||
| 
 | ||||
| static void pvr2_buffer_set_queued(struct pvr2_buffer *bp) | ||||
| { | ||||
| 	unsigned long irq_flags; | ||||
| 	struct pvr2_stream *sp; | ||||
| 	BUFFER_CHECK(bp); | ||||
| 	sp = bp->stream; | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||||
| 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s", | ||||
| 		   bp, | ||||
| 		   pvr2_buffer_state_decode(bp->state), | ||||
| 		   pvr2_buffer_state_decode(pvr2_buffer_state_queued)); | ||||
| 	spin_lock_irqsave(&sp->list_lock,irq_flags); | ||||
| 	pvr2_buffer_remove(bp); | ||||
| 	list_add_tail(&bp->list_overhead,&sp->queued_list); | ||||
| 	bp->state = pvr2_buffer_state_queued; | ||||
| 	(sp->q_count)++; | ||||
| 	sp->q_bcount += bp->max_count; | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||||
| 		   "/*---TRACE_FLOW---*/" | ||||
| 		   " bufferPool     %8s inc cap=%07d cnt=%02d", | ||||
| 		   pvr2_buffer_state_decode(bp->state), | ||||
| 		   sp->q_bcount,sp->q_count); | ||||
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||||
| } | ||||
| 
 | ||||
| static void pvr2_buffer_wipe(struct pvr2_buffer *bp) | ||||
| { | ||||
| 	if (bp->state == pvr2_buffer_state_queued) { | ||||
| 		usb_kill_urb(bp->purb); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int pvr2_buffer_init(struct pvr2_buffer *bp, | ||||
| 			    struct pvr2_stream *sp, | ||||
| 			    unsigned int id) | ||||
| { | ||||
| 	memset(bp,0,sizeof(*bp)); | ||||
| 	bp->signature = BUFFER_SIG; | ||||
| 	bp->id = id; | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_POOL, | ||||
| 		   "/*---TRACE_FLOW---*/ bufferInit     %p stream=%p",bp,sp); | ||||
| 	bp->stream = sp; | ||||
| 	bp->state = pvr2_buffer_state_none; | ||||
| 	INIT_LIST_HEAD(&bp->list_overhead); | ||||
| 	bp->purb = usb_alloc_urb(0,GFP_KERNEL); | ||||
| 	if (! bp->purb) return -ENOMEM; | ||||
| #ifdef SANITY_CHECK_BUFFERS | ||||
| 	pvr2_buffer_describe(bp,"create"); | ||||
| #endif | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void pvr2_buffer_done(struct pvr2_buffer *bp) | ||||
| { | ||||
| #ifdef SANITY_CHECK_BUFFERS | ||||
| 	pvr2_buffer_describe(bp,"delete"); | ||||
| #endif | ||||
| 	pvr2_buffer_wipe(bp); | ||||
| 	pvr2_buffer_set_none(bp); | ||||
| 	bp->signature = 0; | ||||
| 	bp->stream = NULL; | ||||
| 	usb_free_urb(bp->purb); | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_POOL,"/*---TRACE_FLOW---*/" | ||||
| 		   " bufferDone     %p",bp); | ||||
| } | ||||
| 
 | ||||
| static int pvr2_stream_buffer_count(struct pvr2_stream *sp,unsigned int cnt) | ||||
| { | ||||
| 	int ret; | ||||
| 	unsigned int scnt; | ||||
| 
 | ||||
| 	/* Allocate buffers pointer array in multiples of 32 entries */ | ||||
| 	if (cnt == sp->buffer_total_count) return 0; | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_POOL, | ||||
| 		   "/*---TRACE_FLOW---*/ poolResize    " | ||||
| 		   " stream=%p cur=%d adj=%+d", | ||||
| 		   sp, | ||||
| 		   sp->buffer_total_count, | ||||
| 		   cnt-sp->buffer_total_count); | ||||
| 
 | ||||
| 	scnt = cnt & ~0x1f; | ||||
| 	if (cnt > scnt) scnt += 0x20; | ||||
| 
 | ||||
| 	if (cnt > sp->buffer_total_count) { | ||||
| 		if (scnt > sp->buffer_slot_count) { | ||||
| 			struct pvr2_buffer **nb; | ||||
| 			nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL); | ||||
| 			if (!nb) return -ENOMEM; | ||||
| 			if (sp->buffer_slot_count) { | ||||
| 				memcpy(nb,sp->buffers, | ||||
| 				       sp->buffer_slot_count * sizeof(*nb)); | ||||
| 				kfree(sp->buffers); | ||||
| 			} | ||||
| 			sp->buffers = nb; | ||||
| 			sp->buffer_slot_count = scnt; | ||||
| 		} | ||||
| 		while (sp->buffer_total_count < cnt) { | ||||
| 			struct pvr2_buffer *bp; | ||||
| 			bp = kmalloc(sizeof(*bp),GFP_KERNEL); | ||||
| 			if (!bp) return -ENOMEM; | ||||
| 			ret = pvr2_buffer_init(bp,sp,sp->buffer_total_count); | ||||
| 			if (ret) { | ||||
| 				kfree(bp); | ||||
| 				return -ENOMEM; | ||||
| 			} | ||||
| 			sp->buffers[sp->buffer_total_count] = bp; | ||||
| 			(sp->buffer_total_count)++; | ||||
| 			pvr2_buffer_set_idle(bp); | ||||
| 		} | ||||
| 	} else { | ||||
| 		while (sp->buffer_total_count > cnt) { | ||||
| 			struct pvr2_buffer *bp; | ||||
| 			bp = sp->buffers[sp->buffer_total_count - 1]; | ||||
| 			/* Paranoia */ | ||||
| 			sp->buffers[sp->buffer_total_count - 1] = NULL; | ||||
| 			(sp->buffer_total_count)--; | ||||
| 			pvr2_buffer_done(bp); | ||||
| 			kfree(bp); | ||||
| 		} | ||||
| 		if (scnt < sp->buffer_slot_count) { | ||||
| 			struct pvr2_buffer **nb = NULL; | ||||
| 			if (scnt) { | ||||
| 				nb = kmemdup(sp->buffers, scnt * sizeof(*nb), | ||||
| 					     GFP_KERNEL); | ||||
| 				if (!nb) return -ENOMEM; | ||||
| 			} | ||||
| 			kfree(sp->buffers); | ||||
| 			sp->buffers = nb; | ||||
| 			sp->buffer_slot_count = scnt; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp) | ||||
| { | ||||
| 	struct pvr2_buffer *bp; | ||||
| 	unsigned int cnt; | ||||
| 
 | ||||
| 	if (sp->buffer_total_count == sp->buffer_target_count) return 0; | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_POOL, | ||||
| 		   "/*---TRACE_FLOW---*/" | ||||
| 		   " poolCheck      stream=%p cur=%d tgt=%d", | ||||
| 		   sp,sp->buffer_total_count,sp->buffer_target_count); | ||||
| 
 | ||||
| 	if (sp->buffer_total_count < sp->buffer_target_count) { | ||||
| 		return pvr2_stream_buffer_count(sp,sp->buffer_target_count); | ||||
| 	} | ||||
| 
 | ||||
| 	cnt = 0; | ||||
| 	while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) { | ||||
| 		bp = sp->buffers[sp->buffer_total_count - (cnt + 1)]; | ||||
| 		if (bp->state != pvr2_buffer_state_idle) break; | ||||
| 		cnt++; | ||||
| 	} | ||||
| 	if (cnt) { | ||||
| 		pvr2_stream_buffer_count(sp,sp->buffer_total_count - cnt); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void pvr2_stream_internal_flush(struct pvr2_stream *sp) | ||||
| { | ||||
| 	struct list_head *lp; | ||||
| 	struct pvr2_buffer *bp1; | ||||
| 	while ((lp = sp->queued_list.next) != &sp->queued_list) { | ||||
| 		bp1 = list_entry(lp,struct pvr2_buffer,list_overhead); | ||||
| 		pvr2_buffer_wipe(bp1); | ||||
| 		/* At this point, we should be guaranteed that no
 | ||||
| 		   completion callback may happen on this buffer.  But it's | ||||
| 		   possible that it might have completed after we noticed | ||||
| 		   it but before we wiped it.  So double check its status | ||||
| 		   here first. */ | ||||
| 		if (bp1->state != pvr2_buffer_state_queued) continue; | ||||
| 		pvr2_buffer_set_idle(bp1); | ||||
| 	} | ||||
| 	if (sp->buffer_total_count != sp->buffer_target_count) { | ||||
| 		pvr2_stream_achieve_buffer_count(sp); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void pvr2_stream_init(struct pvr2_stream *sp) | ||||
| { | ||||
| 	spin_lock_init(&sp->list_lock); | ||||
| 	mutex_init(&sp->mutex); | ||||
| 	INIT_LIST_HEAD(&sp->queued_list); | ||||
| 	INIT_LIST_HEAD(&sp->ready_list); | ||||
| 	INIT_LIST_HEAD(&sp->idle_list); | ||||
| } | ||||
| 
 | ||||
| static void pvr2_stream_done(struct pvr2_stream *sp) | ||||
| { | ||||
| 	mutex_lock(&sp->mutex); do { | ||||
| 		pvr2_stream_internal_flush(sp); | ||||
| 		pvr2_stream_buffer_count(sp,0); | ||||
| 	} while (0); mutex_unlock(&sp->mutex); | ||||
| } | ||||
| 
 | ||||
| static void buffer_complete(struct urb *urb) | ||||
| { | ||||
| 	struct pvr2_buffer *bp = urb->context; | ||||
| 	struct pvr2_stream *sp; | ||||
| 	unsigned long irq_flags; | ||||
| 	BUFFER_CHECK(bp); | ||||
| 	sp = bp->stream; | ||||
| 	bp->used_count = 0; | ||||
| 	bp->status = 0; | ||||
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||||
| 		   "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d", | ||||
| 		   bp,urb->status,urb->actual_length); | ||||
| 	spin_lock_irqsave(&sp->list_lock,irq_flags); | ||||
| 	if ((!(urb->status)) || | ||||
| 	    (urb->status == -ENOENT) || | ||||
| 	    (urb->status == -ECONNRESET) || | ||||
| 	    (urb->status == -ESHUTDOWN)) { | ||||
| 		(sp->buffers_processed)++; | ||||
| 		sp->bytes_processed += urb->actual_length; | ||||
| 		bp->used_count = urb->actual_length; | ||||
| 		if (sp->fail_count) { | ||||
| 			pvr2_trace(PVR2_TRACE_TOLERANCE, | ||||
| 				   "stream %p transfer ok" | ||||
| 				   " - fail count reset",sp); | ||||
| 			sp->fail_count = 0; | ||||
| 		} | ||||
| 	} else if (sp->fail_count < sp->fail_tolerance) { | ||||
| 		// We can tolerate this error, because we're below the
 | ||||
| 		// threshold...
 | ||||
| 		(sp->fail_count)++; | ||||
| 		(sp->buffers_failed)++; | ||||
| 		pvr2_trace(PVR2_TRACE_TOLERANCE, | ||||
| 			   "stream %p ignoring error %d" | ||||
| 			   " - fail count increased to %u", | ||||
| 			   sp,urb->status,sp->fail_count); | ||||
| 	} else { | ||||
| 		(sp->buffers_failed)++; | ||||
| 		bp->status = urb->status; | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||||
| 	pvr2_buffer_set_ready(bp); | ||||
| 	if (sp && sp->callback_func) { | ||||
| 		sp->callback_func(sp->callback_data); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct pvr2_stream *pvr2_stream_create(void) | ||||
| { | ||||
| 	struct pvr2_stream *sp; | ||||
| 	sp = kzalloc(sizeof(*sp),GFP_KERNEL); | ||||
| 	if (!sp) return sp; | ||||
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp); | ||||
| 	pvr2_stream_init(sp); | ||||
| 	return sp; | ||||
| } | ||||
| 
 | ||||
| void pvr2_stream_destroy(struct pvr2_stream *sp) | ||||
| { | ||||
| 	if (!sp) return; | ||||
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_destroy: sp=%p",sp); | ||||
| 	pvr2_stream_done(sp); | ||||
| 	kfree(sp); | ||||
| } | ||||
| 
 | ||||
| void pvr2_stream_setup(struct pvr2_stream *sp, | ||||
| 		       struct usb_device *dev, | ||||
| 		       int endpoint, | ||||
| 		       unsigned int tolerance) | ||||
| { | ||||
| 	mutex_lock(&sp->mutex); do { | ||||
| 		pvr2_stream_internal_flush(sp); | ||||
| 		sp->dev = dev; | ||||
| 		sp->endpoint = endpoint; | ||||
| 		sp->fail_tolerance = tolerance; | ||||
| 	} while(0); mutex_unlock(&sp->mutex); | ||||
| } | ||||
| 
 | ||||
| void pvr2_stream_set_callback(struct pvr2_stream *sp, | ||||
| 			      pvr2_stream_callback func, | ||||
| 			      void *data) | ||||
| { | ||||
| 	unsigned long irq_flags; | ||||
| 	mutex_lock(&sp->mutex); do { | ||||
| 		spin_lock_irqsave(&sp->list_lock,irq_flags); | ||||
| 		sp->callback_data = data; | ||||
| 		sp->callback_func = func; | ||||
| 		spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||||
| 	} while(0); mutex_unlock(&sp->mutex); | ||||
| } | ||||
| 
 | ||||
| void pvr2_stream_get_stats(struct pvr2_stream *sp, | ||||
| 			   struct pvr2_stream_stats *stats, | ||||
| 			   int zero_counts) | ||||
| { | ||||
| 	unsigned long irq_flags; | ||||
| 	spin_lock_irqsave(&sp->list_lock,irq_flags); | ||||
| 	if (stats) { | ||||
| 		stats->buffers_in_queue = sp->q_count; | ||||
| 		stats->buffers_in_idle = sp->i_count; | ||||
| 		stats->buffers_in_ready = sp->r_count; | ||||
| 		stats->buffers_processed = sp->buffers_processed; | ||||
| 		stats->buffers_failed = sp->buffers_failed; | ||||
| 		stats->bytes_processed = sp->bytes_processed; | ||||
| 	} | ||||
| 	if (zero_counts) { | ||||
| 		sp->buffers_processed = 0; | ||||
| 		sp->buffers_failed = 0; | ||||
| 		sp->bytes_processed = 0; | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||||
| } | ||||
| 
 | ||||
| /* Query / set the nominal buffer count */ | ||||
| int pvr2_stream_get_buffer_count(struct pvr2_stream *sp) | ||||
| { | ||||
| 	return sp->buffer_target_count; | ||||
| } | ||||
| 
 | ||||
| int pvr2_stream_set_buffer_count(struct pvr2_stream *sp,unsigned int cnt) | ||||
| { | ||||
| 	int ret; | ||||
| 	if (sp->buffer_target_count == cnt) return 0; | ||||
| 	mutex_lock(&sp->mutex); do { | ||||
| 		sp->buffer_target_count = cnt; | ||||
| 		ret = pvr2_stream_achieve_buffer_count(sp); | ||||
| 	} while(0); mutex_unlock(&sp->mutex); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp) | ||||
| { | ||||
| 	struct list_head *lp = sp->idle_list.next; | ||||
| 	if (lp == &sp->idle_list) return NULL; | ||||
| 	return list_entry(lp,struct pvr2_buffer,list_overhead); | ||||
| } | ||||
| 
 | ||||
| struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp) | ||||
| { | ||||
| 	struct list_head *lp = sp->ready_list.next; | ||||
| 	if (lp == &sp->ready_list) return NULL; | ||||
| 	return list_entry(lp,struct pvr2_buffer,list_overhead); | ||||
| } | ||||
| 
 | ||||
| struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id) | ||||
| { | ||||
| 	if (id < 0) return NULL; | ||||
| 	if (id >= sp->buffer_total_count) return NULL; | ||||
| 	return sp->buffers[id]; | ||||
| } | ||||
| 
 | ||||
| int pvr2_stream_get_ready_count(struct pvr2_stream *sp) | ||||
| { | ||||
| 	return sp->r_count; | ||||
| } | ||||
| 
 | ||||
| void pvr2_stream_kill(struct pvr2_stream *sp) | ||||
| { | ||||
| 	struct pvr2_buffer *bp; | ||||
| 	mutex_lock(&sp->mutex); do { | ||||
| 		pvr2_stream_internal_flush(sp); | ||||
| 		while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) { | ||||
| 			pvr2_buffer_set_idle(bp); | ||||
| 		} | ||||
| 		if (sp->buffer_total_count != sp->buffer_target_count) { | ||||
| 			pvr2_stream_achieve_buffer_count(sp); | ||||
| 		} | ||||
| 	} while(0); mutex_unlock(&sp->mutex); | ||||
| } | ||||
| 
 | ||||
| int pvr2_buffer_queue(struct pvr2_buffer *bp) | ||||
| { | ||||
| #undef SEED_BUFFER | ||||
| #ifdef SEED_BUFFER | ||||
| 	unsigned int idx; | ||||
| 	unsigned int val; | ||||
| #endif | ||||
| 	int ret = 0; | ||||
| 	struct pvr2_stream *sp; | ||||
| 	if (!bp) return -EINVAL; | ||||
| 	sp = bp->stream; | ||||
| 	mutex_lock(&sp->mutex); do { | ||||
| 		pvr2_buffer_wipe(bp); | ||||
| 		if (!sp->dev) { | ||||
| 			ret = -EIO; | ||||
| 			break; | ||||
| 		} | ||||
| 		pvr2_buffer_set_queued(bp); | ||||
| #ifdef SEED_BUFFER | ||||
| 		for (idx = 0; idx < (bp->max_count) / 4; idx++) { | ||||
| 			val = bp->id << 24; | ||||
| 			val |= idx; | ||||
| 			((unsigned int *)(bp->ptr))[idx] = val; | ||||
| 		} | ||||
| #endif | ||||
| 		bp->status = -EINPROGRESS; | ||||
| 		usb_fill_bulk_urb(bp->purb,      // struct urb *urb
 | ||||
| 				  sp->dev,       // struct usb_device *dev
 | ||||
| 				  // endpoint (below)
 | ||||
| 				  usb_rcvbulkpipe(sp->dev,sp->endpoint), | ||||
| 				  bp->ptr,       // void *transfer_buffer
 | ||||
| 				  bp->max_count, // int buffer_length
 | ||||
| 				  buffer_complete, | ||||
| 				  bp); | ||||
| 		usb_submit_urb(bp->purb,GFP_KERNEL); | ||||
| 	} while(0); mutex_unlock(&sp->mutex); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	unsigned long irq_flags; | ||||
| 	struct pvr2_stream *sp; | ||||
| 	if (!bp) return -EINVAL; | ||||
| 	sp = bp->stream; | ||||
| 	mutex_lock(&sp->mutex); do { | ||||
| 		spin_lock_irqsave(&sp->list_lock,irq_flags); | ||||
| 		if (bp->state != pvr2_buffer_state_idle) { | ||||
| 			ret = -EPERM; | ||||
| 		} else { | ||||
| 			bp->ptr = ptr; | ||||
| 			bp->stream->i_bcount -= bp->max_count; | ||||
| 			bp->max_count = cnt; | ||||
| 			bp->stream->i_bcount += bp->max_count; | ||||
| 			pvr2_trace(PVR2_TRACE_BUF_FLOW, | ||||
| 				   "/*---TRACE_FLOW---*/ bufferPool    " | ||||
| 				   " %8s cap cap=%07d cnt=%02d", | ||||
| 				   pvr2_buffer_state_decode( | ||||
| 					   pvr2_buffer_state_idle), | ||||
| 				   bp->stream->i_bcount,bp->stream->i_count); | ||||
| 		} | ||||
| 		spin_unlock_irqrestore(&sp->list_lock,irq_flags); | ||||
| 	} while(0); mutex_unlock(&sp->mutex); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| unsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp) | ||||
| { | ||||
| 	return bp->used_count; | ||||
| } | ||||
| 
 | ||||
| int pvr2_buffer_get_status(struct pvr2_buffer *bp) | ||||
| { | ||||
| 	return bp->status; | ||||
| } | ||||
| 
 | ||||
| int pvr2_buffer_get_id(struct pvr2_buffer *bp) | ||||
| { | ||||
| 	return bp->id; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										102
									
								
								drivers/media/usb/pvrusb2/pvrusb2-io.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								drivers/media/usb/pvrusb2/pvrusb2-io.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,102 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_IO_H | ||||
| #define __PVRUSB2_IO_H | ||||
| 
 | ||||
| #include <linux/usb.h> | ||||
| #include <linux/list.h> | ||||
| 
 | ||||
| typedef void (*pvr2_stream_callback)(void *); | ||||
| 
 | ||||
| enum pvr2_buffer_state { | ||||
| 	pvr2_buffer_state_none = 0,   // Not on any list
 | ||||
| 	pvr2_buffer_state_idle = 1,   // Buffer is ready to be used again
 | ||||
| 	pvr2_buffer_state_queued = 2, // Buffer has been queued for filling
 | ||||
| 	pvr2_buffer_state_ready = 3,  // Buffer has data available
 | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_stream; | ||||
| struct pvr2_buffer; | ||||
| 
 | ||||
| struct pvr2_stream_stats { | ||||
| 	unsigned int buffers_in_queue; | ||||
| 	unsigned int buffers_in_idle; | ||||
| 	unsigned int buffers_in_ready; | ||||
| 	unsigned int buffers_processed; | ||||
| 	unsigned int buffers_failed; | ||||
| 	unsigned int bytes_processed; | ||||
| }; | ||||
| 
 | ||||
| /* Initialize / tear down stream structure */ | ||||
| struct pvr2_stream *pvr2_stream_create(void); | ||||
| void pvr2_stream_destroy(struct pvr2_stream *); | ||||
| void pvr2_stream_setup(struct pvr2_stream *, | ||||
| 		       struct usb_device *dev,int endpoint, | ||||
| 		       unsigned int tolerance); | ||||
| void pvr2_stream_set_callback(struct pvr2_stream *, | ||||
| 			      pvr2_stream_callback func, | ||||
| 			      void *data); | ||||
| void pvr2_stream_get_stats(struct pvr2_stream *, | ||||
| 			   struct pvr2_stream_stats *, | ||||
| 			   int zero_counts); | ||||
| 
 | ||||
| /* Query / set the nominal buffer count */ | ||||
| int pvr2_stream_get_buffer_count(struct pvr2_stream *); | ||||
| int pvr2_stream_set_buffer_count(struct pvr2_stream *,unsigned int); | ||||
| 
 | ||||
| /* Get a pointer to a buffer that is either idle, ready, or is specified
 | ||||
|    named. */ | ||||
| struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *); | ||||
| struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *); | ||||
| struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id); | ||||
| 
 | ||||
| /* Find out how many buffers are idle or ready */ | ||||
| int pvr2_stream_get_ready_count(struct pvr2_stream *); | ||||
| 
 | ||||
| 
 | ||||
| /* Kill all pending buffers and throw away any ready buffers as well */ | ||||
| void pvr2_stream_kill(struct pvr2_stream *); | ||||
| 
 | ||||
| /* Set up the actual storage for a buffer */ | ||||
| int pvr2_buffer_set_buffer(struct pvr2_buffer *,void *ptr,unsigned int cnt); | ||||
| 
 | ||||
| /* Find out size of data in the given ready buffer */ | ||||
| unsigned int pvr2_buffer_get_count(struct pvr2_buffer *); | ||||
| 
 | ||||
| /* Retrieve completion code for given ready buffer */ | ||||
| int pvr2_buffer_get_status(struct pvr2_buffer *); | ||||
| 
 | ||||
| /* Retrieve ID of given buffer */ | ||||
| int pvr2_buffer_get_id(struct pvr2_buffer *); | ||||
| 
 | ||||
| /* Start reading into given buffer (kill it if needed) */ | ||||
| int pvr2_buffer_queue(struct pvr2_buffer *); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_IO_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										512
									
								
								drivers/media/usb/pvrusb2/pvrusb2-ioread.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										512
									
								
								drivers/media/usb/pvrusb2/pvrusb2-ioread.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,512 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "pvrusb2-ioread.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include <linux/errno.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/mm.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <asm/uaccess.h> | ||||
| 
 | ||||
| #define BUFFER_COUNT 32 | ||||
| #define BUFFER_SIZE PAGE_ALIGN(0x4000) | ||||
| 
 | ||||
| struct pvr2_ioread { | ||||
| 	struct pvr2_stream *stream; | ||||
| 	char *buffer_storage[BUFFER_COUNT]; | ||||
| 	char *sync_key_ptr; | ||||
| 	unsigned int sync_key_len; | ||||
| 	unsigned int sync_buf_offs; | ||||
| 	unsigned int sync_state; | ||||
| 	unsigned int sync_trashed_count; | ||||
| 	int enabled;         // Streaming is on
 | ||||
| 	int spigot_open;     // OK to pass data to client
 | ||||
| 	int stream_running;  // Passing data to client now
 | ||||
| 
 | ||||
| 	/* State relevant to current buffer being read */ | ||||
| 	struct pvr2_buffer *c_buf; | ||||
| 	char *c_data_ptr; | ||||
| 	unsigned int c_data_len; | ||||
| 	unsigned int c_data_offs; | ||||
| 	struct mutex mutex; | ||||
| }; | ||||
| 
 | ||||
| static int pvr2_ioread_init(struct pvr2_ioread *cp) | ||||
| { | ||||
| 	unsigned int idx; | ||||
| 
 | ||||
| 	cp->stream = NULL; | ||||
| 	mutex_init(&cp->mutex); | ||||
| 
 | ||||
| 	for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||||
| 		cp->buffer_storage[idx] = kmalloc(BUFFER_SIZE,GFP_KERNEL); | ||||
| 		if (!(cp->buffer_storage[idx])) break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (idx < BUFFER_COUNT) { | ||||
| 		// An allocation appears to have failed
 | ||||
| 		for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||||
| 			if (!(cp->buffer_storage[idx])) continue; | ||||
| 			kfree(cp->buffer_storage[idx]); | ||||
| 		} | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void pvr2_ioread_done(struct pvr2_ioread *cp) | ||||
| { | ||||
| 	unsigned int idx; | ||||
| 
 | ||||
| 	pvr2_ioread_setup(cp,NULL); | ||||
| 	for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||||
| 		if (!(cp->buffer_storage[idx])) continue; | ||||
| 		kfree(cp->buffer_storage[idx]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct pvr2_ioread *pvr2_ioread_create(void) | ||||
| { | ||||
| 	struct pvr2_ioread *cp; | ||||
| 	cp = kzalloc(sizeof(*cp),GFP_KERNEL); | ||||
| 	if (!cp) return NULL; | ||||
| 	pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp); | ||||
| 	if (pvr2_ioread_init(cp) < 0) { | ||||
| 		kfree(cp); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	return cp; | ||||
| } | ||||
| 
 | ||||
| void pvr2_ioread_destroy(struct pvr2_ioread *cp) | ||||
| { | ||||
| 	if (!cp) return; | ||||
| 	pvr2_ioread_done(cp); | ||||
| 	pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_destroy id=%p",cp); | ||||
| 	if (cp->sync_key_ptr) { | ||||
| 		kfree(cp->sync_key_ptr); | ||||
| 		cp->sync_key_ptr = NULL; | ||||
| 	} | ||||
| 	kfree(cp); | ||||
| } | ||||
| 
 | ||||
| void pvr2_ioread_set_sync_key(struct pvr2_ioread *cp, | ||||
| 			      const char *sync_key_ptr, | ||||
| 			      unsigned int sync_key_len) | ||||
| { | ||||
| 	if (!cp) return; | ||||
| 
 | ||||
| 	if (!sync_key_ptr) sync_key_len = 0; | ||||
| 	if ((sync_key_len == cp->sync_key_len) && | ||||
| 	    ((!sync_key_len) || | ||||
| 	     (!memcmp(sync_key_ptr,cp->sync_key_ptr,sync_key_len)))) return; | ||||
| 
 | ||||
| 	if (sync_key_len != cp->sync_key_len) { | ||||
| 		if (cp->sync_key_ptr) { | ||||
| 			kfree(cp->sync_key_ptr); | ||||
| 			cp->sync_key_ptr = NULL; | ||||
| 		} | ||||
| 		cp->sync_key_len = 0; | ||||
| 		if (sync_key_len) { | ||||
| 			cp->sync_key_ptr = kmalloc(sync_key_len,GFP_KERNEL); | ||||
| 			if (cp->sync_key_ptr) { | ||||
| 				cp->sync_key_len = sync_key_len; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if (!cp->sync_key_len) return; | ||||
| 	memcpy(cp->sync_key_ptr,sync_key_ptr,cp->sync_key_len); | ||||
| } | ||||
| 
 | ||||
| static void pvr2_ioread_stop(struct pvr2_ioread *cp) | ||||
| { | ||||
| 	if (!(cp->enabled)) return; | ||||
| 	pvr2_trace(PVR2_TRACE_START_STOP, | ||||
| 		   "/*---TRACE_READ---*/ pvr2_ioread_stop id=%p",cp); | ||||
| 	pvr2_stream_kill(cp->stream); | ||||
| 	cp->c_buf = NULL; | ||||
| 	cp->c_data_ptr = NULL; | ||||
| 	cp->c_data_len = 0; | ||||
| 	cp->c_data_offs = 0; | ||||
| 	cp->enabled = 0; | ||||
| 	cp->stream_running = 0; | ||||
| 	cp->spigot_open = 0; | ||||
| 	if (cp->sync_state) { | ||||
| 		pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||||
| 			   "/*---TRACE_READ---*/ sync_state <== 0"); | ||||
| 		cp->sync_state = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int pvr2_ioread_start(struct pvr2_ioread *cp) | ||||
| { | ||||
| 	int stat; | ||||
| 	struct pvr2_buffer *bp; | ||||
| 	if (cp->enabled) return 0; | ||||
| 	if (!(cp->stream)) return 0; | ||||
| 	pvr2_trace(PVR2_TRACE_START_STOP, | ||||
| 		   "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp); | ||||
| 	while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != NULL) { | ||||
| 		stat = pvr2_buffer_queue(bp); | ||||
| 		if (stat < 0) { | ||||
| 			pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||||
| 				   "/*---TRACE_READ---*/" | ||||
| 				   " pvr2_ioread_start id=%p" | ||||
| 				   " error=%d", | ||||
| 				   cp,stat); | ||||
| 			pvr2_ioread_stop(cp); | ||||
| 			return stat; | ||||
| 		} | ||||
| 	} | ||||
| 	cp->enabled = !0; | ||||
| 	cp->c_buf = NULL; | ||||
| 	cp->c_data_ptr = NULL; | ||||
| 	cp->c_data_len = 0; | ||||
| 	cp->c_data_offs = 0; | ||||
| 	cp->stream_running = 0; | ||||
| 	if (cp->sync_key_len) { | ||||
| 		pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||||
| 			   "/*---TRACE_READ---*/ sync_state <== 1"); | ||||
| 		cp->sync_state = 1; | ||||
| 		cp->sync_trashed_count = 0; | ||||
| 		cp->sync_buf_offs = 0; | ||||
| 	} | ||||
| 	cp->spigot_open = 0; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp) | ||||
| { | ||||
| 	return cp->stream; | ||||
| } | ||||
| 
 | ||||
| int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp) | ||||
| { | ||||
| 	int ret; | ||||
| 	unsigned int idx; | ||||
| 	struct pvr2_buffer *bp; | ||||
| 
 | ||||
| 	mutex_lock(&cp->mutex); do { | ||||
| 		if (cp->stream) { | ||||
| 			pvr2_trace(PVR2_TRACE_START_STOP, | ||||
| 				   "/*---TRACE_READ---*/" | ||||
| 				   " pvr2_ioread_setup (tear-down) id=%p",cp); | ||||
| 			pvr2_ioread_stop(cp); | ||||
| 			pvr2_stream_kill(cp->stream); | ||||
| 			if (pvr2_stream_get_buffer_count(cp->stream)) { | ||||
| 				pvr2_stream_set_buffer_count(cp->stream,0); | ||||
| 			} | ||||
| 			cp->stream = NULL; | ||||
| 		} | ||||
| 		if (sp) { | ||||
| 			pvr2_trace(PVR2_TRACE_START_STOP, | ||||
| 				   "/*---TRACE_READ---*/" | ||||
| 				   " pvr2_ioread_setup (setup) id=%p",cp); | ||||
| 			pvr2_stream_kill(sp); | ||||
| 			ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT); | ||||
| 			if (ret < 0) { | ||||
| 				mutex_unlock(&cp->mutex); | ||||
| 				return ret; | ||||
| 			} | ||||
| 			for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||||
| 				bp = pvr2_stream_get_buffer(sp,idx); | ||||
| 				pvr2_buffer_set_buffer(bp, | ||||
| 						       cp->buffer_storage[idx], | ||||
| 						       BUFFER_SIZE); | ||||
| 			} | ||||
| 			cp->stream = sp; | ||||
| 		} | ||||
| 	} while (0); mutex_unlock(&cp->mutex); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	if ((!fl) == (!(cp->enabled))) return ret; | ||||
| 
 | ||||
| 	mutex_lock(&cp->mutex); do { | ||||
| 		if (fl) { | ||||
| 			ret = pvr2_ioread_start(cp); | ||||
| 		} else { | ||||
| 			pvr2_ioread_stop(cp); | ||||
| 		} | ||||
| 	} while (0); mutex_unlock(&cp->mutex); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_ioread_get_buffer(struct pvr2_ioread *cp) | ||||
| { | ||||
| 	int stat; | ||||
| 
 | ||||
| 	while (cp->c_data_len <= cp->c_data_offs) { | ||||
| 		if (cp->c_buf) { | ||||
| 			// Flush out current buffer first.
 | ||||
| 			stat = pvr2_buffer_queue(cp->c_buf); | ||||
| 			if (stat < 0) { | ||||
| 				// Streaming error...
 | ||||
| 				pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||||
| 					   "/*---TRACE_READ---*/" | ||||
| 					   " pvr2_ioread_read id=%p" | ||||
| 					   " queue_error=%d", | ||||
| 					   cp,stat); | ||||
| 				pvr2_ioread_stop(cp); | ||||
| 				return 0; | ||||
| 			} | ||||
| 			cp->c_buf = NULL; | ||||
| 			cp->c_data_ptr = NULL; | ||||
| 			cp->c_data_len = 0; | ||||
| 			cp->c_data_offs = 0; | ||||
| 		} | ||||
| 		// Now get a freshly filled buffer.
 | ||||
| 		cp->c_buf = pvr2_stream_get_ready_buffer(cp->stream); | ||||
| 		if (!cp->c_buf) break; // Nothing ready; done.
 | ||||
| 		cp->c_data_len = pvr2_buffer_get_count(cp->c_buf); | ||||
| 		if (!cp->c_data_len) { | ||||
| 			// Nothing transferred.  Was there an error?
 | ||||
| 			stat = pvr2_buffer_get_status(cp->c_buf); | ||||
| 			if (stat < 0) { | ||||
| 				// Streaming error...
 | ||||
| 				pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||||
| 					   "/*---TRACE_READ---*/" | ||||
| 					   " pvr2_ioread_read id=%p" | ||||
| 					   " buffer_error=%d", | ||||
| 					   cp,stat); | ||||
| 				pvr2_ioread_stop(cp); | ||||
| 				// Give up.
 | ||||
| 				return 0; | ||||
| 			} | ||||
| 			// Start over...
 | ||||
| 			continue; | ||||
| 		} | ||||
| 		cp->c_data_offs = 0; | ||||
| 		cp->c_data_ptr = cp->buffer_storage[ | ||||
| 			pvr2_buffer_get_id(cp->c_buf)]; | ||||
| 	} | ||||
| 	return !0; | ||||
| } | ||||
| 
 | ||||
| static void pvr2_ioread_filter(struct pvr2_ioread *cp) | ||||
| { | ||||
| 	unsigned int idx; | ||||
| 	if (!cp->enabled) return; | ||||
| 	if (cp->sync_state != 1) return; | ||||
| 
 | ||||
| 	// Search the stream for our synchronization key.  This is made
 | ||||
| 	// complicated by the fact that in order to be honest with
 | ||||
| 	// ourselves here we must search across buffer boundaries...
 | ||||
| 	mutex_lock(&cp->mutex); while (1) { | ||||
| 		// Ensure we have a buffer
 | ||||
| 		if (!pvr2_ioread_get_buffer(cp)) break; | ||||
| 		if (!cp->c_data_len) break; | ||||
| 
 | ||||
| 		// Now walk the buffer contents until we match the key or
 | ||||
| 		// run out of buffer data.
 | ||||
| 		for (idx = cp->c_data_offs; idx < cp->c_data_len; idx++) { | ||||
| 			if (cp->sync_buf_offs >= cp->sync_key_len) break; | ||||
| 			if (cp->c_data_ptr[idx] == | ||||
| 			    cp->sync_key_ptr[cp->sync_buf_offs]) { | ||||
| 				// Found the next key byte
 | ||||
| 				(cp->sync_buf_offs)++; | ||||
| 			} else { | ||||
| 				// Whoops, mismatched.  Start key over...
 | ||||
| 				cp->sync_buf_offs = 0; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Consume what we've walked through
 | ||||
| 		cp->c_data_offs += idx; | ||||
| 		cp->sync_trashed_count += idx; | ||||
| 
 | ||||
| 		// If we've found the key, then update state and get out.
 | ||||
| 		if (cp->sync_buf_offs >= cp->sync_key_len) { | ||||
| 			cp->sync_trashed_count -= cp->sync_key_len; | ||||
| 			pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||||
| 				   "/*---TRACE_READ---*/" | ||||
| 				   " sync_state <== 2 (skipped %u bytes)", | ||||
| 				   cp->sync_trashed_count); | ||||
| 			cp->sync_state = 2; | ||||
| 			cp->sync_buf_offs = 0; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (cp->c_data_offs < cp->c_data_len) { | ||||
| 			// Sanity check - should NEVER get here
 | ||||
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 				   "ERROR: pvr2_ioread filter sync problem" | ||||
| 				   " len=%u offs=%u", | ||||
| 				   cp->c_data_len,cp->c_data_offs); | ||||
| 			// Get out so we don't get stuck in an infinite
 | ||||
| 			// loop.
 | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		continue; // (for clarity)
 | ||||
| 	} mutex_unlock(&cp->mutex); | ||||
| } | ||||
| 
 | ||||
| int pvr2_ioread_avail(struct pvr2_ioread *cp) | ||||
| { | ||||
| 	int ret; | ||||
| 	if (!(cp->enabled)) { | ||||
| 		// Stream is not enabled; so this is an I/O error
 | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	if (cp->sync_state == 1) { | ||||
| 		pvr2_ioread_filter(cp); | ||||
| 		if (cp->sync_state == 1) return -EAGAIN; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = 0; | ||||
| 	if (cp->stream_running) { | ||||
| 		if (!pvr2_stream_get_ready_count(cp->stream)) { | ||||
| 			// No data available at all right now.
 | ||||
| 			ret = -EAGAIN; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (pvr2_stream_get_ready_count(cp->stream) < BUFFER_COUNT/2) { | ||||
| 			// Haven't buffered up enough yet; try again later
 | ||||
| 			ret = -EAGAIN; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if ((!(cp->spigot_open)) != (!(ret == 0))) { | ||||
| 		cp->spigot_open = (ret == 0); | ||||
| 		pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||||
| 			   "/*---TRACE_READ---*/ data is %s", | ||||
| 			   cp->spigot_open ? "available" : "pending"); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt) | ||||
| { | ||||
| 	unsigned int copied_cnt; | ||||
| 	unsigned int bcnt; | ||||
| 	const char *src; | ||||
| 	int stat; | ||||
| 	int ret = 0; | ||||
| 	unsigned int req_cnt = cnt; | ||||
| 
 | ||||
| 	if (!cnt) { | ||||
| 		pvr2_trace(PVR2_TRACE_TRAP, | ||||
| 			   "/*---TRACE_READ---*/ pvr2_ioread_read id=%p" | ||||
| 			   " ZERO Request? Returning zero.",cp); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	stat = pvr2_ioread_avail(cp); | ||||
| 	if (stat < 0) return stat; | ||||
| 
 | ||||
| 	cp->stream_running = !0; | ||||
| 
 | ||||
| 	mutex_lock(&cp->mutex); do { | ||||
| 
 | ||||
| 		// Suck data out of the buffers and copy to the user
 | ||||
| 		copied_cnt = 0; | ||||
| 		if (!buf) cnt = 0; | ||||
| 		while (1) { | ||||
| 			if (!pvr2_ioread_get_buffer(cp)) { | ||||
| 				ret = -EIO; | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			if (!cnt) break; | ||||
| 
 | ||||
| 			if (cp->sync_state == 2) { | ||||
| 				// We're repeating the sync key data into
 | ||||
| 				// the stream.
 | ||||
| 				src = cp->sync_key_ptr + cp->sync_buf_offs; | ||||
| 				bcnt = cp->sync_key_len - cp->sync_buf_offs; | ||||
| 			} else { | ||||
| 				// Normal buffer copy
 | ||||
| 				src = cp->c_data_ptr + cp->c_data_offs; | ||||
| 				bcnt = cp->c_data_len - cp->c_data_offs; | ||||
| 			} | ||||
| 
 | ||||
| 			if (!bcnt) break; | ||||
| 
 | ||||
| 			// Don't run past user's buffer
 | ||||
| 			if (bcnt > cnt) bcnt = cnt; | ||||
| 
 | ||||
| 			if (copy_to_user(buf,src,bcnt)) { | ||||
| 				// User supplied a bad pointer?
 | ||||
| 				// Give up - this *will* cause data
 | ||||
| 				// to be lost.
 | ||||
| 				ret = -EFAULT; | ||||
| 				break; | ||||
| 			} | ||||
| 			cnt -= bcnt; | ||||
| 			buf += bcnt; | ||||
| 			copied_cnt += bcnt; | ||||
| 
 | ||||
| 			if (cp->sync_state == 2) { | ||||
| 				// Update offset inside sync key that we're
 | ||||
| 				// repeating back out.
 | ||||
| 				cp->sync_buf_offs += bcnt; | ||||
| 				if (cp->sync_buf_offs >= cp->sync_key_len) { | ||||
| 					// Consumed entire key; switch mode
 | ||||
| 					// to normal.
 | ||||
| 					pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||||
| 						   "/*---TRACE_READ---*/" | ||||
| 						   " sync_state <== 0"); | ||||
| 					cp->sync_state = 0; | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Update buffer offset.
 | ||||
| 				cp->c_data_offs += bcnt; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	} while (0); mutex_unlock(&cp->mutex); | ||||
| 
 | ||||
| 	if (!ret) { | ||||
| 		if (copied_cnt) { | ||||
| 			// If anything was copied, return that count
 | ||||
| 			ret = copied_cnt; | ||||
| 		} else { | ||||
| 			// Nothing copied; suggest to caller that another
 | ||||
| 			// attempt should be tried again later
 | ||||
| 			ret = -EAGAIN; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||||
| 		   "/*---TRACE_READ---*/ pvr2_ioread_read" | ||||
| 		   " id=%p request=%d result=%d", | ||||
| 		   cp,req_cnt,ret); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										48
									
								
								drivers/media/usb/pvrusb2/pvrusb2-ioread.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								drivers/media/usb/pvrusb2/pvrusb2-ioread.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_IOREAD_H | ||||
| #define __PVRUSB2_IOREAD_H | ||||
| 
 | ||||
| #include "pvrusb2-io.h" | ||||
| 
 | ||||
| struct pvr2_ioread; | ||||
| 
 | ||||
| struct pvr2_ioread *pvr2_ioread_create(void); | ||||
| void pvr2_ioread_destroy(struct pvr2_ioread *); | ||||
| int pvr2_ioread_setup(struct pvr2_ioread *,struct pvr2_stream *); | ||||
| struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *); | ||||
| void pvr2_ioread_set_sync_key(struct pvr2_ioread *, | ||||
| 			      const char *sync_key_ptr, | ||||
| 			      unsigned int sync_key_len); | ||||
| int pvr2_ioread_set_enabled(struct pvr2_ioread *,int fl); | ||||
| int pvr2_ioread_read(struct pvr2_ioread *,void __user *buf,unsigned int cnt); | ||||
| int pvr2_ioread_avail(struct pvr2_ioread *); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_IOREAD_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										182
									
								
								drivers/media/usb/pvrusb2/pvrusb2-main.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								drivers/media/usb/pvrusb2/pvrusb2-main.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,182 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/usb.h> | ||||
| #include <linux/videodev2.h> | ||||
| 
 | ||||
| #include "pvrusb2-hdw.h" | ||||
| #include "pvrusb2-devattr.h" | ||||
| #include "pvrusb2-context.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include "pvrusb2-v4l2.h" | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS | ||||
| #include "pvrusb2-sysfs.h" | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ | ||||
| 
 | ||||
| #define DRIVER_AUTHOR "Mike Isely <isely@pobox.com>" | ||||
| #define DRIVER_DESC "Hauppauge WinTV-PVR-USB2 MPEG2 Encoder/Tuner" | ||||
| #define DRIVER_VERSION "V4L in-tree version" | ||||
| 
 | ||||
| #define DEFAULT_DEBUG_MASK (PVR2_TRACE_ERROR_LEGS| \ | ||||
| 			    PVR2_TRACE_INFO| \ | ||||
| 			    PVR2_TRACE_STD| \ | ||||
| 			    PVR2_TRACE_TOLERANCE| \ | ||||
| 			    PVR2_TRACE_TRAP| \ | ||||
| 			    0) | ||||
| 
 | ||||
| int pvrusb2_debug = DEFAULT_DEBUG_MASK; | ||||
| 
 | ||||
| module_param_named(debug,pvrusb2_debug,int,S_IRUGO|S_IWUSR); | ||||
| MODULE_PARM_DESC(debug, "Debug trace mask"); | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS | ||||
| static struct pvr2_sysfs_class *class_ptr = NULL; | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ | ||||
| 
 | ||||
| static void pvr_setup_attach(struct pvr2_context *pvr) | ||||
| { | ||||
| 	/* Create association with v4l layer */ | ||||
| 	pvr2_v4l2_create(pvr); | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DVB | ||||
| 	/* Create association with dvb layer */ | ||||
| 	pvr2_dvb_create(pvr); | ||||
| #endif | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS | ||||
| 	pvr2_sysfs_create(pvr,class_ptr); | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ | ||||
| } | ||||
| 
 | ||||
| static int pvr_probe(struct usb_interface *intf, | ||||
| 		     const struct usb_device_id *devid) | ||||
| { | ||||
| 	struct pvr2_context *pvr; | ||||
| 
 | ||||
| 	/* Create underlying hardware interface */ | ||||
| 	pvr = pvr2_context_create(intf,devid,pvr_setup_attach); | ||||
| 	if (!pvr) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "Failed to create hdw handler"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr_probe(pvr=%p)",pvr); | ||||
| 
 | ||||
| 	usb_set_intfdata(intf, pvr); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * pvr_disconnect() | ||||
|  * | ||||
|  */ | ||||
| static void pvr_disconnect(struct usb_interface *intf) | ||||
| { | ||||
| 	struct pvr2_context *pvr = usb_get_intfdata(intf); | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) BEGIN",pvr); | ||||
| 
 | ||||
| 	usb_set_intfdata (intf, NULL); | ||||
| 	pvr2_context_disconnect(pvr); | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) DONE",pvr); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static struct usb_driver pvr_driver = { | ||||
| 	.name =         "pvrusb2", | ||||
| 	.id_table =     pvr2_device_table, | ||||
| 	.probe =        pvr_probe, | ||||
| 	.disconnect =   pvr_disconnect | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * pvr_init() / pvr_exit() | ||||
|  * | ||||
|  * This code is run to initialize/exit the driver. | ||||
|  * | ||||
|  */ | ||||
| static int __init pvr_init(void) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr_init"); | ||||
| 
 | ||||
| 	ret = pvr2_context_global_init(); | ||||
| 	if (ret != 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_INIT,"pvr_init failure code=%d",ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS | ||||
| 	class_ptr = pvr2_sysfs_class_create(); | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ | ||||
| 
 | ||||
| 	ret = usb_register(&pvr_driver); | ||||
| 
 | ||||
| 	if (ret == 0) | ||||
| 		printk(KERN_INFO "pvrusb2: " DRIVER_VERSION ":" | ||||
| 		       DRIVER_DESC "\n"); | ||||
| 	if (pvrusb2_debug) | ||||
| 		printk(KERN_INFO "pvrusb2: Debug mask is %d (0x%x)\n", | ||||
| 		       pvrusb2_debug,pvrusb2_debug); | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete"); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void __exit pvr_exit(void) | ||||
| { | ||||
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr_exit"); | ||||
| 
 | ||||
| 	usb_deregister(&pvr_driver); | ||||
| 
 | ||||
| 	pvr2_context_global_done(); | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS | ||||
| 	pvr2_sysfs_class_destroy(class_ptr); | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr_exit complete"); | ||||
| } | ||||
| 
 | ||||
| module_init(pvr_init); | ||||
| module_exit(pvr_exit); | ||||
| 
 | ||||
| MODULE_AUTHOR(DRIVER_AUTHOR); | ||||
| MODULE_DESCRIPTION(DRIVER_DESC); | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_VERSION("0.9.1"); | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										411
									
								
								drivers/media/usb/pvrusb2/pvrusb2-std.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										411
									
								
								drivers/media/usb/pvrusb2/pvrusb2-std.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,411 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "pvrusb2-std.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include <asm/string.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| struct std_name { | ||||
| 	const char *name; | ||||
| 	v4l2_std_id id; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #define CSTD_PAL \ | ||||
| 	(V4L2_STD_PAL_B| \ | ||||
| 	 V4L2_STD_PAL_B1| \ | ||||
| 	 V4L2_STD_PAL_G| \ | ||||
| 	 V4L2_STD_PAL_H| \ | ||||
| 	 V4L2_STD_PAL_I| \ | ||||
| 	 V4L2_STD_PAL_D| \ | ||||
| 	 V4L2_STD_PAL_D1| \ | ||||
| 	 V4L2_STD_PAL_K| \ | ||||
| 	 V4L2_STD_PAL_M| \ | ||||
| 	 V4L2_STD_PAL_N| \ | ||||
| 	 V4L2_STD_PAL_Nc| \ | ||||
| 	 V4L2_STD_PAL_60) | ||||
| 
 | ||||
| #define CSTD_NTSC \ | ||||
| 	(V4L2_STD_NTSC_M| \ | ||||
| 	 V4L2_STD_NTSC_M_JP| \ | ||||
| 	 V4L2_STD_NTSC_M_KR| \ | ||||
| 	 V4L2_STD_NTSC_443) | ||||
| 
 | ||||
| #define CSTD_ATSC \ | ||||
| 	(V4L2_STD_ATSC_8_VSB| \ | ||||
| 	 V4L2_STD_ATSC_16_VSB) | ||||
| 
 | ||||
| #define CSTD_SECAM \ | ||||
| 	(V4L2_STD_SECAM_B| \ | ||||
| 	 V4L2_STD_SECAM_D| \ | ||||
| 	 V4L2_STD_SECAM_G| \ | ||||
| 	 V4L2_STD_SECAM_H| \ | ||||
| 	 V4L2_STD_SECAM_K| \ | ||||
| 	 V4L2_STD_SECAM_K1| \ | ||||
| 	 V4L2_STD_SECAM_L| \ | ||||
| 	 V4L2_STD_SECAM_LC) | ||||
| 
 | ||||
| #define TSTD_B   (V4L2_STD_PAL_B|V4L2_STD_SECAM_B) | ||||
| #define TSTD_B1  (V4L2_STD_PAL_B1) | ||||
| #define TSTD_D   (V4L2_STD_PAL_D|V4L2_STD_SECAM_D) | ||||
| #define TSTD_D1  (V4L2_STD_PAL_D1) | ||||
| #define TSTD_G   (V4L2_STD_PAL_G|V4L2_STD_SECAM_G) | ||||
| #define TSTD_H   (V4L2_STD_PAL_H|V4L2_STD_SECAM_H) | ||||
| #define TSTD_I   (V4L2_STD_PAL_I) | ||||
| #define TSTD_K   (V4L2_STD_PAL_K|V4L2_STD_SECAM_K) | ||||
| #define TSTD_K1  (V4L2_STD_SECAM_K1) | ||||
| #define TSTD_L   (V4L2_STD_SECAM_L) | ||||
| #define TSTD_M   (V4L2_STD_PAL_M|V4L2_STD_NTSC_M) | ||||
| #define TSTD_N   (V4L2_STD_PAL_N) | ||||
| #define TSTD_Nc  (V4L2_STD_PAL_Nc) | ||||
| #define TSTD_60  (V4L2_STD_PAL_60) | ||||
| 
 | ||||
| #define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_ATSC|CSTD_SECAM) | ||||
| 
 | ||||
| /* Mapping of standard bits to color system */ | ||||
| static const struct std_name std_groups[] = { | ||||
| 	{"PAL",CSTD_PAL}, | ||||
| 	{"NTSC",CSTD_NTSC}, | ||||
| 	{"SECAM",CSTD_SECAM}, | ||||
| 	{"ATSC",CSTD_ATSC}, | ||||
| }; | ||||
| 
 | ||||
| /* Mapping of standard bits to modulation system */ | ||||
| static const struct std_name std_items[] = { | ||||
| 	{"B",TSTD_B}, | ||||
| 	{"B1",TSTD_B1}, | ||||
| 	{"D",TSTD_D}, | ||||
| 	{"D1",TSTD_D1}, | ||||
| 	{"G",TSTD_G}, | ||||
| 	{"H",TSTD_H}, | ||||
| 	{"I",TSTD_I}, | ||||
| 	{"K",TSTD_K}, | ||||
| 	{"K1",TSTD_K1}, | ||||
| 	{"L",TSTD_L}, | ||||
| 	{"LC",V4L2_STD_SECAM_LC}, | ||||
| 	{"M",TSTD_M}, | ||||
| 	{"Mj",V4L2_STD_NTSC_M_JP}, | ||||
| 	{"443",V4L2_STD_NTSC_443}, | ||||
| 	{"Mk",V4L2_STD_NTSC_M_KR}, | ||||
| 	{"N",TSTD_N}, | ||||
| 	{"Nc",TSTD_Nc}, | ||||
| 	{"60",TSTD_60}, | ||||
| 	{"8VSB",V4L2_STD_ATSC_8_VSB}, | ||||
| 	{"16VSB",V4L2_STD_ATSC_16_VSB}, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| // Search an array of std_name structures and return a pointer to the
 | ||||
| // element with the matching name.
 | ||||
| static const struct std_name *find_std_name(const struct std_name *arrPtr, | ||||
| 					    unsigned int arrSize, | ||||
| 					    const char *bufPtr, | ||||
| 					    unsigned int bufSize) | ||||
| { | ||||
| 	unsigned int idx; | ||||
| 	const struct std_name *p; | ||||
| 	for (idx = 0; idx < arrSize; idx++) { | ||||
| 		p = arrPtr + idx; | ||||
| 		if (strlen(p->name) != bufSize) continue; | ||||
| 		if (!memcmp(bufPtr,p->name,bufSize)) return p; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, | ||||
| 		       unsigned int bufSize) | ||||
| { | ||||
| 	v4l2_std_id id = 0; | ||||
| 	v4l2_std_id cmsk = 0; | ||||
| 	v4l2_std_id t; | ||||
| 	int mMode = 0; | ||||
| 	unsigned int cnt; | ||||
| 	char ch; | ||||
| 	const struct std_name *sp; | ||||
| 
 | ||||
| 	while (bufSize) { | ||||
| 		if (!mMode) { | ||||
| 			cnt = 0; | ||||
| 			while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++; | ||||
| 			if (cnt >= bufSize) return 0; // No more characters
 | ||||
| 			sp = find_std_name(std_groups, ARRAY_SIZE(std_groups), | ||||
| 					   bufPtr,cnt); | ||||
| 			if (!sp) return 0; // Illegal color system name
 | ||||
| 			cnt++; | ||||
| 			bufPtr += cnt; | ||||
| 			bufSize -= cnt; | ||||
| 			mMode = !0; | ||||
| 			cmsk = sp->id; | ||||
| 			continue; | ||||
| 		} | ||||
| 		cnt = 0; | ||||
| 		while (cnt < bufSize) { | ||||
| 			ch = bufPtr[cnt]; | ||||
| 			if (ch == ';') { | ||||
| 				mMode = 0; | ||||
| 				break; | ||||
| 			} | ||||
| 			if (ch == '/') break; | ||||
| 			cnt++; | ||||
| 		} | ||||
| 		sp = find_std_name(std_items, ARRAY_SIZE(std_items), | ||||
| 				   bufPtr,cnt); | ||||
| 		if (!sp) return 0; // Illegal modulation system ID
 | ||||
| 		t = sp->id & cmsk; | ||||
| 		if (!t) return 0; // Specific color + modulation system illegal
 | ||||
| 		id |= t; | ||||
| 		if (cnt < bufSize) cnt++; | ||||
| 		bufPtr += cnt; | ||||
| 		bufSize -= cnt; | ||||
| 	} | ||||
| 
 | ||||
| 	if (idPtr) *idPtr = id; | ||||
| 	return !0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, | ||||
| 				v4l2_std_id id) | ||||
| { | ||||
| 	unsigned int idx1,idx2; | ||||
| 	const struct std_name *ip,*gp; | ||||
| 	int gfl,cfl; | ||||
| 	unsigned int c1,c2; | ||||
| 	cfl = 0; | ||||
| 	c1 = 0; | ||||
| 	for (idx1 = 0; idx1 < ARRAY_SIZE(std_groups); idx1++) { | ||||
| 		gp = std_groups + idx1; | ||||
| 		gfl = 0; | ||||
| 		for (idx2 = 0; idx2 < ARRAY_SIZE(std_items); idx2++) { | ||||
| 			ip = std_items + idx2; | ||||
| 			if (!(gp->id & ip->id & id)) continue; | ||||
| 			if (!gfl) { | ||||
| 				if (cfl) { | ||||
| 					c2 = scnprintf(bufPtr,bufSize,";"); | ||||
| 					c1 += c2; | ||||
| 					bufSize -= c2; | ||||
| 					bufPtr += c2; | ||||
| 				} | ||||
| 				cfl = !0; | ||||
| 				c2 = scnprintf(bufPtr,bufSize, | ||||
| 					       "%s-",gp->name); | ||||
| 				gfl = !0; | ||||
| 			} else { | ||||
| 				c2 = scnprintf(bufPtr,bufSize,"/"); | ||||
| 			} | ||||
| 			c1 += c2; | ||||
| 			bufSize -= c2; | ||||
| 			bufPtr += c2; | ||||
| 			c2 = scnprintf(bufPtr,bufSize, | ||||
| 				       ip->name); | ||||
| 			c1 += c2; | ||||
| 			bufSize -= c2; | ||||
| 			bufPtr += c2; | ||||
| 		} | ||||
| 	} | ||||
| 	return c1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Template data for possible enumerated video standards.  Here we group
 | ||||
| // standards which share common frame rates and resolution.
 | ||||
| static struct v4l2_standard generic_standards[] = { | ||||
| 	{ | ||||
| 		.id             = (TSTD_B|TSTD_B1| | ||||
| 				   TSTD_D|TSTD_D1| | ||||
| 				   TSTD_G| | ||||
| 				   TSTD_H| | ||||
| 				   TSTD_I| | ||||
| 				   TSTD_K|TSTD_K1| | ||||
| 				   TSTD_L| | ||||
| 				   V4L2_STD_SECAM_LC | | ||||
| 				   TSTD_N|TSTD_Nc), | ||||
| 		.frameperiod    = | ||||
| 		{ | ||||
| 			.numerator  = 1, | ||||
| 			.denominator= 25 | ||||
| 		}, | ||||
| 		.framelines     = 625, | ||||
| 		.reserved       = {0,0,0,0} | ||||
| 	}, { | ||||
| 		.id             = (TSTD_M| | ||||
| 				   V4L2_STD_NTSC_M_JP| | ||||
| 				   V4L2_STD_NTSC_M_KR), | ||||
| 		.frameperiod    = | ||||
| 		{ | ||||
| 			.numerator  = 1001, | ||||
| 			.denominator= 30000 | ||||
| 		}, | ||||
| 		.framelines     = 525, | ||||
| 		.reserved       = {0,0,0,0} | ||||
| 	}, { // This is a total wild guess
 | ||||
| 		.id             = (TSTD_60), | ||||
| 		.frameperiod    = | ||||
| 		{ | ||||
| 			.numerator  = 1001, | ||||
| 			.denominator= 30000 | ||||
| 		}, | ||||
| 		.framelines     = 525, | ||||
| 		.reserved       = {0,0,0,0} | ||||
| 	}, { // This is total wild guess
 | ||||
| 		.id             = V4L2_STD_NTSC_443, | ||||
| 		.frameperiod    = | ||||
| 		{ | ||||
| 			.numerator  = 1001, | ||||
| 			.denominator= 30000 | ||||
| 		}, | ||||
| 		.framelines     = 525, | ||||
| 		.reserved       = {0,0,0,0} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| static struct v4l2_standard *match_std(v4l2_std_id id) | ||||
| { | ||||
| 	unsigned int idx; | ||||
| 	for (idx = 0; idx < ARRAY_SIZE(generic_standards); idx++) { | ||||
| 		if (generic_standards[idx].id & id) { | ||||
| 			return generic_standards + idx; | ||||
| 		} | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id) | ||||
| { | ||||
| 	struct v4l2_standard *template; | ||||
| 	int idx; | ||||
| 	unsigned int bcnt; | ||||
| 	template = match_std(id); | ||||
| 	if (!template) return 0; | ||||
| 	idx = std->index; | ||||
| 	memcpy(std,template,sizeof(*template)); | ||||
| 	std->index = idx; | ||||
| 	std->id = id; | ||||
| 	bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id); | ||||
| 	std->name[bcnt] = 0; | ||||
| 	pvr2_trace(PVR2_TRACE_STD,"Set up standard idx=%u name=%s", | ||||
| 		   std->index,std->name); | ||||
| 	return !0; | ||||
| } | ||||
| 
 | ||||
| /* These are special cases of combined standards that we should enumerate
 | ||||
|    separately if the component pieces are present. */ | ||||
| static v4l2_std_id std_mixes[] = { | ||||
| 	V4L2_STD_PAL_B | V4L2_STD_PAL_G, | ||||
| 	V4L2_STD_PAL_D | V4L2_STD_PAL_K, | ||||
| 	V4L2_STD_SECAM_B | V4L2_STD_SECAM_G, | ||||
| 	V4L2_STD_SECAM_D | V4L2_STD_SECAM_K, | ||||
| }; | ||||
| 
 | ||||
| struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, | ||||
| 					   v4l2_std_id id) | ||||
| { | ||||
| 	unsigned int std_cnt = 0; | ||||
| 	unsigned int idx,bcnt,idx2; | ||||
| 	v4l2_std_id idmsk,cmsk,fmsk; | ||||
| 	struct v4l2_standard *stddefs; | ||||
| 
 | ||||
| 	if (pvrusb2_debug & PVR2_TRACE_STD) { | ||||
| 		char buf[100]; | ||||
| 		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id); | ||||
| 		pvr2_trace( | ||||
| 			PVR2_TRACE_STD,"Mapping standards mask=0x%x (%.*s)", | ||||
| 			(int)id,bcnt,buf); | ||||
| 	} | ||||
| 
 | ||||
| 	*countptr = 0; | ||||
| 	std_cnt = 0; | ||||
| 	fmsk = 0; | ||||
| 	for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) { | ||||
| 		if (!(idmsk & cmsk)) continue; | ||||
| 		cmsk &= ~idmsk; | ||||
| 		if (match_std(idmsk)) { | ||||
| 			std_cnt++; | ||||
| 			continue; | ||||
| 		} | ||||
| 		fmsk |= idmsk; | ||||
| 	} | ||||
| 
 | ||||
| 	for (idx2 = 0; idx2 < ARRAY_SIZE(std_mixes); idx2++) { | ||||
| 		if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Don't complain about ATSC standard values */ | ||||
| 	fmsk &= ~CSTD_ATSC; | ||||
| 
 | ||||
| 	if (fmsk) { | ||||
| 		char buf[100]; | ||||
| 		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); | ||||
| 		pvr2_trace( | ||||
| 			PVR2_TRACE_ERROR_LEGS, | ||||
| 			"WARNING:" | ||||
| 			" Failed to classify the following standard(s): %.*s", | ||||
| 			bcnt,buf); | ||||
| 	} | ||||
| 
 | ||||
| 	pvr2_trace(PVR2_TRACE_STD,"Setting up %u unique standard(s)", | ||||
| 		   std_cnt); | ||||
| 	if (!std_cnt) return NULL; // paranoia
 | ||||
| 
 | ||||
| 	stddefs = kzalloc(sizeof(struct v4l2_standard) * std_cnt, | ||||
| 			  GFP_KERNEL); | ||||
| 	if (!stddefs) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	for (idx = 0; idx < std_cnt; idx++) | ||||
| 		stddefs[idx].index = idx; | ||||
| 
 | ||||
| 	idx = 0; | ||||
| 
 | ||||
| 	/* Enumerate potential special cases */ | ||||
| 	for (idx2 = 0; (idx2 < ARRAY_SIZE(std_mixes)) && (idx < std_cnt); | ||||
| 	     idx2++) { | ||||
| 		if (!(id & std_mixes[idx2])) continue; | ||||
| 		if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++; | ||||
| 	} | ||||
| 	/* Now enumerate individual pieces */ | ||||
| 	for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) { | ||||
| 		if (!(idmsk & cmsk)) continue; | ||||
| 		cmsk &= ~idmsk; | ||||
| 		if (!pvr2_std_fill(stddefs+idx,idmsk)) continue; | ||||
| 		idx++; | ||||
| 	} | ||||
| 
 | ||||
| 	*countptr = std_cnt; | ||||
| 	return stddefs; | ||||
| } | ||||
| 
 | ||||
| v4l2_std_id pvr2_std_get_usable(void) | ||||
| { | ||||
| 	return CSTD_ALL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										59
									
								
								drivers/media/usb/pvrusb2/pvrusb2-std.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								drivers/media/usb/pvrusb2/pvrusb2-std.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_STD_H | ||||
| #define __PVRUSB2_STD_H | ||||
| 
 | ||||
| #include <linux/videodev2.h> | ||||
| 
 | ||||
| // Convert string describing one or more video standards into a mask of V4L
 | ||||
| // standard bits.  Return true if conversion succeeds otherwise return
 | ||||
| // false.  String is expected to be of the form: C1-x/y;C2-a/b where C1 and
 | ||||
| // C2 are color system names (e.g. "PAL", "NTSC") and x, y, a, and b are
 | ||||
| // modulation schemes (e.g. "M", "B", "G", etc).
 | ||||
| int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, | ||||
| 		       unsigned int bufSize); | ||||
| 
 | ||||
| // Convert any arbitrary set of video standard bits into an unambiguous
 | ||||
| // readable string.  Return value is the number of bytes consumed in the
 | ||||
| // buffer.  The formatted string is of a form that can be parsed by our
 | ||||
| // sibling std_std_to_id() function.
 | ||||
| unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, | ||||
| 				v4l2_std_id id); | ||||
| 
 | ||||
| // Create an array of suitable v4l2_standard structures given a bit mask of
 | ||||
| // video standards to support.  The array is allocated from the heap, and
 | ||||
| // the number of elements is returned in the first argument.
 | ||||
| struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, | ||||
| 					   v4l2_std_id id); | ||||
| 
 | ||||
| // Return mask of which video standard bits are valid
 | ||||
| v4l2_std_id pvr2_std_get_usable(void); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_STD_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										861
									
								
								drivers/media/usb/pvrusb2/pvrusb2-sysfs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										861
									
								
								drivers/media/usb/pvrusb2/pvrusb2-sysfs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,861 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/string.h> | ||||
| #include <linux/slab.h> | ||||
| #include "pvrusb2-sysfs.h" | ||||
| #include "pvrusb2-hdw.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||||
| #include "pvrusb2-debugifc.h" | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||||
| 
 | ||||
| #define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__) | ||||
| 
 | ||||
| struct pvr2_sysfs { | ||||
| 	struct pvr2_channel channel; | ||||
| 	struct device *class_dev; | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||||
| 	struct pvr2_sysfs_debugifc *debugifc; | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||||
| 	struct pvr2_sysfs_ctl_item *item_first; | ||||
| 	struct pvr2_sysfs_ctl_item *item_last; | ||||
| 	struct device_attribute attr_v4l_minor_number; | ||||
| 	struct device_attribute attr_v4l_radio_minor_number; | ||||
| 	struct device_attribute attr_unit_number; | ||||
| 	struct device_attribute attr_bus_info; | ||||
| 	struct device_attribute attr_hdw_name; | ||||
| 	struct device_attribute attr_hdw_desc; | ||||
| 	int v4l_minor_number_created_ok; | ||||
| 	int v4l_radio_minor_number_created_ok; | ||||
| 	int unit_number_created_ok; | ||||
| 	int bus_info_created_ok; | ||||
| 	int hdw_name_created_ok; | ||||
| 	int hdw_desc_created_ok; | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||||
| struct pvr2_sysfs_debugifc { | ||||
| 	struct device_attribute attr_debugcmd; | ||||
| 	struct device_attribute attr_debuginfo; | ||||
| 	int debugcmd_created_ok; | ||||
| 	int debuginfo_created_ok; | ||||
| }; | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||||
| 
 | ||||
| struct pvr2_sysfs_ctl_item { | ||||
| 	struct device_attribute attr_name; | ||||
| 	struct device_attribute attr_type; | ||||
| 	struct device_attribute attr_min; | ||||
| 	struct device_attribute attr_max; | ||||
| 	struct device_attribute attr_def; | ||||
| 	struct device_attribute attr_enum; | ||||
| 	struct device_attribute attr_bits; | ||||
| 	struct device_attribute attr_val; | ||||
| 	struct device_attribute attr_custom; | ||||
| 	struct pvr2_ctrl *cptr; | ||||
| 	int ctl_id; | ||||
| 	struct pvr2_sysfs *chptr; | ||||
| 	struct pvr2_sysfs_ctl_item *item_next; | ||||
| 	struct attribute *attr_gen[8]; | ||||
| 	struct attribute_group grp; | ||||
| 	int created_ok; | ||||
| 	char name[80]; | ||||
| }; | ||||
| 
 | ||||
| struct pvr2_sysfs_class { | ||||
| 	struct class class; | ||||
| }; | ||||
| 
 | ||||
| static ssize_t show_name(struct device *class_dev, | ||||
| 			 struct device_attribute *attr, | ||||
| 			 char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	const char *name; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_name); | ||||
| 	name = pvr2_ctrl_get_desc(cip->cptr); | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s", | ||||
| 			 cip->chptr, cip->ctl_id, name); | ||||
| 	if (!name) return -EINVAL; | ||||
| 	return scnprintf(buf, PAGE_SIZE, "%s\n", name); | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_type(struct device *class_dev, | ||||
| 			 struct device_attribute *attr, | ||||
| 			 char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	const char *name; | ||||
| 	enum pvr2_ctl_type tp; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_type); | ||||
| 	tp = pvr2_ctrl_get_type(cip->cptr); | ||||
| 	switch (tp) { | ||||
| 	case pvr2_ctl_int: name = "integer"; break; | ||||
| 	case pvr2_ctl_enum: name = "enum"; break; | ||||
| 	case pvr2_ctl_bitmask: name = "bitmask"; break; | ||||
| 	case pvr2_ctl_bool: name = "boolean"; break; | ||||
| 	default: name = "?"; break; | ||||
| 	} | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s", | ||||
| 			 cip->chptr, cip->ctl_id, name); | ||||
| 	if (!name) return -EINVAL; | ||||
| 	return scnprintf(buf, PAGE_SIZE, "%s\n", name); | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_min(struct device *class_dev, | ||||
| 			struct device_attribute *attr, | ||||
| 			char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	long val; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_min); | ||||
| 	val = pvr2_ctrl_get_min(cip->cptr); | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld", | ||||
| 			 cip->chptr, cip->ctl_id, val); | ||||
| 	return scnprintf(buf, PAGE_SIZE, "%ld\n", val); | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_max(struct device *class_dev, | ||||
| 			struct device_attribute *attr, | ||||
| 			char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	long val; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_max); | ||||
| 	val = pvr2_ctrl_get_max(cip->cptr); | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld", | ||||
| 			 cip->chptr, cip->ctl_id, val); | ||||
| 	return scnprintf(buf, PAGE_SIZE, "%ld\n", val); | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_def(struct device *class_dev, | ||||
| 			struct device_attribute *attr, | ||||
| 			char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	int val; | ||||
| 	int ret; | ||||
| 	unsigned int cnt = 0; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_def); | ||||
| 	ret = pvr2_ctrl_get_def(cip->cptr, &val); | ||||
| 	if (ret < 0) return ret; | ||||
| 	ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val, | ||||
| 				     buf, PAGE_SIZE - 1, &cnt); | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %.*s (%d)", | ||||
| 			 cip->chptr, cip->ctl_id, cnt, buf, val); | ||||
| 	buf[cnt] = '\n'; | ||||
| 	return cnt + 1; | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_val_norm(struct device *class_dev, | ||||
| 			     struct device_attribute *attr, | ||||
| 			     char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	int val; | ||||
| 	int ret; | ||||
| 	unsigned int cnt = 0; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); | ||||
| 	ret = pvr2_ctrl_get_value(cip->cptr, &val); | ||||
| 	if (ret < 0) return ret; | ||||
| 	ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val, | ||||
| 				     buf, PAGE_SIZE - 1, &cnt); | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)", | ||||
| 			 cip->chptr, cip->ctl_id, cnt, buf, val); | ||||
| 	buf[cnt] = '\n'; | ||||
| 	return cnt+1; | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_val_custom(struct device *class_dev, | ||||
| 			       struct device_attribute *attr, | ||||
| 			       char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	int val; | ||||
| 	int ret; | ||||
| 	unsigned int cnt = 0; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); | ||||
| 	ret = pvr2_ctrl_get_value(cip->cptr, &val); | ||||
| 	if (ret < 0) return ret; | ||||
| 	ret = pvr2_ctrl_custom_value_to_sym(cip->cptr, ~0, val, | ||||
| 					    buf, PAGE_SIZE - 1, &cnt); | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)", | ||||
| 			 cip->chptr, cip->ctl_id, cnt, buf, val); | ||||
| 	buf[cnt] = '\n'; | ||||
| 	return cnt+1; | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_enum(struct device *class_dev, | ||||
| 			 struct device_attribute *attr, | ||||
| 			 char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	long val; | ||||
| 	unsigned int bcnt, ccnt, ecnt; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_enum); | ||||
| 	ecnt = pvr2_ctrl_get_cnt(cip->cptr); | ||||
| 	bcnt = 0; | ||||
| 	for (val = 0; val < ecnt; val++) { | ||||
| 		pvr2_ctrl_get_valname(cip->cptr, val, buf + bcnt, | ||||
| 				      PAGE_SIZE - bcnt, &ccnt); | ||||
| 		if (!ccnt) continue; | ||||
| 		bcnt += ccnt; | ||||
| 		if (bcnt >= PAGE_SIZE) break; | ||||
| 		buf[bcnt] = '\n'; | ||||
| 		bcnt++; | ||||
| 	} | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)", | ||||
| 			 cip->chptr, cip->ctl_id); | ||||
| 	return bcnt; | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_bits(struct device *class_dev, | ||||
| 			 struct device_attribute *attr, | ||||
| 			 char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	int valid_bits, msk; | ||||
| 	unsigned int bcnt, ccnt; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_bits); | ||||
| 	valid_bits = pvr2_ctrl_get_mask(cip->cptr); | ||||
| 	bcnt = 0; | ||||
| 	for (msk = 1; valid_bits; msk <<= 1) { | ||||
| 		if (!(msk & valid_bits)) continue; | ||||
| 		valid_bits &= ~msk; | ||||
| 		pvr2_ctrl_get_valname(cip->cptr, msk, buf + bcnt, | ||||
| 				      PAGE_SIZE - bcnt, &ccnt); | ||||
| 		bcnt += ccnt; | ||||
| 		if (bcnt >= PAGE_SIZE) break; | ||||
| 		buf[bcnt] = '\n'; | ||||
| 		bcnt++; | ||||
| 	} | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)", | ||||
| 			 cip->chptr, cip->ctl_id); | ||||
| 	return bcnt; | ||||
| } | ||||
| 
 | ||||
| static int store_val_any(struct pvr2_sysfs_ctl_item *cip, int customfl, | ||||
| 			 const char *buf,unsigned int count) | ||||
| { | ||||
| 	int ret; | ||||
| 	int mask,val; | ||||
| 	if (customfl) { | ||||
| 		ret = pvr2_ctrl_custom_sym_to_value(cip->cptr, buf, count, | ||||
| 						    &mask, &val); | ||||
| 	} else { | ||||
| 		ret = pvr2_ctrl_sym_to_value(cip->cptr, buf, count, | ||||
| 					     &mask, &val); | ||||
| 	} | ||||
| 	if (ret < 0) return ret; | ||||
| 	ret = pvr2_ctrl_set_mask_value(cip->cptr, mask, val); | ||||
| 	pvr2_hdw_commit_ctl(cip->chptr->channel.hdw); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static ssize_t store_val_norm(struct device *class_dev, | ||||
| 			      struct device_attribute *attr, | ||||
| 			      const char *buf, size_t count) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	int ret; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"", | ||||
| 			 cip->chptr, cip->ctl_id, (int)count, buf); | ||||
| 	ret = store_val_any(cip, 0, buf, count); | ||||
| 	if (!ret) ret = count; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static ssize_t store_val_custom(struct device *class_dev, | ||||
| 				struct device_attribute *attr, | ||||
| 				const char *buf, size_t count) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	int ret; | ||||
| 	cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); | ||||
| 	pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"", | ||||
| 			 cip->chptr, cip->ctl_id, (int)count, buf); | ||||
| 	ret = store_val_any(cip, 1, buf, count); | ||||
| 	if (!ret) ret = count; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip; | ||||
| 	struct pvr2_ctrl *cptr; | ||||
| 	unsigned int cnt,acnt; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); | ||||
| 	if (!cptr) return; | ||||
| 
 | ||||
| 	cip = kzalloc(sizeof(*cip),GFP_KERNEL); | ||||
| 	if (!cip) return; | ||||
| 	pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip); | ||||
| 
 | ||||
| 	cip->cptr = cptr; | ||||
| 	cip->ctl_id = ctl_id; | ||||
| 
 | ||||
| 	cip->chptr = sfp; | ||||
| 	cip->item_next = NULL; | ||||
| 	if (sfp->item_last) { | ||||
| 		sfp->item_last->item_next = cip; | ||||
| 	} else { | ||||
| 		sfp->item_first = cip; | ||||
| 	} | ||||
| 	sfp->item_last = cip; | ||||
| 
 | ||||
| 	sysfs_attr_init(&cip->attr_name.attr); | ||||
| 	cip->attr_name.attr.name = "name"; | ||||
| 	cip->attr_name.attr.mode = S_IRUGO; | ||||
| 	cip->attr_name.show = show_name; | ||||
| 
 | ||||
| 	sysfs_attr_init(&cip->attr_type.attr); | ||||
| 	cip->attr_type.attr.name = "type"; | ||||
| 	cip->attr_type.attr.mode = S_IRUGO; | ||||
| 	cip->attr_type.show = show_type; | ||||
| 
 | ||||
| 	sysfs_attr_init(&cip->attr_min.attr); | ||||
| 	cip->attr_min.attr.name = "min_val"; | ||||
| 	cip->attr_min.attr.mode = S_IRUGO; | ||||
| 	cip->attr_min.show = show_min; | ||||
| 
 | ||||
| 	sysfs_attr_init(&cip->attr_max.attr); | ||||
| 	cip->attr_max.attr.name = "max_val"; | ||||
| 	cip->attr_max.attr.mode = S_IRUGO; | ||||
| 	cip->attr_max.show = show_max; | ||||
| 
 | ||||
| 	sysfs_attr_init(&cip->attr_def.attr); | ||||
| 	cip->attr_def.attr.name = "def_val"; | ||||
| 	cip->attr_def.attr.mode = S_IRUGO; | ||||
| 	cip->attr_def.show = show_def; | ||||
| 
 | ||||
| 	sysfs_attr_init(&cip->attr_val.attr); | ||||
| 	cip->attr_val.attr.name = "cur_val"; | ||||
| 	cip->attr_val.attr.mode = S_IRUGO; | ||||
| 
 | ||||
| 	sysfs_attr_init(&cip->attr_custom.attr); | ||||
| 	cip->attr_custom.attr.name = "custom_val"; | ||||
| 	cip->attr_custom.attr.mode = S_IRUGO; | ||||
| 
 | ||||
| 	sysfs_attr_init(&cip->attr_enum.attr); | ||||
| 	cip->attr_enum.attr.name = "enum_val"; | ||||
| 	cip->attr_enum.attr.mode = S_IRUGO; | ||||
| 	cip->attr_enum.show = show_enum; | ||||
| 
 | ||||
| 	sysfs_attr_init(&cip->attr_bits.attr); | ||||
| 	cip->attr_bits.attr.name = "bit_val"; | ||||
| 	cip->attr_bits.attr.mode = S_IRUGO; | ||||
| 	cip->attr_bits.show = show_bits; | ||||
| 
 | ||||
| 	if (pvr2_ctrl_is_writable(cptr)) { | ||||
| 		cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; | ||||
| 		cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP; | ||||
| 	} | ||||
| 
 | ||||
| 	acnt = 0; | ||||
| 	cip->attr_gen[acnt++] = &cip->attr_name.attr; | ||||
| 	cip->attr_gen[acnt++] = &cip->attr_type.attr; | ||||
| 	cip->attr_gen[acnt++] = &cip->attr_val.attr; | ||||
| 	cip->attr_gen[acnt++] = &cip->attr_def.attr; | ||||
| 	cip->attr_val.show = show_val_norm; | ||||
| 	cip->attr_val.store = store_val_norm; | ||||
| 	if (pvr2_ctrl_has_custom_symbols(cptr)) { | ||||
| 		cip->attr_gen[acnt++] = &cip->attr_custom.attr; | ||||
| 		cip->attr_custom.show = show_val_custom; | ||||
| 		cip->attr_custom.store = store_val_custom; | ||||
| 	} | ||||
| 	switch (pvr2_ctrl_get_type(cptr)) { | ||||
| 	case pvr2_ctl_enum: | ||||
| 		// Control is an enumeration
 | ||||
| 		cip->attr_gen[acnt++] = &cip->attr_enum.attr; | ||||
| 		break; | ||||
| 	case pvr2_ctl_int: | ||||
| 		// Control is an integer
 | ||||
| 		cip->attr_gen[acnt++] = &cip->attr_min.attr; | ||||
| 		cip->attr_gen[acnt++] = &cip->attr_max.attr; | ||||
| 		break; | ||||
| 	case pvr2_ctl_bitmask: | ||||
| 		// Control is an bitmask
 | ||||
| 		cip->attr_gen[acnt++] = &cip->attr_bits.attr; | ||||
| 		break; | ||||
| 	default: break; | ||||
| 	} | ||||
| 
 | ||||
| 	cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s", | ||||
| 			pvr2_ctrl_get_name(cptr)); | ||||
| 	cip->name[cnt] = 0; | ||||
| 	cip->grp.name = cip->name; | ||||
| 	cip->grp.attrs = cip->attr_gen; | ||||
| 
 | ||||
| 	ret = sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); | ||||
| 	if (ret) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "sysfs_create_group error: %d", | ||||
| 			   ret); | ||||
| 		return; | ||||
| 	} | ||||
| 	cip->created_ok = !0; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||||
| static ssize_t debuginfo_show(struct device *, struct device_attribute *, | ||||
| 			      char *); | ||||
| static ssize_t debugcmd_show(struct device *, struct device_attribute *, | ||||
| 			     char *); | ||||
| static ssize_t debugcmd_store(struct device *, struct device_attribute *, | ||||
| 			      const char *, size_t count); | ||||
| 
 | ||||
| static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) | ||||
| { | ||||
| 	struct pvr2_sysfs_debugifc *dip; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	dip = kzalloc(sizeof(*dip),GFP_KERNEL); | ||||
| 	if (!dip) return; | ||||
| 	sysfs_attr_init(&dip->attr_debugcmd.attr); | ||||
| 	dip->attr_debugcmd.attr.name = "debugcmd"; | ||||
| 	dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP; | ||||
| 	dip->attr_debugcmd.show = debugcmd_show; | ||||
| 	dip->attr_debugcmd.store = debugcmd_store; | ||||
| 	sysfs_attr_init(&dip->attr_debuginfo.attr); | ||||
| 	dip->attr_debuginfo.attr.name = "debuginfo"; | ||||
| 	dip->attr_debuginfo.attr.mode = S_IRUGO; | ||||
| 	dip->attr_debuginfo.show = debuginfo_show; | ||||
| 	sfp->debugifc = dip; | ||||
| 	ret = device_create_file(sfp->class_dev,&dip->attr_debugcmd); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "device_create_file error: %d", | ||||
| 			   ret); | ||||
| 	} else { | ||||
| 		dip->debugcmd_created_ok = !0; | ||||
| 	} | ||||
| 	ret = device_create_file(sfp->class_dev,&dip->attr_debuginfo); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "device_create_file error: %d", | ||||
| 			   ret); | ||||
| 	} else { | ||||
| 		dip->debuginfo_created_ok = !0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp) | ||||
| { | ||||
| 	if (!sfp->debugifc) return; | ||||
| 	if (sfp->debugifc->debuginfo_created_ok) { | ||||
| 		device_remove_file(sfp->class_dev, | ||||
| 					 &sfp->debugifc->attr_debuginfo); | ||||
| 	} | ||||
| 	if (sfp->debugifc->debugcmd_created_ok) { | ||||
| 		device_remove_file(sfp->class_dev, | ||||
| 					 &sfp->debugifc->attr_debugcmd); | ||||
| 	} | ||||
| 	kfree(sfp->debugifc); | ||||
| 	sfp->debugifc = NULL; | ||||
| } | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp) | ||||
| { | ||||
| 	unsigned int idx,cnt; | ||||
| 	cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw); | ||||
| 	for (idx = 0; idx < cnt; idx++) { | ||||
| 		pvr2_sysfs_add_control(sfp,idx); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_sysfs_tear_down_controls(struct pvr2_sysfs *sfp) | ||||
| { | ||||
| 	struct pvr2_sysfs_ctl_item *cip1,*cip2; | ||||
| 	for (cip1 = sfp->item_first; cip1; cip1 = cip2) { | ||||
| 		cip2 = cip1->item_next; | ||||
| 		if (cip1->created_ok) { | ||||
| 			sysfs_remove_group(&sfp->class_dev->kobj,&cip1->grp); | ||||
| 		} | ||||
| 		pvr2_sysfs_trace("Destroying pvr2_sysfs_ctl_item id=%p",cip1); | ||||
| 		kfree(cip1); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_sysfs_class_release(struct class *class) | ||||
| { | ||||
| 	struct pvr2_sysfs_class *clp; | ||||
| 	clp = container_of(class,struct pvr2_sysfs_class,class); | ||||
| 	pvr2_sysfs_trace("Destroying pvr2_sysfs_class id=%p",clp); | ||||
| 	kfree(clp); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_sysfs_release(struct device *class_dev) | ||||
| { | ||||
| 	pvr2_sysfs_trace("Releasing class_dev id=%p",class_dev); | ||||
| 	kfree(class_dev); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void class_dev_destroy(struct pvr2_sysfs *sfp) | ||||
| { | ||||
| 	struct device *dev; | ||||
| 	if (!sfp->class_dev) return; | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||||
| 	pvr2_sysfs_tear_down_debugifc(sfp); | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||||
| 	pvr2_sysfs_tear_down_controls(sfp); | ||||
| 	if (sfp->hdw_desc_created_ok) { | ||||
| 		device_remove_file(sfp->class_dev, | ||||
| 				   &sfp->attr_hdw_desc); | ||||
| 	} | ||||
| 	if (sfp->hdw_name_created_ok) { | ||||
| 		device_remove_file(sfp->class_dev, | ||||
| 				   &sfp->attr_hdw_name); | ||||
| 	} | ||||
| 	if (sfp->bus_info_created_ok) { | ||||
| 		device_remove_file(sfp->class_dev, | ||||
| 					 &sfp->attr_bus_info); | ||||
| 	} | ||||
| 	if (sfp->v4l_minor_number_created_ok) { | ||||
| 		device_remove_file(sfp->class_dev, | ||||
| 					 &sfp->attr_v4l_minor_number); | ||||
| 	} | ||||
| 	if (sfp->v4l_radio_minor_number_created_ok) { | ||||
| 		device_remove_file(sfp->class_dev, | ||||
| 					 &sfp->attr_v4l_radio_minor_number); | ||||
| 	} | ||||
| 	if (sfp->unit_number_created_ok) { | ||||
| 		device_remove_file(sfp->class_dev, | ||||
| 					 &sfp->attr_unit_number); | ||||
| 	} | ||||
| 	pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev); | ||||
| 	dev_set_drvdata(sfp->class_dev, NULL); | ||||
| 	dev = sfp->class_dev->parent; | ||||
| 	sfp->class_dev->parent = NULL; | ||||
| 	put_device(dev); | ||||
| 	device_unregister(sfp->class_dev); | ||||
| 	sfp->class_dev = NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static ssize_t v4l_minor_number_show(struct device *class_dev, | ||||
| 				     struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	sfp = dev_get_drvdata(class_dev); | ||||
| 	if (!sfp) return -EINVAL; | ||||
| 	return scnprintf(buf,PAGE_SIZE,"%d\n", | ||||
| 			 pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, | ||||
| 						       pvr2_v4l_type_video)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static ssize_t bus_info_show(struct device *class_dev, | ||||
| 			     struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	sfp = dev_get_drvdata(class_dev); | ||||
| 	if (!sfp) return -EINVAL; | ||||
| 	return scnprintf(buf,PAGE_SIZE,"%s\n", | ||||
| 			 pvr2_hdw_get_bus_info(sfp->channel.hdw)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static ssize_t hdw_name_show(struct device *class_dev, | ||||
| 			     struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	sfp = dev_get_drvdata(class_dev); | ||||
| 	if (!sfp) return -EINVAL; | ||||
| 	return scnprintf(buf,PAGE_SIZE,"%s\n", | ||||
| 			 pvr2_hdw_get_type(sfp->channel.hdw)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static ssize_t hdw_desc_show(struct device *class_dev, | ||||
| 			     struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	sfp = dev_get_drvdata(class_dev); | ||||
| 	if (!sfp) return -EINVAL; | ||||
| 	return scnprintf(buf,PAGE_SIZE,"%s\n", | ||||
| 			 pvr2_hdw_get_desc(sfp->channel.hdw)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static ssize_t v4l_radio_minor_number_show(struct device *class_dev, | ||||
| 					   struct device_attribute *attr, | ||||
| 					   char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	sfp = dev_get_drvdata(class_dev); | ||||
| 	if (!sfp) return -EINVAL; | ||||
| 	return scnprintf(buf,PAGE_SIZE,"%d\n", | ||||
| 			 pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, | ||||
| 						       pvr2_v4l_type_radio)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static ssize_t unit_number_show(struct device *class_dev, | ||||
| 				struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	sfp = dev_get_drvdata(class_dev); | ||||
| 	if (!sfp) return -EINVAL; | ||||
| 	return scnprintf(buf,PAGE_SIZE,"%d\n", | ||||
| 			 pvr2_hdw_get_unit_number(sfp->channel.hdw)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void class_dev_create(struct pvr2_sysfs *sfp, | ||||
| 			     struct pvr2_sysfs_class *class_ptr) | ||||
| { | ||||
| 	struct usb_device *usb_dev; | ||||
| 	struct device *class_dev; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw); | ||||
| 	if (!usb_dev) return; | ||||
| 	class_dev = kzalloc(sizeof(*class_dev),GFP_KERNEL); | ||||
| 	if (!class_dev) return; | ||||
| 
 | ||||
| 	pvr2_sysfs_trace("Creating class_dev id=%p",class_dev); | ||||
| 
 | ||||
| 	class_dev->class = &class_ptr->class; | ||||
| 
 | ||||
| 	dev_set_name(class_dev, "%s", | ||||
| 		     pvr2_hdw_get_device_identifier(sfp->channel.hdw)); | ||||
| 
 | ||||
| 	class_dev->parent = get_device(&usb_dev->dev); | ||||
| 
 | ||||
| 	sfp->class_dev = class_dev; | ||||
| 	dev_set_drvdata(class_dev, sfp); | ||||
| 	ret = device_register(class_dev); | ||||
| 	if (ret) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "device_register failed"); | ||||
| 		put_device(class_dev); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	sysfs_attr_init(&sfp->attr_v4l_minor_number.attr); | ||||
| 	sfp->attr_v4l_minor_number.attr.name = "v4l_minor_number"; | ||||
| 	sfp->attr_v4l_minor_number.attr.mode = S_IRUGO; | ||||
| 	sfp->attr_v4l_minor_number.show = v4l_minor_number_show; | ||||
| 	sfp->attr_v4l_minor_number.store = NULL; | ||||
| 	ret = device_create_file(sfp->class_dev, | ||||
| 				       &sfp->attr_v4l_minor_number); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "device_create_file error: %d", | ||||
| 			   ret); | ||||
| 	} else { | ||||
| 		sfp->v4l_minor_number_created_ok = !0; | ||||
| 	} | ||||
| 
 | ||||
| 	sysfs_attr_init(&sfp->attr_v4l_radio_minor_number.attr); | ||||
| 	sfp->attr_v4l_radio_minor_number.attr.name = "v4l_radio_minor_number"; | ||||
| 	sfp->attr_v4l_radio_minor_number.attr.mode = S_IRUGO; | ||||
| 	sfp->attr_v4l_radio_minor_number.show = v4l_radio_minor_number_show; | ||||
| 	sfp->attr_v4l_radio_minor_number.store = NULL; | ||||
| 	ret = device_create_file(sfp->class_dev, | ||||
| 				       &sfp->attr_v4l_radio_minor_number); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "device_create_file error: %d", | ||||
| 			   ret); | ||||
| 	} else { | ||||
| 		sfp->v4l_radio_minor_number_created_ok = !0; | ||||
| 	} | ||||
| 
 | ||||
| 	sysfs_attr_init(&sfp->attr_unit_number.attr); | ||||
| 	sfp->attr_unit_number.attr.name = "unit_number"; | ||||
| 	sfp->attr_unit_number.attr.mode = S_IRUGO; | ||||
| 	sfp->attr_unit_number.show = unit_number_show; | ||||
| 	sfp->attr_unit_number.store = NULL; | ||||
| 	ret = device_create_file(sfp->class_dev,&sfp->attr_unit_number); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "device_create_file error: %d", | ||||
| 			   ret); | ||||
| 	} else { | ||||
| 		sfp->unit_number_created_ok = !0; | ||||
| 	} | ||||
| 
 | ||||
| 	sysfs_attr_init(&sfp->attr_bus_info.attr); | ||||
| 	sfp->attr_bus_info.attr.name = "bus_info_str"; | ||||
| 	sfp->attr_bus_info.attr.mode = S_IRUGO; | ||||
| 	sfp->attr_bus_info.show = bus_info_show; | ||||
| 	sfp->attr_bus_info.store = NULL; | ||||
| 	ret = device_create_file(sfp->class_dev, | ||||
| 				       &sfp->attr_bus_info); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "device_create_file error: %d", | ||||
| 			   ret); | ||||
| 	} else { | ||||
| 		sfp->bus_info_created_ok = !0; | ||||
| 	} | ||||
| 
 | ||||
| 	sysfs_attr_init(&sfp->attr_hdw_name.attr); | ||||
| 	sfp->attr_hdw_name.attr.name = "device_hardware_type"; | ||||
| 	sfp->attr_hdw_name.attr.mode = S_IRUGO; | ||||
| 	sfp->attr_hdw_name.show = hdw_name_show; | ||||
| 	sfp->attr_hdw_name.store = NULL; | ||||
| 	ret = device_create_file(sfp->class_dev, | ||||
| 				 &sfp->attr_hdw_name); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "device_create_file error: %d", | ||||
| 			   ret); | ||||
| 	} else { | ||||
| 		sfp->hdw_name_created_ok = !0; | ||||
| 	} | ||||
| 
 | ||||
| 	sysfs_attr_init(&sfp->attr_hdw_desc.attr); | ||||
| 	sfp->attr_hdw_desc.attr.name = "device_hardware_description"; | ||||
| 	sfp->attr_hdw_desc.attr.mode = S_IRUGO; | ||||
| 	sfp->attr_hdw_desc.show = hdw_desc_show; | ||||
| 	sfp->attr_hdw_desc.store = NULL; | ||||
| 	ret = device_create_file(sfp->class_dev, | ||||
| 				 &sfp->attr_hdw_desc); | ||||
| 	if (ret < 0) { | ||||
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 			   "device_create_file error: %d", | ||||
| 			   ret); | ||||
| 	} else { | ||||
| 		sfp->hdw_desc_created_ok = !0; | ||||
| 	} | ||||
| 
 | ||||
| 	pvr2_sysfs_add_controls(sfp); | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||||
| 	pvr2_sysfs_add_debugifc(sfp); | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pvr2_sysfs_internal_check(struct pvr2_channel *chp) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	sfp = container_of(chp,struct pvr2_sysfs,channel); | ||||
| 	if (!sfp->channel.mc_head->disconnect_flag) return; | ||||
| 	pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_sysfs id=%p",sfp); | ||||
| 	class_dev_destroy(sfp); | ||||
| 	pvr2_channel_done(&sfp->channel); | ||||
| 	kfree(sfp); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp, | ||||
| 				     struct pvr2_sysfs_class *class_ptr) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	sfp = kzalloc(sizeof(*sfp),GFP_KERNEL); | ||||
| 	if (!sfp) return sfp; | ||||
| 	pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p",sfp); | ||||
| 	pvr2_channel_init(&sfp->channel,mp); | ||||
| 	sfp->channel.check_func = pvr2_sysfs_internal_check; | ||||
| 
 | ||||
| 	class_dev_create(sfp,class_ptr); | ||||
| 	return sfp; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| struct pvr2_sysfs_class *pvr2_sysfs_class_create(void) | ||||
| { | ||||
| 	struct pvr2_sysfs_class *clp; | ||||
| 	clp = kzalloc(sizeof(*clp),GFP_KERNEL); | ||||
| 	if (!clp) return clp; | ||||
| 	pvr2_sysfs_trace("Creating and registering pvr2_sysfs_class id=%p", | ||||
| 			 clp); | ||||
| 	clp->class.name = "pvrusb2"; | ||||
| 	clp->class.class_release = pvr2_sysfs_class_release; | ||||
| 	clp->class.dev_release = pvr2_sysfs_release; | ||||
| 	if (class_register(&clp->class)) { | ||||
| 		pvr2_sysfs_trace( | ||||
| 			"Registration failed for pvr2_sysfs_class id=%p",clp); | ||||
| 		kfree(clp); | ||||
| 		clp = NULL; | ||||
| 	} | ||||
| 	return clp; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp) | ||||
| { | ||||
| 	pvr2_sysfs_trace("Unregistering pvr2_sysfs_class id=%p", clp); | ||||
| 	class_unregister(&clp->class); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | ||||
| static ssize_t debuginfo_show(struct device *class_dev, | ||||
| 			      struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	sfp = dev_get_drvdata(class_dev); | ||||
| 	if (!sfp) return -EINVAL; | ||||
| 	pvr2_hdw_trigger_module_log(sfp->channel.hdw); | ||||
| 	return pvr2_debugifc_print_info(sfp->channel.hdw,buf,PAGE_SIZE); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static ssize_t debugcmd_show(struct device *class_dev, | ||||
| 			     struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	sfp = dev_get_drvdata(class_dev); | ||||
| 	if (!sfp) return -EINVAL; | ||||
| 	return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static ssize_t debugcmd_store(struct device *class_dev, | ||||
| 			      struct device_attribute *attr, | ||||
| 			      const char *buf, size_t count) | ||||
| { | ||||
| 	struct pvr2_sysfs *sfp; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	sfp = dev_get_drvdata(class_dev); | ||||
| 	if (!sfp) return -EINVAL; | ||||
| 
 | ||||
| 	ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf,count); | ||||
| 	if (ret < 0) return ret; | ||||
| 	return count; | ||||
| } | ||||
| #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										46
									
								
								drivers/media/usb/pvrusb2/pvrusb2-sysfs.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								drivers/media/usb/pvrusb2/pvrusb2-sysfs.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_SYSFS_H | ||||
| #define __PVRUSB2_SYSFS_H | ||||
| 
 | ||||
| #include <linux/list.h> | ||||
| #include <linux/sysfs.h> | ||||
| #include "pvrusb2-context.h" | ||||
| 
 | ||||
| struct pvr2_sysfs; | ||||
| struct pvr2_sysfs_class; | ||||
| 
 | ||||
| struct pvr2_sysfs_class *pvr2_sysfs_class_create(void); | ||||
| void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *); | ||||
| 
 | ||||
| struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *, | ||||
| 				     struct pvr2_sysfs_class *); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_SYSFS_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										62
									
								
								drivers/media/usb/pvrusb2/pvrusb2-util.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								drivers/media/usb/pvrusb2/pvrusb2-util.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_UTIL_H | ||||
| #define __PVRUSB2_UTIL_H | ||||
| 
 | ||||
| #define PVR2_DECOMPOSE_LE(t,i,d) \ | ||||
|     do {    \ | ||||
| 	(t)[i] = (d) & 0xff;\ | ||||
| 	(t)[i+1] = ((d) >> 8) & 0xff;\ | ||||
| 	(t)[i+2] = ((d) >> 16) & 0xff;\ | ||||
| 	(t)[i+3] = ((d) >> 24) & 0xff;\ | ||||
|     } while(0) | ||||
| 
 | ||||
| #define PVR2_DECOMPOSE_BE(t,i,d) \ | ||||
|     do {    \ | ||||
| 	(t)[i+3] = (d) & 0xff;\ | ||||
| 	(t)[i+2] = ((d) >> 8) & 0xff;\ | ||||
| 	(t)[i+1] = ((d) >> 16) & 0xff;\ | ||||
| 	(t)[i] = ((d) >> 24) & 0xff;\ | ||||
|     } while(0) | ||||
| 
 | ||||
| #define PVR2_COMPOSE_LE(t,i) \ | ||||
|     ((((u32)((t)[i+3])) << 24) | \ | ||||
|      (((u32)((t)[i+2])) << 16) | \ | ||||
|      (((u32)((t)[i+1])) << 8) | \ | ||||
|      ((u32)((t)[i]))) | ||||
| 
 | ||||
| #define PVR2_COMPOSE_BE(t,i) \ | ||||
|     ((((u32)((t)[i])) << 24) | \ | ||||
|      (((u32)((t)[i+1])) << 16) | \ | ||||
|      (((u32)((t)[i+2])) << 8) | \ | ||||
|      ((u32)((t)[i+3]))) | ||||
| 
 | ||||
| 
 | ||||
| #endif /* __PVRUSB2_UTIL_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										1372
									
								
								drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1372
									
								
								drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										39
									
								
								drivers/media/usb/pvrusb2/pvrusb2-v4l2.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								drivers/media/usb/pvrusb2/pvrusb2-v4l2.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| #ifndef __PVRUSB2_V4L2_H | ||||
| #define __PVRUSB2_V4L2_H | ||||
| 
 | ||||
| #include "pvrusb2-context.h" | ||||
| 
 | ||||
| struct pvr2_v4l2; | ||||
| 
 | ||||
| struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_V4L2_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 75 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										114
									
								
								drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,114 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|    This source file is specifically designed to interface with the | ||||
|    saa711x support that is available in the v4l available starting | ||||
|    with linux 2.6.15. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #include "pvrusb2-video-v4l.h" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include <linux/videodev2.h> | ||||
| #include <media/v4l2-common.h> | ||||
| #include <media/saa7115.h> | ||||
| #include <linux/errno.h> | ||||
| 
 | ||||
| struct routing_scheme { | ||||
| 	const int *def; | ||||
| 	unsigned int cnt; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static const int routing_scheme0[] = { | ||||
| 	[PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, | ||||
| 	/* In radio mode, we mute the video, but point at one
 | ||||
| 	   spot just to stay consistent */ | ||||
| 	[PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, | ||||
| 	[PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE5, | ||||
| 	[PVR2_CVAL_INPUT_SVIDEO] =  SAA7115_SVIDEO2, | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme routing_def0 = { | ||||
| 	.def = routing_scheme0, | ||||
| 	.cnt = ARRAY_SIZE(routing_scheme0), | ||||
| }; | ||||
| 
 | ||||
| static const int routing_scheme1[] = { | ||||
| 	[PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, | ||||
| 	[PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, | ||||
| 	[PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE3, | ||||
| 	[PVR2_CVAL_INPUT_SVIDEO] =  SAA7115_SVIDEO2, /* or SVIDEO0, it seems */ | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme routing_def1 = { | ||||
| 	.def = routing_scheme1, | ||||
| 	.cnt = ARRAY_SIZE(routing_scheme1), | ||||
| }; | ||||
| 
 | ||||
| static const struct routing_scheme *routing_schemes[] = { | ||||
| 	[PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, | ||||
| 	[PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, | ||||
| }; | ||||
| 
 | ||||
| void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) | ||||
| { | ||||
| 	if (hdw->input_dirty || hdw->force_dirty) { | ||||
| 		const struct routing_scheme *sp; | ||||
| 		unsigned int sid = hdw->hdw_desc->signal_routing_scheme; | ||||
| 		u32 input; | ||||
| 
 | ||||
| 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", | ||||
| 			   hdw->input_val); | ||||
| 
 | ||||
| 		sp = (sid < ARRAY_SIZE(routing_schemes)) ? | ||||
| 			routing_schemes[sid] : NULL; | ||||
| 		if ((sp == NULL) || | ||||
| 		    (hdw->input_val < 0) || | ||||
| 		    (hdw->input_val >= sp->cnt)) { | ||||
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||||
| 				   "*** WARNING *** subdev v4l2 set_input:" | ||||
| 				   " Invalid routing scheme (%u)" | ||||
| 				   " and/or input (%d)", | ||||
| 				   sid, hdw->input_val); | ||||
| 			return; | ||||
| 		} | ||||
| 		input = sp->def[hdw->input_val]; | ||||
| 		sd->ops->video->s_routing(sd, input, 0, 0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										48
									
								
								drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __PVRUSB2_VIDEO_V4L_H | ||||
| #define __PVRUSB2_VIDEO_V4L_H | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|    This module connects the pvrusb2 driver to the I2C chip level | ||||
|    driver which handles device video processing.  This interface is | ||||
|    used internally by the driver; higher level code should only | ||||
|    interact through the interface provided by pvrusb2-hdw.h. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); | ||||
| 
 | ||||
| #endif /* __PVRUSB2_VIDEO_V4L_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										70
									
								
								drivers/media/usb/pvrusb2/pvrusb2-wm8775.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								drivers/media/usb/pvrusb2/pvrusb2-wm8775.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|    This source file is specifically designed to interface with the | ||||
|    wm8775. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #include "pvrusb2-wm8775.h" | ||||
| 
 | ||||
| 
 | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| #include "pvrusb2-debug.h" | ||||
| #include <linux/videodev2.h> | ||||
| #include <media/v4l2-common.h> | ||||
| #include <linux/errno.h> | ||||
| 
 | ||||
| void pvr2_wm8775_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) | ||||
| { | ||||
| 	if (hdw->input_dirty || hdw->force_dirty) { | ||||
| 		u32 input; | ||||
| 
 | ||||
| 		switch (hdw->input_val) { | ||||
| 		case PVR2_CVAL_INPUT_RADIO: | ||||
| 			input = 1; | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* All other cases just use the second input */ | ||||
| 			input = 2; | ||||
| 			break; | ||||
| 		} | ||||
| 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev wm8775" | ||||
| 			   " set_input(val=%d route=0x%x)", | ||||
| 			   hdw->input_val, input); | ||||
| 
 | ||||
| 		sd->ops->audio->s_routing(sd, input, 0, 0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										52
									
								
								drivers/media/usb/pvrusb2/pvrusb2-wm8775.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								drivers/media/usb/pvrusb2/pvrusb2-wm8775.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __PVRUSB2_WM8775_H | ||||
| #define __PVRUSB2_WM8775_H | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
|    This module connects the pvrusb2 driver to the I2C chip level | ||||
|    driver which performs analog -> digital audio conversion for | ||||
|    external audio inputs.  This interface is used internally by the | ||||
|    driver; higher level code should only interact through the | ||||
|    interface provided by pvrusb2-hdw.h. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #include "pvrusb2-hdw-internal.h" | ||||
| 
 | ||||
| void pvr2_wm8775_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); | ||||
| 
 | ||||
| 
 | ||||
| #endif /* __PVRUSB2_WM8775_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
							
								
								
									
										42
									
								
								drivers/media/usb/pvrusb2/pvrusb2.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								drivers/media/usb/pvrusb2/pvrusb2.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||||
|  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> | ||||
|  * | ||||
|  *  This program is free software; you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation; either version 2 of the License | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with this program; if not, write to the Free Software | ||||
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __PVRUSB2_H | ||||
| #define __PVRUSB2_H | ||||
| 
 | ||||
| /* Maximum number of pvrusb2 instances we can track at once.  You
 | ||||
|    might want to increase this - however the driver operation will not | ||||
|    be impaired if it is too small.  Instead additional units just | ||||
|    won't have an ID assigned and it might not be possible to specify | ||||
|    module parameters for those extra units. */ | ||||
| #define PVR_NUM 20 | ||||
| 
 | ||||
| #endif /* __PVRUSB2_H */ | ||||
| 
 | ||||
| /*
 | ||||
|   Stuff for Emacs to see, in order to encourage consistent editing style: | ||||
|   *** Local Variables: *** | ||||
|   *** mode: c *** | ||||
|   *** fill-column: 70 *** | ||||
|   *** tab-width: 8 *** | ||||
|   *** c-basic-offset: 8 *** | ||||
|   *** End: *** | ||||
|   */ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 awab228
						awab228