2005-04-16 15:20:36 -07:00
/*
pcd . c ( c ) 1997 - 8 Grant R . Guenther < grant @ torque . net >
Under the terms of the GNU General Public License .
This is a high - level driver for parallel port ATAPI CD - ROM
drives based on chips supported by the paride module .
By default , the driver will autoprobe for a single parallel
port ATAPI CD - ROM drive , but if their individual parameters are
specified , the driver can handle up to 4 drives .
The behaviour of the pcd driver can be altered by setting
some parameters from the insmod command line . The following
parameters are adjustable :
drive0 These four arguments can be arrays of
drive1 1 - 6 integers as follows :
drive2
drive3 < prt > , < pro > , < uni > , < mod > , < slv > , < dly >
Where ,
< prt > is the base of the parallel port address for
the corresponding drive . ( required )
< pro > is the protocol number for the adapter that
supports this drive . These numbers are
logged by ' paride ' when the protocol modules
are initialised . ( 0 if not given )
< uni > for those adapters that support chained
devices , this is the unit selector for the
chain of devices on the given port . It should
be zero for devices that don ' t support chaining .
( 0 if not given )
< mod > this can be - 1 to choose the best mode , or one
of the mode numbers supported by the adapter .
( - 1 if not given )
< slv > ATAPI CD - ROMs can be jumpered to master or slave .
Set this to 0 to choose the master drive , 1 to
choose the slave , - 1 ( the default ) to choose the
first drive found .
< dly > some parallel ports require the driver to
go more slowly . - 1 sets a default value that
should work with the chosen protocol . Otherwise ,
set this to a small integer , the larger it is
the slower the port i / o . In some cases , setting
this to zero will speed up the device . ( default - 1 )
major You may use this parameter to overide the
default major number ( 46 ) that this driver
will use . Be sure to change the device
name as well .
name This parameter is a character string that
contains the name the kernel will use for this
device ( in / proc output , for instance ) .
( default " pcd " )
verbose This parameter controls the amount of logging
that the driver will do . Set it to 0 for
normal operation , 1 to see autoprobe progress
messages , or 2 to see additional debugging
output . ( default 0 )
nice This parameter controls the driver ' s use of
idle CPU time , at the expense of some speed .
If this driver is built into the kernel , you can use kernel
the following command line parameters , with the same values
as the corresponding module parameters listed above :
pcd . drive0
pcd . drive1
pcd . drive2
pcd . drive3
pcd . nice
In addition , you can use the parameter pcd . disable to disable
the driver entirely .
*/
/* Changes:
1.01 GRG 1998.01 .24 Added test unit ready support
1.02 GRG 1998.05 .06 Changes to pcd_completion , ready_wait ,
and loosen interpretation of ATAPI
standard for clearing error status .
Use spinlocks . Eliminate sti ( ) .
1.03 GRG 1998.06 .16 Eliminated an Ugh
1.04 GRG 1998.08 .15 Added extra debugging , improvements to
pcd_completion , use HZ in loop timing
1.05 GRG 1998.08 .16 Conformed to " Uniform CD-ROM " standard
1.06 GRG 1998.08 .19 Added audio ioctl support
1.07 GRG 1998.09 .24 Increased reset timeout , added jumbo support
*/
# define PCD_VERSION "1.07"
# define PCD_MAJOR 46
# define PCD_NAME "pcd"
# define PCD_UNITS 4
/* Here are things one can override from the insmod command.
Most are autoprobed by paride unless set here . Verbose is off
by default .
*/
static int verbose = 0 ;
static int major = PCD_MAJOR ;
static char * name = PCD_NAME ;
static int nice = 0 ;
static int disable = 0 ;
static int drive0 [ 6 ] = { 0 , 0 , 0 , - 1 , - 1 , - 1 } ;
static int drive1 [ 6 ] = { 0 , 0 , 0 , - 1 , - 1 , - 1 } ;
static int drive2 [ 6 ] = { 0 , 0 , 0 , - 1 , - 1 , - 1 } ;
static int drive3 [ 6 ] = { 0 , 0 , 0 , - 1 , - 1 , - 1 } ;
static int ( * drives [ 4 ] ) [ 6 ] = { & drive0 , & drive1 , & drive2 , & drive3 } ;
static int pcd_drive_count ;
enum { D_PRT , D_PRO , D_UNI , D_MOD , D_SLV , D_DLY } ;
/* end of parameters */
# include <linux/module.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/cdrom.h>
# include <linux/spinlock.h>
# include <linux/blkdev.h>
# include <asm/uaccess.h>
2007-04-23 14:41:07 -07:00
static DEFINE_SPINLOCK ( pcd_lock ) ;
2005-04-16 15:20:36 -07:00
module_param ( verbose , bool , 0644 ) ;
module_param ( major , int , 0 ) ;
module_param ( name , charp , 0 ) ;
module_param ( nice , int , 0 ) ;
module_param_array ( drive0 , int , NULL , 0 ) ;
module_param_array ( drive1 , int , NULL , 0 ) ;
module_param_array ( drive2 , int , NULL , 0 ) ;
module_param_array ( drive3 , int , NULL , 0 ) ;
# include "paride.h"
# include "pseudo.h"
# define PCD_RETRIES 5
# define PCD_TMO 800 /* timeout in jiffies */
# define PCD_DELAY 50 /* spin delay in uS */
# define PCD_READY_TMO 20 /* in seconds */
# define PCD_RESET_TMO 100 /* in tenths of a second */
# define PCD_SPIN (1000000*PCD_TMO) / (HZ*PCD_DELAY)
# define IDE_ERR 0x01
# define IDE_DRQ 0x08
# define IDE_READY 0x40
# define IDE_BUSY 0x80
static int pcd_open ( struct cdrom_device_info * cdi , int purpose ) ;
static void pcd_release ( struct cdrom_device_info * cdi ) ;
static int pcd_drive_status ( struct cdrom_device_info * cdi , int slot_nr ) ;
static int pcd_media_changed ( struct cdrom_device_info * cdi , int slot_nr ) ;
static int pcd_tray_move ( struct cdrom_device_info * cdi , int position ) ;
static int pcd_lock_door ( struct cdrom_device_info * cdi , int lock ) ;
static int pcd_drive_reset ( struct cdrom_device_info * cdi ) ;
static int pcd_get_mcn ( struct cdrom_device_info * cdi , struct cdrom_mcn * mcn ) ;
static int pcd_audio_ioctl ( struct cdrom_device_info * cdi ,
unsigned int cmd , void * arg ) ;
static int pcd_packet ( struct cdrom_device_info * cdi ,
struct packet_command * cgc ) ;
static int pcd_detect ( void ) ;
static void pcd_probe_capabilities ( void ) ;
static void do_pcd_read_drq ( void ) ;
2007-07-24 09:28:11 +02:00
static void do_pcd_request ( struct request_queue * q ) ;
2005-04-16 15:20:36 -07:00
static void do_pcd_read ( void ) ;
struct pcd_unit {
struct pi_adapter pia ; /* interface to paride layer */
struct pi_adapter * pi ;
int drive ; /* master/slave */
int last_sense ; /* result of last request sense */
int changed ; /* media change seen */
int present ; /* does this unit exist ? */
char * name ; /* pcd0, pcd1, etc */
struct cdrom_device_info info ; /* uniform cdrom interface */
struct gendisk * disk ;
} ;
static struct pcd_unit pcd [ PCD_UNITS ] ;
static char pcd_scratch [ 64 ] ;
static char pcd_buffer [ 2048 ] ; /* raw block buffer */
static int pcd_bufblk = - 1 ; /* block in buffer, in CD units,
- 1 for nothing there . See also
pd_unit .
*/
/* the variables below are used mainly in the I/O request engine, which
processes only one request at a time .
*/
static struct pcd_unit * pcd_current ; /* current request's drive */
static struct request * pcd_req ;
static int pcd_retries ; /* retries on current request */
static int pcd_busy ; /* request being processed ? */
static int pcd_sector ; /* address of next requested sector */
static int pcd_count ; /* number of blocks still to do */
static char * pcd_buf ; /* buffer for request in progress */
/* kernel glue structures */
2008-03-02 09:35:06 -05:00
static int pcd_block_open ( struct block_device * bdev , fmode_t mode )
2005-04-16 15:20:36 -07:00
{
2008-03-02 09:35:06 -05:00
struct pcd_unit * cd = bdev - > bd_disk - > private_data ;
return cdrom_open ( & cd - > info , bdev , mode ) ;
2005-04-16 15:20:36 -07:00
}
2008-03-02 09:35:06 -05:00
static int pcd_block_release ( struct gendisk * disk , fmode_t mode )
2005-04-16 15:20:36 -07:00
{
2008-03-02 09:35:06 -05:00
struct pcd_unit * cd = disk - > private_data ;
cdrom_release ( & cd - > info , mode ) ;
2007-10-07 17:54:28 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2008-03-02 09:35:06 -05:00
static int pcd_block_ioctl ( struct block_device * bdev , fmode_t mode ,
2005-04-16 15:20:36 -07:00
unsigned cmd , unsigned long arg )
{
2008-03-02 09:35:06 -05:00
struct pcd_unit * cd = bdev - > bd_disk - > private_data ;
return cdrom_ioctl ( & cd - > info , bdev , mode , cmd , arg ) ;
2005-04-16 15:20:36 -07:00
}
static int pcd_block_media_changed ( struct gendisk * disk )
{
struct pcd_unit * cd = disk - > private_data ;
return cdrom_media_changed ( & cd - > info ) ;
}
2009-09-21 17:01:13 -07:00
static const struct block_device_operations pcd_bdops = {
2005-04-16 15:20:36 -07:00
. owner = THIS_MODULE ,
2008-03-02 09:35:06 -05:00
. open = pcd_block_open ,
. release = pcd_block_release ,
. locked_ioctl = pcd_block_ioctl ,
2005-04-16 15:20:36 -07:00
. media_changed = pcd_block_media_changed ,
} ;
static struct cdrom_device_ops pcd_dops = {
. open = pcd_open ,
. release = pcd_release ,
. drive_status = pcd_drive_status ,
. media_changed = pcd_media_changed ,
. tray_move = pcd_tray_move ,
. lock_door = pcd_lock_door ,
. get_mcn = pcd_get_mcn ,
. reset = pcd_drive_reset ,
. audio_ioctl = pcd_audio_ioctl ,
. generic_packet = pcd_packet ,
. capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK |
CDC_MCN | CDC_MEDIA_CHANGED | CDC_RESET |
CDC_PLAY_AUDIO | CDC_GENERIC_PACKET | CDC_CD_R |
CDC_CD_RW ,
} ;
static void pcd_init_units ( void )
{
struct pcd_unit * cd ;
int unit ;
pcd_drive_count = 0 ;
for ( unit = 0 , cd = pcd ; unit < PCD_UNITS ; unit + + , cd + + ) {
struct gendisk * disk = alloc_disk ( 1 ) ;
if ( ! disk )
continue ;
cd - > disk = disk ;
cd - > pi = & cd - > pia ;
cd - > present = 0 ;
cd - > last_sense = 0 ;
cd - > changed = 1 ;
cd - > drive = ( * drives [ unit ] ) [ D_SLV ] ;
if ( ( * drives [ unit ] ) [ D_PRT ] )
pcd_drive_count + + ;
cd - > name = & cd - > info . name [ 0 ] ;
snprintf ( cd - > name , sizeof ( cd - > info . name ) , " %s%d " , name , unit ) ;
cd - > info . ops = & pcd_dops ;
cd - > info . handle = cd ;
cd - > info . speed = 0 ;
cd - > info . capacity = 1 ;
cd - > info . mask = 0 ;
disk - > major = major ;
disk - > first_minor = unit ;
strcpy ( disk - > disk_name , cd - > name ) ; /* umm... */
disk - > fops = & pcd_bdops ;
}
}
static int pcd_open ( struct cdrom_device_info * cdi , int purpose )
{
struct pcd_unit * cd = cdi - > handle ;
if ( ! cd - > present )
return - ENODEV ;
return 0 ;
}
static void pcd_release ( struct cdrom_device_info * cdi )
{
}
static inline int status_reg ( struct pcd_unit * cd )
{
return pi_read_regr ( cd - > pi , 1 , 6 ) ;
}
static inline int read_reg ( struct pcd_unit * cd , int reg )
{
return pi_read_regr ( cd - > pi , 0 , reg ) ;
}
static inline void write_reg ( struct pcd_unit * cd , int reg , int val )
{
pi_write_regr ( cd - > pi , 0 , reg , val ) ;
}
static int pcd_wait ( struct pcd_unit * cd , int go , int stop , char * fun , char * msg )
{
int j , r , e , s , p ;
j = 0 ;
while ( ( ( ( r = status_reg ( cd ) ) & go ) | | ( stop & & ( ! ( r & stop ) ) ) )
& & ( j + + < PCD_SPIN ) )
udelay ( PCD_DELAY ) ;
if ( ( r & ( IDE_ERR & stop ) ) | | ( j > = PCD_SPIN ) ) {
s = read_reg ( cd , 7 ) ;
e = read_reg ( cd , 1 ) ;
p = read_reg ( cd , 2 ) ;
if ( j > = PCD_SPIN )
e | = 0x100 ;
if ( fun )
printk ( " %s: %s %s: alt=0x%x stat=0x%x err=0x%x "
" loop=%d phase=%d \n " ,
cd - > name , fun , msg , r , s , e , j , p ) ;
return ( s < < 8 ) + r ;
}
return 0 ;
}
static int pcd_command ( struct pcd_unit * cd , char * cmd , int dlen , char * fun )
{
pi_connect ( cd - > pi ) ;
write_reg ( cd , 6 , 0xa0 + 0x10 * cd - > drive ) ;
if ( pcd_wait ( cd , IDE_BUSY | IDE_DRQ , 0 , fun , " before command " ) ) {
pi_disconnect ( cd - > pi ) ;
return - 1 ;
}
write_reg ( cd , 4 , dlen % 256 ) ;
write_reg ( cd , 5 , dlen / 256 ) ;
write_reg ( cd , 7 , 0xa0 ) ; /* ATAPI packet command */
if ( pcd_wait ( cd , IDE_BUSY , IDE_DRQ , fun , " command DRQ " ) ) {
pi_disconnect ( cd - > pi ) ;
return - 1 ;
}
if ( read_reg ( cd , 2 ) ! = 1 ) {
printk ( " %s: %s: command phase error \n " , cd - > name , fun ) ;
pi_disconnect ( cd - > pi ) ;
return - 1 ;
}
pi_write_block ( cd - > pi , cmd , 12 ) ;
return 0 ;
}
static int pcd_completion ( struct pcd_unit * cd , char * buf , char * fun )
{
int r , d , p , n , k , j ;
r = - 1 ;
k = 0 ;
j = 0 ;
if ( ! pcd_wait ( cd , IDE_BUSY , IDE_DRQ | IDE_READY | IDE_ERR ,
fun , " completion " ) ) {
r = 0 ;
while ( read_reg ( cd , 7 ) & IDE_DRQ ) {
d = read_reg ( cd , 4 ) + 256 * read_reg ( cd , 5 ) ;
n = ( d + 3 ) & 0xfffc ;
p = read_reg ( cd , 2 ) & 3 ;
if ( ( p = = 2 ) & & ( n > 0 ) & & ( j = = 0 ) ) {
pi_read_block ( cd - > pi , buf , n ) ;
if ( verbose > 1 )
printk ( " %s: %s: Read %d bytes \n " ,
cd - > name , fun , n ) ;
r = 0 ;
j + + ;
} else {
if ( verbose > 1 )
printk
( " %s: %s: Unexpected phase %d, d=%d, k=%d \n " ,
cd - > name , fun , p , d , k ) ;
2009-08-24 10:56:38 +02:00
if ( verbose < 2 )
printk_once (
" %s: WARNING: ATAPI phase errors \n " ,
cd - > name ) ;
2005-04-16 15:20:36 -07:00
mdelay ( 1 ) ;
}
if ( k + + > PCD_TMO ) {
printk ( " %s: Stuck DRQ \n " , cd - > name ) ;
break ;
}
if ( pcd_wait
( cd , IDE_BUSY , IDE_DRQ | IDE_READY | IDE_ERR , fun ,
" completion " ) ) {
r = - 1 ;
break ;
}
}
}
pi_disconnect ( cd - > pi ) ;
return r ;
}
static void pcd_req_sense ( struct pcd_unit * cd , char * fun )
{
char rs_cmd [ 12 ] = { 0x03 , 0 , 0 , 0 , 16 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
char buf [ 16 ] ;
int r , c ;
r = pcd_command ( cd , rs_cmd , 16 , " Request sense " ) ;
mdelay ( 1 ) ;
if ( ! r )
pcd_completion ( cd , buf , " Request sense " ) ;
cd - > last_sense = - 1 ;
c = 2 ;
if ( ! r ) {
if ( fun )
printk ( " %s: %s: Sense key: %x, ASC: %x, ASQ: %x \n " ,
cd - > name , fun , buf [ 2 ] & 0xf , buf [ 12 ] , buf [ 13 ] ) ;
c = buf [ 2 ] & 0xf ;
cd - > last_sense =
c | ( ( buf [ 12 ] & 0xff ) < < 8 ) | ( ( buf [ 13 ] & 0xff ) < < 16 ) ;
}
if ( ( c = = 2 ) | | ( c = = 6 ) )
cd - > changed = 1 ;
}
static int pcd_atapi ( struct pcd_unit * cd , char * cmd , int dlen , char * buf , char * fun )
{
int r ;
r = pcd_command ( cd , cmd , dlen , fun ) ;
mdelay ( 1 ) ;
if ( ! r )
r = pcd_completion ( cd , buf , fun ) ;
if ( r )
pcd_req_sense ( cd , fun ) ;
return r ;
}
static int pcd_packet ( struct cdrom_device_info * cdi , struct packet_command * cgc )
{
return pcd_atapi ( cdi - > handle , cgc - > cmd , cgc - > buflen , cgc - > buffer ,
" generic packet " ) ;
}
# define DBMSG(msg) ((verbose>1)?(msg):NULL)
static int pcd_media_changed ( struct cdrom_device_info * cdi , int slot_nr )
{
struct pcd_unit * cd = cdi - > handle ;
int res = cd - > changed ;
if ( res )
cd - > changed = 0 ;
return res ;
}
static int pcd_lock_door ( struct cdrom_device_info * cdi , int lock )
{
char un_cmd [ 12 ] = { 0x1e , 0 , 0 , 0 , lock , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
return pcd_atapi ( cdi - > handle , un_cmd , 0 , pcd_scratch ,
lock ? " lock door " : " unlock door " ) ;
}
static int pcd_tray_move ( struct cdrom_device_info * cdi , int position )
{
char ej_cmd [ 12 ] = { 0x1b , 0 , 0 , 0 , 3 - position , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
return pcd_atapi ( cdi - > handle , ej_cmd , 0 , pcd_scratch ,
position ? " eject " : " close tray " ) ;
}
static void pcd_sleep ( int cs )
{
2005-09-10 00:27:28 -07:00
schedule_timeout_interruptible ( cs ) ;
2005-04-16 15:20:36 -07:00
}
static int pcd_reset ( struct pcd_unit * cd )
{
int i , k , flg ;
int expect [ 5 ] = { 1 , 1 , 1 , 0x14 , 0xeb } ;
pi_connect ( cd - > pi ) ;
write_reg ( cd , 6 , 0xa0 + 0x10 * cd - > drive ) ;
write_reg ( cd , 7 , 8 ) ;
pcd_sleep ( 20 * HZ / 1000 ) ; /* delay a bit */
k = 0 ;
while ( ( k + + < PCD_RESET_TMO ) & & ( status_reg ( cd ) & IDE_BUSY ) )
pcd_sleep ( HZ / 10 ) ;
flg = 1 ;
for ( i = 0 ; i < 5 ; i + + )
flg & = ( read_reg ( cd , i + 1 ) = = expect [ i ] ) ;
if ( verbose ) {
printk ( " %s: Reset (%d) signature = " , cd - > name , k ) ;
for ( i = 0 ; i < 5 ; i + + )
printk ( " %3x " , read_reg ( cd , i + 1 ) ) ;
if ( ! flg )
printk ( " (incorrect) " ) ;
printk ( " \n " ) ;
}
pi_disconnect ( cd - > pi ) ;
return flg - 1 ;
}
static int pcd_drive_reset ( struct cdrom_device_info * cdi )
{
return pcd_reset ( cdi - > handle ) ;
}
static int pcd_ready_wait ( struct pcd_unit * cd , int tmo )
{
char tr_cmd [ 12 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
int k , p ;
k = 0 ;
while ( k < tmo ) {
cd - > last_sense = 0 ;
pcd_atapi ( cd , tr_cmd , 0 , NULL , DBMSG ( " test unit ready " ) ) ;
p = cd - > last_sense ;
if ( ! p )
return 0 ;
if ( ! ( ( ( p & 0xffff ) = = 0x0402 ) | | ( ( p & 0xff ) = = 6 ) ) )
return p ;
k + + ;
pcd_sleep ( HZ ) ;
}
return 0x000020 ; /* timeout */
}
static int pcd_drive_status ( struct cdrom_device_info * cdi , int slot_nr )
{
char rc_cmd [ 12 ] = { 0x25 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
struct pcd_unit * cd = cdi - > handle ;
if ( pcd_ready_wait ( cd , PCD_READY_TMO ) )
return CDS_DRIVE_NOT_READY ;
if ( pcd_atapi ( cd , rc_cmd , 8 , pcd_scratch , DBMSG ( " check media " ) ) )
return CDS_NO_DISC ;
return CDS_DISC_OK ;
}
static int pcd_identify ( struct pcd_unit * cd , char * id )
{
int k , s ;
char id_cmd [ 12 ] = { 0x12 , 0 , 0 , 0 , 36 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
pcd_bufblk = - 1 ;
s = pcd_atapi ( cd , id_cmd , 36 , pcd_buffer , " identify " ) ;
if ( s )
return - 1 ;
if ( ( pcd_buffer [ 0 ] & 0x1f ) ! = 5 ) {
if ( verbose )
printk ( " %s: %s is not a CD-ROM \n " ,
cd - > name , cd - > drive ? " Slave " : " Master " ) ;
return - 1 ;
}
memcpy ( id , pcd_buffer + 16 , 16 ) ;
id [ 16 ] = 0 ;
k = 16 ;
while ( ( k > = 0 ) & & ( id [ k ] < = 0x20 ) ) {
id [ k ] = 0 ;
k - - ;
}
printk ( " %s: %s: %s \n " , cd - > name , cd - > drive ? " Slave " : " Master " , id ) ;
return 0 ;
}
/*
* returns 0 , with id set if drive is detected
* - 1 , if drive detection failed
*/
static int pcd_probe ( struct pcd_unit * cd , int ms , char * id )
{
if ( ms = = - 1 ) {
for ( cd - > drive = 0 ; cd - > drive < = 1 ; cd - > drive + + )
if ( ! pcd_reset ( cd ) & & ! pcd_identify ( cd , id ) )
return 0 ;
} else {
cd - > drive = ms ;
if ( ! pcd_reset ( cd ) & & ! pcd_identify ( cd , id ) )
return 0 ;
}
return - 1 ;
}
static void pcd_probe_capabilities ( void )
{
int unit , r ;
char buffer [ 32 ] ;
char cmd [ 12 ] = { 0x5a , 1 < < 3 , 0x2a , 0 , 0 , 0 , 0 , 18 , 0 , 0 , 0 , 0 } ;
struct pcd_unit * cd ;
for ( unit = 0 , cd = pcd ; unit < PCD_UNITS ; unit + + , cd + + ) {
if ( ! cd - > present )
continue ;
r = pcd_atapi ( cd , cmd , 18 , buffer , " mode sense capabilities " ) ;
if ( r )
continue ;
/* we should now have the cap page */
if ( ( buffer [ 11 ] & 1 ) = = 0 )
cd - > info . mask | = CDC_CD_R ;
if ( ( buffer [ 11 ] & 2 ) = = 0 )
cd - > info . mask | = CDC_CD_RW ;
if ( ( buffer [ 12 ] & 1 ) = = 0 )
cd - > info . mask | = CDC_PLAY_AUDIO ;
if ( ( buffer [ 14 ] & 1 ) = = 0 )
cd - > info . mask | = CDC_LOCK ;
if ( ( buffer [ 14 ] & 8 ) = = 0 )
cd - > info . mask | = CDC_OPEN_TRAY ;
if ( ( buffer [ 14 ] > > 6 ) = = 0 )
cd - > info . mask | = CDC_CLOSE_TRAY ;
}
}
static int pcd_detect ( void )
{
char id [ 18 ] ;
int k , unit ;
struct pcd_unit * cd ;
printk ( " %s: %s version %s, major %d, nice %d \n " ,
name , name , PCD_VERSION , major , nice ) ;
k = 0 ;
if ( pcd_drive_count = = 0 ) { /* nothing spec'd - so autoprobe for 1 */
cd = pcd ;
if ( pi_init ( cd - > pi , 1 , - 1 , - 1 , - 1 , - 1 , - 1 , pcd_buffer ,
PI_PCD , verbose , cd - > name ) ) {
if ( ! pcd_probe ( cd , - 1 , id ) & & cd - > disk ) {
cd - > present = 1 ;
k + + ;
} else
pi_release ( cd - > pi ) ;
}
} else {
for ( unit = 0 , cd = pcd ; unit < PCD_UNITS ; unit + + , cd + + ) {
int * conf = * drives [ unit ] ;
if ( ! conf [ D_PRT ] )
continue ;
if ( ! pi_init ( cd - > pi , 0 , conf [ D_PRT ] , conf [ D_MOD ] ,
conf [ D_UNI ] , conf [ D_PRO ] , conf [ D_DLY ] ,
pcd_buffer , PI_PCD , verbose , cd - > name ) )
continue ;
if ( ! pcd_probe ( cd , conf [ D_SLV ] , id ) & & cd - > disk ) {
cd - > present = 1 ;
k + + ;
} else
pi_release ( cd - > pi ) ;
}
}
if ( k )
return 0 ;
printk ( " %s: No CD-ROM drive found \n " , name ) ;
for ( unit = 0 , cd = pcd ; unit < PCD_UNITS ; unit + + , cd + + )
put_disk ( cd - > disk ) ;
return - 1 ;
}
/* I/O request processing */
static struct request_queue * pcd_queue ;
2007-07-24 09:28:11 +02:00
static void do_pcd_request ( struct request_queue * q )
2005-04-16 15:20:36 -07:00
{
if ( pcd_busy )
return ;
while ( 1 ) {
2009-05-08 11:54:06 +09:00
if ( ! pcd_req ) {
2009-05-08 11:54:16 +09:00
pcd_req = blk_fetch_request ( q ) ;
2009-05-08 11:54:06 +09:00
if ( ! pcd_req )
return ;
}
2005-04-16 15:20:36 -07:00
if ( rq_data_dir ( pcd_req ) = = READ ) {
struct pcd_unit * cd = pcd_req - > rq_disk - > private_data ;
if ( cd ! = pcd_current )
pcd_bufblk = - 1 ;
pcd_current = cd ;
2009-05-07 22:24:39 +09:00
pcd_sector = blk_rq_pos ( pcd_req ) ;
pcd_count = blk_rq_cur_sectors ( pcd_req ) ;
2005-04-16 15:20:36 -07:00
pcd_buf = pcd_req - > buffer ;
pcd_busy = 1 ;
ps_set_intr ( do_pcd_read , NULL , 0 , nice ) ;
return ;
2009-05-08 11:54:06 +09:00
} else {
__blk_end_request_all ( pcd_req , - EIO ) ;
pcd_req = NULL ;
}
2005-04-16 15:20:36 -07:00
}
}
2009-04-23 11:05:19 +09:00
static inline void next_request ( int err )
2005-04-16 15:20:36 -07:00
{
unsigned long saved_flags ;
spin_lock_irqsave ( & pcd_lock , saved_flags ) ;
2009-05-08 11:54:06 +09:00
if ( ! __blk_end_request_cur ( pcd_req , err ) )
pcd_req = NULL ;
2005-04-16 15:20:36 -07:00
pcd_busy = 0 ;
do_pcd_request ( pcd_queue ) ;
spin_unlock_irqrestore ( & pcd_lock , saved_flags ) ;
}
static int pcd_ready ( void )
{
return ( ( ( status_reg ( pcd_current ) & ( IDE_BUSY | IDE_DRQ ) ) = = IDE_DRQ ) ) ;
}
static void pcd_transfer ( void )
{
while ( pcd_count & & ( pcd_sector / 4 = = pcd_bufblk ) ) {
int o = ( pcd_sector % 4 ) * 512 ;
memcpy ( pcd_buf , pcd_buffer + o , 512 ) ;
pcd_count - - ;
pcd_buf + = 512 ;
pcd_sector + + ;
}
}
static void pcd_start ( void )
{
int b , i ;
char rd_cmd [ 12 ] = { 0xa8 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 } ;
pcd_bufblk = pcd_sector / 4 ;
b = pcd_bufblk ;
for ( i = 0 ; i < 4 ; i + + ) {
rd_cmd [ 5 - i ] = b & 0xff ;
b = b > > 8 ;
}
if ( pcd_command ( pcd_current , rd_cmd , 2048 , " read block " ) ) {
pcd_bufblk = - 1 ;
2009-04-23 11:05:19 +09:00
next_request ( - EIO ) ;
2005-04-16 15:20:36 -07:00
return ;
}
mdelay ( 1 ) ;
ps_set_intr ( do_pcd_read_drq , pcd_ready , PCD_TMO , nice ) ;
}
static void do_pcd_read ( void )
{
pcd_busy = 1 ;
pcd_retries = 0 ;
pcd_transfer ( ) ;
if ( ! pcd_count ) {
2009-04-23 11:05:19 +09:00
next_request ( 0 ) ;
2005-04-16 15:20:36 -07:00
return ;
}
pi_do_claimed ( pcd_current - > pi , pcd_start ) ;
}
static void do_pcd_read_drq ( void )
{
unsigned long saved_flags ;
if ( pcd_completion ( pcd_current , pcd_buffer , " read block " ) ) {
if ( pcd_retries < PCD_RETRIES ) {
mdelay ( 1 ) ;
pcd_retries + + ;
pi_do_claimed ( pcd_current - > pi , pcd_start ) ;
return ;
}
pcd_bufblk = - 1 ;
2009-04-23 11:05:19 +09:00
next_request ( - EIO ) ;
2005-04-16 15:20:36 -07:00
return ;
}
do_pcd_read ( ) ;
spin_lock_irqsave ( & pcd_lock , saved_flags ) ;
do_pcd_request ( pcd_queue ) ;
spin_unlock_irqrestore ( & pcd_lock , saved_flags ) ;
}
/* the audio_ioctl stuff is adapted from sr_ioctl.c */
static int pcd_audio_ioctl ( struct cdrom_device_info * cdi , unsigned int cmd , void * arg )
{
struct pcd_unit * cd = cdi - > handle ;
switch ( cmd ) {
case CDROMREADTOCHDR :
{
char cmd [ 12 ] =
{ GPCMD_READ_TOC_PMA_ATIP , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 12 ,
0 , 0 , 0 } ;
struct cdrom_tochdr * tochdr =
( struct cdrom_tochdr * ) arg ;
char buffer [ 32 ] ;
int r ;
r = pcd_atapi ( cd , cmd , 12 , buffer , " read toc header " ) ;
tochdr - > cdth_trk0 = buffer [ 2 ] ;
tochdr - > cdth_trk1 = buffer [ 3 ] ;
return r ? - EIO : 0 ;
}
case CDROMREADTOCENTRY :
{
char cmd [ 12 ] =
{ GPCMD_READ_TOC_PMA_ATIP , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 12 ,
0 , 0 , 0 } ;
struct cdrom_tocentry * tocentry =
( struct cdrom_tocentry * ) arg ;
unsigned char buffer [ 32 ] ;
int r ;
cmd [ 1 ] =
( tocentry - > cdte_format = = CDROM_MSF ? 0x02 : 0 ) ;
cmd [ 6 ] = tocentry - > cdte_track ;
r = pcd_atapi ( cd , cmd , 12 , buffer , " read toc entry " ) ;
tocentry - > cdte_ctrl = buffer [ 5 ] & 0xf ;
tocentry - > cdte_adr = buffer [ 5 ] > > 4 ;
tocentry - > cdte_datamode =
( tocentry - > cdte_ctrl & 0x04 ) ? 1 : 0 ;
if ( tocentry - > cdte_format = = CDROM_MSF ) {
tocentry - > cdte_addr . msf . minute = buffer [ 9 ] ;
tocentry - > cdte_addr . msf . second = buffer [ 10 ] ;
tocentry - > cdte_addr . msf . frame = buffer [ 11 ] ;
} else
tocentry - > cdte_addr . lba =
( ( ( ( ( buffer [ 8 ] < < 8 ) + buffer [ 9 ] ) < < 8 )
+ buffer [ 10 ] ) < < 8 ) + buffer [ 11 ] ;
return r ? - EIO : 0 ;
}
default :
return - ENOSYS ;
}
}
static int pcd_get_mcn ( struct cdrom_device_info * cdi , struct cdrom_mcn * mcn )
{
char cmd [ 12 ] =
{ GPCMD_READ_SUBCHANNEL , 0 , 0x40 , 2 , 0 , 0 , 0 , 0 , 24 , 0 , 0 , 0 } ;
char buffer [ 32 ] ;
if ( pcd_atapi ( cdi - > handle , cmd , 24 , buffer , " get mcn " ) )
return - EIO ;
memcpy ( mcn - > medium_catalog_number , buffer + 9 , 13 ) ;
mcn - > medium_catalog_number [ 13 ] = 0 ;
return 0 ;
}
static int __init pcd_init ( void )
{
struct pcd_unit * cd ;
int unit ;
if ( disable )
2006-12-06 20:36:43 -08:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
pcd_init_units ( ) ;
if ( pcd_detect ( ) )
2006-12-06 20:36:43 -08:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
/* get the atapi capabilities page */
pcd_probe_capabilities ( ) ;
if ( register_blkdev ( major , name ) ) {
for ( unit = 0 , cd = pcd ; unit < PCD_UNITS ; unit + + , cd + + )
put_disk ( cd - > disk ) ;
2006-12-06 20:36:43 -08:00
return - EBUSY ;
2005-04-16 15:20:36 -07:00
}
pcd_queue = blk_init_queue ( do_pcd_request , & pcd_lock ) ;
if ( ! pcd_queue ) {
unregister_blkdev ( major , name ) ;
for ( unit = 0 , cd = pcd ; unit < PCD_UNITS ; unit + + , cd + + )
put_disk ( cd - > disk ) ;
2006-12-06 20:36:43 -08:00
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
}
for ( unit = 0 , cd = pcd ; unit < PCD_UNITS ; unit + + , cd + + ) {
if ( cd - > present ) {
register_cdrom ( & cd - > info ) ;
cd - > disk - > private_data = cd ;
cd - > disk - > queue = pcd_queue ;
add_disk ( cd - > disk ) ;
}
}
return 0 ;
}
static void __exit pcd_exit ( void )
{
struct pcd_unit * cd ;
int unit ;
for ( unit = 0 , cd = pcd ; unit < PCD_UNITS ; unit + + , cd + + ) {
if ( cd - > present ) {
del_gendisk ( cd - > disk ) ;
pi_release ( cd - > pi ) ;
unregister_cdrom ( & cd - > info ) ;
}
put_disk ( cd - > disk ) ;
}
blk_cleanup_queue ( pcd_queue ) ;
unregister_blkdev ( major , name ) ;
}
MODULE_LICENSE ( " GPL " ) ;
module_init ( pcd_init )
module_exit ( pcd_exit )