aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/scripts/python/netdev-times.py
blob: 9aa0a32972e80fa7f49b1226751f04970e299882 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
# Display a process of packets and processed time.
# It helps us to investigate networking or network device.
#
# options
# tx: show only tx chart
# rx: show only rx chart
# dev=: show only thing related to specified device
# debug: work with debug mode. It shows buffer status.

import os
import sys

sys.path.append(os.environ['PERF_EXEC_PATH'] + \
	'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')

from perf_trace_context import *
from Core import *
from Util import *

all_event_list = []; # insert all tracepoint event related with this script
irq_dic = {}; # key is cpu and value is a list which stacks irqs
              # which raise NET_RX softirq
net_rx_dic = {}; # key is cpu and value include time of NET_RX softirq-entry
		 # and a list which stacks receive
receive_hunk_list = []; # a list which include a sequence of receive events
rx_skb_list = []; # received packet list for matching
		       # skb_copy_datagram_iovec

buffer_budget = 65536; # the budget of rx_skb_list, tx_queue_list and
		       # tx_xmit_list
of_count_rx_skb_list = 0; # overflow count

tx_queue_list = []; # list of packets which pass through dev_queue_xmit
of_count_tx_queue_list = 0; # overflow count

tx_xmit_list = [];  # list of packets which pass through dev_hard_start_xmit
of_count_tx_xmit_list = 0; # overflow count

tx_free_list = [];  # list of packets which is freed

# options
show_tx = 0;
show_rx = 0;
dev = 0; # store a name of device specified by option "dev="
debug = 0;

# indices of event_info tuple
EINFO_IDX_NAME=   0
EINFO_IDX_CONTEXT=1
EINFO_IDX_CPU=    2
EINFO_IDX_TIME=   3
EINFO_IDX_PID=    4
EINFO_IDX_COMM=   5

# Calculate a time interval(msec) from src(nsec) to dst(nsec)
def diff_msec(src, dst):
	return (dst - src) / 1000000.0

# Display a process of transmitting a packet
def print_transmit(hunk):
	if dev != 0 and hunk['dev'].find(dev) < 0:
		return
	print "%7s %5d %6d.%06dsec %12.3fmsec      %12.3fmsec" % \
		(hunk['dev'], hunk['len'],
		nsecs_secs(hunk['queue_t']),
		nsecs_nsecs(hunk['queue_t'])/1000,
		diff_msec(hunk['queue_t'], hunk['xmit_t']),
		diff_msec(hunk['xmit_t'], hunk['free_t']))

# Format for displaying rx packet processing
PF_IRQ_ENTRY= "  irq_entry(+%.3fmsec irq=%d:%s)"
PF_SOFT_ENTRY="  softirq_entry(+%.3fmsec)"
PF_NAPI_POLL= "  napi_poll_exit(+%.3fmsec %s)"
PF_JOINT=     "         |"
PF_WJOINT=    "         |            |"
PF_NET_RECV=  "         |---netif_receive_skb(+%.3fmsec skb=%x len=%d)"
PF_NET_RX=    "         |---netif_rx(+%.3fmsec skb=%x)"
PF_CPY_DGRAM= "         |      skb_copy_datagram_iovec(+%.3fmsec %d:%s)"
PF_KFREE_SKB= "         |      kfree_skb(+%.3fmsec location=%x)"
PF_CONS_SKB=  "         |      consume_skb(+%.3fmsec)"

# Display a process of received packets and interrputs associated with
# a NET_RX softirq
def print_receive(hunk):
	show_hunk = 0
	irq_list = hunk['irq_list']
	cpu = irq_list[0]['cpu']
	base_t = irq_list[0]['irq_ent_t']
	# check if this hunk should be showed
	if dev != 0:
		for i in range(len(irq_list)):
			if irq_list[i]['name'].find(dev) >= 0:
				show_hunk = 1
				break
	else:
		show_hunk = 1
	if show_hunk == 0:
		return

	print "%d.%06dsec cpu=%d" % \
		(nsecs_secs(base_t), nsecs_nsecs(base_t)/1000, cpu)
	for i in range(len(irq_list)):
		print PF_IRQ_ENTRY % \
			(diff_msec(base_t, irq_list[i]['irq_ent_t']),
			irq_list[i]['irq'], irq_list[i]['name'])
		print PF_JOINT
		irq_event_list = irq_list[i]['event_list']
		for j in range(len(irq_event_list)):
			irq_event = irq_event_list[j]
			if irq_event['event'] == 'netif_rx':
				print PF_NET_RX % \
					(diff_msec(base_t, irq_event['time']),
					irq_event['skbaddr'])
				print PF_JOINT
	print PF_SOFT_ENTRY % \
		diff_msec(base_t, hunk['sirq_ent_t'])
	print PF_JOINT
	event_list = hunk['event_list']
	for i in range(len(event_list)):
		event = event_list[i]
		if event['event_name'] == 'napi_poll':
			print PF_NAPI_POLL % \
			    (diff_msec(base_t, event['event_t']), event['dev'])
			if i == len(event_list) - 1:
				print ""
			else:
				print PF_JOINT
		else:
			print PF_NET_RECV % \
			    (diff_msec(base_t, event['event_t']), event['skbaddr'],
				event['len'])
			if 'comm' in event.keys():
				print PF_WJOINT
				print PF_CPY_DGRAM % \
					(diff_msec(base_t, event['comm_t']),
					event['pid'], event['comm'])
			elif 'handle' in event.keys():
				print PF_WJOINT
				if event['handle'] == "kfree_skb":
					print PF_KFREE_SKB % \
						(diff_msec(base_t,
						event['comm_t']),
						event['location'])
				elif event['handle'] == "consume_skb":
					print PF_CONS_SKB % \
						diff_msec(base_t,
							event['comm_t'])
			print PF_JOINT

def trace_begin():
	global show_tx
	global show_rx
	global dev
	global debug

	for i in range(len(sys.argv)):
		if i == 0:
			continue
		arg = sys.argv[i]
		if arg == 'tx':
			show_tx = 1
		elif arg =='rx':
			show_rx = 1
		elif arg.find('dev=',0, 4) >= 0:
			dev = arg[4:]
		elif arg == 'debug':
			debug = 1
	if show_tx == 0  and show_rx == 0:
		show_tx = 1
		show_rx = 1

def trace_end():
	# order all events in time
	all_event_list.sort(lambda a,b :cmp(a[EINFO_IDX_TIME],
					    b[EINFO_IDX_TIME]))
	# process all events
	for i in range(len(all_event_list)):
		event_info = all_event_list[i]
		name = event_info[EINFO_IDX_NAME]
		if name == 'irq__softirq_exit':
			handle_irq_softirq_exit(event_info)
		elif name == 'irq__softirq_entry':
			handle_irq_softirq_entry(event_info)
		elif name == 'irq__softirq_raise':
			handle_irq_softirq_raise(event_info)
		elif name == 'irq__irq_handler_entry':
			handle_irq_handler_entry(event_info)
		elif name == 'irq__irq_handler_exit':
			handle_irq_handler_exit(event_info)
		elif name == 'napi__napi_poll':
			handle_napi_poll(event_info)
		elif name == 'net__netif_receive_skb':
			handle_netif_receive_skb(event_info)
		elif name == 'net__netif_rx':
			handle_netif_rx(event_info)
		elif name == 'skb__skb_copy_datagram_iovec':
			handle_skb_copy_datagram_iovec(event_info)
		elif name == 'net__net_dev_queue':
			handle_net_dev_queue(event_info)
		elif name == 'net__net_dev_xmit':
			handle_net_dev_xmit(event_info)
		elif name == 'skb__kfree_skb':
			handle_kfree_skb(event_info)
		elif name == 'skb__consume_skb':
			handle_consume_skb(event_info)
	# display receive hunks
	if show_rx:
		for i in range(len(receive_hunk_list)):
			print_receive(receive_hunk_list[i])
	# display transmit hunks
	if show_tx:
		print "   dev    len      Qdisc        " \
			"       netdevice             free"
		for i in range(len(tx_free_list)):
			print_transmit(tx_free_list[i])
	if debug:
		print "debug buffer status"
		print "----------------------------"
		print "xmit Qdisc:remain:%d overflow:%d" % \
			(len(tx_queue_list), of_count_tx_queue_list)
		print "xmit netdevice:remain:%d overflow:%d" % \
			(len(tx_xmit_list), of_count_tx_xmit_list)
		print "receive:remain:%d overflow:%d" % \
			(len(rx_skb_list), of_count_rx_skb_list)

# called from perf, when it finds a correspoinding event
def irq__softirq_entry(name, context, cpu, sec, nsec, pid, comm, vec):
	if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
		return
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
	all_event_list.append(event_info)

def irq__softirq_exit(name, context, cpu, sec, nsec, pid, comm, vec):
	if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
		return
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
	all_event_list.append(event_info)

def irq__softirq_raise(name, context, cpu, sec, nsec, pid, comm, vec):
	if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX":
		return
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec)
	all_event_list.append(event_info)

