libata: identify and init ZPODD devices
[deliverable/linux.git] / drivers / ata / libata-zpodd.c
CommitLineData
afe75951
AL
1#include <linux/libata.h>
2#include <linux/cdrom.h>
3
4#include "libata.h"
5
6enum odd_mech_type {
7 ODD_MECH_TYPE_SLOT,
8 ODD_MECH_TYPE_DRAWER,
9 ODD_MECH_TYPE_UNSUPPORTED,
10};
11
12struct zpodd {
13 enum odd_mech_type mech_type; /* init during probe, RO afterwards */
14 struct ata_device *dev;
15};
16
17/* Per the spec, only slot type and drawer type ODD can be supported */
18static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
19{
20 char buf[16];
21 unsigned int ret;
22 struct rm_feature_desc *desc = (void *)(buf + 8);
23 struct ata_taskfile tf = {};
24
25 char cdb[] = { GPCMD_GET_CONFIGURATION,
26 2, /* only 1 feature descriptor requested */
27 0, 3, /* 3, removable medium feature */
28 0, 0, 0,/* reserved */
29 0, sizeof(buf),
30 0, 0, 0,
31 };
32
33 tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
34 tf.command = ATA_CMD_PACKET;
35 tf.protocol = ATAPI_PROT_PIO;
36 tf.lbam = sizeof(buf);
37
38 ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
39 buf, sizeof(buf), 0);
40 if (ret)
41 return ODD_MECH_TYPE_UNSUPPORTED;
42
43 if (be16_to_cpu(desc->feature_code) != 3)
44 return ODD_MECH_TYPE_UNSUPPORTED;
45
46 if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1)
47 return ODD_MECH_TYPE_SLOT;
48 else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1)
49 return ODD_MECH_TYPE_DRAWER;
50 else
51 return ODD_MECH_TYPE_UNSUPPORTED;
52}
53
54static bool odd_can_poweroff(struct ata_device *ata_dev)
55{
56 acpi_handle handle;
57 acpi_status status;
58 struct acpi_device *acpi_dev;
59
60 handle = ata_dev_acpi_handle(ata_dev);
61 if (!handle)
62 return false;
63
64 status = acpi_bus_get_device(handle, &acpi_dev);
65 if (ACPI_FAILURE(status))
66 return false;
67
68 return acpi_device_can_poweroff(acpi_dev);
69}
70
71void zpodd_init(struct ata_device *dev)
72{
73 enum odd_mech_type mech_type;
74 struct zpodd *zpodd;
75
76 if (dev->zpodd)
77 return;
78
79 if (!odd_can_poweroff(dev))
80 return;
81
82 mech_type = zpodd_get_mech_type(dev);
83 if (mech_type == ODD_MECH_TYPE_UNSUPPORTED)
84 return;
85
86 zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL);
87 if (!zpodd)
88 return;
89
90 zpodd->mech_type = mech_type;
91
92 zpodd->dev = dev;
93 dev->zpodd = zpodd;
94}
95
96void zpodd_exit(struct ata_device *dev)
97{
98 kfree(dev->zpodd);
99 dev->zpodd = NULL;
100}
This page took 0.029151 seconds and 5 git commands to generate.