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>
67 #include <asm/system.h>
69 #include <asm/uaccess.h>
71 #define MAJOR_NR GOLDSTAR_CDROM_MAJOR
72 #include <linux/blkdev.h>
75 static int gscdPresent = 0;
77 static unsigned char gscd_buf[2048]; /* buffer for block size conversion */
78 static int gscd_bn = -1;
79 static short gscd_port = GSCD_BASE_ADDR;
80 module_param_named(gscd, gscd_port, short, 0);
82 /* Kommt spaeter vielleicht noch mal dran ...
83 * static DECLARE_WAIT_QUEUE_HEAD(gscd_waitq);
86 static void gscd_read_cmd(struct request *req);
87 static void gscd_hsg2msf(long hsg, struct msf *msf);
88 static void gscd_bin2bcd(unsigned char *p);
90 /* Schnittstellen zum Kern/FS */
92 static void __do_gscd_request(unsigned long dummy);
93 static int gscd_ioctl(struct inode *, struct file *, unsigned int,
95 static int gscd_open(struct inode *, struct file *);
96 static int gscd_release(struct inode *, struct file *);
97 static int check_gscd_med_chg(struct gendisk *disk);
99 /* GoldStar Funktionen */
101 static void cmd_out(int, char *, char *, int);
102 static void cmd_status(void);
103 static void init_cd_drive(int);
105 static int get_status(void);
106 static void clear_Audio(void);
107 static void cc_invalidate(void);
109 /* some things for the next version */
111 static void update_state(void);
112 static long gscd_msf2hsg(struct msf *mp);
113 static int gscd_bcd2bin(unsigned char bcd);
117 /* lo-level cmd-Funktionen */
119 static void cmd_info_in(char *, int);
120 static void cmd_end(void);
121 static void cmd_read_b(char *, int, int);
122 static void cmd_read_w(char *, int, int);
123 static int cmd_unit_alive(void);
124 static void cmd_write_cmd(char *);
127 /* GoldStar Variablen */
129 static int curr_drv_state;
130 static int drv_states[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
132 static int disk_state;
136 static unsigned char drv_num_read;
137 static unsigned char f_dsk_valid;
138 static unsigned char current_drive;
139 static unsigned char f_drv_ok;
142 static char f_AudioPlay;
143 static char f_AudioPause;
144 static int AudioStart_m;
145 static int AudioStart_f;
146 static int AudioEnd_m;
147 static int AudioEnd_f;
149 static DEFINE_TIMER(gscd_timer, NULL, 0, 0);
150 static DEFINE_SPINLOCK(gscd_lock);
151 static struct request_queue *gscd_queue;
153 static struct block_device_operations gscd_fops = {
154 .owner = THIS_MODULE,
156 .release = gscd_release,
158 .media_changed = check_gscd_med_chg,
162 * Checking if the media has been changed
163 * (not yet implemented)
165 static int check_gscd_med_chg(struct gendisk *disk)
168 printk("gscd: check_med_change\n");
175 /* Using new interface for kernel-parameters */
177 static int __init gscd_setup(char *str)
180 (void) get_options(str, ARRAY_SIZE(ints), ints);
188 __setup("gscd=", gscd_setup);
192 static int gscd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
195 unsigned char to_do[10];
200 case CDROMSTART: /* Spin up the drive */
201 /* Don't think we can do this. Even if we could,
202 * I think the drive times out and stops after a while
203 * anyway. For now, ignore it.
207 case CDROMRESUME: /* keine Ahnung was das ist */
213 to_do[0] = CMD_TRAY_CTL;
214 cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
226 * Take care of the different block sizes between cdrom and Linux.
227 * When Linux gets variable block sizes this will probably go away.
230 static void gscd_transfer(struct request *req)
232 while (req->nr_sectors > 0 && gscd_bn == req->sector / 4) {
233 long offs = (req->sector & 3) * 512;
234 memcpy(req->buffer, gscd_buf + offs, 512);
243 * I/O request routine called from Linux kernel.
246 static void do_gscd_request(request_queue_t * q)
248 __do_gscd_request(0);
251 static void __do_gscd_request(unsigned long dummy)
258 req = elv_next_request(gscd_queue);
263 nsect = req->nr_sectors;
265 if (req->sector == -1)
268 if (req->cmd != READ) {
269 printk("GSCD: bad cmd %lu\n", rq_data_dir(req));
276 /* if we satisfied the request from the buffer, we're done. */
278 if (req->nr_sectors == 0) {
283 printk("GSCD: block %d, nsect %d\n", block, nsect);
293 * Check the result of the set-mode command. On success, send the
297 static void gscd_read_cmd(struct request *req)
300 struct gscd_Play_msf gscdcmd;
301 char cmd[] = { CMD_READ, 0x80, 0, 0, 0, 0, 1 }; /* cmd mode M-S-F secth sectl */
304 if (disk_state & (ST_NO_DISK | ST_DOOR_OPEN)) {
305 printk("GSCD: no disk or door open\n");
308 if (disk_state & ST_INVALID) {
309 printk("GSCD: disk invalid\n");
312 gscd_bn = -1; /* purge our buffer */
313 block = req->sector / 4;
314 gscd_hsg2msf(block, &gscdcmd.start); /* cvt to msf format */
316 cmd[2] = gscdcmd.start.min;
317 cmd[3] = gscdcmd.start.sec;
318 cmd[4] = gscdcmd.start.frame;
321 printk("GSCD: read msf %d:%d:%d\n", cmd[2], cmd[3],
324 cmd_out(TYPE_DATA, (char *) &cmd,
325 (char *) &gscd_buf[0], 1);
327 gscd_bn = req->sector / 4;
332 SET_TIMER(__do_gscd_request, 1);
337 * Open the device special file. Check that a disk is in.
340 static int gscd_open(struct inode *ip, struct file *fp)
345 printk("GSCD: open\n");
348 if (gscdPresent == 0)
349 return -ENXIO; /* no hardware */
352 st = disk_state & (ST_NO_DISK | ST_DOOR_OPEN);
354 printk("GSCD: no disk or door open\n");
358 /* if (updateToc() < 0)
367 * On close, we flush all gscd blocks from the buffer cache.
370 static int gscd_release(struct inode *inode, struct file *file)
374 printk("GSCD: release\n");
383 static int get_status(void)
388 status = disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01);
390 if (status == (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) {
399 static void cc_invalidate(void)
403 current_drive = 0xFF;
410 static void clear_Audio(void)
426 static int wait_drv_ready(void)
431 found = inb(GSCDPORT(0));
433 read = inb(GSCDPORT(0));
435 } while (read != found);
438 printk("Wait for: %d\n", read);
444 static void cc_Ident(char *respons)
446 char to_do[] = { CMD_IDENT, 0, 0 };
448 cmd_out(TYPE_INFO, (char *) &to_do, (char *) respons, (int) 0x1E);
452 static void cc_SetSpeed(void)
454 char to_do[] = { CMD_SETSPEED, 0, 0 };
458 to_do[1] = speed & 0x0F;
459 cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
463 static void cc_Reset(void)
465 char to_do[] = { CMD_RESET, 0 };
468 cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
471 static void cmd_status(void)
473 char to_do[] = { CMD_STATUS, 0 };
476 cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
479 printk("GSCD: Status: %d\n", disk_state);
484 static void cmd_out(int cmd_type, char *cmd, char *respo_buf, int respo_count)
489 result = wait_drv_ready();
490 if (result != drv_mode) {
491 unsigned long test_loops = 0xFFFF;
494 outb(curr_drv_state, GSCDPORT(0));
498 result = wait_drv_ready();
500 } while ((result != drv_mode) && (test_loops > 0));
502 if (result != drv_mode) {
503 disk_state = ST_x08 | ST_x04 | ST_INVALID;
508 for (i = 1, dummy = 1; i < 0xFFFF; i++) {
516 if (cmd_unit_alive() != 0x08) {
518 /* game over for this unit */
519 disk_state = ST_x08 | ST_x04 | ST_INVALID;
527 if (drv_mode == 0x09) {
529 printk("GSCD: magic ...\n");
530 outb(result, GSCDPORT(2));
533 /* write the command to the drive */
538 result = wait_drv_ready();
539 if (result != drv_mode) {
541 if (result == 0x04) { /* Mode 4 */
546 disk_state = inb(GSCDPORT(2));
549 result = wait_drv_ready();
550 } while (result != drv_mode);
554 if (result == 0x06) { /* Mode 6 */
560 if (cmd_type == TYPE_DATA) {
564 /* read the data to the buffer (word) */
566 /* (*(cmd+1))?(CD_FRAMESIZE/2):(CD_FRAMESIZE_RAW/2) */
574 /* read the data to the buffer (byte) */
576 /* (*(cmd+1))?(CD_FRAMESIZE):(CD_FRAMESIZE_RAW) */
584 /* read the info to the buffer */
585 cmd_info_in(respo_buf,
595 disk_state = ST_x08 | ST_x04 | ST_INVALID;
607 static void cmd_write_cmd(char *pstr)
616 /* calculate the number of parameter */
620 for (i = 0; i < j; i++) {
621 outb(*pstr, GSCDPORT(2));
627 static int cmd_unit_alive(void)
630 unsigned long max_test_loops;
638 outb(curr_drv_state, GSCDPORT(0));
639 max_test_loops = 0xFFFF;
642 result = wait_drv_ready();
644 } while ((result != 0x08) && (max_test_loops > 0));
650 static void cmd_info_in(char *pb, int count)
663 read = inb(GSCDPORT(2));
672 result = wait_drv_ready();
673 } while (result == 0x0E);
674 } while (result == 6);
681 static void cmd_read_b(char *pb, int count, int size)
695 result = wait_drv_ready();
696 } while (result != 6 || result == 0x0E);
706 for (i = 0; i < size; i++) {
707 *pb = inb(GSCDPORT(2));
718 static void cmd_end(void)
729 result = wait_drv_ready();
730 if (result == drv_mode) {
733 } while (result != 4);
740 disk_state = inb(GSCDPORT(2));
743 result = wait_drv_ready();
744 } while (result != drv_mode);
750 static void cmd_read_w(char *pb, int count, int size)
763 result = wait_drv_ready();
764 } while (result != 6 || result == 0x0E);
771 for (i = 0; i < size; i++) {
772 /* na, hier muss ich noch mal drueber nachdenken */
773 *pb = inw(GSCDPORT(2));
783 static int __init find_drives(void)
791 pdrv = (int *) &drv_states;
792 curr_drv_state = 0xFE;
796 for (i = 0; i < 8; i++) {
799 disk_state &= ST_x08 | ST_x04 | ST_INVALID | ST_x01;
800 if (disk_state != (ST_x08 | ST_x04 | ST_INVALID)) {
802 *pdrv = curr_drv_state;
803 init_cd_drive(drvnum);
814 /* curr_drv_state<<1; <-- das geht irgendwie nicht */
815 /* muss heissen: curr_drv_state <<= 1; (ist ja Wert-Zuweisung) */
819 printk("DriveState: %d\n", curr_drv_state);
827 static void __init init_cd_drive(int num)
832 printk("GSCD: init unit %d\n", num);
833 cc_Ident((char *) &resp);
835 printk("GSCD: identification: ");
836 for (i = 0; i < 0x1E; i++) {
837 printk("%c", resp[i]);
847 static void update_state(void)
852 if ((disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0) {
853 if (disk_state == (ST_x08 | ST_x04 | ST_INVALID)) {
857 if ((disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01))
866 if (disk_state & ST_PLAYING) {
878 static struct gendisk *gscd_disk;
880 static void __exit gscd_exit(void)
884 del_gendisk(gscd_disk);
886 if ((unregister_blkdev(MAJOR_NR, "gscd") == -EINVAL)) {
887 printk("What's that: can't unregister GoldStar-module\n");
890 blk_cleanup_queue(gscd_queue);
891 release_region(gscd_port, GSCD_IO_EXTENT);
892 printk(KERN_INFO "GoldStar-module released.\n");
895 /* This is the common initialisation for the GoldStar drive. */
896 /* It is called at boot time AND for module init. */
897 static int __init gscd_init(void)
903 printk(KERN_INFO "GSCD: version %s\n", GSCD_VERSION);
905 "GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n",
908 if (!request_region(gscd_port, GSCD_IO_EXTENT, "gscd")) {
909 printk(KERN_WARNING "GSCD: Init failed, I/O port (%X) already"
910 " in use.\n", gscd_port);
916 result = wait_drv_ready();
917 if (result == 0x09) {
918 printk(KERN_WARNING "GSCD: DMA kann ich noch nicht!\n");
923 if (result == 0x0b) {
927 printk(KERN_WARNING "GSCD: GoldStar CD-ROM Drive is"
934 if ((result != 0x0b) && (result != 0x09)) {
935 printk(KERN_WARNING "GSCD: GoldStar Interface Adapter does not "
936 "exist or H/W error\n");
941 /* reset all drives */
943 while (drv_states[i] != 0) {
944 curr_drv_state = drv_states[i];
945 printk(KERN_INFO "GSCD: Reset unit %d ... ", i);
951 gscd_disk = alloc_disk(1);
954 gscd_disk->major = MAJOR_NR;
955 gscd_disk->first_minor = 0;
956 gscd_disk->fops = &gscd_fops;
957 sprintf(gscd_disk->disk_name, "gscd");
959 if (register_blkdev(MAJOR_NR, "gscd")) {
964 gscd_queue = blk_init_queue(do_gscd_request, &gscd_lock);
973 gscd_disk->queue = gscd_queue;
976 printk(KERN_INFO "GSCD: GoldStar CD-ROM Drive found.\n");
980 unregister_blkdev(MAJOR_NR, "gscd");
984 release_region(gscd_port, GSCD_IO_EXTENT);
988 static void gscd_hsg2msf(long hsg, struct msf *msf)
990 hsg += CD_MSF_OFFSET;
991 msf->min = hsg / (CD_FRAMES * CD_SECS);
992 hsg %= CD_FRAMES * CD_SECS;
993 msf->sec = hsg / CD_FRAMES;
994 msf->frame = hsg % CD_FRAMES;
996 gscd_bin2bcd(&msf->min); /* convert to BCD */
997 gscd_bin2bcd(&msf->sec);
998 gscd_bin2bcd(&msf->frame);
1002 static void gscd_bin2bcd(unsigned char *p)
1013 static long gscd_msf2hsg(struct msf *mp)
1015 return gscd_bcd2bin(mp->frame)
1016 + gscd_bcd2bin(mp->sec) * CD_FRAMES
1017 + gscd_bcd2bin(mp->min) * CD_FRAMES * CD_SECS - CD_MSF_OFFSET;
1020 static int gscd_bcd2bin(unsigned char bcd)
1022 return (bcd >> 4) * 10 + (bcd & 0xF);
1026 MODULE_AUTHOR("Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>");
1027 MODULE_LICENSE("GPL");
1028 module_init(gscd_init);
1029 module_exit(gscd_exit);
1030 MODULE_ALIAS_BLOCKDEV_MAJOR(GOLDSTAR_CDROM_MAJOR);