From owner-freebsd-scsi Sun Nov 10 23:48:42 2002 Delivered-To: freebsd-scsi@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 4CD6137B401 for ; Sun, 10 Nov 2002 23:47:38 -0800 (PST) Received: from panzer.kdm.org (panzer.kdm.org [216.160.178.169]) by mx1.FreeBSD.org (Postfix) with ESMTP id 049A743E4A for ; Sun, 10 Nov 2002 23:47:32 -0800 (PST) (envelope-from ken@panzer.kdm.org) Received: from panzer.kdm.org (localhost [127.0.0.1]) by panzer.kdm.org (8.12.5/8.12.5) with ESMTP id gAB7lOKD084444 for ; Mon, 11 Nov 2002 00:47:24 -0700 (MST) (envelope-from ken@panzer.kdm.org) Received: (from ken@localhost) by panzer.kdm.org (8.12.5/8.12.5/Submit) id gAB7lOew084443 for scsi@FreeBSD.org; Mon, 11 Nov 2002 00:47:24 -0700 (MST) (envelope-from ken) Date: Mon, 11 Nov 2002 00:47:24 -0700 From: "Kenneth D. Merry" To: scsi@FreeBSD.org Subject: mode sense/mode select patches for cd(4) Message-ID: <20021111004723.A84290@panzer.kdm.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="ikeVEW9yuYc//A+q" Content-Disposition: inline User-Agent: Mutt/1.2.5.1i Sender: owner-freebsd-scsi@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org --ikeVEW9yuYc//A+q Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Okay, here are patches against -current (first set) and -stable (second set) to add automatic detection of drives that can't handle 6 byte mode sense and mode select. I'd appreciate it if folks could test this, especially people with CDROM drives running via usb, firewire, or ATAPICAM, but also people with older SCSI CDROM drives. To test this, apply the patch (for -current or -stable as appropriate) and do the following: cdcontrol status cdcontrol info cdcontrol play (make sure audio comes out) cdcontrol next (make sure it skips to the next track here) If you get an error after running 'cdcontrol status', try it again. The error reporting when we turn on 10-byte commands is a bit verbose at the moment, so don't worry about that. You shouldn't get an error after the second invocation of 'cdcontrol status'. If you get an error in response to 'cdcontrol next', definitely let me know what kind of drive you have (dmesg is fine) and how it is hooked up (SCSI, firewire, ATAPICAM, usb, etc.). The SCSI-2 PLAY_TRACK command has apparantly been deprecated in the MMC specs, so some drives don't support it. I'd like to get a handle on which ones do and don't, if possible. (The work around isn't hard, but isn't in this patch.) Reports of success or failure are welcome. ======= Now for more details.... When we get an illegal request in response to a 6 byte mode sense or mode select, we reformat the command to be a 10-byte mode sense or mode select and reissue it. These patches also work with drives that do not return block descriptors (even when dbd is 0 in the mode sense command), and drives that return multiple block descriptors. (They work better with the former than the latter; with the latter, they mostly just bail out.) While commands like READ(6) and WRITE(6) can be translated down in the SIM driver, like the ATAPICAM and usb code do, MODE_SENSE(6) and MODE_SELECT(6) cannot be translated, because the 10-byte version of the parameter list is longer. This means that when, e.g., a MODE_SENSE(6) command is translated to a MODE_SENSE(10) command, the page will likely be truncated (unless more space is allocated than needed), and since the peripheral driver code isn't expecting the returned data to be in MODE_SENSE(10) format, it'll be looking in the wrong place for the mode page. The SIM drivers have no way of knowing how long the buffer is that they've been given for a mode sense or mode select, so they can't just up the dxfer_len, change the CDB and call it good. The only way to do translation effectively for these two commands is where the buffer was originally allocated -- in the peripheral driver. So I have implemented translation and 10-byte mode sense and mode select for the cd(4) driver. Why do translation? Why not just default to 10-byte commands? Because some SCSI CDROM drives, maybe quite a few, don't support 10 byte mode sense and mode select. (The 8x Plextor in my test box doesn't, for example.) The right way to do this, eventually, is with the CAM_NEW_TRAN_CODE. 6byte/10byte can be a function of the protocol, and we can hopefully select the right size command up front. It will take a lot of infrastructure work to make it happen, but that functionality will hopefully solve several problems. One thing we need to do, regardless of when these patches go in, is disable MODE_SENSE(6) and MODE_SELECT(6) translation in ATAPICAM and the usb code -- it can't work properly, for the reasons I outlined above. Comments are welcome on the patches; they're more complicated than I would like, but unfortunately that complication seems necessary to get the desired functionality. These patches are not quite in their final form -- some of the debugging output will be turned off, and I have yet to add a loader tuneable or sysctl variable to control the 6 byte / 10 byte switch. Many thanks to David Kleiner for testing multiple iterations of these patches, he has been a big help. Ken -- Kenneth Merry ken@kdm.org --ikeVEW9yuYc//A+q Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="scsi_cd.cmdsize.20021109" ==== //depot/FreeBSD-ken/src/sys/cam/scsi/scsi_all.c#22 - /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_all.c ==== *** /tmp/tmp.269.0 Sat Nov 9 23:26:58 2002 --- /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_all.c Thu Oct 17 23:19:51 2002 *************** *** 2447,2458 **** u_int8_t page, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ ! if (param_len < 256) { /* * We can fit in a 6 byte cdb. */ --- 2447,2470 ---- u_int8_t page, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { + return(scsi_mode_sense_len(csio, retries, cbfcnp, tag_action, dbd, + page_code, page, param_buf, param_len, 0, + sense_len, timeout)); + } + void + scsi_mode_sense_len(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int dbd, u_int8_t page_code, + u_int8_t page, u_int8_t *param_buf, u_int32_t param_len, + int minimum_cmd_size, u_int8_t sense_len, u_int32_t timeout) + { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ ! if ((param_len < 256) ! && (minimum_cmd_size < 10)) { /* * We can fit in a 6 byte cdb. */ *************** *** 2500,2511 **** u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ ! if (param_len < 256) { /* * We can fit in a 6 byte cdb. */ --- 2512,2537 ---- u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { + return(scsi_mode_select_len(csio, retries, cbfcnp, tag_action, + scsi_page_fmt, save_pages, param_buf, + param_len, 0, sense_len, timeout)); + } + + void + scsi_mode_select_len(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int scsi_page_fmt, int save_pages, + u_int8_t *param_buf, u_int32_t param_len, + int minimum_cmd_size, u_int8_t sense_len, + u_int32_t timeout) + { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ ! if ((param_len < 256) ! && (minimum_cmd_size < 10)) { /* * We can fit in a 6 byte cdb. */ ==== //depot/FreeBSD-ken/src/sys/cam/scsi/scsi_all.h#11 - /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_all.h ==== *** /tmp/tmp.269.1 Sat Nov 9 23:26:58 2002 --- /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_all.h Thu Oct 17 23:19:51 2002 *************** *** 926,931 **** --- 926,940 ---- u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout); + void scsi_mode_sense_len(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, + union ccb *), + u_int8_t tag_action, int dbd, + u_int8_t page_code, u_int8_t page, + u_int8_t *param_buf, u_int32_t param_len, + int minimum_cmd_size, u_int8_t sense_len, + u_int32_t timeout); + void scsi_mode_select(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), *************** *** 933,938 **** --- 942,955 ---- int save_pages, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout); + + void scsi_mode_select_len(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, + union ccb *), + u_int8_t tag_action, int scsi_page_fmt, + int save_pages, u_int8_t *param_buf, + u_int32_t param_len, int minimum_cmd_size, + u_int8_t sense_len, u_int32_t timeout); void scsi_log_sense(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), ==== //depot/FreeBSD-ken/src/sys/cam/scsi/scsi_cd.c#28 - /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_cd.c ==== *** /tmp/tmp.269.2 Sat Nov 9 23:26:58 2002 --- /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_cd.c Sat Nov 9 23:00:06 2002 *************** *** 1,6 **** /* * Copyright (c) 1997 Justin T. Gibbs. ! * Copyright (c) 1997, 1998, 1999, 2000, 2001 Kenneth D. Merry. * All rights reserved. * * Redistribution and use in source and binary forms, with or without --- 1,6 ---- /* * Copyright (c) 1997 Justin T. Gibbs. ! * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002 Kenneth D. Merry. * All rights reserved. * * Redistribution and use in source and binary forms, with or without *************** *** 137,142 **** --- 137,153 ---- struct cam_periph *periph; dev_t dev; eventhandler_tag clonetag; + int minimum_command_size; + }; + + struct cd_page_sizes { + int page; + int page_size; + }; + + static struct cd_page_sizes cd_page_size_table[] = + { + { AUDIO_PAGE, sizeof(struct cd_audio_page)} }; struct cd_quirk_entry { *************** *** 203,208 **** --- 214,220 ---- u_int32_t priority); static void cddone(struct cam_periph *periph, union ccb *start_ccb); + static int cd6byteworkaround(union ccb *ccb); static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags); static void cdprevent(struct cam_periph *periph, int action); *************** *** 211,219 **** u_int32_t start, struct cd_toc_entry *data, u_int32_t len); static int cdgetmode(struct cam_periph *periph, ! struct cd_mode_data *data, u_int32_t page); static int cdsetmode(struct cam_periph *periph, ! struct cd_mode_data *data); static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len); static int cdreadsubchannel(struct cam_periph *periph, --- 223,231 ---- u_int32_t start, struct cd_toc_entry *data, u_int32_t len); static int cdgetmode(struct cam_periph *periph, ! struct cd_mode_params *data, u_int32_t page); static int cdsetmode(struct cam_periph *periph, ! struct cd_mode_params *data); static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len); static int cdreadsubchannel(struct cam_periph *periph, *************** *** 609,614 **** --- 621,628 ---- else softc->quirks = CD_Q_NONE; + softc->minimum_command_size = 6; + /* * We need to register the statistics structure for this device, * but we don't have the blocksize yet for it. So, we register *************** *** 1747,1752 **** --- 1761,1795 ---- xpt_release_ccb(done_ccb); } + static union cd_pages * + cdgetpage(struct cd_mode_params *mode_params) + { + union cd_pages *page; + + if (mode_params->cdb_size == 10) + page = (union cd_pages *)find_mode_page_10( + (struct scsi_mode_header_10 *)mode_params->mode_buf); + else + page = (union cd_pages *)find_mode_page_6( + (struct scsi_mode_header_6 *)mode_params->mode_buf); + + return (page); + } + + static int + cdgetpagesize(int page_num) + { + int i; + + for (i = 0; i < (sizeof(cd_page_size_table)/ + sizeof(cd_page_size_table[0])); i++) { + if (cd_page_size_table[i].page == page_num) + return (cd_page_size_table[i].page_size); + } + + return (-1); + } + static int cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { *************** *** 1785,1807 **** { struct ioc_play_track *args = (struct ioc_play_track *) addr; ! struct cd_mode_data *data; ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYTRACKS\n")); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.flags &= ~CD_PA_SOTC; ! data->page.audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); if (error) break; if (softc->quirks & CD_Q_BCD_TRACKS) { --- 1828,1854 ---- { struct ioc_play_track *args = (struct ioc_play_track *) addr; ! struct cd_mode_params params; ! union cd_pages *page; ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYTRACKS\n")); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.flags &= ~CD_PA_SOTC; ! page->audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); if (error) break; if (softc->quirks & CD_Q_BCD_TRACKS) { *************** *** 1819,1841 **** { struct ioc_play_msf *args = (struct ioc_play_msf *) addr; ! struct cd_mode_data *data; ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYMSF\n")); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.flags &= ~CD_PA_SOTC; ! data->page.audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); if (error) break; error = cdplaymsf(periph, --- 1866,1892 ---- { struct ioc_play_msf *args = (struct ioc_play_msf *) addr; ! struct cd_mode_params params; ! union cd_pages *page; ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYMSF\n")); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.flags &= ~CD_PA_SOTC; ! page->audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); if (error) break; error = cdplaymsf(periph, *************** *** 1851,1873 **** { struct ioc_play_blocks *args = (struct ioc_play_blocks *) addr; ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYBLOCKS\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.flags &= ~CD_PA_SOTC; ! data->page.audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); if (error) break; error = cdplay(periph, args->blk, args->len); --- 1902,1928 ---- { struct ioc_play_blocks *args = (struct ioc_play_blocks *) addr; ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYBLOCKS\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.flags &= ~CD_PA_SOTC; ! page->audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); if (error) break; error = cdplay(periph, args->blk, args->len); *************** *** 2157,2352 **** break; case CDIOCSETPATCH: { ! struct ioc_patch *arg = (struct ioc_patch *) addr; ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETPATCH\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = arg->patch[0]; ! data->page.audio.port[RIGHT_PORT].channels = arg->patch[1]; ! data->page.audio.port[2].channels = arg->patch[2]; ! data->page.audio.port[3].channels = arg->patch[3]; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCGETVOL\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } arg->vol[LEFT_PORT] = ! data->page.audio.port[LEFT_PORT].volume; arg->vol[RIGHT_PORT] = ! data->page.audio.port[RIGHT_PORT].volume; ! arg->vol[2] = data->page.audio.port[2].volume; ! arg->vol[3] = data->page.audio.port[3].volume; ! free(data, M_TEMP); } break; case CDIOCSETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETVOL\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = CHANNEL_0; ! data->page.audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT]; ! data->page.audio.port[RIGHT_PORT].channels = CHANNEL_1; ! data->page.audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT]; ! data->page.audio.port[2].volume = arg->vol[2]; ! data->page.audio.port[3].volume = arg->vol[3]; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCSETMONO: { ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMONO\n")); ! data = malloc(sizeof(struct cd_mode_data), ! M_TEMP, M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; ! data->page.audio.port[RIGHT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; ! data->page.audio.port[2].channels = 0; ! data->page.audio.port[3].channels = 0; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCSETSTEREO: { ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETSTEREO\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL; ! data->page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; ! data->page.audio.port[2].channels = 0; ! data->page.audio.port[3].channels = 0; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCSETMUTE: { ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMUTE\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = 0; ! data->page.audio.port[RIGHT_PORT].channels = 0; ! data->page.audio.port[2].channels = 0; ! data->page.audio.port[3].channels = 0; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCSETLEFT: { ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETLEFT\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = ! LEFT_CHANNEL; ! data->page.audio.port[RIGHT_PORT].channels = ! LEFT_CHANNEL; ! data->page.audio.port[2].channels = 0; ! data->page.audio.port[3].channels = 0; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCSETRIGHT: { ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETRIGHT\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = ! RIGHT_CHANNEL; ! data->page.audio.port[RIGHT_PORT].channels = ! RIGHT_CHANNEL; ! data->page.audio.port[2].channels = 0; ! data->page.audio.port[3].channels = 0; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCRESUME: --- 2212,2437 ---- break; case CDIOCSETPATCH: { ! struct ioc_patch *arg = (struct ioc_patch *)addr; ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETPATCH\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = arg->patch[0]; ! page->audio.port[RIGHT_PORT].channels = arg->patch[1]; ! page->audio.port[2].channels = arg->patch[2]; ! page->audio.port[3].channels = arg->patch[3]; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCGETVOL\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf= malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } + page = cdgetpage(¶ms); + arg->vol[LEFT_PORT] = ! page->audio.port[LEFT_PORT].volume; arg->vol[RIGHT_PORT] = ! page->audio.port[RIGHT_PORT].volume; ! arg->vol[2] = page->audio.port[2].volume; ! arg->vol[3] = page->audio.port[3].volume; ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETVOL\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = CHANNEL_0; ! page->audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT]; ! page->audio.port[RIGHT_PORT].channels = CHANNEL_1; ! page->audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT]; ! page->audio.port[2].volume = arg->vol[2]; ! page->audio.port[3].volume = arg->vol[3]; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETMONO: { ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMONO\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; ! page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; ! page->audio.port[2].channels = 0; ! page->audio.port[3].channels = 0; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETSTEREO: { ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETSTEREO\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL; ! page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; ! page->audio.port[2].channels = 0; ! page->audio.port[3].channels = 0; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETMUTE: { ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMUTE\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(¶ms, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = 0; ! page->audio.port[RIGHT_PORT].channels = 0; ! page->audio.port[2].channels = 0; ! page->audio.port[3].channels = 0; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETLEFT: { ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETLEFT\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL; ! page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL; ! page->audio.port[2].channels = 0; ! page->audio.port[3].channels = 0; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETRIGHT: { ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETRIGHT\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = RIGHT_CHANNEL; ! page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; ! page->audio.port[2].channels = 0; ! page->audio.port[3].channels = 0; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCRESUME: *************** *** 2521,2534 **** --- 2606,2770 ---- } static int + cd6byteworkaround(union ccb *ccb) + { + u_int8_t *cdb; + struct cam_periph *periph; + struct cd_softc *softc; + struct cd_mode_params *params; + int frozen; + + periph = xpt_path_periph(ccb->ccb_h.path); + softc = (struct cd_softc *)periph->softc; + + cdb = ccb->csio.cdb_io.cdb_bytes; + + if ((ccb->ccb_h.flags & CAM_CDB_POINTER) + || ((cdb[0] != MODE_SENSE_6) + && (cdb[0] != MODE_SELECT_6))) + return (0); + + /* + * Because there is no other convenient place to stash the overall + * cd_mode_params structure pointer, we have to grab it like this. + * This means that ALL MODE_SENSE and MODE_SELECT requests in the + * cd(4) driver MUST go through cdgetmode() and cdsetmode()! + */ + params = (struct cd_mode_params *)(ccb->csio.data_ptr - + __offsetof(struct cd_mode_params, mode_buf)); + + params->cdb_size = 10; + softc->minimum_command_size = 10; + xpt_print_path(ccb->ccb_h.path); + printf("MODE_SENSE(6)/MODE_SELECT(6) failed, increasing " + "minimum CDB size to 10 bytes\n"); + /* XXX KDM remove this */ + printf("CAM Status %#x\n", ccb->ccb_h.status); + printf("SCSI Status %#x\n", ccb->csio.scsi_status); + scsi_sense_print(&ccb->csio); + + if (cdb[0] == MODE_SENSE_6) { + struct scsi_mode_sense_10 ms10; + struct scsi_mode_sense_6 *ms6; + int len; + + ms6 = (struct scsi_mode_sense_6 *)cdb; + + bzero(&ms10, sizeof(ms10)); + ms10.opcode = MODE_SENSE_10; + ms10.byte2 = ms6->byte2; + ms10.page = ms6->page; + + /* + * 10 byte mode header, block descriptor, + * sizeof(union cd_pages) + */ + len = sizeof(struct cd_mode_data_10); + ccb->csio.dxfer_len = len; + + scsi_ulto2b(len, ms10.length); + ms10.control = ms6->control; + bcopy(&ms10, cdb, 10); + ccb->csio.cdb_len = 10; + } else { + struct scsi_mode_select_10 ms10; + struct scsi_mode_select_6 *ms6; + struct scsi_mode_header_6 *header6; + struct scsi_mode_header_10 *header10; + struct scsi_mode_page_header *page_header; + int blk_desc_len, page_num, page_size, len; + + ms6 = (struct scsi_mode_select_6 *)cdb; + + bzero(&ms10, sizeof(ms10)); + ms10.opcode = MODE_SELECT_10; + ms10.byte2 = ms6->byte2; + + header6 = (struct scsi_mode_header_6 *)params->mode_buf; + header10 = (struct scsi_mode_header_10 *)params->mode_buf; + + page_header = find_mode_page_6(header6); + page_num = page_header->page_code; + + blk_desc_len = header6->blk_desc_len; + + page_size = cdgetpagesize(page_num); + + if (page_size != (page_header->page_length + + sizeof(*page_header))) + page_size = page_header->page_length + + sizeof(*page_header); + + len = sizeof(*header10) + blk_desc_len + page_size; + + len = min(params->alloc_len, len); + + /* + * Since the 6 byte parameter header is shorter than the 10 + * byte parameter header, we need to copy the actual mode + * page data, and the block descriptor, if any, so things wind + * up in the right place. The regions will overlap, but + * bcopy() does the right thing. + */ + bcopy(params->mode_buf + sizeof(*header6), + params->mode_buf + sizeof(*header10), + len - sizeof(*header10)); + + /* Make sure these fields are set correctly. */ + scsi_ulto2b(0, header10->data_length); + header10->medium_type = 0; + scsi_ulto2b(blk_desc_len, header10->blk_desc_len); + + ccb->csio.dxfer_len = len; + + scsi_ulto2b(len, ms10.length); + ms10.control = ms6->control; + bcopy(&ms10, cdb, 10); + ccb->csio.cdb_len = 10; + } + + frozen = (ccb->ccb_h.status & CAM_DEV_QFRZN) != 0; + ccb->ccb_h.status = CAM_REQUEUE_REQ; + xpt_action(ccb); + if (frozen) { + cam_release_devq(ccb->ccb_h.path, + /*relsim_flags*/0, + /*openings*/0, + /*timeout*/0, + /*getcount_only*/0); + } + + return (ERESTART); + } + + static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) { struct cd_softc *softc; struct cam_periph *periph; + int error; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct cd_softc *)periph->softc; + error = 0; + + if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) + && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) + && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND) + && ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0) + && ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) { + int sense_key, error_code, asc, ascq; + + scsi_extract_sense(&ccb->csio.sense_data, + &error_code, &sense_key, &asc, &ascq); + if (sense_key == SSD_KEY_ILLEGAL_REQUEST) + error = cd6byteworkaround(ccb); + } + + if (error == ERESTART) + return (error); + /* * XXX * Until we have a better way of doing pack validation, *************** *** 2638,2720 **** } static int ! cdgetmode(struct cam_periph *periph, struct cd_mode_data *data, u_int32_t page) { ! struct scsi_mode_sense_6 *scsi_cmd; ! struct ccb_scsiio *csio; union ccb *ccb; int error; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; ! bzero(data, sizeof(*data)); ! cam_fill_csio(csio, ! /* retries */ 1, ! /* cbfcnp */ cddone, ! /* flags */ CAM_DIR_IN, ! /* tag_action */ MSG_SIMPLE_Q_TAG, ! /* data_ptr */ (u_int8_t *)data, ! /* dxfer_len */ sizeof(*data), ! /* sense_len */ SSD_FULL_SIZE, ! sizeof(struct scsi_mode_sense_6), ! /* timeout */ 50000); ! scsi_cmd = (struct scsi_mode_sense_6 *)&csio->cdb_io.cdb_bytes; ! bzero (scsi_cmd, sizeof(*scsi_cmd)); ! scsi_cmd->page = page; ! scsi_cmd->length = sizeof(*data) & 0xff; ! scsi_cmd->opcode = MODE_SENSE; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int ! cdsetmode(struct cam_periph *periph, struct cd_mode_data *data) { ! struct scsi_mode_select_6 *scsi_cmd; ! struct ccb_scsiio *csio; union ccb *ccb; int error; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; error = 0; ! cam_fill_csio(csio, ! /* retries */ 1, ! /* cbfcnp */ cddone, ! /* flags */ CAM_DIR_OUT, ! /* tag_action */ MSG_SIMPLE_Q_TAG, ! /* data_ptr */ (u_int8_t *)data, ! /* dxfer_len */ sizeof(*data), ! /* sense_len */ SSD_FULL_SIZE, ! sizeof(struct scsi_mode_select_6), ! /* timeout */ 50000); ! scsi_cmd = (struct scsi_mode_select_6 *)&csio->cdb_io.cdb_bytes; ! bzero(scsi_cmd, sizeof(*scsi_cmd)); ! scsi_cmd->opcode = MODE_SELECT; ! scsi_cmd->byte2 |= SMS_PF; ! scsi_cmd->length = sizeof(*data) & 0xff; ! data->header.data_length = 0; ! /* ! * SONY drives do not allow a mode select with a medium_type ! * value that has just been returned by a mode sense; use a ! * medium_type of 0 (Default) instead. ! */ ! data->header.medium_type = 0; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); --- 2874,3051 ---- } + /* + * All MODE_SENSE requests in the cd(4) driver MUST go through this + * routine. See comments in cd6byteworkaround() for details. + */ static int ! cdgetmode(struct cam_periph *periph, struct cd_mode_params *data, ! u_int32_t page) { ! struct ccb_scsiio *csio; ! struct cd_softc *softc; union ccb *ccb; + int param_len; int error; + softc = (struct cd_softc *)periph->softc; + ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; ! data->cdb_size = softc->minimum_command_size; ! if (data->cdb_size < 10) ! param_len = sizeof(struct cd_mode_data); ! else ! param_len = sizeof(struct cd_mode_data_10); ! /* Don't say we've got more room than we actually allocated */ ! param_len = min(param_len, data->alloc_len); ! scsi_mode_sense_len(csio, ! /* retries */ 1, ! /* cbfcnp */ cddone, ! /* tag_action */ MSG_SIMPLE_Q_TAG, ! /* dbd */ 0, ! /* page_code */ SMS_PAGE_CTRL_CURRENT, ! /* page */ page, ! /* param_buf */ data->mode_buf, ! /* param_len */ param_len, ! /* minimum_cmd_size */ softc->minimum_command_size, ! /* sense_len */ SSD_FULL_SIZE, ! /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); + /* + * This is a bit of belt-and-suspenders checking, but if we run + * into a situation where the target sends back multiple block + * descriptors, we might not have enough space in the buffer to + * see the whole mode page. Better to return an error than + * potentially access memory beyond our malloced region. + */ + if (error == 0) { + u_int32_t data_len; + + if (data->cdb_size == 10) { + struct scsi_mode_header_10 *hdr10; + + hdr10 = (struct scsi_mode_header_10 *)data->mode_buf; + data_len = scsi_2btoul(hdr10->data_length); + data_len += sizeof(hdr10->data_length); + } else { + struct scsi_mode_header_6 *hdr6; + + hdr6 = (struct scsi_mode_header_6 *)data->mode_buf; + data_len = hdr6->data_length; + data_len += sizeof(hdr6->data_length); + } + + /* + * Complain if there is more mode data available than we + * allocated space for. This could potentially happen if + * we miscalculated the page length for some reason, if the + * drive returns multiple block descriptors, or if it sets + * the data length incorrectly. + */ + if (data_len > data->alloc_len) { + xpt_print_path(periph->path); + printf("allocated modepage %d length %d < returned " + "length %d\n", page, data->alloc_len, data_len); + + error = ENOSPC; + } + } return(error); } + /* + * All MODE_SELECT requests in the cd(4) driver MUST go through this + * routine. See comments in cd6byteworkaround() for details. + */ static int ! cdsetmode(struct cam_periph *periph, struct cd_mode_params *data) { ! struct ccb_scsiio *csio; ! struct cd_softc *softc; union ccb *ccb; + int cdb_size, param_len; int error; + softc = (struct cd_softc *)periph->softc; + ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; error = 0; ! /* ! * If the data is formatted for the 10 byte version of the mode ! * select parameter list, we need to use the 10 byte CDB. ! * Otherwise, we use whatever the stored minimum command size. ! */ ! if (data->cdb_size == 10) ! cdb_size = data->cdb_size; ! else ! cdb_size = softc->minimum_command_size; ! if (cdb_size >= 10) { ! struct scsi_mode_header_10 *mode_header; ! u_int32_t data_len; ! mode_header = (struct scsi_mode_header_10 *)data->mode_buf; ! ! data_len = scsi_2btoul(mode_header->data_length); ! ! scsi_ulto2b(0, mode_header->data_length); ! /* ! * SONY drives do not allow a mode select with a medium_type ! * value that has just been returned by a mode sense; use a ! * medium_type of 0 (Default) instead. ! */ ! mode_header->medium_type = 0; ! ! /* ! * Pass back whatever the drive passed to us, plus the size ! * of the data length field. ! */ ! param_len = data_len + sizeof(mode_header->data_length); ! ! } else { ! struct scsi_mode_header_6 *mode_header; ! ! mode_header = (struct scsi_mode_header_6 *)data->mode_buf; ! ! param_len = mode_header->data_length + 1; ! ! mode_header->data_length = 0; ! /* ! * SONY drives do not allow a mode select with a medium_type ! * value that has just been returned by a mode sense; use a ! * medium_type of 0 (Default) instead. ! */ ! mode_header->medium_type = 0; ! } ! ! /* Don't say we've got more room than we actually allocated */ ! param_len = min(param_len, data->alloc_len); ! ! scsi_mode_select_len(csio, ! /* retries */ 1, ! /* cbfcnp */ cddone, ! /* tag_action */ MSG_SIMPLE_Q_TAG, ! /* scsi_page_fmt */ 1, ! /* save_pages */ 0, ! /* param_buf */ data->mode_buf, ! /* param_len */ param_len, ! /* minimum_cmd_size */ cdb_size, ! /* sense_len */ SSD_FULL_SIZE, ! /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); ==== //depot/FreeBSD-ken/src/sys/cam/scsi/scsi_cd.h#5 - /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_cd.h ==== *** /tmp/tmp.269.3 Sat Nov 9 23:26:58 2002 --- /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_cd.h Fri Nov 8 22:49:52 2002 *************** *** 655,692 **** u_int8_t length_0; /* Least significant */ }; ! union cd_pages { ! struct audio_page { ! u_int8_t page_code; ! #define CD_PAGE_CODE 0x3F ! #define AUDIO_PAGE 0x0e ! #define CD_PAGE_PS 0x80 ! u_int8_t param_len; ! u_int8_t flags; ! #define CD_PA_SOTC 0x02 ! #define CD_PA_IMMED 0x04 ! u_int8_t unused[2]; ! u_int8_t format_lba; ! #define CD_PA_FORMAT_LBA 0x0F ! #define CD_PA_APR_VALID 0x80 ! u_int8_t lb_per_sec[2]; ! struct port_control ! { ! u_int8_t channels; ! #define CHANNEL 0x0F ! #define CHANNEL_0 1 ! #define CHANNEL_1 2 ! #define CHANNEL_2 4 ! #define CHANNEL_3 8 ! #define LEFT_CHANNEL CHANNEL_0 ! #define RIGHT_CHANNEL CHANNEL_1 ! u_int8_t volume; ! } port[4]; ! #define LEFT_PORT 0 ! #define RIGHT_PORT 1 ! }audio; }; struct cd_mode_data --- 655,701 ---- u_int8_t length_0; /* Least significant */ }; ! struct cd_audio_page { ! u_int8_t page_code; ! #define CD_PAGE_CODE 0x3F ! #define AUDIO_PAGE 0x0e ! #define CD_PAGE_PS 0x80 ! u_int8_t param_len; ! u_int8_t flags; ! #define CD_PA_SOTC 0x02 ! #define CD_PA_IMMED 0x04 ! u_int8_t unused[2]; ! u_int8_t format_lba; ! #define CD_PA_FORMAT_LBA 0x0F ! #define CD_PA_APR_VALID 0x80 ! u_int8_t lb_per_sec[2]; ! struct port_control { ! u_int8_t channels; ! #define CHANNEL 0x0F ! #define CHANNEL_0 1 ! #define CHANNEL_1 2 ! #define CHANNEL_2 4 ! #define CHANNEL_3 8 ! #define LEFT_CHANNEL CHANNEL_0 ! #define RIGHT_CHANNEL CHANNEL_1 ! u_int8_t volume; ! } port[4]; ! #define LEFT_PORT 0 ! #define RIGHT_PORT 1 ! }; ! ! union cd_pages ! { ! struct cd_audio_page audio; ! }; ! ! struct cd_mode_data_10 ! { ! struct scsi_mode_header_10 header; ! struct scsi_mode_blk_desc blk_desc; ! union cd_pages page; }; struct cd_mode_data *************** *** 694,699 **** --- 703,729 ---- struct scsi_mode_header_6 header; struct scsi_mode_blk_desc blk_desc; union cd_pages page; + }; + + union cd_mode_data_6_10 + { + struct cd_mode_data mode_data_6; + struct cd_mode_data_10 mode_data_10; + }; + + #if 0 + struct cd_mode_params + { + int cdb_size; + union cd_mode_data_6_10 mode_data; + }; + #endif + + struct cd_mode_params + { + int cdb_size; + int alloc_len; + u_int8_t *mode_buf; }; __BEGIN_DECLS --ikeVEW9yuYc//A+q Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="scsi_cd.cmdsize.stable.20021109" ==== //depot/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_all.c#7 - /usr/home/ken/perforce2/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_all.c ==== *** /tmp/tmp.1941.0 Sat Nov 9 23:41:11 2002 --- /usr/home/ken/perforce2/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_all.c Sat Nov 9 23:30:37 2002 *************** *** 2568,2579 **** u_int8_t page, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ ! if (param_len < 256) { /* * We can fit in a 6 byte cdb. */ --- 2568,2591 ---- u_int8_t page, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { + return(scsi_mode_sense_len(csio, retries, cbfcnp, tag_action, dbd, + page_code, page, param_buf, param_len, 0, + sense_len, timeout)); + } + void + scsi_mode_sense_len(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int dbd, u_int8_t page_code, + u_int8_t page, u_int8_t *param_buf, u_int32_t param_len, + int minimum_cmd_size, u_int8_t sense_len, u_int32_t timeout) + { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ ! if ((param_len < 256) ! && (minimum_cmd_size < 10)) { /* * We can fit in a 6 byte cdb. */ *************** *** 2621,2632 **** u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ ! if (param_len < 256) { /* * We can fit in a 6 byte cdb. */ --- 2633,2658 ---- u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { + return(scsi_mode_select_len(csio, retries, cbfcnp, tag_action, + scsi_page_fmt, save_pages, param_buf, + param_len, 0, sense_len, timeout)); + } + + void + scsi_mode_select_len(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, int scsi_page_fmt, int save_pages, + u_int8_t *param_buf, u_int32_t param_len, + int minimum_cmd_size, u_int8_t sense_len, + u_int32_t timeout) + { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ ! if ((param_len < 256) ! && (minimum_cmd_size < 10)) { /* * We can fit in a 6 byte cdb. */ ==== //depot/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_all.h#2 - /usr/home/ken/perforce2/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_all.h ==== *** /tmp/tmp.1941.1 Sat Nov 9 23:41:11 2002 --- /usr/home/ken/perforce2/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_all.h Sat Nov 9 23:30:37 2002 *************** *** 833,838 **** --- 833,847 ---- u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout); + void scsi_mode_sense_len(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, + union ccb *), + u_int8_t tag_action, int dbd, + u_int8_t page_code, u_int8_t page, + u_int8_t *param_buf, u_int32_t param_len, + int minimum_cmd_size, u_int8_t sense_len, + u_int32_t timeout); + void scsi_mode_select(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), *************** *** 840,845 **** --- 849,862 ---- int save_pages, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout); + + void scsi_mode_select_len(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, + union ccb *), + u_int8_t tag_action, int scsi_page_fmt, + int save_pages, u_int8_t *param_buf, + u_int32_t param_len, int minimum_cmd_size, + u_int8_t sense_len, u_int32_t timeout); void scsi_read_capacity(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, ==== //depot/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_cd.c#6 - /usr/home/ken/perforce2/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_cd.c ==== *** /tmp/tmp.1941.2 Sat Nov 9 23:41:11 2002 --- /usr/home/ken/perforce2/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_cd.c Sat Nov 9 23:39:54 2002 *************** *** 1,6 **** /* * Copyright (c) 1997 Justin T. Gibbs. ! * Copyright (c) 1997, 1998, 1999, 2000, 2001 Kenneth D. Merry. * All rights reserved. * * Redistribution and use in source and binary forms, with or without --- 1,6 ---- /* * Copyright (c) 1997 Justin T. Gibbs. ! * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002 Kenneth D. Merry. * All rights reserved. * * Redistribution and use in source and binary forms, with or without *************** *** 136,141 **** --- 136,152 ---- struct cdchanger *changer; int bufs_left; struct cam_periph *periph; + int minimum_command_size; + }; + + struct cd_page_sizes { + int page; + int page_size; + }; + + static struct cd_page_sizes cd_page_size_table[] = + { + { AUDIO_PAGE, sizeof(struct cd_audio_page)} }; struct cd_quirk_entry { *************** *** 203,208 **** --- 214,220 ---- u_int32_t priority); static void cddone(struct cam_periph *periph, union ccb *start_ccb); + static int cd6byteworkaround(union ccb *ccb); static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags); static void cdprevent(struct cam_periph *periph, int action); *************** *** 212,220 **** u_int32_t start, struct cd_toc_entry *data, u_int32_t len); static int cdgetmode(struct cam_periph *periph, ! struct cd_mode_data *data, u_int32_t page); static int cdsetmode(struct cam_periph *periph, ! struct cd_mode_data *data); static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len); static int cdreadsubchannel(struct cam_periph *periph, --- 224,232 ---- u_int32_t start, struct cd_toc_entry *data, u_int32_t len); static int cdgetmode(struct cam_periph *periph, ! struct cd_mode_params *data, u_int32_t page); static int cdsetmode(struct cam_periph *periph, ! struct cd_mode_params *data); static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len); static int cdreadsubchannel(struct cam_periph *periph, *************** *** 614,619 **** --- 626,633 ---- else softc->quirks = CD_Q_NONE; + softc->minimum_command_size = 6; + /* * We need to register the statistics structure for this device, * but we don't have the blocksize yet for it. So, we register *************** *** 1814,1819 **** --- 1828,1862 ---- xpt_release_ccb(done_ccb); } + static union cd_pages * + cdgetpage(struct cd_mode_params *mode_params) + { + union cd_pages *page; + + if (mode_params->cdb_size == 10) + page = (union cd_pages *)find_mode_page_10( + (struct scsi_mode_header_10 *)mode_params->mode_buf); + else + page = (union cd_pages *)find_mode_page_6( + (struct scsi_mode_header_6 *)mode_params->mode_buf); + + return (page); + } + + static int + cdgetpagesize(int page_num) + { + int i; + + for (i = 0; i < (sizeof(cd_page_size_table)/ + sizeof(cd_page_size_table[0])); i++) { + if (cd_page_size_table[i].page == page_num) + return (cd_page_size_table[i].page_size); + } + + return (-1); + } + static int cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { *************** *** 1846,1868 **** { struct ioc_play_track *args = (struct ioc_play_track *) addr; ! struct cd_mode_data *data; ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYTRACKS\n")); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.flags &= ~CD_PA_SOTC; ! data->page.audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); if (error) break; if (softc->quirks & CD_Q_BCD_TRACKS) { --- 1889,1915 ---- { struct ioc_play_track *args = (struct ioc_play_track *) addr; ! struct cd_mode_params params; ! union cd_pages *page; ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYTRACKS\n")); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.flags &= ~CD_PA_SOTC; ! page->audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); if (error) break; if (softc->quirks & CD_Q_BCD_TRACKS) { *************** *** 1880,1902 **** { struct ioc_play_msf *args = (struct ioc_play_msf *) addr; ! struct cd_mode_data *data; ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYMSF\n")); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.flags &= ~CD_PA_SOTC; ! data->page.audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); if (error) break; error = cdplaymsf(periph, --- 1927,1953 ---- { struct ioc_play_msf *args = (struct ioc_play_msf *) addr; ! struct cd_mode_params params; ! union cd_pages *page; ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYMSF\n")); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.flags &= ~CD_PA_SOTC; ! page->audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); if (error) break; error = cdplaymsf(periph, *************** *** 1912,1934 **** { struct ioc_play_blocks *args = (struct ioc_play_blocks *) addr; ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYBLOCKS\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.flags &= ~CD_PA_SOTC; ! data->page.audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); if (error) break; error = cdplay(periph, args->blk, args->len); --- 1963,1989 ---- { struct ioc_play_blocks *args = (struct ioc_play_blocks *) addr; ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYBLOCKS\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.flags &= ~CD_PA_SOTC; ! page->audio.flags |= CD_PA_IMMED; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); if (error) break; error = cdplay(periph, args->blk, args->len); *************** *** 2218,2413 **** break; case CDIOCSETPATCH: { ! struct ioc_patch *arg = (struct ioc_patch *) addr; ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETPATCH\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = arg->patch[0]; ! data->page.audio.port[RIGHT_PORT].channels = arg->patch[1]; ! data->page.audio.port[2].channels = arg->patch[2]; ! data->page.audio.port[3].channels = arg->patch[3]; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCGETVOL\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } arg->vol[LEFT_PORT] = ! data->page.audio.port[LEFT_PORT].volume; arg->vol[RIGHT_PORT] = ! data->page.audio.port[RIGHT_PORT].volume; ! arg->vol[2] = data->page.audio.port[2].volume; ! arg->vol[3] = data->page.audio.port[3].volume; ! free(data, M_TEMP); } break; case CDIOCSETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETVOL\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = CHANNEL_0; ! data->page.audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT]; ! data->page.audio.port[RIGHT_PORT].channels = CHANNEL_1; ! data->page.audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT]; ! data->page.audio.port[2].volume = arg->vol[2]; ! data->page.audio.port[3].volume = arg->vol[3]; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCSETMONO: { ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMONO\n")); ! data = malloc(sizeof(struct cd_mode_data), ! M_TEMP, M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; ! data->page.audio.port[RIGHT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; ! data->page.audio.port[2].channels = 0; ! data->page.audio.port[3].channels = 0; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCSETSTEREO: { ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETSTEREO\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL; ! data->page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; ! data->page.audio.port[2].channels = 0; ! data->page.audio.port[3].channels = 0; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCSETMUTE: { ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMUTE\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = 0; ! data->page.audio.port[RIGHT_PORT].channels = 0; ! data->page.audio.port[2].channels = 0; ! data->page.audio.port[3].channels = 0; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCSETLEFT: { ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETLEFT\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = ! LEFT_CHANNEL; ! data->page.audio.port[RIGHT_PORT].channels = ! LEFT_CHANNEL; ! data->page.audio.port[2].channels = 0; ! data->page.audio.port[3].channels = 0; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCSETRIGHT: { ! struct cd_mode_data *data; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETRIGHT\n")); ! data = malloc(sizeof(struct cd_mode_data), M_TEMP, ! M_WAITOK); ! error = cdgetmode(periph, data, AUDIO_PAGE); if (error) { ! free(data, M_TEMP); break; } ! data->page.audio.port[LEFT_PORT].channels = ! RIGHT_CHANNEL; ! data->page.audio.port[RIGHT_PORT].channels = ! RIGHT_CHANNEL; ! data->page.audio.port[2].channels = 0; ! data->page.audio.port[3].channels = 0; ! error = cdsetmode(periph, data); ! free(data, M_TEMP); } break; case CDIOCRESUME: --- 2273,2498 ---- break; case CDIOCSETPATCH: { ! struct ioc_patch *arg = (struct ioc_patch *)addr; ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETPATCH\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = arg->patch[0]; ! page->audio.port[RIGHT_PORT].channels = arg->patch[1]; ! page->audio.port[2].channels = arg->patch[2]; ! page->audio.port[3].channels = arg->patch[3]; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCGETVOL\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf= malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } + page = cdgetpage(¶ms); + arg->vol[LEFT_PORT] = ! page->audio.port[LEFT_PORT].volume; arg->vol[RIGHT_PORT] = ! page->audio.port[RIGHT_PORT].volume; ! arg->vol[2] = page->audio.port[2].volume; ! arg->vol[3] = page->audio.port[3].volume; ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETVOL\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = CHANNEL_0; ! page->audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT]; ! page->audio.port[RIGHT_PORT].channels = CHANNEL_1; ! page->audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT]; ! page->audio.port[2].volume = arg->vol[2]; ! page->audio.port[3].volume = arg->vol[3]; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETMONO: { ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMONO\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; ! page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; ! page->audio.port[2].channels = 0; ! page->audio.port[3].channels = 0; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETSTEREO: { ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETSTEREO\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL; ! page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; ! page->audio.port[2].channels = 0; ! page->audio.port[3].channels = 0; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETMUTE: { ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMUTE\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(¶ms, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = 0; ! page->audio.port[RIGHT_PORT].channels = 0; ! page->audio.port[2].channels = 0; ! page->audio.port[3].channels = 0; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETLEFT: { ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETLEFT\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL; ! page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL; ! page->audio.port[2].channels = 0; ! page->audio.port[3].channels = 0; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCSETRIGHT: { ! struct cd_mode_params params; ! union cd_pages *page; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETRIGHT\n")); ! params.alloc_len = sizeof(union cd_mode_data_6_10); ! params.mode_buf = malloc(params.alloc_len, M_TEMP, ! M_WAITOK | M_ZERO); ! ! error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { ! free(params.mode_buf, M_TEMP); break; } ! page = cdgetpage(¶ms); ! ! page->audio.port[LEFT_PORT].channels = RIGHT_CHANNEL; ! page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; ! page->audio.port[2].channels = 0; ! page->audio.port[3].channels = 0; ! error = cdsetmode(periph, ¶ms); ! free(params.mode_buf, M_TEMP); } break; case CDIOCRESUME: *************** *** 2648,2661 **** --- 2733,2897 ---- } static int + cd6byteworkaround(union ccb *ccb) + { + u_int8_t *cdb; + struct cam_periph *periph; + struct cd_softc *softc; + struct cd_mode_params *params; + int frozen; + + periph = xpt_path_periph(ccb->ccb_h.path); + softc = (struct cd_softc *)periph->softc; + + cdb = ccb->csio.cdb_io.cdb_bytes; + + if ((ccb->ccb_h.flags & CAM_CDB_POINTER) + || ((cdb[0] != MODE_SENSE_6) + && (cdb[0] != MODE_SELECT_6))) + return (0); + + /* + * Because there is no other convenient place to stash the overall + * cd_mode_params structure pointer, we have to grab it like this. + * This means that ALL MODE_SENSE and MODE_SELECT requests in the + * cd(4) driver MUST go through cdgetmode() and cdsetmode()! + */ + params = (struct cd_mode_params *)(ccb->csio.data_ptr - + __offsetof(struct cd_mode_params, mode_buf)); + + params->cdb_size = 10; + softc->minimum_command_size = 10; + xpt_print_path(ccb->ccb_h.path); + printf("MODE_SENSE(6)/MODE_SELECT(6) failed, increasing " + "minimum CDB size to 10 bytes\n"); + /* XXX KDM remove this */ + printf("CAM Status %#x\n", ccb->ccb_h.status); + printf("SCSI Status %#x\n", ccb->csio.scsi_status); + scsi_sense_print(&ccb->csio); + + if (cdb[0] == MODE_SENSE_6) { + struct scsi_mode_sense_10 ms10; + struct scsi_mode_sense_6 *ms6; + int len; + + ms6 = (struct scsi_mode_sense_6 *)cdb; + + bzero(&ms10, sizeof(ms10)); + ms10.opcode = MODE_SENSE_10; + ms10.byte2 = ms6->byte2; + ms10.page = ms6->page; + + /* + * 10 byte mode header, block descriptor, + * sizeof(union cd_pages) + */ + len = sizeof(struct cd_mode_data_10); + ccb->csio.dxfer_len = len; + + scsi_ulto2b(len, ms10.length); + ms10.control = ms6->control; + bcopy(&ms10, cdb, 10); + ccb->csio.cdb_len = 10; + } else { + struct scsi_mode_select_10 ms10; + struct scsi_mode_select_6 *ms6; + struct scsi_mode_header_6 *header6; + struct scsi_mode_header_10 *header10; + struct scsi_mode_page_header *page_header; + int blk_desc_len, page_num, page_size, len; + + ms6 = (struct scsi_mode_select_6 *)cdb; + + bzero(&ms10, sizeof(ms10)); + ms10.opcode = MODE_SELECT_10; + ms10.byte2 = ms6->byte2; + + header6 = (struct scsi_mode_header_6 *)params->mode_buf; + header10 = (struct scsi_mode_header_10 *)params->mode_buf; + + page_header = find_mode_page_6(header6); + page_num = page_header->page_code; + + blk_desc_len = header6->blk_desc_len; + + page_size = cdgetpagesize(page_num); + + if (page_size != (page_header->page_length + + sizeof(*page_header))) + page_size = page_header->page_length + + sizeof(*page_header); + + len = sizeof(*header10) + blk_desc_len + page_size; + + len = min(params->alloc_len, len); + + /* + * Since the 6 byte parameter header is shorter than the 10 + * byte parameter header, we need to copy the actual mode + * page data, and the block descriptor, if any, so things wind + * up in the right place. The regions will overlap, but + * bcopy() does the right thing. + */ + bcopy(params->mode_buf + sizeof(*header6), + params->mode_buf + sizeof(*header10), + len - sizeof(*header10)); + + /* Make sure these fields are set correctly. */ + scsi_ulto2b(0, header10->data_length); + header10->medium_type = 0; + scsi_ulto2b(blk_desc_len, header10->blk_desc_len); + + ccb->csio.dxfer_len = len; + + scsi_ulto2b(len, ms10.length); + ms10.control = ms6->control; + bcopy(&ms10, cdb, 10); + ccb->csio.cdb_len = 10; + } + + frozen = (ccb->ccb_h.status & CAM_DEV_QFRZN) != 0; + ccb->ccb_h.status = CAM_REQUEUE_REQ; + xpt_action(ccb); + if (frozen) { + cam_release_devq(ccb->ccb_h.path, + /*relsim_flags*/0, + /*openings*/0, + /*timeout*/0, + /*getcount_only*/0); + } + + return (ERESTART); + } + + static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) { struct cd_softc *softc; struct cam_periph *periph; + int error; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct cd_softc *)periph->softc; + error = 0; + + if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) + && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) + && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND) + && ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0) + && ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) { + int sense_key, error_code, asc, ascq; + + scsi_extract_sense(&ccb->csio.sense_data, + &error_code, &sense_key, &asc, &ascq); + if (sense_key == SSD_KEY_ILLEGAL_REQUEST) + error = cd6byteworkaround(ccb); + } + + if (error == ERESTART) + return (error); + /* * XXX * Until we have a better way of doing pack validation, *************** *** 2764,2847 **** return(error); } ! static int ! cdgetmode(struct cam_periph *periph, struct cd_mode_data *data, u_int32_t page) { ! struct scsi_mode_sense_6 *scsi_cmd; ! struct ccb_scsiio *csio; union ccb *ccb; int error; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; ! bzero(data, sizeof(*data)); ! cam_fill_csio(csio, ! /* retries */ 1, ! /* cbfcnp */ cddone, ! /* flags */ CAM_DIR_IN, ! /* tag_action */ MSG_SIMPLE_Q_TAG, ! /* data_ptr */ (u_int8_t *)data, ! /* dxfer_len */ sizeof(*data), ! /* sense_len */ SSD_FULL_SIZE, ! sizeof(struct scsi_mode_sense_6), ! /* timeout */ 50000); ! scsi_cmd = (struct scsi_mode_sense_6 *)&csio->cdb_io.cdb_bytes; ! bzero (scsi_cmd, sizeof(*scsi_cmd)); ! scsi_cmd->page = page; ! scsi_cmd->length = sizeof(*data) & 0xff; ! scsi_cmd->opcode = MODE_SENSE; error = cdrunccb(ccb, cderror, /*cam_flags*/0, /*sense_flags*/SF_RETRY_UA|SF_RETRY_SELTO); xpt_release_ccb(ccb); return(error); } static int ! cdsetmode(struct cam_periph *periph, struct cd_mode_data *data) { ! struct scsi_mode_select_6 *scsi_cmd; ! struct ccb_scsiio *csio; union ccb *ccb; int error; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; error = 0; ! cam_fill_csio(csio, ! /* retries */ 1, ! /* cbfcnp */ cddone, ! /* flags */ CAM_DIR_OUT, ! /* tag_action */ MSG_SIMPLE_Q_TAG, ! /* data_ptr */ (u_int8_t *)data, ! /* dxfer_len */ sizeof(*data), ! /* sense_len */ SSD_FULL_SIZE, ! sizeof(struct scsi_mode_select_6), ! /* timeout */ 50000); ! scsi_cmd = (struct scsi_mode_select_6 *)&csio->cdb_io.cdb_bytes; ! bzero(scsi_cmd, sizeof(*scsi_cmd)); ! scsi_cmd->opcode = MODE_SELECT; ! scsi_cmd->byte2 |= SMS_PF; ! scsi_cmd->length = sizeof(*data) & 0xff; ! data->header.data_length = 0; ! /* ! * SONY drives do not allow a mode select with a medium_type ! * value that has just been returned by a mode sense; use a ! * medium_type of 0 (Default) instead. ! */ ! data->header.medium_type = 0; error = cdrunccb(ccb, cderror, /*cam_flags*/0, /*sense_flags*/SF_RETRY_UA | SF_RETRY_SELTO); --- 3000,3177 ---- return(error); } ! /* ! * All MODE_SENSE requests in the cd(4) driver MUST go through this ! * routine. See comments in cd6byteworkaround() for details. ! */ static int ! cdgetmode(struct cam_periph *periph, struct cd_mode_params *data, ! u_int32_t page) { ! struct ccb_scsiio *csio; ! struct cd_softc *softc; union ccb *ccb; + int param_len; int error; + softc = (struct cd_softc *)periph->softc; + ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; ! data->cdb_size = softc->minimum_command_size; ! if (data->cdb_size < 10) ! param_len = sizeof(struct cd_mode_data); ! else ! param_len = sizeof(struct cd_mode_data_10); ! /* Don't say we've got more room than we actually allocated */ ! param_len = min(param_len, data->alloc_len); ! scsi_mode_sense_len(csio, ! /* retries */ 1, ! /* cbfcnp */ cddone, ! /* tag_action */ MSG_SIMPLE_Q_TAG, ! /* dbd */ 0, ! /* page_code */ SMS_PAGE_CTRL_CURRENT, ! /* page */ page, ! /* param_buf */ data->mode_buf, ! /* param_len */ param_len, ! /* minimum_cmd_size */ softc->minimum_command_size, ! /* sense_len */ SSD_FULL_SIZE, ! /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/0, /*sense_flags*/SF_RETRY_UA|SF_RETRY_SELTO); xpt_release_ccb(ccb); + /* + * This is a bit of belt-and-suspenders checking, but if we run + * into a situation where the target sends back multiple block + * descriptors, we might not have enough space in the buffer to + * see the whole mode page. Better to return an error than + * potentially access memory beyond our malloced region. + */ + if (error == 0) { + u_int32_t data_len; + + if (data->cdb_size == 10) { + struct scsi_mode_header_10 *hdr10; + + hdr10 = (struct scsi_mode_header_10 *)data->mode_buf; + data_len = scsi_2btoul(hdr10->data_length); + data_len += sizeof(hdr10->data_length); + } else { + struct scsi_mode_header_6 *hdr6; + + hdr6 = (struct scsi_mode_header_6 *)data->mode_buf; + data_len = hdr6->data_length; + data_len += sizeof(hdr6->data_length); + } + + /* + * Complain if there is more mode data available than we + * allocated space for. This could potentially happen if + * we miscalculated the page length for some reason, if the + * drive returns multiple block descriptors, or if it sets + * the data length incorrectly. + */ + if (data_len > data->alloc_len) { + xpt_print_path(periph->path); + printf("allocated modepage %d length %d < returned " + "length %d\n", page, data->alloc_len, data_len); + + error = ENOSPC; + } + } return(error); } + /* + * All MODE_SELECT requests in the cd(4) driver MUST go through this + * routine. See comments in cd6byteworkaround() for details. + */ static int ! cdsetmode(struct cam_periph *periph, struct cd_mode_params *data) { ! struct ccb_scsiio *csio; ! struct cd_softc *softc; union ccb *ccb; + int cdb_size, param_len; int error; + softc = (struct cd_softc *)periph->softc; + ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; error = 0; ! /* ! * If the data is formatted for the 10 byte version of the mode ! * select parameter list, we need to use the 10 byte CDB. ! * Otherwise, we use whatever the stored minimum command size. ! */ ! if (data->cdb_size == 10) ! cdb_size = data->cdb_size; ! else ! cdb_size = softc->minimum_command_size; ! if (cdb_size >= 10) { ! struct scsi_mode_header_10 *mode_header; ! u_int32_t data_len; ! mode_header = (struct scsi_mode_header_10 *)data->mode_buf; ! ! data_len = scsi_2btoul(mode_header->data_length); ! ! scsi_ulto2b(0, mode_header->data_length); ! /* ! * SONY drives do not allow a mode select with a medium_type ! * value that has just been returned by a mode sense; use a ! * medium_type of 0 (Default) instead. ! */ ! mode_header->medium_type = 0; ! ! /* ! * Pass back whatever the drive passed to us, plus the size ! * of the data length field. ! */ ! param_len = data_len + sizeof(mode_header->data_length); ! ! } else { ! struct scsi_mode_header_6 *mode_header; ! ! mode_header = (struct scsi_mode_header_6 *)data->mode_buf; ! ! param_len = mode_header->data_length + 1; ! ! mode_header->data_length = 0; ! /* ! * SONY drives do not allow a mode select with a medium_type ! * value that has just been returned by a mode sense; use a ! * medium_type of 0 (Default) instead. ! */ ! mode_header->medium_type = 0; ! } ! ! /* Don't say we've got more room than we actually allocated */ ! param_len = min(param_len, data->alloc_len); ! ! scsi_mode_select_len(csio, ! /* retries */ 1, ! /* cbfcnp */ cddone, ! /* tag_action */ MSG_SIMPLE_Q_TAG, ! /* scsi_page_fmt */ 1, ! /* save_pages */ 0, ! /* param_buf */ data->mode_buf, ! /* param_len */ param_len, ! /* minimum_cmd_size */ cdb_size, ! /* sense_len */ SSD_FULL_SIZE, ! /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/0, /*sense_flags*/SF_RETRY_UA | SF_RETRY_SELTO); *************** *** 2850,2856 **** return(error); } - static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len) --- 3180,3185 ---- ==== //depot/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_cd.h#1 - /usr/home/ken/perforce2/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_cd.h ==== *** /tmp/tmp.1941.3 Sat Nov 9 23:41:11 2002 --- /usr/home/ken/perforce2/FreeBSD-ken-RELENG_4/src/sys/cam/scsi/scsi_cd.h Sat Nov 9 23:30:37 2002 *************** *** 645,682 **** u_int8_t length_0; /* Least significant */ }; ! union cd_pages { ! struct audio_page { ! u_int8_t page_code; ! #define CD_PAGE_CODE 0x3F ! #define AUDIO_PAGE 0x0e ! #define CD_PAGE_PS 0x80 ! u_int8_t param_len; ! u_int8_t flags; ! #define CD_PA_SOTC 0x02 ! #define CD_PA_IMMED 0x04 ! u_int8_t unused[2]; ! u_int8_t format_lba; ! #define CD_PA_FORMAT_LBA 0x0F ! #define CD_PA_APR_VALID 0x80 ! u_int8_t lb_per_sec[2]; ! struct port_control ! { ! u_int8_t channels; ! #define CHANNEL 0x0F ! #define CHANNEL_0 1 ! #define CHANNEL_1 2 ! #define CHANNEL_2 4 ! #define CHANNEL_3 8 ! #define LEFT_CHANNEL CHANNEL_0 ! #define RIGHT_CHANNEL CHANNEL_1 ! u_int8_t volume; ! } port[4]; ! #define LEFT_PORT 0 ! #define RIGHT_PORT 1 ! }audio; }; struct cd_mode_data --- 645,691 ---- u_int8_t length_0; /* Least significant */ }; ! struct cd_audio_page { ! u_int8_t page_code; ! #define CD_PAGE_CODE 0x3F ! #define AUDIO_PAGE 0x0e ! #define CD_PAGE_PS 0x80 ! u_int8_t param_len; ! u_int8_t flags; ! #define CD_PA_SOTC 0x02 ! #define CD_PA_IMMED 0x04 ! u_int8_t unused[2]; ! u_int8_t format_lba; ! #define CD_PA_FORMAT_LBA 0x0F ! #define CD_PA_APR_VALID 0x80 ! u_int8_t lb_per_sec[2]; ! struct port_control { ! u_int8_t channels; ! #define CHANNEL 0x0F ! #define CHANNEL_0 1 ! #define CHANNEL_1 2 ! #define CHANNEL_2 4 ! #define CHANNEL_3 8 ! #define LEFT_CHANNEL CHANNEL_0 ! #define RIGHT_CHANNEL CHANNEL_1 ! u_int8_t volume; ! } port[4]; ! #define LEFT_PORT 0 ! #define RIGHT_PORT 1 ! }; ! ! union cd_pages ! { ! struct cd_audio_page audio; ! }; ! ! struct cd_mode_data_10 ! { ! struct scsi_mode_header_10 header; ! struct scsi_mode_blk_desc blk_desc; ! union cd_pages page; }; struct cd_mode_data *************** *** 684,689 **** --- 693,719 ---- struct scsi_mode_header_6 header; struct scsi_mode_blk_desc blk_desc; union cd_pages page; + }; + + union cd_mode_data_6_10 + { + struct cd_mode_data mode_data_6; + struct cd_mode_data_10 mode_data_10; + }; + + #if 0 + struct cd_mode_params + { + int cdb_size; + union cd_mode_data_6_10 mode_data; + }; + #endif + + struct cd_mode_params + { + int cdb_size; + int alloc_len; + u_int8_t *mode_buf; }; __BEGIN_DECLS --ikeVEW9yuYc//A+q-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-scsi" in the body of the message