def irq__irq_handler_entry(name, context, cpu, sec, nsec, pid, comm,
			irq, irq_name):
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
			irq, irq_name)
	all_event_list.append(event_info)

def irq__irq_handler_exit(name, context, cpu, sec, nsec, pid, comm, irq, ret):
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, irq, ret)
	all_event_list.append(event_info)

def napi__napi_poll(name, context, cpu, sec, nsec, pid, comm, napi, dev_name):
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
			napi, dev_name)
	all_event_list.append(event_info)

def net__netif_receive_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr,
			skblen, dev_name):
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
			skbaddr, skblen, dev_name)
	all_event_list.append(event_info)

def net__netif_rx(name, context, cpu, sec, nsec, pid, comm, skbaddr,
			skblen, dev_name):
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
			skbaddr, skblen, dev_name)
	all_event_list.append(event_info)

def net__net_dev_queue(name, context, cpu, sec, nsec, pid, comm,
			skbaddr, skblen, dev_name):
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
			skbaddr, skblen, dev_name)
	all_event_list.append(event_info)

def net__net_dev_xmit(name, context, cpu, sec, nsec, pid, comm,
			skbaddr, skblen, rc, dev_name):
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
			skbaddr, skblen, rc ,dev_name)
	all_event_list.append(event_info)

def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm,
			skbaddr, protocol, location):
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
			skbaddr, protocol, location)
	all_event_list.append(event_info)

def skb__consume_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr):
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
			skbaddr)
	all_event_list.append(event_info)

def skb__skb_copy_datagram_iovec(name, context, cpu, sec, nsec, pid, comm,
	skbaddr, skblen):
	event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm,
			skbaddr, skblen)
	all_event_list.append(event_info)

def handle_irq_handler_entry(event_info):
	(name, context, cpu, time, pid, comm, irq, irq_name) = event_info
	if cpu not in irq_dic.keys():
		irq_dic[cpu] = []
	irq_record = {'irq':irq, 'name':irq_name, 'cpu':cpu, 'irq_ent_t':time}
	irq_dic[cpu].append(irq_record)

def handle_irq_handler_exit(event_info):
	(name, context, cpu, time, pid, comm, irq, ret) = event_info
	if cpu not in irq_dic.keys():
		return
	irq_record = irq_dic[cpu].pop()
	if irq != irq_record['irq']:
		return
	irq_record.update({'irq_ext_t':time})
	# if an irq doesn't include NET_RX softirq, drop.
	if 'event_list' in irq_record.keys():
		irq_dic[cpu].append(irq_record)

def handle_irq_softirq_raise(event_info):
	(name, context, cpu, time, pid, comm, vec) = event_info
	if cpu not in irq_dic.keys() \
	or len(irq_dic[cpu]) == 0:
		return
	irq_record = irq_dic[cpu].pop()
	if 'event_list' in irq_record.keys():
		irq_event_list = irq_record['event_list']
	else:
		irq_event_list = []
	irq_event_list.append({'time':time, 'event':'sirq_raise'})
	irq_record.update({'event_list':irq_event_list})
	irq_dic[cpu].append(irq_record)

def handle_irq_softirq_entry(event_info):
	(name, context, cpu, time, pid, comm, vec) = event_info
	net_rx_dic[cpu] = {'sirq_ent_t':time, 'event_list':[]}

def handle_irq_softirq_exit(event_info):
	(name, context, cpu, time, pid, comm, vec) = event_info
	irq_list = []
	event_list = 0
	if cpu in irq_dic.keys():
		irq_list = irq_dic[cpu]
		del irq_dic[cpu]
	if cpu in net_rx_dic.keys():
		sirq_ent_t = net_rx_dic[cpu]['sirq_ent_t']
		event_list = net_rx_dic[cpu]['event_list']
		del net_rx_dic[cpu]
	if irq_list == [] or event_list == 0:
		return
	rec_data = {'sirq_ent_t':sirq_ent_t, 'sirq_ext_t':time,
		    'irq_list':irq_list, 'event_list':event_list}
	# merge information realted to a NET_RX softirq
	receive_hunk_list.append(rec_data)

def handle_napi_poll(event_info):
	(name, context, cpu, time, pid, comm, napi, dev_name) = event_info
	if cpu in net_rx_dic.keys():
		event_list = net_rx_dic[cpu]['event_list']
		rec_data = {'event_name':'napi_poll',
				'dev':dev_name, 'event_t':time}
		event_list.append(rec_data)

def handle_netif_rx(event_info):
	(name, context, cpu, time, pid, comm,
		skbaddr, skblen, dev_name) = event_info
	if cpu not in irq_dic.keys() \
	or len(irq_dic[cpu]) == 0:
		return
	irq_record = irq_dic[cpu].pop()
	if 'event_list' in irq_record.keys():
		irq_event_list = irq_record['event_list']
	else:
		irq_event_list = []
	irq_event_list.append({'time':time, 'event':'netif_rx',
		'skbaddr':skbaddr, 'skblen':skblen, 'dev_name':dev_name})
	irq_record.update({'event_list':irq_event_list})
	irq_dic[cpu].append(irq_record)

def handle_netif_receive_skb(event_info):
	global of_count_rx_skb_list

	(name, context, cpu, time, pid, comm,
		skbaddr, skblen, dev_name) = event_info
	if cpu in net_rx_dic.keys():
		rec_data = {'event_name':'netif_receive_skb',
			    'event_t':time, 'skbaddr':skbaddr, 'len':skblen}
		event_list = net_rx_dic[cpu]['event_list']
		event_list.append(rec_data)
		rx_skb_list.insert(0, rec_data)
		if len(rx_skb_list) > buffer_budget:
			rx_skb_list.pop()
			of_count_rx_skb_list += 1

def handle_net_dev_queue(event_info):
	global of_count_tx_queue_list

	(name, context, cpu, time, pid, comm,
		skbaddr, skblen, dev_name) = event_info
	skb = {'dev':dev_name, 'skbaddr':skbaddr, 'len':skblen, 'queue_t':time}
	tx_queue_list.insert(0, skb)
	if len(tx_queue_list) > buffer_budget:
		tx_queue_list.pop()
		of_count_tx_queue_list += 1

def handle_net_dev_xmit(event_info):
	global of_count_tx_xmit_list

	(name, context, cpu, time, pid, comm,
		skbaddr, skblen, rc, dev_name) = event_info
	if rc == 0: # NETDEV_TX_OK
		for i in range(len(tx_queue_list)):
			skb = tx_queue_list[i]
			if skb['skbaddr'] == skbaddr:
				skb['xmit_t'] = time
				tx_xmit_list.insert(0, skb)
				del tx_queue_list[i]
				if len(tx_xmit_list) > buffer_budget:
					tx_xmit_list.pop()
					of_count_tx_xmit_list += 1
				return

def handle_kfree_skb(event_info):
	(name, context, cpu, time, pid, comm,
		skbaddr, protocol, location) = event_info
	for i in range(len(tx_queue_list)):
		skb = tx_queue_list[i]
		if skb['skbaddr'] == skbaddr:
			del tx_queue_list[i]
			return
	for i in range(len(tx_xmit_list)):
		skb = tx_xmit_list[i]
		if skb['skbaddr'] == skbaddr:
			skb['free_t'] = time
			tx_free_list.append(skb)
			del tx_xmit_list[i]
			return
	for i in range(len(rx_skb_list)):
		rec_data = rx_skb_list[i]
		if rec_data['skbaddr'] == skbaddr:
			rec_data.update({'handle':"kfree_skb",
					'comm':comm, 'pid':pid, 'comm_t':time})
			del rx_skb_list[i]
			return

def handle_consume_skb(event_info):
	(name, context, cpu, time, pid, comm, skbaddr) = event_info
	for i in range(len(tx_xmit_list)):
		skb = tx_xmit_list[i]
		if skb['skbaddr'] == skbaddr:
			skb['free_t'] = time
			tx_free_list.append(skb)
			del tx_xmit_list[i]
			return

def handle_skb_copy_datagram_iovec(event_info):
	(name, context, cpu, time, pid, comm, skbaddr, skblen) = event_info
	for i in range(len(rx_skb_list)):
		rec_data = rx_skb_list[i]
		if skbaddr == rec_data['skbaddr']:
			rec_data.update({'handle':"skb_copy_datagram_iovec",
					'comm':comm, 'pid':pid, 'comm_t':time})
			del rx_skb_list[i]
			return
