From gwoho@ucrmath.ucr.edu Wed Sep 22 00:46:54 EDT 1993 Article: 1156 of comp.os.linux.announce Path: samba.oit.unc.edu!bounce-bounce From: gwoho@ucrmath.ucr.edu (gwoho liu) Newsgroups: comp.os.linux.announce Subject: Driver for Panasonic 563B CD-ROM Followup-To: comp.os.linux.misc Date: 21 Sep 1993 22:20:33 GMT Organization: None Lines: 977 Approved: linux-announce@tc.cornell.edu (Matt Welsh) Message-ID: <27nunh$rdl@samba.oit.unc.edu> Reply-To: gwoho@ucrmath.ucr.edu (gwoho liu) NNTP-Posting-Host: calypso.oit.unc.edu Keywords: Panasonic 563B CD-ROM driver Originator: mdw@sunSITE 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. gwoho@ucrmath.ucr.edu + */ + + #include + + #ifdef CONFIG_PANASONIC_WHATNOT + + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #include + + #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 (blockbuf0 || 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; trybuffer,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: linux-announce@tc.cornell.edu