Subject: kern/10114: *SCSI* support for DVD (syssrc/dev/scsipi/cd.c) broken
To: None <gnats-bugs@gnats.netbsd.org>
From: None <smd@ebone.net>
List: netbsd-bugs
Date: 05/13/2000 14:52:15
>Number: 10114
>Category: kern
>Synopsis: DVD support in syssrc/dev/scsipi/cd.c broken with some SCSI controllers
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat May 13 14:53:01 PDT 2000
>Closed-Date:
>Last-Modified:
>Originator: Sean Doran
>Release: cvs as of 2000 05 09
>Organization:
>Environment:
System: NetBSD crasse.smd.ebone.net 1.4Y NetBSD 1.4Y (SCREAM) #0: Tue May 9 21:44:12 CEST 2000 smd@crasse.smd.ebone.net:/usr/src/sys/arch/i386/compile/SCREAM i386
>Description:
tstdvd fails (ftp://ftp.netbsd.org/pub/incoming/mycroft/dvd-19991031.tgz)
css-auth stuff fails (www.linuxvideo.org)
error depends on SCSI controller (usually works with aic(4) PCMCIA card)
for example, with ahc(4):
: crasse (es) ; ./tstdvd /dev/rcd0d
not Authenticated
Request AGID [1]... AGID 0
Host sending challenge: 09 08 07 06 05 04 03 02 01 00
Send challenge to LU failed
console says:
ahc0: target 1 synchronous at 10.0MHz, offset = 0xf
cd0(ahc0:1:0): Check Condition on CDB: 0xa3 00 00 00 00 00 00 00 00 10 01 00
SENSE KEY: Illegal Request
ASC/ASCQ: System Resource Failure
The DVD auth stuff in syssrc/dev/scsipi/cd.c was directly
copied from Linux, and is the likely bug location.
>How-To-Repeat:
1. grab a _SCSI_ DVD drive (RAM or ROM), put it on adw(4), isp(4)
or ahc(4) controller
2. grab mycroft's tstdvd port, run it against /dev/rcdXc
3. observe one of a variety of different failures, depending
on combination of controller + drive
>Fix:
This information may be useful...
Excerpted from
http://www.us.kernel.org/pub/linux/kernel/people/axboe/cdvd/dvd-cd-2.2.15-A.diff.gz
- --
+ 3.08 May 1, 2000 - Jens Axboe <axboe@suse.de>
+ -- Always return -EROFS for write opens
+ -- Convert to module_init/module_exit style init and remove some
+ of the #ifdef MODULE stuff
+ -- Fix several dvd errors - DVD_LU_SEND_ASF should pass agid,
+ DVD_HOST_SEND_RPC_STATE did not set buffer size in cdb, and
+ dvd_do_auth passed uninitialized data to drive because init_cdrom_command
+ did not clear a 0 sized buffer.
- --
+/* 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;
+}
+
+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;
+}
+
+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);
+
+ 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);
+
+ init_cdrom_command(&cgc, &rpc_state, 0);
+ 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)
+{
+ int ret, i;
+ u_char buf[4 + 4 * 20], *base;
+ struct dvd_layer *layer;
+ struct cdrom_generic_command cgc;
+ struct cdrom_device_ops *cdo = cdi->ops;
+
+ init_cdrom_command(&cgc, buf, sizeof(buf));
+ cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc.cmd[6] = s->physical.layer_num;
+ cgc.cmd[7] = s->type;
+ cgc.cmd[9] = cgc.buflen & 0xff;
+
+ if ((ret = cdo->generic_packet(cdi, &cgc)))
+ return ret;
+
+ base = &buf[4];
+ layer = &s->physical.layer[0];
+
+ /* place the data... really ugly, but at least we won't have to
+ worry about endianess in userspace or here. */
+ for (i = 0; i < 4; ++i, base += 20, ++layer) {
+ 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.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.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;
+
+ init_cdrom_command(&cgc, buf, sizeof(buf));
+ cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc.cmd[7] = s->type;
+ cgc.cmd[9] = cgc.buflen = 0xff;
+
+ if ((ret = cdo->generic_packet(cdi, &cgc)))
+ return ret;
+
+ s->bca.len = buf[0] << 8 | buf[1];
+ if (s->bca.len < 12 || s->bca.len > 188) {
+ cdinfo(CD_WARNING, "Received invalid BCA length (%d)\n", s->bca.len);
+ return -EIO;
+ }
+ memcpy(s->bca.value, &buf[4], s->bca.len);
+
+ return 0;
+}
+
+static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s)
+{
+ int ret = 0, size;
+ u_char *buf;
+ struct cdrom_generic_command cgc;
+ struct cdrom_device_ops *cdo = cdi->ops;
+
+ size = sizeof(s->manufact.value) + 4;
+
+ if ((buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ init_cdrom_command(&cgc, buf, size);
+ cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc.cmd[7] = s->type;
+ cgc.cmd[8] = size >> 8;
+ cgc.cmd[9] = size & 0xff;
+
+ if ((ret = cdo->generic_packet(cdi, &cgc))) {
+ kfree(buf);
+ return ret;
+ }
+
+ s->manufact.len = buf[0] << 8 | buf[1];
+ if (s->manufact.len < 0 || s->manufact.len > 2048) {
+ cdinfo(CD_WARNING, "Received invalid manufacture info length"
+ " (%d)\n", s->bca.len);
+ ret = -EIO;
+ } else {
+ memcpy(s->manufact.value, &buf[4], s->manufact.len);
+ }
+
+ kfree(buf);
+ return ret;
+}
+
+static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s)
+{
+ switch (s->type) {
+ case DVD_STRUCT_PHYSICAL:
+ return dvd_read_physical(cdi, s);
+
+ case DVD_STRUCT_COPYRIGHT:
+ return dvd_read_copyright(cdi, s);
+
+ case DVD_STRUCT_DISCKEY:
+ return dvd_read_disckey(cdi, s);
+
+ case DVD_STRUCT_BCA:
+ return dvd_read_bca(cdi, s);
+
+ case DVD_STRUCT_MANUFACT:
+ return dvd_read_manufact(cdi, s);
+
+ default:
+ cdinfo(CD_WARNING, ": Invalid DVD structure read requested (%d)\n",
+ s->type);
+ return -EINVAL;
+ }
+}
+
... dispatch table ...
+ case DVD_READ_STRUCT: {
+ dvd_struct *s;
+ int size = sizeof(dvd_struct);
+ if (!CDROM_CAN(CDC_DVD))
+ return -ENOSYS;
+ if ((s = (dvd_struct *) kmalloc(size, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n");
+ if (copy_from_user(s, (dvd_struct *)arg, size)) {
+ kfree(s);
+ return -EFAULT;
+ }
+ if ((ret = dvd_read_struct(cdi, s))) {
+ kfree(s);
+ return ret;
+ }
+ if (copy_to_user((dvd_struct *)arg, s, size))
+ ret = -EFAULT;
+ kfree(s);
+ return ret;
+ }
+
+ case DVD_AUTH: {
+ dvd_authinfo ai;
+ if (!CDROM_CAN(CDC_DVD))
+ return -ENOSYS;
+ cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n");
+ IOCTL_IN(arg, dvd_authinfo, ai);
+ if ((ret = dvd_do_auth (cdi, &ai)))
+ return ret;
+ IOCTL_OUT(arg, dvd_authinfo, ai);
+ return 0;
+ }
+
- --
+ 3.08 May 1, 2000 - Jens Axboe <axboe@suse.de>
+ -- Always return -EROFS for write opens
+ -- Convert to module_init/module_exit style init and remove some
+ of the #ifdef MODULE stuff
+ -- Fix several dvd errors - DVD_LU_SEND_ASF should pass agid,
+ DVD_HOST_SEND_RPC_STATE did not set buffer size in cdb, and
+ dvd_do_auth passed uninitialized data to drive because init_cdrom_command
+ did not clear a 0 sized buffer.
+
>Release-Note:
>Audit-Trail:
>Unformatted: