Subject: Driver for Panasonic 563B CD-ROM

From: [email-masked] (gwoho liu)
Date: 21 Sep 1993 22:20:33 GMT

Here is a CD-ROM driver for the Panasonic 563B CD-ROM drive.  One can buy
this double spin drive at B-Dalton's retail for $250.  It has a proprietary
interface.  This CD-ROM drive reads data faster than my cheapo SCSI hard disk.

I wrote this driver by using DOS debug command and disassembling the DOS
device driver which came with the drive.  I have no idea how the Panasonic 563B
is supposed to work, and I just wrote this so that I works with my drive--I
have no idea if all Panasonic 563B drives work identically.

I have made these patches relative to the .99.12 Linux.  I have only tested
it with my kernel, which has zillions of other patches.  (e.g. the shm
bug fix, which probably anyone using shm should apply.)

Since I don't own a single CD-ROM disk and had to borrow one (which I
have returned) to test this thing out, it is not well tested.  I know that
I need to figure out which bit in the toc_entry structure means that the
track is a data track--I think it is the third bit of the second byte, but
since I have only ever put in one CD-ROM disk into my drive, I can not be
sure.  Anyway, I need to set the cdte_datamode field of the cdrom_tocentry
appropriately.  This is ordinarily irrelevant though.

The patches contain some long lines--I hope your newsreader does not mangle
them.  You might watch out for that.

gwoho liu.

diff --context --new-file --recursive linux/config.in linux.new/config.in
*** linux/config.in	Sun Aug 15 11:24:56 1993
--- linux.new/config.in	Tue Sep 21 09:02:54 1993
***************
*** 63,68 ****
--- 63,69 ----
  *
  bool 'Sony CDU31A CDROM driver support' CONFIG_CDU31A n
  bool 'Mitsumi CDROM driver support' CONFIG_MCD n
+ bool 'Panasonic Whatnot CDROM driver support' CONFIG_PANASONIC_WHATNOT y
  *
  * Filesystems
  *
diff --context --new-file --recursive linux/fs/buffer.c linux.new/fs/buffer.c
*** linux/fs/buffer.c	Sat Aug 14 23:47:21 1993
--- linux.new/fs/buffer.c	Tue Sep 21 09:02:54 1993
***************
*** 41,46 ****
--- 41,49 ----
  #ifdef CONFIG_CDU31A
  extern int check_cdu31a_media_change(int, int);
  #endif
+ #ifdef CONFIG_PANASONIC_WHATNOT
+ extern int check_panasonic_whatnot_media_change(int, int);
+ #endif
  #ifdef CONFIG_MCD
  extern int check_mcd_media_change(int, int);
  #endif
***************
*** 243,248 ****
--- 246,257 ----
           case 15: /* Sony CDROM */
  		i = check_cdu31a_media_change(dev, 0);
  		break;
+ #endif
+ 
+ #if defined(CONFIG_PANASONIC_WHATNOT)
+          case 16: /* Panasonic CDROM */
+ 		i = check_panasonic_whatnot_media_change(dev, 0);
+ 		break;
  #endif
  
  #if defined(CONFIG_MCD)
diff --context --new-file --recursive linux/kernel/blk_drv/Makefile linux.new/kernel/blk_drv/Makefile
*** linux/kernel/blk_drv/Makefile	Sat Aug 14 23:47:22 1993
--- linux.new/kernel/blk_drv/Makefile	Tue Sep 21 09:02:54 1993
***************
*** 18,24 ****
  
  SUBDIRS	= scsi
  
! OBJS = xd.o hd.o ll_rw_blk.o floppy.o ramdisk.o genhd.o cdu31a.o mcd.o
  
  all: blk_drv.a scsisubdirs
  
--- 18,24 ----
  
  SUBDIRS	= scsi
  
! OBJS = xd.o hd.o hd1.o ll_rw_blk.o floppy.o ramdisk.o genhd.o cdu31a.o mcd.o panasonic_whatnot.o
  
  all: blk_drv.a scsisubdirs
  
diff --context --new-file --recursive linux/kernel/blk_drv/blk.h linux.new/kernel/blk_drv/blk.h
*** linux/kernel/blk_drv/blk.h	Sat Aug 14 23:47:22 1993
--- linux.new/kernel/blk_drv/blk.h	Tue Sep 21 09:02:54 1993
***************
*** 80,85 ****
--- 80,86 ----
  
  extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
  extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
+ extern unsigned long panasonic_whatnot_init(unsigned long mem_start, unsigned long mem_end);
  extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
  extern int is_read_only(int dev);
  extern void set_device_ro(int dev,int flag);
***************
*** 175,180 ****
--- 176,189 ----
  #define DEVICE_NAME "CDU31A"
  #define DEVICE_REQUEST do_cdu31a_request
  #define DEVICE_NR(device) (MINOR(device))
+ #define DEVICE_ON(device)
+ #define DEVICE_OFF(device)
+ 
+ #elif (MAJOR_NR == 16)
+ /* PANASONIC whatnot CD-ROM */
+ #define DEVICE_NAME "Panasonic whatnot"
+ #define DEVICE_REQUEST do_panasonic_whatnot_request
+ #define DEVICE_NR(device) (MINOR(device))
  #define DEVICE_ON(device)
  #define DEVICE_OFF(device)
  
diff --context --new-file --recursive linux/kernel/blk_drv/ll_rw_blk.c linux.new/kernel/blk_drv/ll_rw_blk.c
*** linux/kernel/blk_drv/ll_rw_blk.c	Sat Aug 14 23:47:22 1993
--- linux.new/kernel/blk_drv/ll_rw_blk.c	Tue Sep 21 09:03:54 1993
***************
*** 47,53 ****
  	{ NULL, NULL },		/* dev lp */
  	{ NULL, NULL },		/* dev pipes */
  	{ NULL, NULL },		/* dev sd */
! 	{ NULL, NULL }		/* dev st */
  };
  
  /*
--- 47,60 ----
  	{ NULL, NULL },		/* dev lp */
  	{ NULL, NULL },		/* dev pipes */
  	{ NULL, NULL },		/* dev sd */
! 	{ NULL, NULL },		/* dev st */
! 	{ NULL, NULL },
! 	{ NULL, NULL },
! 	{ NULL, NULL },
! 	{ NULL, NULL },
! 	{ NULL, NULL },
! 	{ NULL, NULL },
! 	{ NULL, NULL },
  };
  
  /*
***************
*** 434,439 ****
--- 441,449 ----
  #ifdef CONFIG_CDU31A
  	mem_start = cdu31a_init(mem_start,mem_end);
  #endif
+ #ifdef CONFIG_PANASONIC_WHATNOT
+ 	mem_start = panasonic_whatnot_init(mem_start,mem_end);
+ #endif
  #ifdef CONFIG_MCD
  	mem_start = mcd_init(mem_start,mem_end);
  #endif
diff --context --new-file --recursive linux/kernel/blk_drv/panasonic_whatnot.c linux.new/kernel/blk_drv/panasonic_whatnot.c
*** linux/kernel/blk_drv/panasonic_whatnot.c
--- linux.new/kernel/blk_drv/panasonic_whatnot.c	Tue Sep 21 09:02:54 1993
***************
*** 0 ****
--- 1,810 ----
+ /*
+  * Panasonic whatnot CDROM driver thing
+  * gwoho liu. [email-masked]
+ */
+ 
+ #include <linux/config.h>
+ 
+ #ifdef CONFIG_PANASONIC_WHATNOT
+ 
+ #include <linux/errno.h>
+ #include <linux/signal.h>
+ #include <linux/sched.h>
+ #include <linux/timer.h>
+ #include <linux/fs.h>
+ #include <linux/kernel.h>
+ #include <linux/hdreg.h>
+ #include <linux/genhd.h>
+ #include <linux/string.h>
+ 
+ #include <asm/system.h>
+ #include <asm/io.h>
+ #include <asm/segment.h>
+ 
+ #include <linux/cdrom.h>
+ 
+ #define MAJOR_NR 16
+ #include "blk.h"
+ 
+ #define DEBUG
+ 
+ #ifdef DEBUG
+ #define dprintk		printk
+ #else
+ #define dprintk
+ #endif
+ 
+ #define TIMEOUT		200
+ #define BUSY_WAIT_TIME	10
+ #define NAP_TIME	0
+ #define TRIES		3
+ 
+ #define BUFFER_SIZE	16384
+ 
+ struct toc_head {
+ 	unsigned char dunno0;
+ 	unsigned char t0;
+ 	unsigned char t1;
+ 	unsigned char m;
+ 	unsigned char s;
+ 	unsigned char f;
+ };
+ 
+ struct toc_entry {
+ 	unsigned char dunno0;
+ 	unsigned char dunno1;
+ 	unsigned char t;
+ 	unsigned char dunno3;
+ 	unsigned char m;
+ 	unsigned char s;
+ 	unsigned char f;
+ 	unsigned char dunno7;
+ };
+ 
+ struct sub_chan {
+ 	unsigned char dunno0;
+ 	unsigned char dunno1;
+ 	unsigned char t;
+ 	unsigned char i;
+ 	unsigned char am;
+ 	unsigned char as;
+ 	unsigned char af;
+ 	unsigned char rm;
+ 	unsigned char rs;
+ 	unsigned char rf;
+ 	unsigned char dunno10;
+ };
+ 
+ struct who_knows_0x82 {
+ 	unsigned char dunno0;
+ 	unsigned char dunno1;
+ 	unsigned char dunno2;
+ 	unsigned char dunno3;
+ 	unsigned char dunno4;
+ 	unsigned char dunno5;
+ 	unsigned char dunno6;
+ 	unsigned char dunno7;
+ };
+ 
+ struct drive_info_struct {
+ 	int nopen;
+ 	unsigned char okay:1;
+ 	unsigned char changed:1;
+ 	unsigned char changed2:1;
+ 	unsigned char status;
+ 	struct who_knows_0x82 wk0x82;
+ 	struct toc_head head;
+ 	struct toc_entry entry[100];
+ 	int lba_len;
+ 	int buf0;
+ 	int buf1;
+ 	unsigned char *buffer;
+ };
+ 
+ #define S_SPINNING		0x01
+ #define S_WHO_KNOWS_ALWAYS_1	0x02
+ #define S_LOCKED		0x04
+ #define S_PLAYING_AUDIO		0x08
+ #define S_DISK_CHANGED		0x10
+ #define S_HAS_CD		0x20
+ #define S_HAS_SOMETHING		0x40
+ #define S_DOOR_CLOSED		0x80
+ 
+ static ports[] = {0x250,0x260,0};
+ static port;
+ 
+ static struct drive_info_struct drive_info[3];
+ 
+ static struct wait_queue *wait_q = NULL;
+ static struct task_struct *owner = NULL;
+ static in_use = 0;
+ 
+ int
+ check_panasonic_whatnot_media_change(int dev,int flag)
+ {
+ 	int ret;
+ 
+ 	dev = MINOR(dev) >> 6;
+ 	ret = drive_info[dev].changed;
+ 	if (!flag)
+ 		drive_info[dev].changed = 0;
+ 	return ret;
+ }
+ 
+ static inline void
+ take_a_nap(void)
+ {
+ 	current->state = TASK_INTERRUPTIBLE;
+ 	current->timeout = jiffies+NAP_TIME;
+ 	schedule();
+ }
+ 
+ static void
+ lba_to_msf(int lba,unsigned char *msf)
+ {
+ 	lba += 150;
+ 	msf[0] = lba/4500;
+ 	msf[1] = (lba/75) % 60;
+ 	msf[2] = lba % 75;
+ }
+ 
+ static int
+ msf_to_lba(unsigned char *msf)
+ {
+ 	int i;
+ 
+ 	i = msf[0]*4500 + msf[1]*75 + msf[2];
+ 	return i >= 150 ? i-150 : 0;
+ }
+ 
+ static void
+ msf_to_lba_2(void *b)
+ {
+ 	union a {
+ 		struct {
+ 			u_char m;
+ 			u_char s;
+ 			u_char f;
+ 		} msf;
+ 		int lba;
+ 	} *a;
+ 	int i;
+ 
+ 	a = (union a *)b;
+ 	i = a->msf.m*4500 + a->msf.s*75 + a->msf.f;
+ 	a->lba = i >= 150 ? i-150 : 0;
+ }
+ 
+ static int
+ wait_input()
+ {
+ 	int i;
+ 
+ 	i = jiffies;
+ 	while (inb(port+1) & 4) {
+ 		if (jiffies > i+BUSY_WAIT_TIME)
+ 			take_a_nap();
+ 		if (jiffies > i+TIMEOUT)
+ 			return 1;
+ 	}
+ 	return 0;
+ }
+ 
+ static unsigned char *
+ assemble_command(int c,unsigned char *a)
+ {
+ 	a[0] = c;
+ 	a[6] = a[5] = a[4] = a[3] = a[2] = a[1] = 0;
+ 	return a;
+ }
+ 
+ static int
+ input_string(int d,void *a,int n)
+ {
+ 	int i;
+ 
+ 	while (n--) {
+ 		if (wait_input())
+ 			return 1;
+ 		i = inb(port);
+ 		if (a)
+ 			*((unsigned char *)a)++ = i;
+ 	}
+ 	if (wait_input())
+ 		return 1;
+ 	drive_info[d].status = inb(port);
+ 	return 0;
+ }
+ 
+ static selected = -1;
+ 
+ static inline void
+ select_drive(int d)
+ {
+ 	if (selected != d) {
+ 		selected = d;
+ 		outb((d&2)>>1 | (d&1)<<1,port+3);
+ 	}
+ }
+ 
+ static void
+ note_changed(int d)
+ {
+ 	drive_info[d].changed = 1;
+ 	drive_info[d].changed2 = 1;
+ 	drive_info[d].buf0 = drive_info[d].buf1 = 0;
+ }
+ 
+ static void
+ zero_drive_info(int d)
+ {
+ 	struct drive_info_struct *p = drive_info+d;
+ 
+ 	memset(&p->head,0,sizeof(struct toc_head));
+ 	memset(&p->wk0x82,0,sizeof(struct who_knows_0x82));
+ 	p->lba_len = p->buf0 = p->buf1 = 0;
+ }
+ 
+ static int
+ raw_command(int d,unsigned char *c,void *s,int n)
+ {
+ 	int j,ret = 0;
+ 
+ 	cli();
+ 	if (current != owner)
+ 		while (in_use) {
+ 			interruptible_sleep_on(&wait_q);
+ 			if (current->signal & ~current->blocked)
+ 				return -EINTR;
+ 		}
+ 	in_use = 1;
+ 	owner = current;
+ 	sti();
+ 	select_drive(d);
+ 	for (j=0; j<7; j++)
+ 		outb(*c++,port);
+ 	if (input_string(d,s,n))
+ 		ret = -EIO;
+ 	if (drive_info[d].status & 0x10)
+ 		note_changed(d);
+ 	owner = NULL;
+ 	in_use = 0;
+ 	wake_up_interruptible(&wait_q);
+ 	return ret;
+ }
+ 
+ static int
+ read_data(int d,unsigned char *b,int lba,int len)
+ {
+ 	int j,ret = 0;
+ 	unsigned char c[7];
+ 
+ 	cli();
+ 	if (current != owner)
+ 		while (in_use) {
+ 			interruptible_sleep_on(&wait_q);
+ 			if (current->signal & ~current->blocked)
+ 				return -EINTR;
+ 		}
+ 	in_use = 1;
+ 	owner = current;
+ 	sti();
+ 	select_drive(d);
+ 	c[0] = 0x10;
+ 	c[4] = 0;
+ 	c[5] = 0;
+ 	c[6] = len;
+ 	lba_to_msf(lba++,c+1);
+ 	for (j=0; j<7; j++)
+ 		outb(c[j],port);
+ 	outb(1,port+1);
+ 	while (len--) {
+ 		j = jiffies;
+ 		while (inb(port+1) & 2) {
+ 			if (jiffies > j+BUSY_WAIT_TIME)
+ 				take_a_nap();
+ 			if (jiffies > j+TIMEOUT) {
+ 				outb(0,port+1);
+ 				ret = -EIO;
+ 				goto bad;
+ 			}
+ 		}
+ 		__asm__("cld;rep;insb": :"d" (port),"D" (b),"c" (2048):"cx","di");
+ 		b += 2048;
+ 	}
+ 	outb(0,port+1);
+ 	if (wait_input()) {
+ 		ret = -EIO;
+ 		goto bad;
+ 	}
+ 	drive_info[d].status = inb(port);
+ 	if (drive_info[d].status & 0x10)
+ 		note_changed(d);
+ bad:
+ 	owner = NULL;
+ 	in_use = 0;
+ 	wake_up_interruptible(&wait_q);
+ 	return ret;
+ }
+ 
+ static int
+ simple_command(int d,int a,void *t,int b)
+ {
+ 	unsigned char z[7];
+ 
+ 	assemble_command(a,z);
+ 	return raw_command(d,z,t,b);
+ }
+ 
+ static int
+ quick_wait_input()
+ {
+ 	int i;
+ 
+ 	i = jiffies;
+ 	while (inb(port+1) & 4) {
+ 		if (jiffies > i+2)
+ 			return 1;
+ 	}
+ 	return 0;
+ }
+ 
+ static int
+ detect_drive(int d)
+ {
+ 	int j;
+ 	char t[12],*c;
+ 
+ 	select_drive(d);
+ 	outb(0,port+1);
+ 	outb(0x82,port);
+ 	for (j=0; j<6; j++)
+ 		outb(0,port);
+ 	input_string(d,NULL,8);
+ 	for (j=0; j<9; j++) {
+ 		if (quick_wait_input())
+ 			break;
+ 		inb(port);
+ 	}
+ 	outb(0x83,port);
+ 	for (j=0; j<6; j++)
+ 		outb(0,port);
+ 	memset(t,12,0);
+ 	c = t;
+ 	for (j=0; j<12; j++) {
+ 		if (quick_wait_input())
+ 			break;
+ 		*c++ = inb(port);
+ 	}
+ 	if (strncmp(t,"CR-5630",7))
+ 		return 0;
+ 	return 1;
+ }
+ 
+ static int
+ open_tray(int d)
+ {
+ 	return simple_command(d,6,NULL,0);
+ }
+ 
+ #if 0
+ static int
+ close_tray(int d)
+ {
+ 	return simple_command(d,7,NULL,0);
+ }
+ 
+ static int
+ set_double_speed(int d)
+ {
+ 	static unsigned char b[7] = {9,3,0xc0,0,0,0,0};
+ 
+ 	return raw_command(d,b,NULL,0);
+ }
+ 
+ static int
+ set_single_speed(int d)
+ {
+ 	static unsigned char b[7] = {9,3,0,0,0,0,0};
+ 
+ 	return raw_command(d,b,NULL,0);
+ }
+ 
+ #endif
+ 
+ static int
+ set_auto_speed(int d)
+ {
+ 	static unsigned char b[7] = {9,3,0x80,0,0,0,0};
+ 
+ 	return raw_command(d,b,NULL,0);
+ }
+ 
+ static int
+ lock_tray(int d)
+ {
+ 	static unsigned char b[7] = {0xc,1,0,0,0,0,0};
+ 
+ 	return raw_command(d,b,NULL,0);
+ }
+ 
+ static int
+ unlock_tray(int d)
+ {
+ 	return simple_command(d,0xc,NULL,0);
+ }
+ 
+ static int
+ read_toc_etc(int d)
+ {
+ 	struct drive_info_struct *p = drive_info + d;
+ 	unsigned char c[7];
+ 	int j,ret;
+ 
+ 	if (!p->changed2)
+ 		return 0;
+ 	if ((p->status&S_HAS_CD) == 0) {
+ 		zero_drive_info(d);
+ 		return 0;
+ 	}
+ 	if (ret = simple_command(d,0x82,&p->wk0x82,8)) {
+ 		dprintk("Panasonic CD-ROM - 0x82 failed\n");
+ 		return ret;
+ 	}
+ 	if (ret = simple_command(d,0x8b,&p->head,6)) {
+ 		dprintk("Panasonic CD-ROM - 0x8b failed\n");
+ 		return ret;
+ 	}
+ 	p->lba_len = msf_to_lba(&p->head.m);
+ 	assemble_command(0x8c,c);
+ 	for (j=p->head.t0; j<=p->head.t1; j++) {
+ 		c[2] = j;
+ 		if (ret = raw_command(d,c,p->entry+j,8)) {
+ 			dprintk("Panasonic CD-ROM - 0x8c failed\n");
+ 			return ret;
+ 		}
+ 	}
+ 	lock_tray(d);
+ 	set_auto_speed(d);
+ 	p->changed2 = 0;
+ 	return 0;
+ }
+ 
+ static int
+ pause_playback(int d)
+ {
+ 	return simple_command(d,0xd,NULL,0);
+ }
+ 
+ static int
+ resume_playback(int d)
+ {
+ 	static unsigned char a[7] = {0xd,0x80,0,0,0,0,0};
+ 
+ 	return raw_command(d,a,NULL,0);
+ }
+ 
+ static int
+ do_nop(int d)
+ {
+ 	return simple_command(d,5,NULL,0);
+ }
+ 
+ static void
+ find_track_start(int d,int t,unsigned char *msf)
+ {
+ 	struct drive_info_struct *p = drive_info+d;
+ 
+ 	if (t > p->head.t1) {
+ 		msf[0] = p->head.m;
+ 		msf[1] = p->head.s;
+ 		msf[2] = p->head.f;
+ 	}
+ 	else {
+ 		if (t < p->head.t0)
+ 			t = p->head.t0;
+ 		msf[0] = p->entry[t].m;
+ 		msf[1] = p->entry[t].s;
+ 		msf[2] = p->entry[t].f;
+ 	}
+ }
+ 
+ static int
+ play_audio_msf(int d,struct cdrom_msf *msf)
+ {
+ 	unsigned char b[7];
+ 
+ 	b[0] = 0xe;
+ 	b[1] = msf->cdmsf_min0;
+ 	b[2] = msf->cdmsf_sec0;
+ 	b[3] = msf->cdmsf_frame0;
+ 	b[4] = msf->cdmsf_min1;
+ 	b[5] = msf->cdmsf_sec1;
+ 	b[6] = msf->cdmsf_frame1;
+ 	return raw_command(d,b,NULL,0);
+ }
+ 
+ static int
+ play_audio_ti(int d,struct cdrom_ti *ti)
+ {
+ 	unsigned char b[7];
+ 
+ 	b[0] = 0xe;
+ 	find_track_start(d,ti->cdti_trk0,b+1);
+ 	find_track_start(d,ti->cdti_trk1+1,b+4);
+ 	return raw_command(d,b,NULL,0);
+ }
+ 
+ static void
+ do_panasonic_whatnot_request()
+ {
+ 	struct drive_info_struct *p;
+ 	int d,len,lba,try;
+ 	int block,nsect;
+ 
+ 	for (;;) {
+ 		if (!CURRENT || CURRENT->dev<0)
+ 			return;
+ 		INIT_REQUEST;
+ 		d = MINOR(CURRENT->dev) >> 6;
+ 		p = drive_info+d;
+ 		block = CURRENT->sector;
+ 		nsect = CURRENT->nr_sectors;
+ 		if (p->okay == 0) {
+ 			end_request(0);
+ 			continue;
+ 		}
+ 		if (CURRENT->cmd != READ) {
+ 			end_request(0);
+ 			continue;
+ 		}
+ 		if (read_toc_etc(d)) {
+ 			end_request(0);
+ 			continue;
+ 		}
+ 		if ((block+nsect)/4 > p->lba_len) {
+ 			end_request(0);
+ 			continue;
+ 		}
+ 		while (nsect > 0) {
+ 			if (block<p->buf0 || block>=p->buf1) {
+ 				p->buf0 = block & ~3;
+ 				lba = block/4;
+ 				len = BUFFER_SIZE/2048;
+ 				if (lba+len > p->lba_len)
+ 					len = p->lba_len-lba;
+ 				p->buf1 = p->buf0 + len*4;
+ 				for (try=0; try<TRIES; try++)
+ 					if (read_data(d,p->buffer,lba,len) == 0)
+ 						break;
+ 				if (try == TRIES) {
+ 					printk("Panasonic CD-ROM - Read error - block %d\n",lba);
+ 					p->buf0 = p->buf1 = 0;
+ 					end_request(0);
+ 					return;
+ 				}
+ 			}
+ 			memcpy(CURRENT->buffer,p->buffer+(block-p->buf0)*512,512);
+ 			block += 1;
+ 			nsect -= 1;
+ 			CURRENT->buffer += 512;
+ 		}
+ 		end_request(1);
+ 	}
+ }
+ 
+ static int
+ copy_toc_entry(int d,struct cdrom_tocentry *te)
+ {
+ 	struct drive_info_struct *p = drive_info+d;
+ 	struct toc_entry *e;
+ 
+ 	if (te->cdte_track == CDROM_LEADOUT) {
+ 		te->cdte_addr.msf.minute = p->head.m;
+ 		te->cdte_addr.msf.second = p->head.s;
+ 		te->cdte_addr.msf.frame = p->head.f;
+ 	}
+ 	else {
+ 		if (te->cdte_track < p->head.t0)
+ 			return -EINVAL;
+ 		if (te->cdte_track > p->head.t1)
+ 			return -EINVAL;
+ 		e = p->entry+te->cdte_track;
+ 		te->cdte_addr.msf.minute = e->m;
+ 		te->cdte_addr.msf.second = e->s;
+ 		te->cdte_addr.msf.frame = e->f;
+ 	}
+ 	if (te->cdte_format == CDROM_LBA)
+ 		msf_to_lba_2(&te->cdte_addr);
+ 	return 0;
+ }
+ 
+ static int
+ read_subchannel(int d,struct cdrom_subchnl *sc)
+ {
+ 	struct sub_chan t;
+ 
+ 	if (simple_command(d,0x87,&t,11) < 0)
+ 		return -EIO;
+ 	sc->cdsc_trk = t.t;
+ 	sc->cdsc_ind = t.i;
+ 	sc->cdsc_absaddr.msf.minute = t.am;
+ 	sc->cdsc_absaddr.msf.second = t.as;
+ 	sc->cdsc_absaddr.msf.frame = t.af;
+ 	sc->cdsc_reladdr.msf.minute = t.rm;
+ 	sc->cdsc_reladdr.msf.second = t.rs;
+ 	sc->cdsc_reladdr.msf.frame = t.rf;
+ 	if (sc->cdsc_format == CDROM_LBA) {
+ 		msf_to_lba_2(&sc->cdsc_absaddr);
+ 		msf_to_lba_2(&sc->cdsc_reladdr);
+ 	}
+ 	return 0;
+ }
+ 
+ static int
+ panasonic_ioctl(struct inode *inode,struct file *file,unsigned cmd,unsigned arg)
+ {
+ 	int d,ret;
+ 
+ 	if (!inode)
+ 		return -EINVAL;
+ 	d = MINOR(inode->i_rdev) >> 6;
+ 	if (drive_info[d].okay == 0)
+ 		return -ENXIO;
+ 	if (ret = do_nop(d))
+ 		return ret;
+ 	if (ret = read_toc_etc(d))
+ 		return ret;
+ 	switch (cmd) {
+ 		case CDROMSTART:
+ 			return 0;
+ 		case CDROMSTOP:
+ 			return 0;
+ 		case CDROMPLAYMSF: {
+ 			struct cdrom_msf msf;
+ 
+ 			verify_area(VERIFY_READ,(char *)arg,sizeof(struct cdrom_msf));
+ 			memcpy_fromfs(&msf,(void *)arg,sizeof(struct cdrom_msf));
+ 			return play_audio_msf(d,&msf);
+ 		}
+ 		case CDROMPLAYTRKIND: {
+ 			struct cdrom_ti ti;
+ 
+ 			verify_area(VERIFY_READ,(char *)arg,sizeof(struct cdrom_ti));
+ 			memcpy_fromfs(&ti,(void *)arg,sizeof(struct cdrom_ti));
+ 			return play_audio_ti(d,&ti);
+ 		}
+ 		case CDROMPAUSE:
+ 			return pause_playback(d);
+ 		case CDROMRESUME:
+ 			return resume_playback(d);
+ 		case CDROMREADTOCHDR: {
+ 			struct cdrom_tochdr tc;
+ 
+ 			verify_area(VERIFY_WRITE,(char *)arg,sizeof(struct cdrom_tochdr));
+ 			tc.cdth_trk0 = drive_info[d].head.t0;
+ 			tc.cdth_trk1 = drive_info[d].head.t1;
+ 			memcpy_tofs((void *)arg,&tc,sizeof(struct cdrom_tochdr));
+ 			return 0;
+ 		}
+ 		case CDROMREADTOCENTRY: {
+ 			struct cdrom_tocentry te;
+ 
+ 			verify_area(VERIFY_READ,(char *)arg,sizeof(struct cdrom_tocentry));
+ 			verify_area(VERIFY_WRITE,(char *)arg,sizeof(struct cdrom_tocentry));
+ 			memcpy_fromfs(&te,(void *)arg,sizeof(struct cdrom_tocentry));
+ 			ret = copy_toc_entry(d,&te);
+ 			memcpy_tofs((void *)arg,&te,sizeof(struct cdrom_tocentry));
+ 			return ret;
+ 		}
+ 		case CDROMSUBCHNL: {
+ 			struct cdrom_subchnl sc;
+ 
+ 			verify_area(VERIFY_READ,(char *)arg,sizeof(struct cdrom_subchnl));
+ 			verify_area(VERIFY_WRITE,(char *)arg,sizeof(struct cdrom_subchnl));
+ 			memcpy_fromfs(&sc,(void *)arg,sizeof(struct cdrom_subchnl));
+ 			ret = read_subchannel(d,&sc);
+ 			memcpy_tofs((void *)arg,&sc,sizeof(struct cdrom_subchnl));
+ 			return ret;
+ 		}
+ 		case CDROMVOLCTRL:
+ 			return 0;
+ 		case CDROMEJECT:
+ 			note_changed(d);
+ 			unlock_tray(d);
+ 			zero_drive_info(d);
+ 			sync_dev(inode->i_rdev);
+ 			invalidate_buffers(inode->i_rdev);
+ 			return open_tray(d);
+ 		default:
+ 			return -EINVAL;
+ 	}
+ }
+ 
+ static int
+ panasonic_open(struct inode *inode,struct file *filp)
+ {
+ 	int d,ret;
+ 
+ 	if (!inode)
+ 		return -EINVAL;
+ 	d = MINOR(inode->i_rdev) >> 6;
+ 	if (drive_info[d].okay == 0)
+ 		return -ENXIO;
+ 	if (ret = do_nop(d))
+ 		return ret;
+ 	if (ret = read_toc_etc(d))
+ 		return ret;
+ 	check_disk_change(inode->i_rdev);
+ 	drive_info[d].nopen++;
+ 	return 0;
+ }
+ 
+ static void
+ panasonic_release(struct inode *inode,struct file *filp)
+ {
+ 	int d;
+ 
+ 	if (!inode)
+ 		return;
+ 	d = MINOR(inode->i_rdev) >> 6;
+ 	if (drive_info[d].okay == 0)
+ 		return;
+ 	if (drive_info[d].nopen)
+ 		--drive_info[d].nopen;
+ 	if (drive_info[d].nopen == 0) {
+ 		sync_dev(inode->i_rdev);
+ 		invalidate_buffers(inode->i_rdev);
+ 		unlock_tray(d);
+ 	}
+ }
+ 
+ static struct file_operations panasonic_fops = {
+ 	NULL,			/* lseek */
+ 	block_read,		/* read */
+ 	block_write,		/* write */
+ 	NULL,			/* readdir */
+ 	NULL,			/* select */
+ 	panasonic_ioctl,	/* ioctl */
+ 	NULL,			/* mmap */
+ 	panasonic_open,		/* open */
+ 	panasonic_release	/* release */
+ };
+ 
+ unsigned long
+ panasonic_whatnot_init(unsigned long mem_start,unsigned long mem_end)
+ {
+ 	struct drive_info_struct *p;
+ 	int d,i,found = 0;
+ 
+ 	memset(drive_info,0,sizeof(drive_info));
+ 	for (i=0; ports[i]&&!found; i++) {
+ 		port = ports[i];
+ 		for (d=0; d<3; d++) {
+ 			p = drive_info+d;
+ 			if (!detect_drive(d))
+ 				continue;
+ 			printk("Detected Panasonic CD-ROM at 0x%x number %d (/dev/panasonic-cd%d).\n",port,d,d);
+ 			if (!found) {
+ 				if (register_blkdev(MAJOR_NR,"panasonic_whatnot",&panasonic_fops)) {
+ 					printk("Unable to get major %d for Panasonic CD-ROM\n",MAJOR_NR);
+ 					return mem_start;
+ 				}
+ 				found = 1;
+ 			}
+ 			p->okay = 1;
+ 			p->changed2 = 1;
+ 			p->nopen = 0;
+ 			p->buffer = (unsigned char *)mem_start;
+ 			mem_start += BUFFER_SIZE;
+ 			p->buf0 = p->buf1 = 0;
+ 			blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ 			read_ahead[MAJOR_NR] = 8;
+ 		}
+ 	}
+ 	for (i=0; i<150; i++)
+ 		outb(0,port+2);
+ 	return mem_start;
+ }
+ 
+ #endif

-- 
Send submissions for comp.os.linux.announce to: [email-masked]