1 /*
2    "Higher-level" Multimedia Command (MMC) commands which build on
3    the "lower-level" commands.
4 
5    Copyright (C) 2010-2011, 2014 Rocky Bernstein <rocky@gnu.org>
6 
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 # define __CDIO_CONFIG_H__ 1
23 #endif
24 
25 #include <cdio/cdio.h>
26 #include <cdio/mmc_cmds.h>
27 
28 /**
29    Close tray using a MMC START STOP UNIT command.
30    @param p_cdio the CD object to be acted upon.
31    @return DRIVER_OP_SUCCESS (0) if we got the status.
32    return codes are the same as driver_return_code_t
33  */
34 driver_return_code_t
mmc_close_tray(CdIo_t * p_cdio)35 mmc_close_tray(CdIo_t *p_cdio)
36 {
37     return mmc_start_stop_unit(p_cdio, false, false, 0, 0);
38 }
39 
40 /**
41    Eject using MMC commands. If CD-ROM is "locked" we'll unlock it.
42    Command is not "immediate" -- we'll wait for the command to complete.
43    For a more general (and lower-level) routine, @see mmc_start_stop_media.
44 
45    @param p_cdio the CD object to be acted upon.
46    @return DRIVER_OP_SUCCESS (0) if we got the status.
47    return codes are the same as driver_return_code_t
48 */
49 driver_return_code_t
mmc_eject_media(const CdIo_t * p_cdio)50 mmc_eject_media( const CdIo_t *p_cdio )
51 {
52   int i_status = 0;
53   i_status = mmc_prevent_allow_medium_removal(p_cdio, false, false, 0);
54   if (0 != i_status) return i_status;
55 
56   return mmc_start_stop_unit(p_cdio, true, false, 0, 0);
57 
58 }
59 
60 /**
61   Detects if a disc (CD or DVD) is erasable or not.
62 
63   @param p_user_data the CD object to be acted upon.
64 
65   @param b_erasable, if not NULL, on return will be set indicate whether
66   the operation was a success (DRIVER_OP_SUCCESS) or if not to some
67   other value.
68 
69   @return true if the disc is detected as erasable (rewritable), false
70 otherwise.
71  */
72 /* From Frank Endres: */
73 driver_return_code_t
mmc_get_disc_erasable(const CdIo_t * p_cdio,bool * b_erasable)74 mmc_get_disc_erasable(const CdIo_t *p_cdio, bool *b_erasable) {
75     uint8_t buf[42] = { 0, };
76     driver_return_code_t i_status;
77 
78     i_status = mmc_read_disc_information(p_cdio, buf, sizeof(buf),
79 					 CDIO_MMC_READ_DISC_INFO_STANDARD, 0);
80 
81     if (DRIVER_OP_SUCCESS == i_status)
82 	*b_erasable = ((buf[2] & 0x10) ? true : false);
83     else
84 	*b_erasable = false;
85     return i_status;
86 }
87 
88 /* From Frank Endres: */
89 /**
90    Detects the disc type using the SCSI-MMC GET CONFIGURATION command.
91 
92    @param p_cdio the CD object to be acted upon.
93 
94    @param i_status, if not NULL, on return will be set indicate whether
95    the operation was a success (DRIVER_OP_SUCCESS) or if not to some
96    other value.
97 
98    @param p_disctype the disc type set on success.
99    @return DRIVER_OP_SUCCESS (0) if we got the status.
100    return codes are the same as driver_return_code_t
101  */
102 driver_return_code_t
mmc_get_disctype(const CdIo_t * p_cdio,unsigned int i_timeout_ms,cdio_mmc_feature_profile_t * p_disctype)103 mmc_get_disctype( const CdIo_t *p_cdio, unsigned int i_timeout_ms,
104                   cdio_mmc_feature_profile_t *p_disctype)
105 {
106     uint8_t buf[500] = { 0, };
107     driver_return_code_t i_status;
108 
109     if (0 == i_timeout_ms) i_timeout_ms = mmc_timeout_ms;
110     i_status = mmc_get_configuration(p_cdio, &buf, sizeof(buf),
111 				     CDIO_MMC_GET_CONF_ALL_FEATURES,
112 				     0, i_timeout_ms);
113 
114     if (DRIVER_OP_SUCCESS == i_status) {
115 	uint8_t *p, *q;
116 	uint8_t profiles_list_length;
117 	uint16_t profile_number;
118 	bool profile_active;
119 
120 	/* there is always a profile list feature listed at the
121 	   first place of the features list */
122 	p = buf + 8;
123 	profiles_list_length = p[3];
124 	q = p+4;
125 	*p_disctype = CDIO_MMC_FEATURE_PROF_NON_CONFORM;
126 
127 	while ((CDIO_MMC_FEATURE_PROF_NON_CONFORM == *p_disctype) &&
128 	       (q < p + profiles_list_length)) {
129 	    profile_number = CDIO_MMC_GET_LEN16(q);
130 	    profile_active = q[2] & 0x01;
131 
132 	    if (profile_active)
133 		switch (profile_number) {
134 		  case CDIO_MMC_FEATURE_PROF_CD_ROM:
135 		  case CDIO_MMC_FEATURE_PROF_CD_R:
136 		  case CDIO_MMC_FEATURE_PROF_CD_RW:
137 		  case CDIO_MMC_FEATURE_PROF_DVD_ROM:
138 		  case CDIO_MMC_FEATURE_PROF_DVD_R_SEQ:
139 		  case CDIO_MMC_FEATURE_PROF_DVD_RAM:
140 		  case CDIO_MMC_FEATURE_PROF_DVD_RW_RO:
141 		  case CDIO_MMC_FEATURE_PROF_DVD_RW_SEQ:
142 		  case CDIO_MMC_FEATURE_PROF_DVD_R_DL_SEQ:
143 		  case CDIO_MMC_FEATURE_PROF_DVD_R_DL_JR:
144 		  case CDIO_MMC_FEATURE_PROF_DVD_PRW:
145 		  case CDIO_MMC_FEATURE_PROF_DVD_PR:
146 		  case CDIO_MMC_FEATURE_PROF_DVD_PRW_DL:
147 		  case CDIO_MMC_FEATURE_PROF_DVD_PR_DL:
148 		  case CDIO_MMC_FEATURE_PROF_BD_ROM:
149 		  case CDIO_MMC_FEATURE_PROF_BD_SEQ:
150 		  case CDIO_MMC_FEATURE_PROF_BD_R_RANDOM:
151 		  case CDIO_MMC_FEATURE_PROF_BD_RE:
152 		  case CDIO_MMC_FEATURE_PROF_HD_DVD_ROM:
153 		  case CDIO_MMC_FEATURE_PROF_HD_DVD_R:
154 		  case CDIO_MMC_FEATURE_PROF_HD_DVD_RAM:
155 		    *p_disctype = (cdio_mmc_feature_profile_t) profile_number;
156 		    break;
157 		}
158 	    q += 4;
159 	}
160     }
161     return i_status;
162 }
163 
164 /**
165    Run a SCSI-MMC MMC MODE SENSE command (6- or 10-byte version)
166    and put the results in p_buf
167    @param p_cdio the CD object to be acted upon.
168    @param p_buf pointer to location to store mode sense information
169    @param i_size number of bytes allocated to p_buf
170    @param page which "page" of the mode sense command we are interested in
171    @return DRIVER_OP_SUCCESS if we ran the command ok.
172 */
173 driver_return_code_t
mmc_mode_sense(CdIo_t * p_cdio,void * p_buf,unsigned int i_size,int page)174 mmc_mode_sense(CdIo_t *p_cdio, /*out*/ void *p_buf, unsigned int i_size,
175 	       int page)
176 {
177   /* We used to make a choice as to which routine we'd use based
178      cdio_have_atapi(). But since that calls this in its determination,
179      we had an infinite recursion. So we can't use cdio_have_atapi()
180      (until we put in better capability checks.)
181    */
182     if ( DRIVER_OP_SUCCESS == mmc_mode_sense_6(p_cdio, p_buf, i_size, page) )
183 	return DRIVER_OP_SUCCESS;
184     return mmc_mode_sense_10(p_cdio, p_buf, i_size, page);
185 }
186 
187 /**
188   Set the drive speed in CD-ROM speed units.
189 
190   @param p_cdio	   CD structure set by cdio_open().
191   @param i_drive_speed   speed in CD-ROM speed units. Note this
192                          not Kbytes/sec as would be used in the MMC spec or
193 	                 in mmc_set_speed(). To convert CD-ROM speed units
194 		         to Kbs, multiply the number by 176 (for raw data)
195 		         and by 150 (for filesystem data). On many CD-ROM
196 		         drives, specifying a value too large will result
197 		         in using the fastest speed. GNU/Linux ioctl
198 			 treats <= 0 as max speed, so we'll do that here
199 			 as well.
200 
201   @return the drive speed if greater than 0. -1 if we had an error. is -2
202   returned if this is not implemented for the current driver.
203 
204    @see cdio_set_speed and mmc_set_speed
205 */
206 driver_return_code_t
mmc_set_drive_speed(const CdIo_t * p_cdio,int i_drive_speed)207 mmc_set_drive_speed( const CdIo_t *p_cdio, int i_drive_speed )
208 {
209 	if (i_drive_speed <= 0)
210 		i_drive_speed = 0xffff;
211 	else
212 		i_drive_speed *= 176;
213 
214     return mmc_set_speed(p_cdio, i_drive_speed, 0);
215 }
216