Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
PT2ドライババグの注意喚起

この記事は DTV Advent Calendar 2016 6日目の記事です。

MirakurunにおけるPT2サポートは、現在開発中です。 プルリクエストがマージされて、リリースされ次第、使えるようになります。 2.0.0-beta.3では、まだPT2サポートは不十分で、chardev版pt1_drvのバグを回避できません。 今後リリースされるMirakurunのPT2サポートを利用することでchardev版pt1_drvのバグを回避することが可能です。

以下が本文になります。

PT1/2をご利用の方へ大切なお知らせとお願い

chardev版pt1_drvには録画失敗に至る危険性があります。 当該バージョンを未対応のままご利用になりますと、チャンネルの切り替えができなくなり、録画失敗に至る恐れがあります。 なお、再起動しますと、チャンネルの切り替えは正常に行えるようになります。 ハードウェアが故障するということは、ないはずなのでご安心ください。

対象

代表的なのは下記の通りになります。

これ以外にも、バグのあるドライバは転がっているはずです。 詳しくは対策をご覧ください。

対策

driver/pt1_pci.cをご覧ください。 pt1_release関数がありますが、次のような感じになっているかと思います。

static int pt1_release(struct inode *inode, struct file *file)
{
	PT1_CHANNEL	*channel = file->private_data;

	mutex_lock(&channel->ptr->lock);
	SetStream(channel->ptr->regs, channel->channel, FALSE);
	channel->valid = FALSE ;
	printk(KERN_INFO "(%d:%d)Drop=%08d:%08d:%08d:%08d\n", imajor(inode), iminor(inode), channel->drop,
						channel->overflow, channel->counetererr, channel->transerr);
	channel->overflow = 0 ;
	channel->counetererr = 0 ;
	channel->transerr = 0 ;
	channel->drop = 0 ;
	// 停止している場合は起こす
	if(channel->req_dma == TRUE){
		channel->req_dma = FALSE ;
		wake_up(&channel->ptr->dma_wait_q);
	}
	mutex_unlock(&channel->ptr->lock);

	/* send tuner to sleep */
	set_sleepmode(channel->ptr->regs, &channel->lock,
				  channel->address, channel->type, TYPE_SLEEP);
	schedule_timeout_interruptible(msecs_to_jiffies(100));

	return 0;
}

注目してほしいのは次のところです。

	}
	mutex_unlock(&channel->ptr->lock);

	/* send tuner to sleep */
	set_sleepmode(channel->ptr->regs, &channel->lock,
				  channel->address, channel->type, TYPE_SLEEP);
	schedule_timeout_interruptible(msecs_to_jiffies(100));

	return 0;

これを次のように書き換えます。 mutex_unlockをreturnの前に移動させます。

	}

	/* send tuner to sleep */
	set_sleepmode(channel->ptr->regs, &channel->lock,
				  channel->address, channel->type, TYPE_SLEEP);
	schedule_timeout_interruptible(msecs_to_jiffies(100));

	mutex_unlock(&channel->ptr->lock);

	return 0;

確認方法

次のコマンドを実行することで、今、インストールされているドライバにバグがあるかどうか確認できる_かもしれません_

curl -L https://gist.github.com/akimasa/a2fc1fc098dee1e27ab88fab3ff27d23/raw/a0eb1fa2611ea2c930c1a892c1d4e78570ab668b/pt2-driverbug-checker.c > pt2-driverbug-checker.c && gcc -o pt2-driverbug-checker -pthread pt2-driverbug-checker.c && ./pt2-driverbug-checker

2回目からはソースのダウンロードとコンパイルが必要ないので、複数回実行する場合は./pt2-driverbug-checkerだけで問題ないです。

確認後はrmmod pt1_drv; modprobe pt1_drvでドライバを読み直してください。 あるいは、reboot等で再起動してください。 さもないと、チャンネルの切り替えができなくなり録画失敗の原因になります。 バグがなければ大丈夫ですが、多くの環境にはバグがあると思われるので、実施することをおすすめします。

"Your driver is probably safe."という行が表示されたら、多分大丈夫です。 心配なら数回./pt2-driverbug-checkerを実行してみてください。 手元の環境では2回実行しないと、バグの存在が確認できなかったことが結構ありました。

"Your driver has bug."と表示された場合は、残念ながらバグがあります。 対策を実施することをおすすめします。 また、プログラム実行後は、先ほども言ったように、チャンネルの切り替えができなくなります。 rmmod pt1_drv; modprobe pt1_drvでドライバを読み直してください。 あるいは、reboot等で再起動してください。

"Maybe I am using wrong channel."と出て、"Starting test."が表示されずテストが始まらない場合は、チャンネルを適切に書き換える必要があるかもしれません。 42行目が次のような感じになっています。

		.frequencyno = 76,

76を適当な数字に書き換えてください。 recpt1/pt1_dev.hが参考になるのではないかとおもます。 76は地上波放送の26チャンネルの事を意味します。放送の来ているチャンネルに書き換えて実行してみてください。

#include <stdlib.h>
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
/***************************************************************************/
/* IOCTL定義 */
/***************************************************************************/
#define SET_CHANNEL _IOW(0x8D, 0x01, FREQUENCY)
#define START_REC _IO(0x8D, 0x02)
#define STOP_REC _IO(0x8D, 0x03)
#define GET_SIGNAL_STRENGTH _IOR(0x8D, 0x04, int *)
#define LNB_ENABLE _IOW(0x8D, 0x05, int)
#define LNB_DISABLE _IO(0x8D, 0x06)
typedef struct _frequency{
int frequencyno ; // 周波数テーブル番号
int slot ; // スロット番号/加算する周波数
}FREQUENCY;
struct thread_t {
pthread_t thread;
sem_t to_thread;
sem_t to_main;
};
int chktuner(){
int tfd = open("/dev/pt1video3", O_RDONLY);
if(tfd < 0){
fprintf(stderr,"failed to open /dev/pt1video3\n");
return -1;
}
ioctl(tfd, STOP_REC, 0);
const FREQUENCY freq = {
.frequencyno = 76,
.slot = 0,
};
if(ioctl(tfd, SET_CHANNEL, &freq) < 0) {
fprintf(stderr, "Cannot tune pt1video3 to the specified channel: %d\n",errno);
return -2;
}
close(tfd);
tfd = open("/dev/pt1video2", O_RDONLY);
if(tfd < 0){
fprintf(stderr,"failed to open /dev/pt1video2\n");
return -1;
}
ioctl(tfd, STOP_REC, 0);
if(ioctl(tfd, SET_CHANNEL, &freq) < 0) {
fprintf(stderr, "Cannot tune pt1video2 to the specified channel: %d\n",errno);
return -2;
}
close(tfd);
return 0;
}
void *thread_f(void *arg){
struct thread_t *thread = (struct thread_t *) arg;
for(int i=0;i<100;i++){
sem_post(&thread->to_main);
sem_wait(&thread->to_thread);
//usleep(1000*10); //10ms
int tfd = open("/dev/pt1video2", O_RDONLY);
if(tfd < 0){
fprintf(stderr,"failed to open pt1video2:%d",errno);
return (void *) NULL;
}
sem_post(&thread->to_main);
sem_wait(&thread->to_thread);
//usleep(1000*10); //10ms
close(tfd);
}
return (void *) NULL;
}
int main(void){
int ret;
if((ret = chktuner()) < 0){
fprintf(stderr, "Something wrong.\n");
if(ret == -1){
fprintf(stderr, "Maybe Mirakurun or Chinachu is using the tuner.\nStop Mirakurun and Chinachu to perform test.\n");
} else if (ret == -2){
fprintf(stderr, "Maybe I am using wrong channel.\nPlease change \".frequencyno\" and run again.\n");
fprintf(stderr, "Or maybe you are running this command twice.\nExecute following commands AS ROOT to activate tuners.\nrmmod pt1_drv; modprobe pt1_drv\n");
}
exit(EXIT_FAILURE);
}
struct thread_t thread;
sem_init(&thread.to_main,0,0);
sem_init(&thread.to_thread,0,0);
if(pthread_create(&thread.thread, NULL, thread_f, (void *)&thread) != 0){
fprintf(stderr, "something wrong creating thread\n");
exit(EXIT_FAILURE);
}
fprintf(stderr, "Starting test.\nWait about 32 seconds.\n");
for(int i=0;i<100;i++){
sem_wait(&thread.to_main);
sem_post(&thread.to_thread);
int tfd2 = open("/dev/pt1video3", O_RDONLY);
sem_wait(&thread.to_main);
sem_post(&thread.to_thread);
close(tfd2);
fprintf(stderr, "\r%d/100",i+1);
}
pthread_join(thread.thread, NULL);
sem_destroy(&thread.to_main);
sem_destroy(&thread.to_thread);
fprintf(stderr, "\n");
if(chktuner() < 0){
fprintf(stderr, "Your driver has bug.\nExecute following commands AS ROOT to activate tuners.\nrmmod pt1_drv; modprobe pt1_drv\n");
exit(EXIT_FAILURE);
} else {
fprintf(stderr, "Your driver is probably safe.\nYou can run this command a few times to ensure your driver is safe.\n./pt2-driverbug-checker\n");
exit(EXIT_SUCCESS);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.