static int cd_profiles(struct udev *udev, int fd) { struct scsi_cmd sc; unsigned char features[65530]; unsigned int cur_profile = 0; unsigned int len; unsigned int i; int err; /* First query the current profile */ scsi_cmd_init(udev, &sc, features, sizeof(features)); scsi_cmd_set(udev, &sc, 0, 0x46); scsi_cmd_set(udev, &sc, 8, 8); scsi_cmd_set(udev, &sc, 9, 0); err = scsi_cmd_run(udev, &sc, fd, features, 8); if ((err != 0)) { info_scsi_cmd_err(udev, "GET CONFIGURATION", err); /* handle pre-MMC2 drives which do not support GET CONFIGURATION */ if (SK(err) == 0x5 && ASC(err) == 0x20) { info(udev, "drive is pre-MMC2 and does not support 46h get configuration command\n"); info(udev, "trying to work around the problem\n"); return cd_profiles_old_mmc(udev, fd); } return -1; } cur_profile = features[6] << 8 | features[7]; if (cur_profile > 0) { info(udev, "current profile 0x%02x\n", cur_profile); } else { info(udev, "no current profile, assuming no media\n"); return -1; } switch (cur_profile) { case 0x03: case 0x04: case 0x05: info(udev, "profile 0x%02x \n", cur_profile); cd_media = 1; cd_media_mo = 1; break; case 0x08: info(udev, "profile 0x%02x media_cd_rom\n", cur_profile); cd_media = 1; cd_media_cd_rom = 1; break; case 0x09: info(udev, "profile 0x%02x media_cd_r\n", cur_profile); cd_media = 1; cd_media_cd_r = 1; break; case 0x0a: info(udev, "profile 0x%02x media_cd_rw\n", cur_profile); cd_media = 1; cd_media_cd_rw = 1; break; case 0x10: info(udev, "profile 0x%02x media_dvd_ro\n", cur_profile); cd_media = 1; cd_media_dvd_rom = 1; break; case 0x11: info(udev, "profile 0x%02x media_dvd_r\n", cur_profile); cd_media = 1; cd_media_dvd_r = 1; break; case 0x12: info(udev, "profile 0x%02x media_dvd_ram\n", cur_profile); cd_media = 1; cd_media_dvd_ram = 1; break; case 0x13: info(udev, "profile 0x%02x media_dvd_rw_ro\n", cur_profile); cd_media = 1; cd_media_dvd_rw = 1; cd_media_dvd_rw_ro = 1; break; case 0x14: info(udev, "profile 0x%02x media_dvd_rw_seq\n", cur_profile); cd_media = 1; cd_media_dvd_rw = 1; cd_media_dvd_rw_seq = 1; break; case 0x1B: info(udev, "profile 0x%02x media_dvd_plus_r\n", cur_profile); cd_media = 1; cd_media_dvd_plus_r = 1; break; case 0x1A: info(udev, "profile 0x%02x media_dvd_plus_rw\n", cur_profile); cd_media = 1; cd_media_dvd_plus_rw = 1; break; case 0x2A: info(udev, "profile 0x%02x media_dvd_plus_rw_dl\n", cur_profile); cd_media = 1; cd_media_dvd_plus_rw_dl = 1; break; case 0x2B: info(udev, "profile 0x%02x media_dvd_plus_r_dl\n", cur_profile); cd_media = 1; cd_media_dvd_plus_r_dl = 1; break; case 0x40: info(udev, "profile 0x%02x media_bd\n", cur_profile); cd_media = 1; cd_media_bd = 1; break; case 0x41: case 0x42: info(udev, "profile 0x%02x media_bd_r\n", cur_profile); cd_media = 1; cd_media_bd_r = 1; break; case 0x43: info(udev, "profile 0x%02x media_bd_re\n", cur_profile); cd_media = 1; cd_media_bd_re = 1; break; case 0x50: info(udev, "profile 0x%02x media_hddvd\n", cur_profile); cd_media = 1; cd_media_hddvd = 1; break; case 0x51: info(udev, "profile 0x%02x media_hddvd_r\n", cur_profile); cd_media = 1; cd_media_hddvd_r = 1; break; case 0x52: info(udev, "profile 0x%02x media_hddvd_rw\n", cur_profile); cd_media = 1; cd_media_hddvd_rw = 1; break; default: info(udev, "profile 0x%02x <ignored>\n", cur_profile); break; } len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; info(udev, "GET CONFIGURATION: size of features buffer 0x%04x\n", len); if (len > sizeof(features)) { info(udev, "can not get features in a single query, truncating\n"); len = sizeof(features); } else if (len <= 8) { len = sizeof(features); } /* Now get the full feature buffer */ scsi_cmd_init(udev, &sc, features, len); scsi_cmd_set(udev, &sc, 0, 0x46); scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff); scsi_cmd_set(udev, &sc, 8, len & 0xff); scsi_cmd_set(udev, &sc, 9, 0); err = scsi_cmd_run(udev, &sc, fd, features, len); if ((err != 0)) { info_scsi_cmd_err(udev, "GET CONFIGURATION", err); return -1; } /* parse the length once more, in case the drive decided to have other features suddenly :) */ len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; info(udev, "GET CONFIGURATION: size of features buffer 0x%04x\n", len); if (len > sizeof(features)) { info(udev, "can not get features in a single query, truncating\n"); len = sizeof(features); } /* device features */ for (i = 8; i+4 < len; i += (4 + features[i+3])) { unsigned int feature; feature = features[i] << 8 | features[i+1]; switch (feature) { case 0x00: info(udev, "GET CONFIGURATION: feature 'profiles', with %i entries\n", features[i+3] / 4); feature_profiles(udev, &features[i]+4, features[i+3]); break; default: info(udev, "GET CONFIGURATION: feature 0x%04x <ignored>, with 0x%02x bytes\n", feature, features[i+3]); break; } } return 0; }
/* returns 0 if media was detected */ static int cd_profiles(struct udev *udev, int fd) { struct scsi_cmd sc; unsigned char features[65530]; unsigned int cur_profile = 0; unsigned int len; unsigned int i; int err; int ret; ret = -1; /* First query the current profile */ scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x46); scsi_cmd_set(udev, &sc, 8, 8); scsi_cmd_set(udev, &sc, 9, 0); err = scsi_cmd_run(udev, &sc, fd, features, 8); if ((err != 0)) { info_scsi_cmd_err(udev, "GET CONFIGURATION", err); /* handle pre-MMC2 drives which do not support GET CONFIGURATION */ if (SK(err) == 0x5 && IN_SET(ASC(err), 0x20, 0x24)) { log_debug("drive is pre-MMC2 and does not support 46h get configuration command"); log_debug("trying to work around the problem"); ret = cd_profiles_old_mmc(udev, fd); } goto out; } cur_profile = features[6] << 8 | features[7]; if (cur_profile > 0) { log_debug("current profile 0x%02x", cur_profile); feature_profile_media (udev, cur_profile); ret = 0; /* we have media */ } else { log_debug("no current profile, assuming no media"); } len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; log_debug("GET CONFIGURATION: size of features buffer 0x%04x", len); if (len > sizeof(features)) { log_debug("cannot get features in a single query, truncating"); len = sizeof(features); } else if (len <= 8) len = sizeof(features); /* Now get the full feature buffer */ scsi_cmd_init(udev, &sc); scsi_cmd_set(udev, &sc, 0, 0x46); scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff); scsi_cmd_set(udev, &sc, 8, len & 0xff); scsi_cmd_set(udev, &sc, 9, 0); err = scsi_cmd_run(udev, &sc, fd, features, len); if ((err != 0)) { info_scsi_cmd_err(udev, "GET CONFIGURATION", err); return -1; } /* parse the length once more, in case the drive decided to have other features suddenly :) */ len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; log_debug("GET CONFIGURATION: size of features buffer 0x%04x", len); if (len > sizeof(features)) { log_debug("cannot get features in a single query, truncating"); len = sizeof(features); } /* device features */ for (i = 8; i+4 < len; i += (4 + features[i+3])) { unsigned int feature; feature = features[i] << 8 | features[i+1]; switch (feature) { case 0x00: log_debug("GET CONFIGURATION: feature 'profiles', with %i entries", features[i+3] / 4); feature_profiles(udev, &features[i]+4, MIN(features[i+3], len - i - 4)); break; default: log_debug("GET CONFIGURATION: feature 0x%04x <ignored>, with 0x%02x bytes", feature, features[i+3]); break; } } out: return ret; }