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>
73 #define gscd_port gscd /* for compatible parameter passing with "insmod" */
76 static int gscdPresent
= 0;
78 static unsigned char gscd_buf
[2048]; /* buffer for block size conversion */
79 static int gscd_bn
= -1;
80 static short gscd_port
= GSCD_BASE_ADDR
;
81 MODULE_PARM(gscd
, "h");
83 /* Kommt spaeter vielleicht noch mal dran ...
84 * static DECLARE_WAIT_QUEUE_HEAD(gscd_waitq);
87 static void gscd_read_cmd(struct request
*req
);
88 static void gscd_hsg2msf(long hsg
, struct msf
*msf
);
89 static void gscd_bin2bcd(unsigned char *p
);
91 /* Schnittstellen zum Kern/FS */
93 static void __do_gscd_request(unsigned long dummy
);
94 static int gscd_ioctl(struct inode
*, struct file
*, unsigned int,
96 static int gscd_open(struct inode
*, struct file
*);
97 static int gscd_release(struct inode
*, struct file
*);
98 static int check_gscd_med_chg(struct gendisk
*disk
);
100 /* GoldStar Funktionen */
102 static void cmd_out(int, char *, char *, int);
103 static void cmd_status(void);
104 static void init_cd_drive(int);
106 static int get_status(void);
107 static void clear_Audio(void);
108 static void cc_invalidate(void);
110 /* some things for the next version */
112 static void update_state(void);
113 static long gscd_msf2hsg(struct msf
*mp
);
114 static int gscd_bcd2bin(unsigned char bcd
);
118 /* lo-level cmd-Funktionen */
120 static void cmd_info_in(char *, int);
121 static void cmd_end(void);
122 static void cmd_read_b(char *, int, int);
123 static void cmd_read_w(char *, int, int);
124 static int cmd_unit_alive(void);
125 static void cmd_write_cmd(char *);
128 /* GoldStar Variablen */
130 static int curr_drv_state
;
131 static int drv_states
[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
133 static int disk_state
;
137 static unsigned char drv_num_read
;
138 static unsigned char f_dsk_valid
;
139 static unsigned char current_drive
;
140 static unsigned char f_drv_ok
;
143 static char f_AudioPlay
;
144 static char f_AudioPause
;
145 static int AudioStart_m
;
146 static int AudioStart_f
;
147 static int AudioEnd_m
;
148 static int AudioEnd_f
;
150 static struct timer_list gscd_timer
= TIMER_INITIALIZER(NULL
, 0, 0);
151 static spinlock_t gscd_lock
= SPIN_LOCK_UNLOCKED
;
152 static struct request_queue
*gscd_queue
;
154 static struct block_device_operations gscd_fops
= {
155 .owner
= THIS_MODULE
,
157 .release
= gscd_release
,
159 .media_changed
= check_gscd_med_chg
,
163 * Checking if the media has been changed
164 * (not yet implemented)
166 static int check_gscd_med_chg(struct gendisk
*disk
)
169 printk("gscd: check_med_change\n");
176 /* Using new interface for kernel-parameters */
178 static int __init
gscd_setup(char *str
)
181 (void) get_options(str
, ARRAY_SIZE(ints
), ints
);
189 __setup("gscd=", gscd_setup
);
193 static int gscd_ioctl(struct inode
*ip
, struct file
*fp
, unsigned int cmd
,
196 unsigned char to_do
[10];
201 case CDROMSTART
: /* Spin up the drive */
202 /* Don't think we can do this. Even if we could,
203 * I think the drive times out and stops after a while
204 * anyway. For now, ignore it.
208 case CDROMRESUME
: /* keine Ahnung was das ist */
214 to_do
[0] = CMD_TRAY_CTL
;
215 cmd_out(TYPE_INFO
, (char *) &to_do
, (char *) &dummy
, 0);
227 * Take care of the different block sizes between cdrom and Linux.
228 * When Linux gets variable block sizes this will probably go away.
231 static void gscd_transfer(struct request
*req
)
233 while (req
->nr_sectors
> 0 && gscd_bn
== req
->sector
/ 4) {
234 long offs
= (req
->sector
& 3) * 512;
235 memcpy(req
->buffer
, gscd_buf
+ offs
, 512);
244 * I/O request routine called from Linux kernel.
247 static void do_gscd_request(request_queue_t
* q
)
249 __do_gscd_request(0);
252 static void __do_gscd_request(unsigned long dummy
)
259 req
= elv_next_request(gscd_queue
);
264 nsect
= req
->nr_sectors
;
266 if (req
->sector
== -1)
269 if (req
->cmd
!= READ
) {
270 printk("GSCD: bad cmd %lu\n", rq_data_dir(req
));
277 /* if we satisfied the request from the buffer, we're done. */
279 if (req
->nr_sectors
== 0) {
284 printk("GSCD: block %d, nsect %d\n", block
, nsect
);
294 * Check the result of the set-mode command. On success, send the
298 static void gscd_read_cmd(struct request
*req
)
301 struct gscd_Play_msf gscdcmd
;
302 char cmd
[] = { CMD_READ
, 0x80, 0, 0, 0, 0, 1 }; /* cmd mode M-S-F secth sectl */
305 if (disk_state
& (ST_NO_DISK
| ST_DOOR_OPEN
)) {
306 printk("GSCD: no disk or door open\n");
309 if (disk_state
& ST_INVALID
) {
310 printk("GSCD: disk invalid\n");
313 gscd_bn
= -1; /* purge our buffer */
314 block
= req
->sector
/ 4;
315 gscd_hsg2msf(block
, &gscdcmd
.start
); /* cvt to msf format */
317 cmd
[2] = gscdcmd
.start
.min
;
318 cmd
[3] = gscdcmd
.start
.sec
;
319 cmd
[4] = gscdcmd
.start
.frame
;
322 printk("GSCD: read msf %d:%d:%d\n", cmd
[2], cmd
[3],
325 cmd_out(TYPE_DATA
, (char *) &cmd
,
326 (char *) &gscd_buf
[0], 1);
328 gscd_bn
= req
->sector
/ 4;
333 SET_TIMER(__do_gscd_request
, 1);
338 * Open the device special file. Check that a disk is in.
341 static int gscd_open(struct inode
*ip
, struct file
*fp
)
346 printk("GSCD: open\n");
349 if (gscdPresent
== 0)
350 return -ENXIO
; /* no hardware */
353 st
= disk_state
& (ST_NO_DISK
| ST_DOOR_OPEN
);
355 printk("GSCD: no disk or door open\n");
359 /* if (updateToc() < 0)
368 * On close, we flush all gscd blocks from the buffer cache.
371 static int gscd_release(struct inode
*inode
, struct file
*file
)
375 printk("GSCD: release\n");
384 static int get_status(void)
389 status
= disk_state
& (ST_x08
| ST_x04
| ST_INVALID
| ST_x01
);
391 if (status
== (ST_x08
| ST_x04
| ST_INVALID
| ST_x01
)) {
400 static void cc_invalidate(void)
404 current_drive
= 0xFF;
411 static void clear_Audio(void)
427 static int wait_drv_ready(void)
432 found
= inb(GSCDPORT(0));
434 read
= inb(GSCDPORT(0));
436 } while (read
!= found
);
439 printk("Wait for: %d\n", read
);
445 static void cc_Ident(char *respons
)
447 char to_do
[] = { CMD_IDENT
, 0, 0 };
449 cmd_out(TYPE_INFO
, (char *) &to_do
, (char *) respons
, (int) 0x1E);
453 static void cc_SetSpeed(void)
455 char to_do
[] = { CMD_SETSPEED
, 0, 0 };
459 to_do
[1] = speed
& 0x0F;
460 cmd_out(TYPE_INFO
, (char *) &to_do
, (char *) &dummy
, 0);
464 static void cc_Reset(void)
466 char to_do
[] = { CMD_RESET
, 0 };
469 cmd_out(TYPE_INFO
, (char *) &to_do
, (char *) &dummy
, 0);
472 static void cmd_status(void)
474 char to_do
[] = { CMD_STATUS
, 0 };
477 cmd_out(TYPE_INFO
, (char *) &to_do
, (char *) &dummy
, 0);
480 printk("GSCD: Status: %d\n", disk_state
);
485 static void cmd_out(int cmd_type
, char *cmd
, char *respo_buf
, int respo_count
)
490 result
= wait_drv_ready();
491 if (result
!= drv_mode
) {
492 unsigned long test_loops
= 0xFFFF;
495 outb(curr_drv_state
, GSCDPORT(0));
499 result
= wait_drv_ready();
501 } while ((result
!= drv_mode
) && (test_loops
> 0));
503 if (result
!= drv_mode
) {
504 disk_state
= ST_x08
| ST_x04
| ST_INVALID
;
509 for (i
= 1, dummy
= 1; i
< 0xFFFF; i
++) {
517 if (cmd_unit_alive() != 0x08) {
519 /* game over for this unit */
520 disk_state
= ST_x08
| ST_x04
| ST_INVALID
;
528 if (drv_mode
== 0x09) {
530 printk("GSCD: magic ...\n");
531 outb(result
, GSCDPORT(2));
534 /* write the command to the drive */
539 result
= wait_drv_ready();
540 if (result
!= drv_mode
) {
542 if (result
== 0x04) { /* Mode 4 */
547 disk_state
= inb(GSCDPORT(2));
550 result
= wait_drv_ready();
551 } while (result
!= drv_mode
);
555 if (result
== 0x06) { /* Mode 6 */
561 if (cmd_type
== TYPE_DATA
) {
565 /* read the data to the buffer (word) */
567 /* (*(cmd+1))?(CD_FRAMESIZE/2):(CD_FRAMESIZE_RAW/2) */
575 /* read the data to the buffer (byte) */
577 /* (*(cmd+1))?(CD_FRAMESIZE):(CD_FRAMESIZE_RAW) */
585 /* read the info to the buffer */
586 cmd_info_in(respo_buf
,
596 disk_state
= ST_x08
| ST_x04
| ST_INVALID
;
608 static void cmd_write_cmd(char *pstr
)
617 /* calculate the number of parameter */
621 for (i
= 0; i
< j
; i
++) {
622 outb(*pstr
, GSCDPORT(2));
628 static int cmd_unit_alive(void)
631 unsigned long max_test_loops
;
639 outb(curr_drv_state
, GSCDPORT(0));
640 max_test_loops
= 0xFFFF;
643 result
= wait_drv_ready();
645 } while ((result
!= 0x08) && (max_test_loops
> 0));
651 static void cmd_info_in(char *pb
, int count
)
664 read
= inb(GSCDPORT(2));
673 result
= wait_drv_ready();
674 } while (result
== 0x0E);
675 } while (result
== 6);
682 static void cmd_read_b(char *pb
, int count
, int size
)
696 result
= wait_drv_ready();
697 } while (result
!= 6 || result
== 0x0E);
707 for (i
= 0; i
< size
; i
++) {
708 *pb
= inb(GSCDPORT(2));
719 static void cmd_end(void)
730 result
= wait_drv_ready();
731 if (result
== drv_mode
) {
734 } while (result
!= 4);
741 disk_state
= inb(GSCDPORT(2));
744 result
= wait_drv_ready();
745 } while (result
!= drv_mode
);
751 static void cmd_read_w(char *pb
, int count
, int size
)
764 result
= wait_drv_ready();
765 } while (result
!= 6 || result
== 0x0E);
772 for (i
= 0; i
< size
; i
++) {
773 /* na, hier muss ich noch mal drueber nachdenken */
774 *pb
= inw(GSCDPORT(2));
784 static int __init
find_drives(void)
792 pdrv
= (int *) &drv_states
;
793 curr_drv_state
= 0xFE;
797 for (i
= 0; i
< 8; i
++) {
800 disk_state
&= ST_x08
| ST_x04
| ST_INVALID
| ST_x01
;
801 if (disk_state
!= (ST_x08
| ST_x04
| ST_INVALID
)) {
803 *pdrv
= curr_drv_state
;
804 init_cd_drive(drvnum
);
815 /* curr_drv_state<<1; <-- das geht irgendwie nicht */
816 /* muss heissen: curr_drv_state <<= 1; (ist ja Wert-Zuweisung) */
820 printk("DriveState: %d\n", curr_drv_state
);
828 static void __init
init_cd_drive(int num
)
833 printk("GSCD: init unit %d\n", num
);
834 cc_Ident((char *) &resp
);
836 printk("GSCD: identification: ");
837 for (i
= 0; i
< 0x1E; i
++) {
838 printk("%c", resp
[i
]);
848 static void update_state(void)
853 if ((disk_state
& (ST_x08
| ST_x04
| ST_INVALID
| ST_x01
)) == 0) {
854 if (disk_state
== (ST_x08
| ST_x04
| ST_INVALID
)) {
858 if ((disk_state
& (ST_x08
| ST_x04
| ST_INVALID
| ST_x01
))
867 if (disk_state
& ST_PLAYING
) {
879 static struct gendisk
*gscd_disk
;
881 static void __exit
gscd_exit(void)
885 del_gendisk(gscd_disk
);
887 if ((unregister_blkdev(MAJOR_NR
, "gscd") == -EINVAL
)) {
888 printk("What's that: can't unregister GoldStar-module\n");
891 blk_cleanup_queue(gscd_queue
);
892 release_region(gscd_port
, GSCD_IO_EXTENT
);
893 printk(KERN_INFO
"GoldStar-module released.\n");
896 /* This is the common initialisation for the GoldStar drive. */
897 /* It is called at boot time AND for module init. */
898 static int __init
gscd_init(void)
904 printk(KERN_INFO
"GSCD: version %s\n", GSCD_VERSION
);
906 "GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n",
909 if (!request_region(gscd_port
, GSCD_IO_EXTENT
, "gscd")) {
910 printk(KERN_WARNING
"GSCD: Init failed, I/O port (%X) already"
911 " in use.\n", gscd_port
);
917 result
= wait_drv_ready();
918 if (result
== 0x09) {
919 printk(KERN_WARNING
"GSCD: DMA kann ich noch nicht!\n");
924 if (result
== 0x0b) {
928 printk(KERN_WARNING
"GSCD: GoldStar CD-ROM Drive is"
935 if ((result
!= 0x0b) && (result
!= 0x09)) {
936 printk(KERN_WARNING
"GSCD: GoldStar Interface Adapter does not "
937 "exist or H/W error\n");
942 /* reset all drives */
944 while (drv_states
[i
] != 0) {
945 curr_drv_state
= drv_states
[i
];
946 printk(KERN_INFO
"GSCD: Reset unit %d ... ", i
);
952 gscd_disk
= alloc_disk(1);
955 gscd_disk
->major
= MAJOR_NR
;
956 gscd_disk
->first_minor
= 0;
957 gscd_disk
->fops
= &gscd_fops
;
958 sprintf(gscd_disk
->disk_name
, "gscd");
959 sprintf(gscd_disk
->devfs_name
, "gscd");
961 if (register_blkdev(MAJOR_NR
, "gscd")) {
966 gscd_queue
= blk_init_queue(do_gscd_request
, &gscd_lock
);
975 gscd_disk
->queue
= gscd_queue
;
978 printk(KERN_INFO
"GSCD: GoldStar CD-ROM Drive found.\n");
982 unregister_blkdev(MAJOR_NR
, "gscd");
986 release_region(gscd_port
, GSCD_IO_EXTENT
);
990 static void gscd_hsg2msf(long hsg
, struct msf
*msf
)
992 hsg
+= CD_MSF_OFFSET
;
993 msf
->min
= hsg
/ (CD_FRAMES
* CD_SECS
);
994 hsg
%= CD_FRAMES
* CD_SECS
;
995 msf
->sec
= hsg
/ CD_FRAMES
;
996 msf
->frame
= hsg
% CD_FRAMES
;
998 gscd_bin2bcd(&msf
->min
); /* convert to BCD */
999 gscd_bin2bcd(&msf
->sec
);
1000 gscd_bin2bcd(&msf
->frame
);
1004 static void gscd_bin2bcd(unsigned char *p
)
1015 static long gscd_msf2hsg(struct msf
*mp
)
1017 return gscd_bcd2bin(mp
->frame
)
1018 + gscd_bcd2bin(mp
->sec
) * CD_FRAMES
1019 + gscd_bcd2bin(mp
->min
) * CD_FRAMES
* CD_SECS
- CD_MSF_OFFSET
;
1022 static int gscd_bcd2bin(unsigned char bcd
)
1024 return (bcd
>> 4) * 10 + (bcd
& 0xF);
1028 MODULE_AUTHOR("Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>");
1029 MODULE_LICENSE("GPL");
1030 module_init(gscd_init
);
1031 module_exit(gscd_exit
);
1032 MODULE_ALIAS_BLOCKDEV_MAJOR(GOLDSTAR_CDROM_MAJOR
);