1 #define GSCD_VERSION "0.4a Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>"
4 linux/drivers/block/gscd.c - GoldStar R420 CDROM driver
6 Copyright (C) 1995 Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>
7 based upon pre-works by Eberhard Moenkeberg <emoenke@gwdg.de>
10 For all kind of other information about the GoldStar CDROM
11 and this Linux device driver I installed a WWW-URL:
12 http://linux.rz.fh-hannover.de/~raupach
15 If you are the editor of a Linux CD, you should
16 enable gscd.c within your boot floppy kernel and
17 send me one of your CDs for free.
20 --------------------------------------------------------------------
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2, or (at your option)
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 --------------------------------------------------------------------
37 9 November 1999 -- Make kernel-parameter implementation work with 2.3.x
38 Removed init_module & cleanup_module in favor of
39 module_init & module_exit.
40 Torben Mathiasen <tmm@image.dk>
44 /* These settings are for various debug-level. Leave they untouched ... */
46 #define NO_IOCTL_DEBUG
47 #define NO_MODULE_DEBUG
48 #define NO_FUTURE_WORK
49 /*------------------------*/
51 #include <linux/module.h>
53 #include <linux/slab.h>
54 #include <linux/errno.h>
55 #include <linux/signal.h>
56 #include <linux/sched.h>
57 #include <linux/timer.h>
60 #include <linux/kernel.h>
61 #include <linux/cdrom.h>
62 #include <linux/ioport.h>
63 #include <linux/major.h>
64 #include <linux/string.h>
65 #include <linux/init.h>
66 #include <linux/devfs_fs_kernel.h>
68 #include <asm/system.h>
70 #include <asm/uaccess.h>
72 #define MAJOR_NR GOLDSTAR_CDROM_MAJOR
73 #include <linux/blk.h>
74 #define gscd_port gscd /* for compatible parameter passing with "insmod" */
77 static int gscd_blocksizes[1] = { 512 };
79 static int gscdPresent = 0;
81 static unsigned char gscd_buf[2048]; /* buffer for block size conversion */
82 static int gscd_bn = -1;
83 static short gscd_port = GSCD_BASE_ADDR;
84 MODULE_PARM(gscd, "h");
86 /* Kommt spaeter vielleicht noch mal dran ...
87 * static DECLARE_WAIT_QUEUE_HEAD(gscd_waitq);
90 static void gscd_transfer(void);
91 static void gscd_read_cmd(void);
92 static void gscd_hsg2msf(long hsg, struct msf *msf);
93 static void gscd_bin2bcd(unsigned char *p);
95 /* Schnittstellen zum Kern/FS */
97 static void do_gscd_request(request_queue_t *);
98 static void __do_gscd_request(unsigned long dummy);
99 static int gscd_ioctl(struct inode *, struct file *, unsigned int,
101 static int gscd_open(struct inode *, struct file *);
102 static int gscd_release(struct inode *, struct file *);
103 static int check_gscd_med_chg(kdev_t);
105 /* GoldStar Funktionen */
107 static void cc_Reset(void);
108 static int wait_drv_ready(void);
109 static int find_drives(void);
110 static void cmd_out(int, char *, char *, int);
111 static void cmd_status(void);
112 static void cc_Ident(char *);
113 static void cc_SetSpeed(void);
114 static void init_cd_drive(int);
116 static int get_status(void);
117 static void clear_Audio(void);
118 static void cc_invalidate(void);
120 /* some things for the next version */
122 static void update_state(void);
123 static long gscd_msf2hsg(struct msf *mp);
124 static int gscd_bcd2bin(unsigned char bcd);
127 /* common GoldStar Initialization */
129 static int my_gscd_init(void);
132 /* lo-level cmd-Funktionen */
134 static void cmd_info_in(char *, int);
135 static void cmd_end(void);
136 static void cmd_read_b(char *, int, int);
137 static void cmd_read_w(char *, int, int);
138 static int cmd_unit_alive(void);
139 static void cmd_write_cmd(char *);
142 /* GoldStar Variablen */
144 static int curr_drv_state;
145 static int drv_states[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
147 static int disk_state;
151 static unsigned char drv_num_read;
152 static unsigned char f_dsk_valid;
153 static unsigned char current_drive;
154 static unsigned char f_drv_ok;
157 static char f_AudioPlay;
158 static char f_AudioPause;
159 static int AudioStart_m;
160 static int AudioStart_f;
161 static int AudioEnd_m;
162 static int AudioEnd_f;
164 static struct timer_list gscd_timer;
166 static struct block_device_operations gscd_fops = {
169 release:gscd_release,
171 check_media_change:check_gscd_med_chg,
175 * Checking if the media has been changed
176 * (not yet implemented)
178 static int check_gscd_med_chg(kdev_t full_dev)
183 target = MINOR(full_dev);
187 ("GSCD: GoldStar CD-ROM request error: invalid device.\n");
191 printk("gscd: check_med_change\n");
199 /* Using new interface for kernel-parameters */
201 static int __init gscd_setup(char *str)
204 (void) get_options(str, ARRAY_SIZE(ints), ints);
212 __setup("gscd=", gscd_setup);
216 static int gscd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
219 unsigned char to_do[10];
224 case CDROMSTART: /* Spin up the drive */
225 /* Don't think we can do this. Even if we could,
226 * I think the drive times out and stops after a while
227 * anyway. For now, ignore it.
231 case CDROMRESUME: /* keine Ahnung was das ist */
237 to_do[0] = CMD_TRAY_CTL;
238 cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
250 * Take care of the different block sizes between cdrom and Linux.
251 * When Linux gets variable block sizes this will probably go away.
254 static void gscd_transfer(void)
258 while (CURRENT->nr_sectors > 0 && gscd_bn == CURRENT->sector / 4) {
259 offs = (CURRENT->sector & 3) * 512;
260 memcpy(CURRENT->buffer, gscd_buf + offs, 512);
261 CURRENT->nr_sectors--;
263 CURRENT->buffer += 512;
269 * I/O request routine called from Linux kernel.
272 static void do_gscd_request(request_queue_t * q)
274 __do_gscd_request(0);
277 static void __do_gscd_request(unsigned long dummy)
279 unsigned int block, dev;
283 if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE)
286 dev = MINOR(CURRENT->rq_dev);
287 block = CURRENT->sector;
288 nsect = CURRENT->nr_sectors;
290 if (QUEUE_EMPTY || CURRENT->sector == -1)
293 if (CURRENT->cmd != READ) {
294 printk("GSCD: bad cmd %d\n", CURRENT->cmd);
299 if (MINOR(CURRENT->rq_dev) != 0) {
300 printk("GSCD: this version supports only one device\n");
307 /* if we satisfied the request from the buffer, we're done. */
309 if (CURRENT->nr_sectors == 0) {
314 printk("GSCD: dev %d, block %d, nsect %d\n", dev, block, nsect);
325 * Check the result of the set-mode command. On success, send the
329 static void gscd_read_cmd(void)
332 struct gscd_Play_msf gscdcmd;
333 char cmd[] = { CMD_READ, 0x80, 0, 0, 0, 0, 1 }; /* cmd mode M-S-F secth sectl */
338 if (disk_state & (ST_NO_DISK | ST_DOOR_OPEN)) {
339 printk("GSCD: no disk or door open\n");
342 if (disk_state & ST_INVALID) {
343 printk("GSCD: disk invalid\n");
346 gscd_bn = -1; /* purge our buffer */
347 block = CURRENT->sector / 4;
348 gscd_hsg2msf(block, &gscdcmd.start); /* cvt to msf format */
350 cmd[2] = gscdcmd.start.min;
351 cmd[3] = gscdcmd.start.sec;
352 cmd[4] = gscdcmd.start.frame;
355 printk("GSCD: read msf %d:%d:%d\n", cmd[2], cmd[3],
358 cmd_out(TYPE_DATA, (char *) &cmd,
359 (char *) &gscd_buf[0], 1);
361 gscd_bn = CURRENT->sector / 4;
366 SET_TIMER(__do_gscd_request, 1);
371 * Open the device special file. Check that a disk is in.
374 static int gscd_open(struct inode *ip, struct file *fp)
379 printk("GSCD: open\n");
382 if (gscdPresent == 0)
383 return -ENXIO; /* no hardware */
386 st = disk_state & (ST_NO_DISK | ST_DOOR_OPEN);
388 printk("GSCD: no disk or door open\n");
392 /* if (updateToc() < 0)
401 * On close, we flush all gscd blocks from the buffer cache.
404 static int gscd_release(struct inode *inode, struct file *file)
408 printk("GSCD: release\n");
422 status = disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01);
424 if (status == (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) {
433 void cc_invalidate(void)
437 current_drive = 0xFF;
444 void clear_Audio(void)
460 int wait_drv_ready(void)
465 found = inb(GSCDPORT(0));
467 read = inb(GSCDPORT(0));
469 } while (read != found);
472 printk("Wait for: %d\n", read);
478 void cc_Ident(char *respons)
480 char to_do[] = { CMD_IDENT, 0, 0 };
482 cmd_out(TYPE_INFO, (char *) &to_do, (char *) respons, (int) 0x1E);
486 void cc_SetSpeed(void)
488 char to_do[] = { CMD_SETSPEED, 0, 0 };
492 to_do[1] = speed & 0x0F;
493 cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
500 char to_do[] = { CMD_RESET, 0 };
503 cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
508 void cmd_status(void)
510 char to_do[] = { CMD_STATUS, 0 };
513 cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
516 printk("GSCD: Status: %d\n", disk_state);
521 void cmd_out(int cmd_type, char *cmd, char *respo_buf, int respo_count)
526 result = wait_drv_ready();
527 if (result != drv_mode) {
528 unsigned long test_loops = 0xFFFF;
531 outb(curr_drv_state, GSCDPORT(0));
535 result = wait_drv_ready();
537 } while ((result != drv_mode) && (test_loops > 0));
539 if (result != drv_mode) {
540 disk_state = ST_x08 | ST_x04 | ST_INVALID;
545 for (i = 1, dummy = 1; i < 0xFFFF; i++) {
553 if (cmd_unit_alive() != 0x08) {
555 /* game over for this unit */
556 disk_state = ST_x08 | ST_x04 | ST_INVALID;
564 if (drv_mode == 0x09) {
566 printk("GSCD: magic ...\n");
567 outb(result, GSCDPORT(2));
570 /* write the command to the drive */
575 result = wait_drv_ready();
576 if (result != drv_mode) {
578 if (result == 0x04) { /* Mode 4 */
583 disk_state = inb(GSCDPORT(2));
586 result = wait_drv_ready();
587 } while (result != drv_mode);
591 if (result == 0x06) { /* Mode 6 */
597 if (cmd_type == TYPE_DATA) {
601 /* read the data to the buffer (word) */
603 /* (*(cmd+1))?(CD_FRAMESIZE/2):(CD_FRAMESIZE_RAW/2) */
611 /* read the data to the buffer (byte) */
613 /* (*(cmd+1))?(CD_FRAMESIZE):(CD_FRAMESIZE_RAW) */
621 /* read the info to the buffer */
622 cmd_info_in(respo_buf,
632 disk_state = ST_x08 | ST_x04 | ST_INVALID;
644 static void cmd_write_cmd(char *pstr)
653 /* calculate the number of parameter */
657 for (i = 0; i < j; i++) {
658 outb(*pstr, GSCDPORT(2));
664 static int cmd_unit_alive(void)
667 unsigned long max_test_loops;
675 outb(curr_drv_state, GSCDPORT(0));
676 max_test_loops = 0xFFFF;
679 result = wait_drv_ready();
681 } while ((result != 0x08) && (max_test_loops > 0));
687 static void cmd_info_in(char *pb, int count)
700 read = inb(GSCDPORT(2));
709 result = wait_drv_ready();
710 } while (result == 0x0E);
711 } while (result == 6);
718 static void cmd_read_b(char *pb, int count, int size)
732 result = wait_drv_ready();
733 } while (result != 6 || result == 0x0E);
743 for (i = 0; i < size; i++) {
744 *pb = inb(GSCDPORT(2));
755 static void cmd_end(void)
766 result = wait_drv_ready();
767 if (result == drv_mode) {
770 } while (result != 4);
777 disk_state = inb(GSCDPORT(2));
780 result = wait_drv_ready();
781 } while (result != drv_mode);
787 static void cmd_read_w(char *pb, int count, int size)
800 result = wait_drv_ready();
801 } while (result != 6 || result == 0x0E);
808 for (i = 0; i < size; i++) {
809 /* na, hier muss ich noch mal drueber nachdenken */
810 *pb = inw(GSCDPORT(2));
820 int __init find_drives(void)
828 pdrv = (int *) &drv_states;
829 curr_drv_state = 0xFE;
833 for (i = 0; i < 8; i++) {
836 disk_state &= ST_x08 | ST_x04 | ST_INVALID | ST_x01;
837 if (disk_state != (ST_x08 | ST_x04 | ST_INVALID)) {
839 *pdrv = curr_drv_state;
840 init_cd_drive(drvnum);
851 /* curr_drv_state<<1; <-- das geht irgendwie nicht */
852 /* muss heissen: curr_drv_state <<= 1; (ist ja Wert-Zuweisung) */
856 printk("DriveState: %d\n", curr_drv_state);
864 void __init init_cd_drive(int num)
869 printk("GSCD: init unit %d\n", num);
870 cc_Ident((char *) &resp);
872 printk("GSCD: identification: ");
873 for (i = 0; i < 0x1E; i++) {
874 printk("%c", resp[i]);
884 static void update_state(void)
889 if ((disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0) {
890 if (disk_state == (ST_x08 | ST_x04 | ST_INVALID)) {
894 if ((disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01))
903 if (disk_state & ST_PLAYING) {
915 /* Init for the Module-Version */
921 /* call the GoldStar-init */
922 err = my_gscd_init();
927 printk(KERN_INFO "Happy GoldStar !\n");
932 void __exit exit_gscd(void)
936 devfs_unregister(devfs_find_handle
937 (NULL, "gscd", 0, 0, DEVFS_SPECIAL_BLK, 0));
938 if ((devfs_unregister_blkdev(MAJOR_NR, "gscd") == -EINVAL)) {
939 printk("What's that: can't unregister GoldStar-module\n");
942 blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
943 release_region(gscd_port, 4);
944 printk(KERN_INFO "GoldStar-module released.\n");
948 module_init(init_gscd);
950 module_exit(exit_gscd);
953 /* Test for presence of drive and initialize it. Called only at boot time. */
954 int __init gscd_init(void)
956 return my_gscd_init();
960 /* This is the common initialisation for the GoldStar drive. */
961 /* It is called at boot time AND for module init. */
962 int __init my_gscd_init(void)
967 printk(KERN_INFO "GSCD: version %s\n", GSCD_VERSION);
969 "GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n",
972 if (check_region(gscd_port, 4)) {
974 ("GSCD: Init failed, I/O port (%X) already in use.\n",
981 result = wait_drv_ready();
982 if (result == 0x09) {
983 printk("GSCD: DMA kann ich noch nicht!\n");
987 if (result == 0x0b) {
992 ("GSCD: GoldStar CD-ROM Drive is not found.\n");
997 if ((result != 0x0b) && (result != 0x09)) {
999 ("GSCD: GoldStar Interface Adapter does not exist or H/W error\n");
1003 /* reset all drives */
1005 while (drv_states[i] != 0) {
1006 curr_drv_state = drv_states[i];
1007 printk(KERN_INFO "GSCD: Reset unit %d ... ", i);
1013 if (devfs_register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0) {
1015 ("GSCD: Unable to get major %d for GoldStar CD-ROM\n",
1019 devfs_register(NULL, "gscd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
1020 S_IFBLK | S_IRUGO | S_IWUGO, &gscd_fops, NULL);
1022 blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
1023 blksize_size[MAJOR_NR] = gscd_blocksizes;
1024 read_ahead[MAJOR_NR] = 4;
1029 request_region(gscd_port, 4, "gscd");
1030 register_disk(NULL, MKDEV(MAJOR_NR, 0), 1, &gscd_fops, 0);
1032 printk(KERN_INFO "GSCD: GoldStar CD-ROM Drive found.\n");
1036 static void gscd_hsg2msf(long hsg, struct msf *msf)
1038 hsg += CD_MSF_OFFSET;
1039 msf->min = hsg / (CD_FRAMES * CD_SECS);
1040 hsg %= CD_FRAMES * CD_SECS;
1041 msf->sec = hsg / CD_FRAMES;
1042 msf->frame = hsg % CD_FRAMES;
1044 gscd_bin2bcd(&msf->min); /* convert to BCD */
1045 gscd_bin2bcd(&msf->sec);
1046 gscd_bin2bcd(&msf->frame);
1050 static void gscd_bin2bcd(unsigned char *p)
1061 static long gscd_msf2hsg(struct msf *mp)
1063 return gscd_bcd2bin(mp->frame)
1064 + gscd_bcd2bin(mp->sec) * CD_FRAMES
1065 + gscd_bcd2bin(mp->min) * CD_FRAMES * CD_SECS - CD_MSF_OFFSET;
1068 static int gscd_bcd2bin(unsigned char bcd)
1070 return (bcd >> 4) * 10 + (bcd & 0xF);
1074 MODULE_AUTHOR("Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>");
1075 MODULE_LICENSE("GPL");