📄 cdrom.c
字号:
return ret;
/* changed since last call? */
if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
cdi->mc_flags = 0x3; /* set bit on both queues */
ret |= 1;
}
cdi->mc_flags &= ~mask; /* clear bit */
return ret;
}
int cdrom_media_changed(kdev_t dev)
{
struct cdrom_device_info *cdi = cdrom_find_device(dev);
/* This talks to the VFS, which doesn't like errors - just 1 or 0.
* Returning "0" is always safe (media hasn't been changed). Do that
* if the low-level cdrom driver dosn't support media changed. */
if (cdi == NULL || cdi->ops->media_changed == NULL)
return 0;
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
return 0;
return media_changed(cdi, 0);
}
/* badly broken, I know. Is due for a fixup anytime. */
void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype* tracks)
{
struct cdrom_tochdr header;
struct cdrom_tocentry entry;
int ret, i;
tracks->data=0;
tracks->audio=0;
tracks->cdi=0;
tracks->xa=0;
tracks->error=0;
cdinfo(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n");
if (!CDROM_CAN(CDC_PLAY_AUDIO)) {
tracks->error=CDS_NO_INFO;
return;
}
/* Grab the TOC header so we can see how many tracks there are */
if ((ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header))) {
if (ret == -ENOMEDIUM)
tracks->error = CDS_NO_DISC;
else
tracks->error = CDS_NO_INFO;
return;
}
/* check what type of tracks are on this disc */
entry.cdte_format = CDROM_MSF;
for (i = header.cdth_trk0; i <= header.cdth_trk1; i++) {
entry.cdte_track = i;
if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry)) {
tracks->error=CDS_NO_INFO;
return;
}
if (entry.cdte_ctrl & CDROM_DATA_TRACK) {
if (entry.cdte_format == 0x10)
tracks->cdi++;
else if (entry.cdte_format == 0x20)
tracks->xa++;
else
tracks->data++;
} else
tracks->audio++;
cdinfo(CD_COUNT_TRACKS, "track %d: format=%d, ctrl=%d\n",
i, entry.cdte_format, entry.cdte_ctrl);
}
cdinfo(CD_COUNT_TRACKS, "disc has %d tracks: %d=audio %d=data %d=Cd-I %d=XA\n",
header.cdth_trk1, tracks->audio, tracks->data,
tracks->cdi, tracks->xa);
}
/* Requests to the low-level drivers will /always/ be done in the
following format convention:
CDROM_LBA: all data-related requests.
CDROM_MSF: all audio-related requests.
However, a low-level implementation is allowed to refuse this
request, and return information in its own favorite format.
It doesn't make sense /at all/ to ask for a play_audio in LBA
format, or ask for multi-session info in MSF format. However, for
backward compatibility these format requests will be satisfied, but
the requests to the low-level drivers will be sanitized in the more
meaningful format indicated above.
*/
static
void sanitize_format(union cdrom_addr *addr,
u_char * curr, u_char requested)
{
if (*curr == requested)
return; /* nothing to be done! */
if (requested == CDROM_LBA) {
addr->lba = (int) addr->msf.frame +
75 * (addr->msf.second - 2 + 60 * addr->msf.minute);
} else { /* CDROM_MSF */
int lba = addr->lba;
addr->msf.frame = lba % 75;
lba /= 75;
lba += 2;
addr->msf.second = lba % 60;
addr->msf.minute = lba / 60;
}
*curr = requested;
}
void init_cdrom_command(struct cdrom_generic_command *cgc, void *buf, int len,
int type)
{
memset(cgc, 0, sizeof(struct cdrom_generic_command));
if (buf)
memset(buf, 0, len);
cgc->buffer = (char *) buf;
cgc->buflen = len;
cgc->data_direction = type;
cgc->timeout = 5*HZ;
}
/* DVD handling */
#define copy_key(dest,src) memcpy((dest), (src), sizeof(dvd_key))
#define copy_chal(dest,src) memcpy((dest), (src), sizeof(dvd_challenge))
static void setup_report_key(struct cdrom_generic_command *cgc, unsigned agid, unsigned type)
{
cgc->cmd[0] = GPCMD_REPORT_KEY;
cgc->cmd[10] = type | (agid << 6);
switch (type) {
case 0: case 8: case 5: {
cgc->buflen = 8;
break;
}
case 1: {
cgc->buflen = 16;
break;
}
case 2: case 4: {
cgc->buflen = 12;
break;
}
}
cgc->cmd[9] = cgc->buflen;
cgc->data_direction = CGC_DATA_READ;
}
static void setup_send_key(struct cdrom_generic_command *cgc, unsigned agid, unsigned type)
{
cgc->cmd[0] = GPCMD_SEND_KEY;
cgc->cmd[10] = type | (agid << 6);
switch (type) {
case 1: {
cgc->buflen = 16;
break;
}
case 3: {
cgc->buflen = 12;
break;
}
case 6: {
cgc->buflen = 8;
break;
}
}
cgc->cmd[9] = cgc->buflen;
cgc->data_direction = CGC_DATA_WRITE;
}
static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai)
{
int ret;
u_char buf[20];
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
rpc_state_t rpc_state;
memset(buf, 0, sizeof(buf));
init_cdrom_command(&cgc, buf, 0, CGC_DATA_READ);
switch (ai->type) {
/* LU data send */
case DVD_LU_SEND_AGID:
cdinfo(CD_DVD, "entering DVD_LU_SEND_AGID\n");
setup_report_key(&cgc, ai->lsa.agid, 0);
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
ai->lsa.agid = buf[7] >> 6;
/* Returning data, let host change state */
break;
case DVD_LU_SEND_KEY1:
cdinfo(CD_DVD, "entering DVD_LU_SEND_KEY1\n");
setup_report_key(&cgc, ai->lsk.agid, 2);
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
copy_key(ai->lsk.key, &buf[4]);
/* Returning data, let host change state */
break;
case DVD_LU_SEND_CHALLENGE:
cdinfo(CD_DVD, "entering DVD_LU_SEND_CHALLENGE\n");
setup_report_key(&cgc, ai->lsc.agid, 1);
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
copy_chal(ai->lsc.chal, &buf[4]);
/* Returning data, let host change state */
break;
/* Post-auth key */
case DVD_LU_SEND_TITLE_KEY:
cdinfo(CD_DVD, "entering DVD_LU_SEND_TITLE_KEY\n");
setup_report_key(&cgc, ai->lstk.agid, 4);
cgc.cmd[5] = ai->lstk.lba;
cgc.cmd[4] = ai->lstk.lba >> 8;
cgc.cmd[3] = ai->lstk.lba >> 16;
cgc.cmd[2] = ai->lstk.lba >> 24;
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
ai->lstk.cpm = (buf[4] >> 7) & 1;
ai->lstk.cp_sec = (buf[4] >> 6) & 1;
ai->lstk.cgms = (buf[4] >> 4) & 3;
copy_key(ai->lstk.title_key, &buf[5]);
/* Returning data, let host change state */
break;
case DVD_LU_SEND_ASF:
cdinfo(CD_DVD, "entering DVD_LU_SEND_ASF\n");
setup_report_key(&cgc, ai->lsasf.agid, 5);
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
ai->lsasf.asf = buf[7] & 1;
break;
/* LU data receive (LU changes state) */
case DVD_HOST_SEND_CHALLENGE:
cdinfo(CD_DVD, "entering DVD_HOST_SEND_CHALLENGE\n");
setup_send_key(&cgc, ai->hsc.agid, 1);
buf[1] = 0xe;
copy_chal(&buf[4], ai->hsc.chal);
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
ai->type = DVD_LU_SEND_KEY1;
break;
case DVD_HOST_SEND_KEY2:
cdinfo(CD_DVD, "entering DVD_HOST_SEND_KEY2\n");
setup_send_key(&cgc, ai->hsk.agid, 3);
buf[1] = 0xa;
copy_key(&buf[4], ai->hsk.key);
if ((ret = cdo->generic_packet(cdi, &cgc))) {
ai->type = DVD_AUTH_FAILURE;
return ret;
}
ai->type = DVD_AUTH_ESTABLISHED;
break;
/* Misc */
case DVD_INVALIDATE_AGID:
cdinfo(CD_DVD, "entering DVD_INVALIDATE_AGID\n");
setup_report_key(&cgc, ai->lsa.agid, 0x3f);
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
break;
/* Get region settings */
case DVD_LU_SEND_RPC_STATE:
cdinfo(CD_DVD, "entering DVD_LU_SEND_RPC_STATE\n");
setup_report_key(&cgc, 0, 8);
memset(&rpc_state, 0, sizeof(rpc_state_t));
cgc.buffer = (char *) &rpc_state;
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
ai->lrpcs.type = rpc_state.type_code;
ai->lrpcs.vra = rpc_state.vra;
ai->lrpcs.ucca = rpc_state.ucca;
ai->lrpcs.region_mask = rpc_state.region_mask;
ai->lrpcs.rpc_scheme = rpc_state.rpc_scheme;
break;
/* Set region settings */
case DVD_HOST_SEND_RPC_STATE:
cdinfo(CD_DVD, "entering DVD_HOST_SEND_RPC_STATE\n");
setup_send_key(&cgc, 0, 6);
buf[1] = 6;
buf[4] = ai->hrpcs.pdrc;
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
break;
default:
cdinfo(CD_WARNING, "Invalid DVD key ioctl (%d)\n", ai->type);
return -ENOTTY;
}
return 0;
}
static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s)
{
unsigned char buf[20], *base;
struct dvd_layer *layer;
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
int ret, layer_num = s->physical.layer_num;
if (layer_num >= DVD_LAYERS)
return -EINVAL;
init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
cgc.cmd[6] = layer_num;
cgc.cmd[7] = s->type;
cgc.cmd[9] = cgc.buflen & 0xff;
/*
* refrain from reporting errors on non-existing layers (mainly)
*/
cgc.quiet = 1;
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
base = &buf[4];
layer = &s->physical.layer[layer_num];
/*
* place the data... really ugly, but at least we won't have to
* worry about endianess in userspace.
*/
memset(layer, 0, sizeof(*layer));
layer->book_version = base[0] & 0xf;
layer->book_type = base[0] >> 4;
layer->min_rate = base[1] & 0xf;
layer->disc_size = base[1] >> 4;
layer->layer_type = base[2] & 0xf;
layer->track_path = (base[2] >> 4) & 1;
layer->nlayers = (base[2] >> 5) & 3;
layer->track_density = base[3] & 0xf;
layer->linear_density = base[3] >> 4;
layer->start_sector = base[5] << 16 | base[6] << 8 | base[7];
layer->end_sector = base[9] << 16 | base[10] << 8 | base[11];
layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15];
layer->bca = base[16] >> 7;
return 0;
}
static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s)
{
int ret;
u_char buf[8];
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
cgc.cmd[6] = s->copyright.layer_num;
cgc.cmd[7] = s->type;
cgc.cmd[8] = cgc.buflen >> 8;
cgc.cmd[9] = cgc.buflen & 0xff;
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
s->copyright.cpst = buf[4];
s->copyright.rmi = buf[5];
return 0;
}
static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s)
{
int ret, size;
u_char *buf;
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
size = sizeof(s->disckey.value) + 4;
if ((buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL)
return -ENOMEM;
init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
cgc.cmd[7] = s->type;
cgc.cmd[8] = size >> 8;
cgc.cmd[9] = size & 0xff;
cgc.cmd[10] = s->disckey.agid << 6;
if (!(ret = cdo->generic_packet(cdi, &cgc)))
memcpy(s->disckey.value, &buf[4], sizeof(s->disckey.value));
kfree(buf);
return ret;
}
static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s)
{
int ret;
u_char buf[4 + 188];
struct cdrom_generic_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -