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
|
#include <linux/libata.h>
#include <linux/cdrom.h>
#include "libata.h"
enum odd_mech_type {
ODD_MECH_TYPE_SLOT,
ODD_MECH_TYPE_DRAWER,
ODD_MECH_TYPE_UNSUPPORTED,
};
struct zpodd {
enum odd_mech_type mech_type; /* init during probe, RO afterwards */
struct ata_device *dev;
};
/* Per the spec, only slot type and drawer type ODD can be supported */
static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
{
char buf[16];
unsigned int ret;
struct rm_feature_desc *desc = (void *)(buf + 8);
struct ata_taskfile tf = {};
char cdb[] = { GPCMD_GET_CONFIGURATION,
2, /* only 1 feature descriptor requested */
0, 3, /* 3, removable medium feature */
0, 0, 0,/* reserved */
0, sizeof(buf),
0, 0, 0,
};
tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf.command = ATA_CMD_PACKET;
tf.protocol = ATAPI_PROT_PIO;
tf.lbam = sizeof(buf);
ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
buf, sizeof(buf), 0);
if (ret)
return ODD_MECH_TYPE_UNSUPPORTED;
if (be16_to_cpu(desc->feature_code) != 3)
return ODD_MECH_TYPE_UNSUPPORTED;
if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1)
return ODD_MECH_TYPE_SLOT;
else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1)
return ODD_MECH_TYPE_DRAWER;
else
return ODD_MECH_TYPE_UNSUPPORTED;
}
static bool odd_can_poweroff(struct ata_device *ata_dev)
{
acpi_handle handle;
acpi_status status;
struct acpi_device *acpi_dev;
handle = ata_dev_acpi_handle(ata_dev);
if (!handle)
return false;
status = acpi_bus_get_device(handle, &acpi_dev);
if (ACPI_FAILURE(status))
return false;
return acpi_device_can_poweroff(acpi_dev);
}
void zpodd_init(struct ata_device *dev)
{
enum odd_mech_type mech_type;
struct zpodd *zpodd;
if (dev->zpodd)
return;
if (!odd_can_poweroff(dev))
return;
mech_type = zpodd_get_mech_type(dev);
if (mech_type == ODD_MECH_TYPE_UNSUPPORTED)
return;
zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL);
if (!zpodd)
return;
zpodd->mech_type = mech_type;
zpodd->dev = dev;
dev->zpodd = zpodd;
}
void zpodd_exit(struct ata_device *dev)
{
kfree(dev->zpodd);
dev->zpodd = NULL;
}
|