diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/sr_vendor.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/sr_vendor.c')
-rw-r--r-- | drivers/scsi/sr_vendor.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/drivers/scsi/sr_vendor.c b/drivers/scsi/sr_vendor.c new file mode 100644 index 000000000000..78274dc91f5c --- /dev/null +++ b/drivers/scsi/sr_vendor.c | |||
@@ -0,0 +1,329 @@ | |||
1 | /* -*-linux-c-*- | ||
2 | |||
3 | * vendor-specific code for SCSI CD-ROM's goes here. | ||
4 | * | ||
5 | * This is needed becauce most of the new features (multisession and | ||
6 | * the like) are too new to be included into the SCSI-II standard (to | ||
7 | * be exact: there is'nt anything in my draft copy). | ||
8 | * | ||
9 | * Aug 1997: Ha! Got a SCSI-3 cdrom spec across my fingers. SCSI-3 does | ||
10 | * multisession using the READ TOC command (like SONY). | ||
11 | * | ||
12 | * Rearranged stuff here: SCSI-3 is included allways, support | ||
13 | * for NEC/TOSHIBA/HP commands is optional. | ||
14 | * | ||
15 | * Gerd Knorr <kraxel@cs.tu-berlin.de> | ||
16 | * | ||
17 | * -------------------------------------------------------------------------- | ||
18 | * | ||
19 | * support for XA/multisession-CD's | ||
20 | * | ||
21 | * - NEC: Detection and support of multisession CD's. | ||
22 | * | ||
23 | * - TOSHIBA: Detection and support of multisession CD's. | ||
24 | * Some XA-Sector tweaking, required for older drives. | ||
25 | * | ||
26 | * - SONY: Detection and support of multisession CD's. | ||
27 | * added by Thomas Quinot <thomas@cuivre.freenix.fr> | ||
28 | * | ||
29 | * - PIONEER, HITACHI, PLEXTOR, MATSHITA, TEAC, PHILIPS: known to | ||
30 | * work with SONY (SCSI3 now) code. | ||
31 | * | ||
32 | * - HP: Much like SONY, but a little different... (Thomas) | ||
33 | * HP-Writers only ??? Maybe other CD-Writers work with this too ? | ||
34 | * HP 6020 writers now supported. | ||
35 | */ | ||
36 | |||
37 | #include <linux/config.h> | ||
38 | #include <linux/cdrom.h> | ||
39 | #include <linux/errno.h> | ||
40 | #include <linux/string.h> | ||
41 | #include <linux/bcd.h> | ||
42 | #include <linux/blkdev.h> | ||
43 | |||
44 | #include <scsi/scsi.h> | ||
45 | #include <scsi/scsi_cmnd.h> | ||
46 | #include <scsi/scsi_device.h> | ||
47 | #include <scsi/scsi_host.h> | ||
48 | #include <scsi/scsi_ioctl.h> | ||
49 | |||
50 | #include "sr.h" | ||
51 | |||
52 | #if 0 | ||
53 | #define DEBUG | ||
54 | #endif | ||
55 | |||
56 | /* here are some constants to sort the vendors into groups */ | ||
57 | |||
58 | #define VENDOR_SCSI3 1 /* default: scsi-3 mmc */ | ||
59 | |||
60 | #define VENDOR_NEC 2 | ||
61 | #define VENDOR_TOSHIBA 3 | ||
62 | #define VENDOR_WRITER 4 /* pre-scsi3 writers */ | ||
63 | |||
64 | #define VENDOR_TIMEOUT 30*HZ | ||
65 | |||
66 | void sr_vendor_init(Scsi_CD *cd) | ||
67 | { | ||
68 | #ifndef CONFIG_BLK_DEV_SR_VENDOR | ||
69 | cd->vendor = VENDOR_SCSI3; | ||
70 | #else | ||
71 | char *vendor = cd->device->vendor; | ||
72 | char *model = cd->device->model; | ||
73 | |||
74 | /* default */ | ||
75 | cd->vendor = VENDOR_SCSI3; | ||
76 | if (cd->readcd_known) | ||
77 | /* this is true for scsi3/mmc drives - no more checks */ | ||
78 | return; | ||
79 | |||
80 | if (cd->device->type == TYPE_WORM) { | ||
81 | cd->vendor = VENDOR_WRITER; | ||
82 | |||
83 | } else if (!strncmp(vendor, "NEC", 3)) { | ||
84 | cd->vendor = VENDOR_NEC; | ||
85 | if (!strncmp(model, "CD-ROM DRIVE:25", 15) || | ||
86 | !strncmp(model, "CD-ROM DRIVE:36", 15) || | ||
87 | !strncmp(model, "CD-ROM DRIVE:83", 15) || | ||
88 | !strncmp(model, "CD-ROM DRIVE:84 ", 16) | ||
89 | #if 0 | ||
90 | /* my NEC 3x returns the read-raw data if a read-raw | ||
91 | is followed by a read for the same sector - aeb */ | ||
92 | || !strncmp(model, "CD-ROM DRIVE:500", 16) | ||
93 | #endif | ||
94 | ) | ||
95 | /* these can't handle multisession, may hang */ | ||
96 | cd->cdi.mask |= CDC_MULTI_SESSION; | ||
97 | |||
98 | } else if (!strncmp(vendor, "TOSHIBA", 7)) { | ||
99 | cd->vendor = VENDOR_TOSHIBA; | ||
100 | |||
101 | } | ||
102 | #endif | ||
103 | } | ||
104 | |||
105 | |||
106 | /* small handy function for switching block length using MODE SELECT, | ||
107 | * used by sr_read_sector() */ | ||
108 | |||
109 | int sr_set_blocklength(Scsi_CD *cd, int blocklength) | ||
110 | { | ||
111 | unsigned char *buffer; /* the buffer for the ioctl */ | ||
112 | struct packet_command cgc; | ||
113 | struct ccs_modesel_head *modesel; | ||
114 | int rc, density = 0; | ||
115 | |||
116 | #ifdef CONFIG_BLK_DEV_SR_VENDOR | ||
117 | if (cd->vendor == VENDOR_TOSHIBA) | ||
118 | density = (blocklength > 2048) ? 0x81 : 0x83; | ||
119 | #endif | ||
120 | |||
121 | buffer = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA); | ||
122 | if (!buffer) | ||
123 | return -ENOMEM; | ||
124 | |||
125 | #ifdef DEBUG | ||
126 | printk("%s: MODE SELECT 0x%x/%d\n", cd->cdi.name, density, blocklength); | ||
127 | #endif | ||
128 | memset(&cgc, 0, sizeof(struct packet_command)); | ||
129 | cgc.cmd[0] = MODE_SELECT; | ||
130 | cgc.cmd[1] = (1 << 4); | ||
131 | cgc.cmd[4] = 12; | ||
132 | modesel = (struct ccs_modesel_head *) buffer; | ||
133 | memset(modesel, 0, sizeof(*modesel)); | ||
134 | modesel->block_desc_length = 0x08; | ||
135 | modesel->density = density; | ||
136 | modesel->block_length_med = (blocklength >> 8) & 0xff; | ||
137 | modesel->block_length_lo = blocklength & 0xff; | ||
138 | cgc.buffer = buffer; | ||
139 | cgc.buflen = sizeof(*modesel); | ||
140 | cgc.data_direction = DMA_TO_DEVICE; | ||
141 | cgc.timeout = VENDOR_TIMEOUT; | ||
142 | if (0 == (rc = sr_do_ioctl(cd, &cgc))) { | ||
143 | cd->device->sector_size = blocklength; | ||
144 | } | ||
145 | #ifdef DEBUG | ||
146 | else | ||
147 | printk("%s: switching blocklength to %d bytes failed\n", | ||
148 | cd->cdi.name, blocklength); | ||
149 | #endif | ||
150 | kfree(buffer); | ||
151 | return rc; | ||
152 | } | ||
153 | |||
154 | /* This function gets called after a media change. Checks if the CD is | ||
155 | multisession, asks for offset etc. */ | ||
156 | |||
157 | int sr_cd_check(struct cdrom_device_info *cdi) | ||
158 | { | ||
159 | Scsi_CD *cd = cdi->handle; | ||
160 | unsigned long sector; | ||
161 | unsigned char *buffer; /* the buffer for the ioctl */ | ||
162 | struct packet_command cgc; | ||
163 | int rc, no_multi; | ||
164 | |||
165 | if (cd->cdi.mask & CDC_MULTI_SESSION) | ||
166 | return 0; | ||
167 | |||
168 | buffer = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA); | ||
169 | if (!buffer) | ||
170 | return -ENOMEM; | ||
171 | |||
172 | sector = 0; /* the multisession sector offset goes here */ | ||
173 | no_multi = 0; /* flag: the drive can't handle multisession */ | ||
174 | rc = 0; | ||
175 | |||
176 | memset(&cgc, 0, sizeof(struct packet_command)); | ||
177 | |||
178 | switch (cd->vendor) { | ||
179 | |||
180 | case VENDOR_SCSI3: | ||
181 | cgc.cmd[0] = READ_TOC; | ||
182 | cgc.cmd[8] = 12; | ||
183 | cgc.cmd[9] = 0x40; | ||
184 | cgc.buffer = buffer; | ||
185 | cgc.buflen = 12; | ||
186 | cgc.quiet = 1; | ||
187 | cgc.data_direction = DMA_FROM_DEVICE; | ||
188 | cgc.timeout = VENDOR_TIMEOUT; | ||
189 | rc = sr_do_ioctl(cd, &cgc); | ||
190 | if (rc != 0) | ||
191 | break; | ||
192 | if ((buffer[0] << 8) + buffer[1] < 0x0a) { | ||
193 | printk(KERN_INFO "%s: Hmm, seems the drive " | ||
194 | "doesn't support multisession CD's\n", cd->cdi.name); | ||
195 | no_multi = 1; | ||
196 | break; | ||
197 | } | ||
198 | sector = buffer[11] + (buffer[10] << 8) + | ||
199 | (buffer[9] << 16) + (buffer[8] << 24); | ||
200 | if (buffer[6] <= 1) { | ||
201 | /* ignore sector offsets from first track */ | ||
202 | sector = 0; | ||
203 | } | ||
204 | break; | ||
205 | |||
206 | #ifdef CONFIG_BLK_DEV_SR_VENDOR | ||
207 | case VENDOR_NEC:{ | ||
208 | unsigned long min, sec, frame; | ||
209 | cgc.cmd[0] = 0xde; | ||
210 | cgc.cmd[1] = 0x03; | ||
211 | cgc.cmd[2] = 0xb0; | ||
212 | cgc.buffer = buffer; | ||
213 | cgc.buflen = 0x16; | ||
214 | cgc.quiet = 1; | ||
215 | cgc.data_direction = DMA_FROM_DEVICE; | ||
216 | cgc.timeout = VENDOR_TIMEOUT; | ||
217 | rc = sr_do_ioctl(cd, &cgc); | ||
218 | if (rc != 0) | ||
219 | break; | ||
220 | if (buffer[14] != 0 && buffer[14] != 0xb0) { | ||
221 | printk(KERN_INFO "%s: Hmm, seems the cdrom " | ||
222 | "doesn't support multisession CD's\n", | ||
223 | cd->cdi.name); | ||
224 | no_multi = 1; | ||
225 | break; | ||
226 | } | ||
227 | min = BCD2BIN(buffer[15]); | ||
228 | sec = BCD2BIN(buffer[16]); | ||
229 | frame = BCD2BIN(buffer[17]); | ||
230 | sector = min * CD_SECS * CD_FRAMES + sec * CD_FRAMES + frame; | ||
231 | break; | ||
232 | } | ||
233 | |||
234 | case VENDOR_TOSHIBA:{ | ||
235 | unsigned long min, sec, frame; | ||
236 | |||
237 | /* we request some disc information (is it a XA-CD ?, | ||
238 | * where starts the last session ?) */ | ||
239 | cgc.cmd[0] = 0xc7; | ||
240 | cgc.cmd[1] = 0x03; | ||
241 | cgc.buffer = buffer; | ||
242 | cgc.buflen = 4; | ||
243 | cgc.quiet = 1; | ||
244 | cgc.data_direction = DMA_FROM_DEVICE; | ||
245 | cgc.timeout = VENDOR_TIMEOUT; | ||
246 | rc = sr_do_ioctl(cd, &cgc); | ||
247 | if (rc == -EINVAL) { | ||
248 | printk(KERN_INFO "%s: Hmm, seems the drive " | ||
249 | "doesn't support multisession CD's\n", | ||
250 | cd->cdi.name); | ||
251 | no_multi = 1; | ||
252 | break; | ||
253 | } | ||
254 | if (rc != 0) | ||
255 | break; | ||
256 | min = BCD2BIN(buffer[1]); | ||
257 | sec = BCD2BIN(buffer[2]); | ||
258 | frame = BCD2BIN(buffer[3]); | ||
259 | sector = min * CD_SECS * CD_FRAMES + sec * CD_FRAMES + frame; | ||
260 | if (sector) | ||
261 | sector -= CD_MSF_OFFSET; | ||
262 | sr_set_blocklength(cd, 2048); | ||
263 | break; | ||
264 | } | ||
265 | |||
266 | case VENDOR_WRITER: | ||
267 | cgc.cmd[0] = READ_TOC; | ||
268 | cgc.cmd[8] = 0x04; | ||
269 | cgc.cmd[9] = 0x40; | ||
270 | cgc.buffer = buffer; | ||
271 | cgc.buflen = 0x04; | ||
272 | cgc.quiet = 1; | ||
273 | cgc.data_direction = DMA_FROM_DEVICE; | ||
274 | cgc.timeout = VENDOR_TIMEOUT; | ||
275 | rc = sr_do_ioctl(cd, &cgc); | ||
276 | if (rc != 0) { | ||
277 | break; | ||
278 | } | ||
279 | if ((rc = buffer[2]) == 0) { | ||
280 | printk(KERN_WARNING | ||
281 | "%s: No finished session\n", cd->cdi.name); | ||
282 | break; | ||
283 | } | ||
284 | cgc.cmd[0] = READ_TOC; /* Read TOC */ | ||
285 | cgc.cmd[6] = rc & 0x7f; /* number of last session */ | ||
286 | cgc.cmd[8] = 0x0c; | ||
287 | cgc.cmd[9] = 0x40; | ||
288 | cgc.buffer = buffer; | ||
289 | cgc.buflen = 12; | ||
290 | cgc.quiet = 1; | ||
291 | cgc.data_direction = DMA_FROM_DEVICE; | ||
292 | cgc.timeout = VENDOR_TIMEOUT; | ||
293 | rc = sr_do_ioctl(cd, &cgc); | ||
294 | if (rc != 0) { | ||
295 | break; | ||
296 | } | ||
297 | sector = buffer[11] + (buffer[10] << 8) + | ||
298 | (buffer[9] << 16) + (buffer[8] << 24); | ||
299 | break; | ||
300 | #endif /* CONFIG_BLK_DEV_SR_VENDOR */ | ||
301 | |||
302 | default: | ||
303 | /* should not happen */ | ||
304 | printk(KERN_WARNING | ||
305 | "%s: unknown vendor code (%i), not initialized ?\n", | ||
306 | cd->cdi.name, cd->vendor); | ||
307 | sector = 0; | ||
308 | no_multi = 1; | ||
309 | break; | ||
310 | } | ||
311 | cd->ms_offset = sector; | ||
312 | cd->xa_flag = 0; | ||
313 | if (CDS_AUDIO != sr_disk_status(cdi) && 1 == sr_is_xa(cd)) | ||
314 | cd->xa_flag = 1; | ||
315 | |||
316 | if (2048 != cd->device->sector_size) { | ||
317 | sr_set_blocklength(cd, 2048); | ||
318 | } | ||
319 | if (no_multi) | ||
320 | cdi->mask |= CDC_MULTI_SESSION; | ||
321 | |||
322 | #ifdef DEBUG | ||
323 | if (sector) | ||
324 | printk(KERN_DEBUG "%s: multisession offset=%lu\n", | ||
325 | cd->cdi.name, sector); | ||
326 | #endif | ||
327 | kfree(buffer); | ||
328 | return rc; | ||
329 | } | ||