an class="hl kwd">bio_sectors(failed_command->bio), 4U); sector &= ~(bio_sectors - 1); if (sector < get_capacity(info->disk) && drive->probed_capacity - sector < 4 * 75) set_capacity(info->disk, sector); } } ide_cd_log_error(drive->name, failed_command, sense); } static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense, struct request *failed_command) { struct cdrom_info *info = drive->driver_data; struct request *rq = &info->request_sense_request; ide_debug_log(IDE_DBG_SENSE, "Call %s\n", __func__); if (sense == NULL) sense = &info->sense_data; /* stuff the sense request in front of our current request */ blk_rq_init(NULL, rq); rq->cmd_type = REQ_TYPE_ATA_PC; rq->rq_disk = info->disk; rq->data = sense; rq->cmd[0] = GPCMD_REQUEST_SENSE; rq->cmd[4] = 18; rq->data_len = 18; rq->cmd_type = REQ_TYPE_SENSE; rq->cmd_flags |= REQ_PREEMPT; /* NOTE! Save the failed command in "rq->buffer" */ rq->buffer = (void *) failed_command; if (failed_command) ide_debug_log(IDE_DBG_SENSE, "failed_cmd: 0x%x\n", failed_command->cmd[0]); ide_do_drive_cmd(drive, rq); } static void cdrom_end_request(ide_drive_t *drive, int uptodate) { struct request *rq = drive->hwif->rq; int nsectors = rq->hard_cur_sectors; ide_debug_log(IDE_DBG_FUNC, "Call %s, cmd: 0x%x, uptodate: 0x%x, " "nsectors: %d\n", __func__, rq->cmd[0], uptodate, nsectors); if (blk_sense_request(rq) && uptodate) { /* * For REQ_TYPE_SENSE, "rq->buffer" points to the original * failed request */ struct request *failed = (struct request *) rq->buffer; struct cdrom_info *info = drive->driver_data; void *sense = &info->sense_data; if (failed) { if (failed->sense) { sense = failed->sense; failed->sense_len = rq->sense_len; } cdrom_analyze_sense_data(drive, failed, sense); /* * now end the failed request */ if (blk_fs_request(failed)) { if (ide_end_dequeued_request(drive, failed, 0, failed->hard_nr_sectors)) BUG(); } else { if (blk_end_request(failed, -EIO, failed->data_len)) BUG(); } } else cdrom_analyze_sense_data(drive, NULL, sense); } if (!rq->current_nr_sectors && blk_fs_request(rq)) uptodate = 1; /* make sure it's fully ended */ if (blk_pc_request(rq)) nsectors = (rq->data_len + 511) >> 9; if (!nsectors) nsectors = 1; ide_debug_log(IDE_DBG_FUNC, "Exit %s, uptodate: 0x%x, nsectors: %d\n", __func__, uptodate, nsectors); ide_end_request(drive, uptodate, nsectors); } static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 st) { if (st & 0x80) return; ide_dump_status(drive, msg, st); } /* * Returns: * 0: if the request should be continued. * 1: if the request was ended. */ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) { ide_hwif_t *hwif = drive->hwif; struct request *rq = hwif->rq; int stat, err, sense_key; /* check for errors */ stat = hwif->tp_ops->read_status(hwif); if (stat_ret) *stat_ret = stat; if (OK_STAT(stat, good_stat, BAD_R_STAT)) return 0; /* get the IDE error register */ err = ide_read_error(drive); sense_key = err >> 4; if (rq == NULL) { printk(KERN_ERR PFX "%s: missing rq in %s\n", drive->name, __func__); return 1; } ide_debug_log(IDE_DBG_RQ, "%s: stat: 0x%x, good_stat: 0x%x, " "rq->cmd[0]: 0x%x, rq->cmd_type: 0x%x, err: 0x%x\n", __func__, stat, good_stat, rq->cmd[0], rq->cmd_type, err); if (blk_sense_request(rq)) { /* * We got an error trying to get sense info from the drive * (probably while trying to recover from a former error). * Just give up. */ rq->cmd_flags |= REQ_FAILED; cdrom_end_request(drive, 0); ide_error(drive, "request sense failure", stat); return 1; } else if (blk_pc_request(rq) || rq->cmd_type == REQ_TYPE_ATA_PC) { /* All other functions, except for READ. */ /* * if we have an error, pass back CHECK_CONDITION as the * scsi status byte */ if (blk_pc_request(rq) && !rq->errors) rq->errors = SAM_STAT_CHECK_CONDITION; /* check for tray open */ if (sense_key == NOT_READY) { cdrom_saw_media_change(drive); } else if (sense_key == UNIT_ATTENTION) { /* check for media change */ cdrom_saw_media_change(drive); return 0; } else if (sense_key == ILLEGAL_REQUEST && rq->cmd[0] == GPCMD_START_STOP_UNIT) { /* * Don't print error message for this condition-- * SFF8090i indicates that 5/24/00 is the correct * response to a request to close the tray if the * drive doesn't have that capability. * cdrom_log_sense() knows this! */ } else if (!(rq->cmd_flags & REQ_QUIET)) { /* otherwise, print an error */ ide_dump_status(drive, "packet command error", stat); } rq->cmd_flags |= REQ_FAILED; /* * instead of playing games with moving completions around, * remove failed request completely and end it when the * request sense has completed */ goto end_request; } else if (blk_fs_request(rq)) { int do_end_request = 0; /* handle errors from READ and WRITE requests */ if (blk_noretry_request(rq)) do_end_request = 1; if (sense_key == NOT_READY) { /* tray open */ if (rq_data_dir(rq) == READ) { cdrom_saw_media_change(drive); /* fail the request */ printk(KERN_ERR PFX "%s: tray open\n", drive->name); do_end_request = 1; } else { struct cdrom_info *info = drive->driver_data; /* * Allow the drive 5 seconds to recover, some * devices will return this error while flushing * data from cache. */ if (!rq->errors) info->write_timeout = jiffies + ATAPI_WAIT_WRITE_BUSY; rq->errors = 1; if (time_after(jiffies, info->write_timeout)) do_end_request = 1; else { struct request_queue *q = drive->queue; unsigned long flags; /* * take a breather relying on the unplug * timer to kick us again */ spin_lock_irqsave(q->queue_lock, flags); blk_plug_device(q); spin_unlock_irqrestore(q->queue_lock, flags); return 1; } } } else if (sense_key == UNIT_ATTENTION) { /* media change */ cdrom_saw_media_change(drive); /* * Arrange to retry the request but be sure to give up * if we've retried too many times. */ if (++rq->errors > ERROR_MAX) do_end_request = 1; } else if (sense_key == ILLEGAL_REQUEST || sense_key == DATA_PROTECT) { /* * No point in retrying after an illegal request or data * protect error. */ ide_dump_status_no_sense(drive, "command error", stat); do_end_request = 1; } else if (sense_key == MEDIUM_ERROR) { /* * No point in re-trying a zillion times on a bad * sector. If we got here the error is not correctable. */ ide_dump_status_no_sense(drive, "media error (bad sector)", stat); do_end_request = 1; } else if (sense_key == BLANK_CHECK) { /* disk appears blank ?? */ ide_dump_status_no_sense(drive, "media error (blank)", stat); do_end_request = 1; } else if ((err & ~ATA_ABORTED) != 0) { /* go to the default handler for other errors */ ide_error(drive, "cdrom_decode_status", stat); return 1; } else if ((++rq->errors > ERROR_MAX)) { /* we've racked up too many retries, abort */ do_end_request = 1; } /* * End a request through request sense analysis when we have * sense data. We need this in order to perform end of media * processing. */ if (do_end_request) goto end_request; /* * If we got a CHECK_CONDITION status, queue * a request sense command. */ if (stat & ATA_ERR) cdrom_queue_request_sense(drive, NULL, NULL); } else { blk_dump_rq_flags(rq, PFX "bad rq"); cdrom_end_request(drive, 0); } /* retry, or handle the next request */ return 1; end_request: if (stat & ATA_ERR) { struct request_queue *q = drive->queue; unsigned long flags; spin_lock_irqsave(q->queue_lock, flags); blkdev_dequeue_request(rq); spin_unlock_irqrestore(q->queue_lock, flags); hwif->rq = NULL; cdrom_queue_request_sense(drive, rq->sense, rq); } else cdrom_end_request(drive, 0); return 1; } /* * Check the contents of the interrupt reason register from the cdrom * and attempt to recover if there are problems. Returns 0 if everything's * ok; nonzero if the request has been terminated. */ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, int len, int ireason, int rw) { ide_hwif_t *hwif = drive->hwif; ide_debug_log(IDE_DBG_FUNC, "Call %s, ireason: 0x%x, rw: 0x%x\n", __func__, ireason, rw); /* * ireason == 0: the drive wants to receive data from us * ireason == 2: the drive is expecting to transfer data to us */ if (ireason == (!rw << 1)) return 0; else if (ireason == (rw << 1)) { /* whoops... */ printk(KERN_ERR PFX "%s: %s: wrong transfer direction!\n", drive->name, __func__); ide_pad_transfer(drive, rw, len); } else if (rw == 0 && ireason == 1) { /* * Some drives (ASUS) seem to tell us that status info is * available. Just get it and ignore. */ (void)hwif->tp_ops->read_status(hwif); return 0; } else { /* drive wants a command packet, or invalid ireason... */ printk(KERN_ERR PFX "%s: %s: bad interrupt reason 0x%02x\n", drive->name, __func__, ireason); } if (rq->cmd_type == REQ_TYPE_ATA_PC) rq->cmd_flags |= REQ_FAILED; cdrom_end_request(drive, 0); return -1; } /* * Assume that the drive will always provide data in multiples of at least * SECTOR_SIZE, as it gets hairy to keep track of the transfers otherwise. */ static int ide_cd_check_transfer_size(ide_drive_t *drive, int len) { ide_debug_log(IDE_DBG_FUNC, "Call %s, len: %d\n", __func__, len); if ((len % SECTOR_SIZE) == 0) return 0; printk(KERN_ERR PFX "%s: %s: Bad transfer size %d\n", drive->name, __func__, len); if (drive->atapi_flags & IDE_AFLAG_LIMIT_NFRAMES) printk(KERN_ERR PFX "This drive is not supported by this " "version of the driver\n"); else { printk(KERN_ERR PFX "Trying to limit transfer sizes\n"); drive->atapi_flags |= IDE_AFLAG_LIMIT_NFRAMES; } return 1; } static ide_startstop_t ide_cd_prepare_rw_request(ide_drive_t *drive, struct request *rq) { ide_debug_log(IDE_DBG_RQ, "Call %s: rq->cmd_flags: 0x%x\n", __func__, rq->cmd_flags); if (rq_data_dir(rq) == READ) { unsigned short sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS; int nskip = rq->sector & (sectors_per_frame - 1); /* * If the requested sector doesn't start on a frame boundary, * we must adjust the start of the transfer so that it does, * and remember to skip the first few sectors. * * If the rq->current_nr_sectors field is larger than the size * of the buffer, it will mean that we're to skip a number of * sectors equal to the amount by which rq->current_nr_sectors * is larger than the buffer size. */ if (nskip > 0) { /* sanity check... */ if (rq->current_nr_sectors != bio_cur_sectors(rq->bio)) { printk(KERN_ERR PFX "%s: %s: buffer botch (%u)\n", drive->name, __func__, rq->current_nr_sectors); cdrom_end_request(drive, 0); return ide_stopped; } rq->current_nr_sectors += nskip; } } /* set up the command */ rq->timeout = ATAPI_WAIT_PC; return ide_started; } /* * Fix up a possibly partially-processed request so that we can start it over * entirely, or even put it back on the request queue. */ static void ide_cd_restore_request(ide_drive_t *drive, struct request *rq) { ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); if (rq->buffer != bio_data(rq->bio)) { sector_t n = (rq->buffer - (char *)bio_data(rq->bio)) / SECTOR_SIZE; rq->buffer = bio_data(rq->bio); rq->nr_sectors += n; rq->sector -= n; } rq->current_nr_sectors = bio_cur_sectors(rq->bio); rq->hard_cur_sectors = rq->current_nr_sectors; rq->hard_nr_sectors = rq->nr_sectors; rq->hard_sector = rq->sector; rq->q->prep_rq_fn(rq->q, rq); } static void ide_cd_request_sense_fixup(ide_drive_t *drive, struct request *rq) { ide_debug_log(IDE_DBG_FUNC, "Call %s, rq->cmd[0]: 0x%x\n", __func__, rq->cmd[0]); /* * Some of the trailing request sense fields are optional, * and some drives don't send them. Sigh. */ if (rq->cmd[0] == GPCMD_REQUEST_SENSE && rq->data_len > 0 && rq->data_len <= 5) while (rq->data_len > 0) { *(u8 *)rq->data++ = 0; --rq->data_len; } } int ide_cd_queue_pc(ide_drive_t *drive, const unsigned char *cmd, int write, void *buffer, unsigned *bufflen, struct request_sense *sense, int timeout, unsigned int cmd_flags) { struct cdrom_info *info = drive->driver_data; struct request_sense local_sense; int retries = 10; unsigned int flags = 0; if (!sense) sense = &local_sense; ide_debug_log(IDE_DBG_PC, "Call %s, cmd[0]: 0x%x, write: 0x%x, " "timeout: %d, cmd_flags: 0x%x\n", __func__, cmd[0], write, timeout, cmd_flags); /* start of retry loop */ do { struct request *rq; int error; rq = blk_get_request(drive->queue, write, __GFP_WAIT); memcpy(rq->cmd, cmd, BLK_MAX_CDB); rq->cmd_type = REQ_TYPE_ATA_PC; rq->sense = sense; rq->cmd_flags |= cmd_flags; rq->timeout = timeout; if (buffer) { rq->data = buffer; rq->data_len = *bufflen; } error = blk_execute_rq(drive->queue, info->disk, rq, 0); if (buffer) *bufflen = rq->data_len; flags = rq->cmd_flags; blk_put_request(rq); /* * FIXME: we should probably abort/retry or something in case of * failure. */ if (flags & REQ_FAILED) { /* * The request failed. Retry if it was due to a unit * attention status (usually means media was changed). */ struct request_sense *reqbuf = sense; if (reqbuf->sense_key == UNIT_ATTENTION) cdrom_saw_media_change(drive); else if (reqbuf->sense_key == NOT_READY && reqbuf->asc == 4 && reqbuf->ascq != 4) { /* * The drive is in the process of loading * a disk. Retry, but wait a little to give * the drive time to complete the load. */ ssleep(2); } else { /* otherwise, don't retry */ retries = 0; } --retries; } /* end of retry loop */ } while ((flags & REQ_FAILED) && retries >= 0); /* return an error if the command failed */ return (flags & REQ_FAILED) ? -EIO : 0; } /* * Called from blk_end_request_callback() after the data of the request is * completed and before the request itself is completed. By returning value '1', * blk_end_request_callback() returns immediately without completing it. */ static int cdrom_newpc_intr_dummy_cb(struct request *rq) { return 1; } static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; struct request *rq = hwif->rq; xfer_func_t *xferfunc; ide_expiry_t *expiry = NULL; int dma_error = 0, dma, stat, thislen, uptodate = 0; int write = (rq_data_dir(rq) == WRITE) ? 1 : 0; unsigned int timeout; u16 len; u8 ireason; ide_debug_log(IDE_DBG_PC, "Call %s, rq->cmd[0]: 0x%x, write: 0x%x\n", __func__, rq->cmd[0], write); /* check for errors */ dma = drive->dma; if (dma) { drive->dma = 0; dma_error = hwif->dma_ops->dma_end(drive); if (dma_error) { printk(KERN_ERR PFX "%s: DMA %s error\n", drive->name, write ? "write" : "read"); ide_dma_off(drive); } } if (cdrom_decode_status(drive, 0, &stat)) return ide_stopped; /* using dma, transfer is complete now */ if (dma) { if (dma_error) return ide_error(drive, "dma error", stat); if (blk_fs_request(rq)) { ide_end_request(drive, 1, rq->nr_sectors); return ide_stopped; } else if (rq->cmd_type == REQ_TYPE_ATA_PC && !rq->bio) { ide_end_request(drive, 1, 1); return ide_stopped; } goto end_request; } ide_read_bcount_and_ireason(drive, &len, &ireason); thislen = blk_fs_request(rq) ? len : rq->data_len; if (thislen > len) thislen = len; ide_debug_log(IDE_DBG_PC, "%s: DRQ: stat: 0x%x, thislen: %d\n", __func__, stat, thislen); /* If DRQ is clear, the command has completed. */ if ((stat & ATA_DRQ) == 0) { if (blk_fs_request(rq)) { /* * If we're not done reading/writing, complain. * Otherwise, complete the command normally. */ uptodate = 1; if (rq->current_nr_sectors > 0) { printk(KERN_ERR PFX "%s: %s: data underrun " "(%d blocks)\n", drive->name, __func__, rq->current_nr_sectors); if (!write) rq->cmd_flags |= REQ_FAILED; uptodate = 0; } cdrom_end_request(drive, uptodate); return ide_stopped; } else if (!blk_pc_request(rq)) { ide_cd_request_sense_fixup(drive, rq); /* complain if we still have data left to transfer */ uptodate = rq->data_len ? 0 : 1; } goto end_request; } /* check which way to transfer data */ if (ide_cd_check_ireason(drive, rq, len, ireason, write)) return ide_stopped; if (blk_fs_request(rq)) { if (write == 0) { int nskip; if (ide_cd_check_transfer_size(drive, len)) { cdrom_end_request(drive, 0); return ide_stopped; } /* * First, figure out if we need to bit-bucket * any of the leading sectors. */ nskip = min_t(int, rq->current_nr_sectors - bio_cur_sectors(rq->bio), thislen >> 9); if (nskip > 0) { ide_pad_transfer(drive, write, nskip << 9); rq->current_nr_sectors -= nskip; thislen -= (nskip << 9); } } } if (ireason == 0) { write = 1; xferfunc = hwif->tp_ops->output_data; } else { write = 0; xferfunc = hwif->tp_ops->input_data; } ide_debug_log(IDE_DBG_PC, "%s: data transfer, rq->cmd_type: 0x%x, " "ireason: 0x%x\n", __func__, rq->cmd_type, ireason); /* transfer data */ while (thislen > 0) { u8 *ptr = blk_fs_request(rq) ? NULL : rq->data; int blen = rq->data_len; /* bio backed? */ if (rq->bio) { if (blk_fs_request(rq)) { ptr = rq->buffer; blen = rq->current_nr_sectors << 9; } else { ptr = bio_data(rq->bio); blen = bio_iovec(rq->bio)->bv_len; } } if (!ptr) { if (blk_fs_request(rq) && !write) /* * If the buffers are full, pipe the rest into * oblivion. */ ide_pad_transfer(drive, 0, thislen); else { printk(KERN_ERR PFX "%s: confused, missing data\n", drive->name); blk_dump_rq_flags(rq, rq_data_dir(rq) ? "cdrom_newpc_intr, write" : "cdrom_newpc_intr, read"); } break; } if (blen > thislen) blen = thislen; xferfunc(drive, NULL, ptr, blen); thislen -= blen; len -= blen; if (blk_fs_request(rq)) { rq->buffer += blen; rq->nr_sectors -= (blen >> 9); rq->current_nr_sectors -= (blen >> 9); rq->sector += (blen >> 9); if (rq->current_nr_sectors == 0 && rq->nr_sectors) cdrom_end_request(drive, 1); } else { rq->data_len -= blen; /* * The request can't be completed until DRQ is cleared. * So complete the data, but don't complete the request * using the dummy function for the callback feature * of blk_end_request_callback(). */ if (rq->bio) blk_end_request_callback(rq, 0, blen, cdrom_newpc_intr_dummy_cb); else rq->data += blen; } if (!write && blk_sense_request(rq)) rq->sense_len += blen; } /* pad, if necessary */ if (!blk_fs_request(rq) && len > 0) ide_pad_transfer(drive, write, len); if (blk_pc_request(rq)) { timeout = rq->timeout; } else { timeout = ATAPI_WAIT_PC; if (!blk_fs_request(rq)) expiry = ide_cd_expiry; } ide_set_handler(drive, cdrom_newpc_intr, timeout, expiry); return ide_started; end_request: if (blk_pc_request(rq)) { unsigned int dlen = rq->data_len; if (dma) rq->data_len = 0; if (blk_end_request(rq, 0, dlen)) BUG(); hwif->rq = NULL; } else { if (!uptodate) rq->cmd_flags |= REQ_FAILED; cdrom_end_request(drive, uptodate); } return ide_stopped; } static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) { struct cdrom_info *cd = drive->driver_data; int write = rq_data_dir(rq) == WRITE; unsigned short sectors_per_frame = queue_hardsect_size(drive->queue) >> SECTOR_BITS; ide_debug_log(IDE_DBG_RQ, "Call %s, rq->cmd[0]: 0x%x, write: 0x%x, " "secs_per_frame: %u\n", __func__, rq->cmd[0], write, sectors_per_frame); if (write) { /* disk has become write protected */ if (get_disk_ro(cd->disk)) { cdrom_end_request(drive, 0); return ide_stopped; } } else { /* * We may be retrying this request after an error. Fix up any * weirdness which might be present in the request packet. */ ide_cd_restore_request(drive, rq); } /* use DMA, if possible / writes *must* be hardware frame aligned */ if ((rq->nr_sectors & (sectors_per_frame - 1)) || (rq->sector & (sectors_per_frame - 1))) { if (write) { cdrom_end_request(drive, 0); return ide_stopped; } drive->dma = 0; } else drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); if (write) cd->devinfo.media_written = 1; return ide_started; } static void cdrom_do_block_pc(ide_drive_t *drive, struct request *rq) { ide_debug_log(IDE_DBG_PC, "Call %s, rq->cmd[0]: 0x%x, " "rq->cmd_type: 0x%x\n", __func__, rq->cmd[0], rq->cmd_type); if (blk_pc_request(rq)) rq->cmd_flags |= REQ_QUIET; else rq->cmd_flags &= ~REQ_FAILED; drive->dma = 0; /* sg request */ if (rq->bio || ((rq->cmd_type == REQ_TYPE_ATA_PC) && rq->data_len)) { struct request_queue *q = drive->queue; unsigned int alignment; char *buf; if (rq->bio) buf = bio_data(rq->bio); else buf = rq->data; drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); /* * check if dma is safe * * NOTE! The "len" and "addr" checks should possibly have * separate masks. */ alignment = queue_dma_alignment(q) | q->dma_pad_mask; if ((unsigned long)buf & alignment || rq->data_len & q->dma_pad_mask || object_is_on_stack(buf)) drive->dma = 0; } } static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, sector_t block) { ide_debug_log(IDE_DBG_RQ, "Call %s, rq->cmd[0]: 0x%x, " "rq->cmd_type: 0x%x, block: %llu\n", __func__, rq->cmd[0], rq->cmd_type, (unsigned long long)block); if (blk_fs_request(rq)) { if (cdrom_start_rw(drive, rq) == ide_stopped) return ide_stopped; if (ide_cd_prepare_rw_request(drive, rq) == ide_stopped) return ide_stopped; } else if (blk_sense_request(rq) || blk_pc_request(rq) || rq->cmd_type == REQ_TYPE_ATA_PC) { if (!rq->timeout) rq->timeout = ATAPI_WAIT_PC; cdrom_do_block_pc(drive, rq); } else if (blk_special_request(rq)) { /* right now this can only be a reset... */ cdrom_end_request(drive, 1); return ide_stopped; } else { blk_dump_rq_flags(rq, DRV_NAME " bad flags"); cdrom_end_request(drive, 0); return ide_stopped; } return ide_issue_pc(drive); } /* * Ioctl handling. * * Routines which queue packet commands take as a final argument a pointer to a * request_sense struct. If execution of the command results in an error with a * CHECK CONDITION status, this structure will be filled with the results of the * subsequent request sense command. The pointer can also be NULL, in which case * no sense information is returned. */ static void msf_from_bcd(struct atapi_msf *msf) { msf->minute = bcd2bin(msf->minute); msf->second = bcd2bin(msf->second); msf->frame = bcd2bin(msf->frame); } int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense) { struct cdrom_info *info = drive->driver_data; struct cdrom_device_info *cdi = &info->devinfo; unsigned char cmd[BLK_MAX_CDB]; ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); memset(cmd, 0, BLK_MAX_CDB); cmd[0] = GPCMD_TEST_UNIT_READY; /* * Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to switch CDs * instead of supporting the LOAD_UNLOAD opcode. */ cmd[7] = cdi->sanyo_slot % 3; return ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, sense, 0, REQ_QUIET); } static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, unsigned long *sectors_per_frame, struct request_sense *sense) { struct { __be32 lba; __be32 blocklen; } capbuf; int stat; unsigned char cmd[BLK_MAX_CDB]; unsigned len = sizeof(capbuf); u32 blocklen; ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); memset(cmd, 0, BLK_MAX_CDB); cmd[0] = GPCMD_READ_CDVD_CAPACITY; stat = ide_cd_queue_pc(drive, cmd, 0, &capbuf, &len, sense, 0, REQ_QUIET); if (stat) return stat; /* * Sanity check the given block size */ blocklen = be32_to_cpu(capbuf.blocklen); switch (blocklen) { case 512: case 1024: case 2048: case 4096: break; default: printk(KERN_ERR PFX "%s: weird block size %u\n", drive->name, blocklen); printk(KERN_ERR PFX "%s: default to 2kb block size\n", drive->name); blocklen = 2048; break; } *capacity = 1 + be32_to_cpu(capbuf.lba); *sectors_per_frame = blocklen >> SECTOR_BITS; ide_debug_log(IDE_DBG_PROBE, "%s: cap: %lu, sectors_per_frame: %lu\n", __func__, *capacity, *sectors_per_frame); return 0; } static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag, int format, char *buf, int buflen, struct request_sense *sense) { unsigned char cmd[BLK_MAX_CDB]; ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); memset(cmd, 0, BLK_MAX_CDB); cmd[0] = GPCMD_READ_TOC_PMA_ATIP; cmd[6] = trackno; cmd[7] = (buflen >> 8); cmd[8] = (buflen & 0xff); cmd[9] = (format << 6); if (msf_flag) cmd[1] = 2; return ide_cd_queue_pc(drive, cmd, 0, buf, &buflen, sense, 0, REQ_QUIET); } /* Try to read the entire TOC for the disk into our internal buffer. */ int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense) { int stat, ntracks, i; struct cdrom_info *info = drive->driver_data; struct cdrom_device_info *cdi = &info->devinfo; struct atapi_toc *toc = info->toc; struct { struct atapi_toc_header hdr; struct atapi_toc_entry ent; } ms_tmp; long last_written; unsigned long sectors_per_frame = SECTORS_PER_FRAME; ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); if (toc == NULL) { /* try to allocate space */ toc = kmalloc(sizeof(struct atapi_toc), GFP_KERNEL); if (toc == NULL) { printk(KERN_ERR PFX "%s: No cdrom TOC buffer!\n", drive->name); return -ENOMEM; } info->toc = toc; } /* * Check to see if the existing data is still valid. If it is, * just return. */ (void) cdrom_check_status(drive, sense); if (drive->atapi_flags & IDE_AFLAG_TOC_VALID) return 0; /* try to get the total cdrom capacity and sector size */ stat = cdrom_read_capacity(drive, &toc->capacity, &sectors_per_frame, sense); if (stat) toc->capacity = 0x1fffff; set_capacity(info->disk, toc->capacity * sectors_per_frame); /* save a private copy of the TOC capacity for error handling */ drive->probed_capacity = toc->capacity * sectors_per_frame; blk_queue_hardsect_size(drive->queue, sectors_per_frame << SECTOR_BITS); /* first read just the header, so we know how long the TOC is */ stat = cdrom_read_tocentry(drive, 0, 1, 0, (char *) &toc->hdr, sizeof(struct atapi_toc_header), sense); if (stat) return stat; if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) { toc->hdr.first_track = bcd2bin(toc->hdr.first_track); toc->hdr.last_track = bcd2bin(toc->hdr.last_track); } ntracks = toc->hdr.last_track - toc->hdr.first_track + 1; if (ntracks <= 0) return -EIO; if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS; /* now read the whole schmeer */ stat = cdrom_read_tocentry(drive, toc->hdr.first_track, 1, 0, (char *)&toc->hdr, sizeof(struct atapi_toc_header) + (ntracks + 1) * sizeof(struct atapi_toc_entry), sense); if (stat && toc->hdr.first_track > 1) { /* * Cds with CDI tracks only don't have any TOC entries, despite * of this the returned values are * first_track == last_track = number of CDI tracks + 1, * so that this case is indistinguishable from the same layout * plus an additional audio track. If we get an error for the * regular case, we assume a CDI without additional audio * tracks. In this case the readable TOC is empty (CDI tracks * are not included) and only holds the Leadout entry. * * Heiko Eißfeldt. */ ntracks = 0; stat = cdrom_read_tocentry(drive, CDROM_LEADOUT, 1, 0, (char *)&toc->hdr, sizeof(struct atapi_toc_header) + (ntracks + 1) * sizeof(struct atapi_toc_entry), sense); if (stat) return stat; if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) { toc->hdr.first_track = (u8)bin2bcd(CDROM_LEADOUT); toc->hdr.last_track = (u8)bin2bcd(CDROM_LEADOUT); } else { toc->hdr.first_track = CDROM_LEADOUT; toc->hdr.last_track = CDROM_LEADOUT; } } if (stat) return stat; toc->hdr.toc_length = be16_to_cpu(toc->hdr.toc_length); if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) { toc->hdr.first_track = bcd2bin(toc->hdr.first_track); toc->hdr.last_track = bcd2bin(toc->hdr.last_track); } for (i = 0; i <= ntracks; i++) { if (drive->atapi_flags & IDE_AFLAG_TOCADDR_AS_BCD) { if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) toc->ent[i].track = bcd2bin(toc->ent[i].track); msf_from_bcd(&toc->ent[i].addr.msf); } toc->ent[i].addr.lba = msf_to_lba(toc->ent[i].addr.msf.minute, toc->ent[i].addr.msf.second, toc->ent[i].addr.msf.frame); } if (toc->hdr.first_track != CDROM_LEADOUT) { /* read the multisession information */ stat = cdrom_read_tocentry(drive, 0, 0, 1, (char *)&ms_tmp, sizeof(ms_tmp), sense); if (stat) return stat; toc->last_session_lba = be32_to_cpu(ms_tmp.ent.addr.lba); } else { ms_tmp.hdr.last_track = CDROM_LEADOUT; ms_tmp.hdr.first_track = ms_tmp.hdr.last_track; toc->last_session_lba = msf_to_lba(0, 2, 0); /* 0m 2s 0f */ } if (drive->atapi_flags & IDE_AFLAG_TOCADDR_AS_BCD) { /* re-read multisession information using MSF format */ stat = cdrom_read_tocentry(drive, 0, 1, 1, (char *)&ms_tmp, sizeof(ms_tmp), sense); if (stat) return stat; msf_from_bcd(&ms_tmp.ent.addr.msf); toc->last_session_lba = msf_to_lba(ms_tmp.ent.addr.msf.minute, ms_tmp.ent.addr.msf.second, ms_tmp.ent.addr.msf.frame); } toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track); /* now try to get the total cdrom capacity */ stat = cdrom_get_last_written(cdi, &last_written); if (!stat && (last_written > toc->capacity)) { toc->capacity = last_written; set_capacity(info->disk, toc->capacity * sectors_per_frame); drive->probed_capacity = toc->capacity * sectors_per_frame; } /* Remember that we've read this stuff. */ drive->atapi_flags |= IDE_AFLAG_TOC_VALID; return 0; } int ide_cdrom_get_capabilities(ide_drive_t *drive, u8 *buf) { struct cdrom_info *info = drive->driver_data; struct cdrom_device_info *cdi = &info->devinfo; struct packet_command cgc; int stat, attempts = 3, size = ATAPI_CAPABILITIES_PAGE_SIZE; ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); if ((drive->atapi_flags & IDE_AFLAG_FULL_CAPS_PAGE) == 0) size -= ATAPI_CAPABILITIES_PAGE_PAD_SIZE; init_cdrom_command(&cgc, buf, size, CGC_DATA_UNKNOWN); do { /* we seem to get stat=0x01,err=0x00 the first time (??) */ stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0); if (!stat) break; } while (--attempts); return stat; } void ide_cdrom_update_speed(ide_drive_t *drive, u8 *buf) { struct cdrom_info *cd = drive->driver_data; u16 curspeed, maxspeed; ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); if (drive->atapi_flags & IDE_AFLAG_LE_SPEED_FIELDS) { curspeed = le16_to_cpup((__le16 *)&buf[8 + 14]); maxspeed = le16_to_cpup((__le16 *)&buf[8 + 8]); } else { curspeed = be16_to_cpup((__be16 *)&buf[8 + 14]); maxspeed = be16_to_cpup((__be16 *)&buf[8 + 8]); } ide_debug_log(IDE_DBG_PROBE, "%s: curspeed: %u, maxspeed: %u\n", __func__, curspeed, maxspeed); cd->current_speed = (curspeed + (176/2)) / 176; cd->max_speed = (maxspeed + (176/2)) / 176; } #define IDE_CD_CAPABILITIES \ (CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | \ CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | \ CDC_PLAY_AUDIO | CDC_RESET | CDC_DRIVE_STATUS | CDC_CD_R | \ CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_GENERIC_PACKET | \ CDC_MO_DRIVE | CDC_MRW | CDC_MRW_W | CDC_RAM) static struct cdrom_device_ops ide_cdrom_dops = { .open = ide_cdrom_open_real, .release = ide_cdrom_release_real, .drive_status = ide_cdrom_drive_status, .media_changed = ide_cdrom_check_media_change_real, .tray_move = ide_cdrom_tray_move, .lock_door = ide_cdrom_lock_door, .select_speed = ide_cdrom_select_speed, .get_last_session = ide_cdrom_get_last_session, .get_mcn = ide_cdrom_get_mcn, .reset = ide_cdrom_reset, .audio_ioctl = ide_cdrom_audio_ioctl, .capability = IDE_CD_CAPABILITIES, .generic_packet = ide_cdrom_packet, }; static int ide_cdrom_register(ide_drive_t *drive, int nslots) { struct cdrom_info *info = drive->driver_data; struct cdrom_device_info *devinfo = &info->devinfo; ide_debug_log(IDE_DBG_PROBE, "Call %s, nslots: %d\n", __func__, nslots); devinfo->ops = &ide_cdrom_dops; devinfo->speed = info->current_speed; devinfo->capacity = nslots; devinfo->handle = drive; strcpy(devinfo->name, drive->name); if (drive->atapi_flags & IDE_AFLAG_NO_SPEED_SELECT) devinfo->mask |= CDC_SELECT_SPEED; devinfo->disk = info->disk; return register_cdrom(devinfo); } static int ide_cdrom_probe_capabilities(ide_drive_t *drive) { struct cdrom_info *cd = drive->driver_data; struct cdrom_device_info *cdi = &cd->devinfo; u8 buf[ATAPI_CAPABILITIES_PAGE_SIZE]; mechtype_t mechtype; int nslots = 1; ide_debug_log(IDE_DBG_PROBE, "Call %s, drive->media: 0x%x, " "drive->atapi_flags: 0x%lx\n", __func__, drive->media, drive->atapi_flags); cdi->mask = (CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_SELECT_DISC | CDC_PLAY_AUDIO | CDC_MO_DRIVE | CDC_RAM); if (drive->media == ide_optical) { cdi->mask &= ~(CDC_MO_DRIVE | CDC_RAM); printk(KERN_ERR PFX "%s: ATAPI magneto-optical drive\n", drive->name); return nslots; } if (drive->atapi_flags & IDE_AFLAG_PRE_ATAPI12) { drive->atapi_flags &= ~IDE_AFLAG_NO_EJECT; cdi->mask &= ~CDC_PLAY_AUDIO; return nslots; } /* * We have to cheat a little here. the packet will eventually be queued * with ide_cdrom_packet(), which extracts the drive from cdi->handle. * Since this device hasn't been registered with the Uniform layer yet, * it can't do this. Same goes for cdi->ops. */ cdi->handle = drive; cdi->ops = &ide_cdrom_dops; if (ide_cdrom_get_capabilities(drive, buf)) return 0; if ((buf[8 + 6] & 0x01) == 0) drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING; if (buf[8 + 6] & 0x08) drive->atapi_flags &= ~IDE_AFLAG_NO_EJECT; if (buf[8 + 3] & 0x01) cdi->mask &= ~CDC_CD_R; if (buf[8 + 3] & 0x02) cdi->mask &= ~(CDC_CD_RW | CDC_RAM); if (buf[8 + 2] & 0x38) cdi->mask &= ~CDC_DVD; if (buf[8 + 3] & 0x20) cdi->mask &= ~(CDC_DVD_RAM | CDC_RAM); if (buf[8 + 3] & 0x10) cdi->mask &= ~CDC_DVD_R; if ((buf[8 + 4] & 0x01) || (drive->atapi_flags & IDE_AFLAG_PLAY_AUDIO_OK)) cdi->mask &= ~CDC_PLAY_AUDIO; mechtype = buf[8 + 6] >> 5; if (mechtype == mechtype_caddy || mechtype == mechtype_popup || (drive->atapi_flags & IDE_AFLAG_NO_AUTOCLOSE)) cdi->mask |= CDC_CLOSE_TRAY; if (cdi->sanyo_slot > 0) { cdi->mask &= ~CDC_SELECT_DISC; nslots = 3; } else if (mechtype == mechtype_individual_changer || mechtype == mechtype_cartridge_changer) { nslots = cdrom_number_of_slots(cdi); if (nslots > 1) cdi->mask &= ~CDC_SELECT_DISC; } ide_cdrom_update_speed(drive, buf); printk(KERN_INFO PFX "%s: ATAPI", drive->name); /* don't print speed if the drive reported 0 */ if (cd->max_speed) printk(KERN_CONT " %dX", cd->max_speed); printk(KERN_CONT " %s", (cdi->mask & CDC_DVD) ? "CD-ROM" : "DVD-ROM"); if ((cdi->mask & CDC_DVD_R) == 0 || (cdi->mask & CDC_DVD_RAM) == 0) printk(KERN_CONT " DVD%s%s", (cdi->mask & CDC_DVD_R) ? "" : "-R", (cdi->mask & CDC_DVD_RAM) ? "" : "/RAM"); if ((cdi->mask & CDC_CD_R) == 0 || (cdi->mask & CDC_CD_RW) == 0) printk(KERN_CONT " CD%s%s", (cdi->mask & CDC_CD_R) ? "" : "-R", (cdi->mask & CDC_CD_RW) ? "" : "/RW"); if ((cdi->mask & CDC_SELECT_DISC) == 0) printk(KERN_CONT " changer w/%d slots", nslots); else printk(KERN_CONT " drive"); printk(KERN_CONT ", %dkB Cache\n", be16_to_cpup((__be16 *)&buf[8 + 12])); return nslots; } /* standard prep_rq_fn that builds 10 byte cmds */ static int ide_cdrom_prep_fs(struct request_queue *q, struct request *rq) { int hard_sect = queue_hardsect_size(q); long block = (long)rq->hard_sector / (hard_sect >> 9); unsigned long blocks = rq->hard_nr_sectors / (hard_sect >> 9); memset(rq->cmd, 0, BLK_MAX_CDB); if (rq_data_dir(rq) == READ) rq->cmd[0] = GPCMD_READ_10; else rq->cmd[0] = GPCMD_WRITE_10; /* * fill in lba */ rq->cmd[2] = (block >> 24) & 0xff; rq->cmd[3] = (block >> 16) & 0xff; rq->cmd[4] = (block >> 8) & 0xff; rq->cmd[5] = block & 0xff; /* * and transfer length */ rq->cmd[7] = (blocks >> 8) & 0xff; rq->cmd[8] = blocks & 0xff; rq->cmd_len = 10; return BLKPREP_OK; } /* * Most of the SCSI commands are supported directly by ATAPI devices. * This transform handles the few exceptions. */ static int ide_cdrom_prep_pc(struct request *rq) { u8 *c = rq->cmd; /* transform 6-byte read/write commands to the 10-byte version */ if (c[0] == READ_6 || c[0] == WRITE_6) { c[8] = c[4]; c[5] = c[3]; c[4] = c[2]; c[3] = c[1] & 0x1f; c[2] = 0; c[1] &= 0xe0; c[0] += (READ_10 - READ_6); rq->cmd_len = 10; return BLKPREP_OK; } /* * it's silly to pretend we understand 6-byte sense commands, just * reject with ILLEGAL_REQUEST and the caller should take the * appropriate action */ if (c[0] == MODE_SENSE || c[0] == MODE_SELECT) { rq->errors = ILLEGAL_REQUEST; return BLKPREP_KILL; } return BLKPREP_OK; } static int ide_cdrom_prep_fn(struct request_queue *q, struct request *rq) { if (blk_fs_request(rq)) return ide_cdrom_prep_fs(q, rq); else if (blk_pc_request(rq)) return ide_cdrom_prep_pc(rq); return 0; } struct cd_list_entry { const char *id_model; const char *id_firmware; unsigned int cd_flags; }; #ifdef CONFIG_IDE_PROC_FS static sector_t ide_cdrom_capacity(ide_drive_t *drive) { unsigned long capacity, sectors_per_frame; if (cdrom_read_capacity(drive, &capacity, &sectors_per_frame, NULL)) return 0; return capacity * sectors_per_frame; } static int proc_idecd_read_capacity(char *page, char **start, off_t off, int count, int *eof, void *data) { ide_drive_t *drive = data; int len; len = sprintf(page, "%llu\n", (long long)ide_cdrom_capacity(drive)); PROC_IDE_READ_RETURN(page, start, off, count, eof, len); } static ide_proc_entry_t idecd_proc[] = { { "capacity", S_IFREG|S_IRUGO, proc_idecd_read_capacity, NULL }, { NULL, 0, NULL, NULL } }; static ide_proc_entry_t *ide_cd_proc_entries(ide_drive_t *drive) { return idecd_proc; } static const struct ide_proc_devset *ide_cd_proc_devsets(ide_drive_t *drive) { return NULL; } #endif static const struct cd_list_entry ide_cd_quirks_list[] = { /* Limit transfer size per interrupt. */ { "SAMSUNG CD-ROM SCR-2430", NULL, IDE_AFLAG_LIMIT_NFRAMES }, { "SAMSUNG CD-ROM SCR-2432", NULL, IDE_AFLAG_LIMIT_NFRAMES }, /* SCR-3231 doesn't support the SET_CD_SPEED command. */ { "SAMSUNG CD-ROM SCR-3231", NULL, IDE_AFLAG_NO_SPEED_SELECT }, /* Old NEC260 (not R) was released before ATAPI 1.2 spec. */ { "NEC CD-ROM DRIVE:260", "1.01", IDE_AFLAG_TOCADDR_AS_BCD | IDE_AFLAG_PRE_ATAPI12, }, /* Vertos 300, some versions of this drive like to talk BCD. */ { "V003S0DS", NULL, IDE_AFLAG_VERTOS_300_SSD, }, /* Vertos 600 ESD. */ { "V006E0DS", NULL, IDE_AFLAG_VERTOS_600_ESD, }, /* * Sanyo 3 CD changer uses a non-standard command for CD changing * (by default standard ATAPI support for CD changers is used). */ { "CD-ROM CDR-C3 G", NULL, IDE_AFLAG_SANYO_3CD }, { "CD-ROM CDR-C3G", NULL, IDE_AFLAG_SANYO_3CD }, { "CD-ROM CDR_C36", NULL, IDE_AFLAG_SANYO_3CD }, /* Stingray 8X CD-ROM. */ { "STINGRAY 8422 IDE 8X CD-ROM 7-27-95", NULL, IDE_AFLAG_PRE_ATAPI12 }, /* * ACER 50X CD-ROM and WPI 32X CD-ROM require the full spec length * mode sense page capabilities size, but older drives break. */ { "ATAPI CD ROM DRIVE 50X MAX", NULL, IDE_AFLAG_FULL_CAPS_PAGE }, { "WPI CDS-32X", NULL, IDE_AFLAG_FULL_CAPS_PAGE }, /* ACER/AOpen 24X CD-ROM has the speed fields byte-swapped. */ { "", "241N", IDE_AFLAG_LE_SPEED_FIELDS }, /* * Some drives used by Apple don't advertise audio play * but they do support reading TOC & audio datas. */ { "MATSHITADVD-ROM SR-8187", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, { "MATSHITADVD-ROM SR-8186", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, { "MATSHITADVD-ROM SR-8176", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, { "MATSHITADVD-ROM SR-8174", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, { "Optiarc DVD RW AD-5200A", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, { "Optiarc DVD RW AD-7200A", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, { "Optiarc DVD RW AD-7543A", NULL, IDE_AFLAG_NO_AUTOCLOSE }, { "TEAC CD-ROM CD-224E", NULL, IDE_AFLAG_NO_AUTOCLOSE }, { NULL, NULL, 0 } }; static unsigned int ide_cd_flags(u16 *id) { const struct cd_list_entry *cle = ide_cd_quirks_list; while (cle->id_model) { if (strcmp(cle->id_model, (char *)&id[ATA_ID_PROD]) == 0 && (cle->id_firmware == NULL || strstr((char *)&id[ATA_ID_FW_REV], cle->id_firmware))) return cle->cd_flags; cle++; } return 0; } static int ide_cdrom_setup(ide_drive_t *drive) { struct cdrom_info *cd = drive->driver_data; struct cdrom_device_info *cdi = &cd->devinfo; u16 *id = drive->id; char *fw_rev = (char *)&id[ATA_ID_FW_REV]; int nslots; ide_debug_log(IDE_DBG_PROBE, "Call %s\n", __func__); blk_queue_prep_rq(drive->queue, ide_cdrom_prep_fn); blk_queue_dma_alignment(drive->queue, 31); blk_queue_update_dma_pad(drive->queue, 15); drive->queue->unplug_delay = (1 * HZ) / 1000; if (!drive->queue->unplug_delay) drive->queue->unplug_delay = 1; drive->dev_flags |= IDE_DFLAG_MEDIA_CHANGED; drive->atapi_flags = IDE_AFLAG_NO_EJECT | ide_cd_flags(id); if ((drive->atapi_flags & IDE_AFLAG_VERTOS_300_SSD) && fw_rev[4] == '1' && fw_rev[6] <= '2') drive->atapi_flags |= (IDE_AFLAG_TOCTRACKS_AS_BCD | IDE_AFLAG_TOCADDR_AS_BCD); else if ((drive->atapi_flags & IDE_AFLAG_VERTOS_600_ESD) && fw_rev[4] == '1' && fw_rev[6] <= '2') drive->atapi_flags |= IDE_AFLAG_TOCTRACKS_AS_BCD; else if (drive->atapi_flags & IDE_AFLAG_SANYO_3CD) /* 3 => use CD in slot 0 */ cdi->sanyo_slot = 3; nslots = ide_cdrom_probe_capabilities(drive); /* set correct block size */ blk_queue_hardsect_size(drive->queue, CD_FRAMESIZE); if (ide_cdrom_register(drive, nslots)) { printk(KERN_ERR PFX "%s: %s failed to register device with the" " cdrom driver.\n", drive->name, __func__); cd->devinfo.handle = NULL; return 1; } ide_proc_register_driver(drive, cd->driver); return 0; } static void ide_cd_remove(ide_drive_t *drive) { struct cdrom_info *info = drive->driver_data; ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); ide_proc_unregister_driver(drive, info->driver); del_gendisk(info->disk); ide_cd_put(info); } static void ide_cd_release(struct kref *kref) { struct cdrom_info *info = to_ide_drv(kref, cdrom_info); struct cdrom_device_info *devinfo = &info->devinfo; ide_drive_t *drive = info->drive; struct gendisk *g = info->disk; ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__); kfree(info->toc); if (devinfo->handle == drive) unregister_cdrom(devinfo); drive->driver_data = NULL; blk_queue_prep_rq(drive->queue, NULL); g->private_data = NULL; put_disk(g); kfree(info); } static int ide_cd_probe(ide_drive_t *); static struct ide_driver ide_cdrom_driver = { .gen_driver = { .owner = THIS_MODULE, .name = "ide-cdrom", .bus = &ide_bus_type, }, .probe = ide_cd_probe, .remove = ide_cd_remove, .version = IDECD_VERSION, .do_request = ide_cd_do_request, .end_request = ide_end_request, #ifdef CONFIG_IDE_PROC_FS .proc_entries = ide_cd_proc_entries, .proc_devsets = ide_cd_proc_devsets, #endif }; static int idecd_open(struct block_device *bdev, fmode_t mode) { struct cdrom_info *info = ide_cd_get(bdev->bd_disk); int rc = -ENOMEM; if (!info) return -ENXIO; rc = cdrom_open(&info->devinfo, bdev, mode); if (rc < 0) ide_cd_put(info); return rc; } static int idecd_release(struct gendisk *disk, fmode_t mode) { struct cdrom_info *info = ide_drv_g(disk, cdrom_info); cdrom_release(&info->devinfo, mode); ide_cd_put(info); return 0; } static int idecd_set_spindown(struct cdrom_device_info *cdi, unsigned long arg) { struct packet_command cgc; char buffer[16]; int stat; char spindown; if (copy_from_user(&spindown, (void __user *)arg, sizeof(char))) return -EFAULT; init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN); stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0); if (stat) return stat; buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f); return cdrom_mode_select(cdi, &cgc); } static int idecd_get_spindown(struct cdrom_device_info *cdi, unsigned long arg) { struct packet_command cgc; char buffer[16]; int stat; char spindown; init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN); stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0); if (stat) return stat; spindown = buffer[11] & 0x0f; if (copy_to_user((void __user *)arg, &spindown, sizeof(char))) return -EFAULT; return 0; } static int idecd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { struct cdrom_info *info = ide_drv_g(bdev->bd_disk, cdrom_info); int err; switch (cmd) { case CDROMSETSPINDOWN: return idecd_set_spindown(&info->devinfo, arg); case CDROMGETSPINDOWN: return idecd_get_spindown(&info->devinfo, arg); default: break; } err = generic_ide_ioctl(info->drive, bdev, cmd, arg); if (err == -EINVAL) err = cdrom_ioctl(&info->devinfo, bdev, mode, cmd, arg); return err; } static int idecd_media_changed(struct gendisk *disk) { struct cdrom_info *info = ide_drv_g(disk, cdrom_info); return cdrom_media_changed(&info->devinfo); } static int idecd_revalidate_disk(struct gendisk *disk) { struct cdrom_info *info = ide_drv_g(disk, cdrom_info); struct request_sense sense; ide_cd_read_toc(info->drive, &sense); return 0; } static struct block_device_operations idecd_ops = { .owner = THIS_MODULE, .open = idecd_open, .release = idecd_release, .locked_ioctl = idecd_ioctl, .media_changed = idecd_media_changed, .revalidate_disk = idecd_revalidate_disk }; /* module options */ static char *ignore; module_param(ignore, charp, 0400); static unsigned long debug_mask; module_param(debug_mask, ulong, 0644); MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); static int ide_cd_probe(ide_drive_t *drive) { struct cdrom_info *info; struct gendisk *g; struct request_sense sense; ide_debug_log(IDE_DBG_PROBE, "Call %s, drive->driver_req: %s, " "drive->media: 0x%x\n", __func__, drive->driver_req, drive->media); if (!strstr("ide-cdrom", drive->driver_req)) goto failed; if (drive->media != ide_cdrom && drive->media != ide_optical) goto failed; /* skip drives that we were told to ignore */ if (ignore != NULL) { if (strstr(ignore, drive->name)) { printk(KERN_INFO PFX "ignoring drive %s\n", drive->name); goto failed; } } drive->debug_mask = debug_mask; drive->irq_handler = cdrom_newpc_intr; info = kzalloc(sizeof(struct cdrom_info), GFP_KERNEL); if (info == NULL) { printk(KERN_ERR PFX "%s: Can't allocate a cdrom structure\n", drive->name); goto failed; } g = alloc_disk(1 << PARTN_BITS); if (!g) goto out_free_cd; ide_init_disk(g, drive); kref_init(&info->kref); info->drive = drive; info->driver = &ide_cdrom_driver; info->disk = g; g->private_data = &info->driver; drive->driver_data = info; g->minors = 1; g->driverfs_dev = &drive->gendev; g->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE; if (ide_cdrom_setup(drive)) { ide_cd_release(&info->kref); goto failed; } ide_cd_read_toc(drive, &sense); g->fops = &idecd_ops; g->flags |= GENHD_FL_REMOVABLE; add_disk(g); return 0; out_free_cd: kfree(info); failed: return -ENODEV; } static void __exit ide_cdrom_exit(void) { driver_unregister(&ide_cdrom_driver.gen_driver); } static int __init ide_cdrom_init(void) { printk(KERN_INFO DRV_NAME " driver " IDECD_VERSION "\n"); return driver_register(&ide_cdrom_driver.gen_driver); } MODULE_ALIAS("ide:*m-cdrom*"); MODULE_ALIAS("ide-cd"); module_init(ide_cdrom_init); module_exit(ide_cdrom_exit); MODULE_LICENSE("GPL");