📄 sr.c
字号:
*((unsigned int*)buf+1) = 0x16; /* and receive 0x16 bytes */ cmd[0] = 0xde; cmd[1] = 0x03; cmd[2] = 0xb0; rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device, SCSI_IOCTL_SEND_COMMAND, buf); if (rc != 0) { if (rc != 0x28000002) /* drop "not ready" */ printk(KERN_WARNING"sr_photocd: ioctl error (NEC): 0x%x\n",rc); break; } if (rec[14] != 0 && rec[14] != 0xb0) { printk(KERN_INFO"sr_photocd: (NEC) Hmm, seems the CDROM doesn't support multisession CD's\n"); no_multi = 1; break; } min = (unsigned long) rec[15]/16*10 + (unsigned long) rec[15]%16; sec = (unsigned long) rec[16]/16*10 + (unsigned long) rec[16]%16; frame = (unsigned long) rec[17]/16*10 + (unsigned long) rec[17]%16; sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame; is_xa = (rec[14] == 0xb0);#ifdef DEBUG if (sector) { printk(KERN_DEBUG "sr_photocd: multisession CD detected. start: %lu\n",sector); }#endif break; case SCSI_MAN_TOSHIBA:#ifdef DEBUG printk(KERN_DEBUG "sr_photocd: use TOSHIBA code\n");#endif /* we request some disc information (is it a XA-CD ?, * where starts the last session ?) */ memset(buf,0,40); *((unsigned int*)buf) = (unsigned int) 0; *((unsigned int*)buf+1) = (unsigned int) 4; /* receive 4 bytes */ cmd[0] = (unsigned char) 0x00c7; cmd[1] = (unsigned char) 3; rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device, SCSI_IOCTL_SEND_COMMAND, buf); if (rc != 0) { if (rc == 0x28000002) { /* Got a "not ready" - error. No chance to find out if this is * because there is no CD in the drive or because the drive * don't knows multisession CD's. So I need to do an extra * check... */ if (!kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device, SCSI_IOCTL_TEST_UNIT_READY, NULL)) { printk(KERN_INFO "sr_photocd: (TOSHIBA) Hmm, seems the CDROM doesn't support multisession CD's\n"); no_multi = 1; } } else printk(KERN_INFO"sr_photocd: ioctl error (TOSHIBA #1): 0x%x\n",rc); break; /* if the first ioctl fails, we don't call the second one */ } is_xa = (rec[0] == 0x20); min = (unsigned long) rec[1]/16*10 + (unsigned long) rec[1]%16; sec = (unsigned long) rec[2]/16*10 + (unsigned long) rec[2]%16; frame = (unsigned long) rec[3]/16*10 + (unsigned long) rec[3]%16; sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame; if (sector) { sector -= CD_BLOCK_OFFSET;#ifdef DEBUG printk(KERN_DEBUG "sr_photocd: multisession CD detected: start: %lu\n",sector);#endif } /* now we do a get_density... */ memset(buf,0,40); *((unsigned int*)buf) = (unsigned int) 0; *((unsigned int*)buf+1) = (unsigned int) 12; cmd[0] = (unsigned char) MODE_SENSE; cmd[2] = (unsigned char) 1; cmd[4] = (unsigned char) 12; rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device, SCSI_IOCTL_SEND_COMMAND, buf); if (rc != 0) { printk(KERN_WARNING "sr_photocd: ioctl error (TOSHIBA #2): 0x%x\n",rc); break; }#ifdef DEBUG printk(KERN_DEBUG "sr_photocd: get_density: 0x%x\n",rec[4]);#endif /* ...and only if necessary a set_density */ if ((rec[4] != 0x81 && is_xa) || (rec[4] != 0 && !is_xa)) {#ifdef DEBUG printk(KERN_DEBUG "sr_photocd: doing set_density\n");#endif memset(buf,0,40); *((unsigned int*)buf) = (unsigned int) 12; /* send 12 bytes */ *((unsigned int*)buf+1) = (unsigned int) 0; cmd[0] = (unsigned char) MODE_SELECT; cmd[1] = (unsigned char) (1 << 4); cmd[4] = (unsigned char) 12; send = &cmd[6]; /* this is a 6-Byte command */ send[ 3] = (unsigned char) 0x08; /* data for cmd */ /* density 0x81 for XA, 0 else */ send[ 4] = (is_xa) ? (unsigned char) 0x81 : (unsigned char) 0; send[10] = (unsigned char) 0x08; rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device, SCSI_IOCTL_SEND_COMMAND, buf); if (rc != 0) { printk(KERN_WARNING "sr_photocd: ioctl error (TOSHIBA #3): 0x%x\n",rc); } /* The set_density command may have changed the * sector size or capacity. */ scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size = 1; } break; case SCSI_MAN_SONY: /* Thomas QUINOT <thomas@melchior.cuivre.fdn.fr> */ case SCSI_MAN_PIONEER: case SCSI_MAN_UNKNOWN:#ifdef DEBUG printk(KERN_DEBUG "sr_photocd: use SONY/PIONEER code\n");#endif get_sectorsize(MINOR(inode->i_rdev)); /* spinup (avoid timeout) */ memset(buf,0,40); *((unsigned int*)buf) = 0x0; /* we send nothing... */ *((unsigned int*)buf+1) = 0x0c; /* and receive 0x0c bytes */ cmd[0] = READ_TOC; cmd[8] = 0x0c; cmd[9] = 0x40; rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device, SCSI_IOCTL_SEND_COMMAND, buf); if (rc != 0) { if (rc != 0x28000002) /* drop "not ready" */ printk(KERN_WARNING "sr_photocd: ioctl error (SONY/PIONEER): 0x%x\n",rc); break; } if ((rec[0] << 8) + rec[1] < 0x0a) { printk(KERN_INFO "sr_photocd: (SONY/PIONEER) Hmm, seems the CDROM doesn't support multisession CD's\n"); no_multi = 1; break; } sector = rec[11] + (rec[10] << 8) + (rec[9] << 16) + (rec[8] << 24); is_xa = !!sector;#ifdef DEBUG if (sector) printk (KERN_DEBUG "sr_photocd: multisession CD detected. start: %lu\n",sector);#endif break; case SCSI_MAN_NEC_OLDCDR: default: sector = 0; no_multi = 1; break; } scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = sector; if (is_xa) scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x01; else scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01; if (no_multi) scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x02; return;}static int sr_open(struct inode * inode, struct file * filp){ if(MINOR(inode->i_rdev) >= sr_template.nr_dev || !scsi_CDs[MINOR(inode->i_rdev)].device) return -ENXIO; /* No such device */ if (filp->f_mode & 2) return -EROFS; if(sr_template.usage_count) (*sr_template.usage_count)++; sr_ioctl(inode,filp,CDROMCLOSETRAY,0); check_disk_change(inode->i_rdev); if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++) sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0); if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count) (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)++; sr_photocd(inode); /* If this device did not have media in the drive at boot time, then * we would have been unable to get the sector size. Check to see if * this is the case, and try again. */ if(scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size) get_sectorsize(MINOR(inode->i_rdev)); return 0;}/* * do_sr_request() is the request handler function for the sr driver. * Its function in life is to take block device requests, and * translate them to SCSI commands. */static void do_sr_request (void){ Scsi_Cmnd * SCpnt = NULL; struct request * req = NULL; Scsi_Device * SDev; unsigned long flags; int flag = 0; while (1==1){ save_flags(flags); cli(); if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) { restore_flags(flags); return; }; INIT_SCSI_REQUEST; SDev = scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device; /* * I am not sure where the best place to do this is. We need * to hook in a place where we are likely to come if in user * space. */ if( SDev->was_reset ) { /* * We need to relock the door, but we might * be in an interrupt handler. Only do this * from user space, since we do not want to * sleep from an interrupt. */ if( SDev->removable && !intr_count ) { scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0); } SDev->was_reset = 0; } if (flag++ == 0) SCpnt = allocate_device(&CURRENT, scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device, 0); else SCpnt = NULL; restore_flags(flags); /* This is a performance enhancement. We dig down into the request list and * try to find a queueable request (i.e. device not busy, and host able to * accept another command. If we find one, then we queue it. This can * make a big difference on systems with more than one disk drive. We want * to have the interrupts off when monkeying with the request list, because * otherwise the kernel might try to slip in a request in between somewhere. */ if (!SCpnt && sr_template.nr_dev > 1){ struct request *req1; req1 = NULL; save_flags(flags); cli(); req = CURRENT; while(req){ SCpnt = request_queueable(req, scsi_CDs[DEVICE_NR(req->rq_dev)].device); if(SCpnt) break; req1 = req; req = req->next; }; if (SCpnt && req->rq_status == RQ_INACTIVE) { if (req == CURRENT) CURRENT = CURRENT->next; else req1->next = req->next; }; restore_flags(flags); }; if (!SCpnt) return; /* Could not find anything to do */ wake_up(&wait_for_request); /* Queue command */ requeue_sr_request(SCpnt); }; /* While */} void requeue_sr_request (Scsi_Cmnd * SCpnt){ unsigned int dev, block, realcount; unsigned char cmd[10], *buffer, tries; int this_count, start, end_rec; tries = 2; repeat: if(!SCpnt || SCpnt->request.rq_status == RQ_INACTIVE) { do_sr_request(); return; } dev = MINOR(SCpnt->request.rq_dev); block = SCpnt->request.sector; buffer = NULL; this_count = 0; if (dev >= sr_template.nr_dev) { /* printk("CD-ROM request error: invalid device.\n"); */ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors); tries = 2; goto repeat; } if (!scsi_CDs[dev].use) { /* printk("CD-ROM request error: device marked not in use.\n"); */ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors); tries = 2; goto repeat; } if (scsi_CDs[dev].device->changed) { /* * quietly refuse to do anything to a changed disc * until the changed bit has been reset */ /* printk("CD-ROM has been changed. Prohibiting further I/O.\n"); */ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors); tries = 2; goto repeat; } switch (SCpnt->request.cmd) { case WRITE: SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors); goto repeat; break; case READ : cmd[0] = READ_6; break; default : panic ("Unknown sr command %d\n", SCpnt->request.cmd); } cmd[1] = (SCpnt->lun << 5) & 0xe0; /* * Now do the grungy work of figuring out which sectors we need, and * where in memory we are going to put them. * * The variables we need are: * * this_count= number of 512 byte sectors being read * block = starting cdrom sector to read. * realcount = # of cdrom sectors to read * * The major difference between a scsi disk and a scsi cdrom * is that we will always use scatter-gather if we can, because we can * work around the fact that the buffer cache has a block size of 1024, * and we have 2048 byte sectors. This code should work for buffers that * are any multiple of 512 bytes long. */ SCpnt->use_sg = 0; if (SCpnt->host->sg_tablesize > 0 && (!need_isa_buffer || dma_free_sectors >= 10)) { struct buffer_head * bh; struct scatterlist * sgpnt; int count, this_count_max; bh = SCpnt->request.bh; this_count = 0; count = 0; this_count_max = (scsi_CDs[dev].ten ? 0xffff : 0xff) << 4; /* Calculate how many links we can use. First see if we need * a padding record at the start */ this_count = SCpnt->request.sector % 4; if(this_count) count++; while(bh && count < SCpnt->host->sg_tablesize) { if ((this_count + (bh->b_size >> 9)) > this_count_max) break; this_count += (bh->b_size >> 9); count++; bh = bh->b_reqnext; }; /* Fix up in case of an odd record at the end */ end_rec = 0; if(this_count % 4) { if (count < SCpnt->host->sg_tablesize) { count++; end_rec = (4 - (this_count % 4)) << 9; this_count += 4 - (this_count % 4); } else { count--; this_count -= (this_count % 4); }; }; SCpnt->use_sg = count; /* Number of chains */ /* scsi_malloc can only allocate in chunks of 512 bytes */ count = (SCpnt->use_sg * sizeof(struct scatterlist) + 511) & ~511; SCpnt->sglist_len = count; sgpnt = (struct scatterlist * ) scsi_malloc(count); if (!sgpnt) { printk("Warning - running *really* short on DMA buffers\n"); SCpnt->use_sg = 0; /* No memory left - bail out */ } else { buffer = (unsigned char *) sgpnt; count = 0; bh = SCpnt->request.bh; if(SCpnt->request.sector % 4) { sgpnt[count].length = (SCpnt->request.sector % 4) << 9; sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length); if(!sgpnt[count].address) panic("SCSI DMA pool exhausted."); sgpnt[count].alt_address = sgpnt[count].address; /* Flag to delete if needed */ count++; }; for(bh = SCpnt->request.bh; count < SCpnt->use_sg; count++, bh = bh->b_reqnext) { if (bh) { /* Need a placeholder at the end of the record? */ sgpnt[count].address = bh->b_data; sgpnt[count].length = bh->b_size; sgpnt[count].alt_address = NULL; } else { sgpnt[count].address = (char *) scsi_malloc(end_rec); if(!sgpnt[count].address) panic("SCSI DMA pool exhausted."); sgpnt[count].length = end_rec; sgpnt[count].alt_address = sgpnt[count].address; if (count+1 != SCpnt->use_sg) panic("Bad sr request list"); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -