[char-misc-next 00/11 V4] Add Client MEI bus and NFC device
This is take 4 on the MEI bus + NFC device patches This patch set adds implementation of MEI CLIENT BUS abstraction over MEI device, this allows standard Linux device drivers to access functionality exposed by MEI device clients that was previously available only to the user space through /dev/mei The first exercises is to export the NFC radio More information can be found under Documentation/misc-devices/mei/mei-client-bus.txt v3 -> v4: The bus is named mei client bus - Renames: * mei_bus_driver to mei_cl_driver * mei_bus_device to mei_cl_device v2 -> v3: - Renames: * mei_device to mei_host. The mei_host pointers are still called *dev as I didn't want the first patch to get too fat. * mei_bus_driver to mei_driver * mei_bus_client to mei_device - mei_driver structure changes: * name pointer addition * MEI id table * probe routine now takes the probed MEI id as an argument - mei-bus.txt update according to the mei_driver changes and the structure renaming. - All exported symbols converted to EXPORT_SYMBOL_GPL. - to_mei_* macros moved to bus.c - drivers/misc/mei/bus.h deleted, all API definitions moved to mei_dev.h - mei_device structure clenup: mei_host, mei_driver, and name fields removed. - Fixed driver owner: mei_driver_register() is now a macro over __mei_driver_register, using THIS_MODULE as the default owner. Samuel Ortiz (11): mei: bus: Initial MEI Client bus type implementation mei: bus: Implement driver registration mei: bus: Initial implementation for I/O routines mei: bus: Add bus related structures to mei_cl mei: bus: Call bus routines from the core code mei: bus: Synchronous API for the data transmission mei: bus: Implement bus driver data setter/getter mei: nfc: Initial nfc implementation mei: nfc: Connect also the regular ME client mei: nfc: Add NFC device to the MEI bus mei: nfc: Implement MEI bus IO ops Documentation/misc-devices/mei/mei-client-bus.txt | 143 +++ drivers/misc/mei/Kconfig | 7 + drivers/misc/mei/Makefile | 2 + drivers/misc/mei/bus.c| 474 ++ drivers/misc/mei/client.c | 4 + drivers/misc/mei/init.c | 3 + drivers/misc/mei/interrupt.c | 2 + drivers/misc/mei/mei_dev.h| 100 - drivers/misc/mei/nfc.c| 458 + drivers/misc/mei/nfc.h| 141 +++ drivers/misc/mei/pci-me.c | 21 +- include/linux/mei_cl_bus.h| 113 ++ 12 files changed, 1465 insertions(+), 3 deletions(-) create mode 100644 Documentation/misc-devices/mei/mei-client-bus.txt create mode 100644 drivers/misc/mei/bus.c create mode 100644 drivers/misc/mei/nfc.c create mode 100644 drivers/misc/mei/nfc.h create mode 100644 include/linux/mei_cl_bus.h -- 1.8.1.3 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 01/11 V4] mei: bus: Initial MEI Client bus type implementation
From: Samuel Ortiz mei cleint bus will present some of the me clients as devices for other standard subsystems Implement the probe, remove, match and the device addtion routines. A mei-cleint-bus.txt document describing the rationale and the API usage is also added. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- Documentation/misc-devices/mei/mei-client-bus.txt | 143 drivers/misc/mei/Makefile | 1 + drivers/misc/mei/bus.c| 151 ++ drivers/misc/mei/mei_dev.h| 27 include/linux/mei_cl_bus.h| 92 + 5 files changed, 414 insertions(+) create mode 100644 Documentation/misc-devices/mei/mei-client-bus.txt create mode 100644 drivers/misc/mei/bus.c create mode 100644 include/linux/mei_cl_bus.h diff --git a/Documentation/misc-devices/mei/mei-client-bus.txt b/Documentation/misc-devices/mei/mei-client-bus.txt new file mode 100644 index 000..5fb2fa3 --- /dev/null +++ b/Documentation/misc-devices/mei/mei-client-bus.txt @@ -0,0 +1,143 @@ +Intel(R) Management Engine (ME) Client bus API +=== + + +Rationale += +MEI misc character device is useful for dedicated applications to send and receive +data to the many FW appliance found in Intel's ME from the user space. +However for some of the ME functionalities it make sense to leverage existing software +stack and expose them through existing kernel subsystems. + +In order to plug seamlessly into the kernel device driver model we add kernel virtual +bus abstraction on top of the MEI driver. This allows implementing linux kernel drivers +for the various MEI features as a stand alone entities found in their respective subsystem. +Existing device drivers can even potentially be re-used by adding an MEI CL bus layer to +the existing code. + + +MEI CL bus API +=== +A driver implementation for an MEI Client is very similar to existing bus +based device drivers. The driver registers itself as an MEI CL bus driver through +the mei_cl_driver structure: + +struct mei_cl_driver { + struct device_driver driver; + const char *name; + + const struct mei_cl_id *id_table; + + int (*probe)(struct mei_cl_device *dev, const struct mei_cl_id *id); + int (*remove)(struct mei_cl_device *dev); +}; + +struct mei_cl_id { + char name[MEI_NAME_SIZE]; + uuid_le uuid; +}; + +The mei_cl_id structure allows the driver to bind itself against an ME UUID and a +device name. There is typically one ME UUID per technology and the mei_cl_id name +field matches a specific implementation of that technology. As an example, +the ME supports NFC devices from multiple IP vendors and device types. +All of them have the same ME UUID but the ME bus code will assign +each of them a different name. There is some analogy to device class and device +vendor terms. + +To actually register a driver on the ME Client bus one must call the mei_cl_add_driver() +API. This is typically called at module init time. + +Once registered on the ME Client bus, a driver will typically try to do some I/O on +this bus and this should be done through the mei_cl_send() and mei_cl_recv() +routines. The latter is synchronous (blocks and sleeps until data shows up). +In order for drivers to be notified of pending events waiting for them (e.g. +an Rx event) they can register an event handler through the +mei_cl_register_event_cb() routine. Currently only the MEI_EVENT_RX event +will trigger an event handler call and the driver implementation is supposed +to call mei_recv() from the event handler in order to fetch the pending +received buffers. + + +Example +=== +As a theoretical example let's pretend the ME comes with a "contact" NFC IP. +The driver init and exit routines for this device would look like: + +#define CONTACT_DRIVER_NAME "contact" + +#define NFC_UUID UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, 0x94, \ + 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c) + +static struct mei_cl_id contact_mei_cl_tbl[] = { + { CONTACT_DRIVER_NAME, NFC_UUID }, + + /* required last entry */ + { } +}; + +static struct mei_cl_driver contact_driver = { + .id_table = contact_mei_tbl, + .name = CONTACT_DRIVER_NAME, + + .probe = contact_probe, + .remove = contact_remove, +}; + +static int contact_init(void) +{ + int r; + + r = mei_cl_driver_register(&contact_driver); + if (r) { + pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n"); + return r; + } + + return 0; +} + +static void __exit contact_exit(void) +{ + mei_cl_driver_unregister(&contact_driver); +} + +module_init(contact_init); +module_exit(contact_exit); + +And the driver's simplified probe routine would look like that: +
[char-misc-next 06/11 V4] mei: bus: Synchronous API for the data transmission
From: Samuel Ortiz Define a truly synchronous API for the bus Tx path by putting all pending request to the write list and wait for the interrupt tx handler to wake us up. The ___mei_cl_send() out path is also slightly reworked to make it look more like main.c:mei_write(). Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 39 +-- drivers/misc/mei/mei_dev.h | 1 + 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index d55f6d8..d542950 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -200,7 +200,8 @@ void mei_cl_driver_unregister(struct mei_cl_driver *driver) } EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); -int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) +static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, + bool blocking) { struct mei_device *dev; struct mei_msg_hdr mei_hdr; @@ -251,11 +252,8 @@ int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) cb->buf_idx = 0; mei_hdr.msg_complete = 0; cl->writing_state = MEI_WRITING; - list_add_tail(&cb->list, &dev->write_list.list); - - mutex_unlock(&dev->device_lock); - return length; + goto out; } dev->hbuf_is_ready = false; @@ -281,19 +279,30 @@ int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) cl->writing_state = MEI_WRITING; cb->buf_idx = mei_hdr.length; - if (!mei_hdr.msg_complete) { - list_add_tail(&cb->list, &dev->write_list.list); - } else { +out: + if (mei_hdr.msg_complete) { if (mei_cl_flow_ctrl_reduce(cl)) { - err = -EIO; + err = -ENODEV; goto out_err; } - list_add_tail(&cb->list, &dev->write_waiting_list.list); + } else { + list_add_tail(&cb->list, &dev->write_list.list); } mutex_unlock(&dev->device_lock); + if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { + if (wait_event_interruptible(cl->tx_wait, + cl->writing_state == MEI_WRITE_COMPLETE)) { + if (signal_pending(current)) + err = -EINTR; + err = -ERESTARTSYS; + mutex_lock(&dev->device_lock); + goto out_err; + } + } + return mei_hdr.length; out_err: @@ -360,6 +369,16 @@ out: return r_length; } +inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + return ___mei_cl_send(cl, buf, length, 0); +} + +inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + return ___mei_cl_send(cl, buf, length, 1); +} + int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) { struct mei_cl *cl = device->cl; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 0391dc9..7199789 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -273,6 +273,7 @@ struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, uuid_le uuid, char *name); void mei_cl_remove_device(struct mei_cl_device *device); +int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length); int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length); int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); -- 1.8.1.3 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 11/11 V4] mei: nfc: Implement MEI bus IO ops
From: Samuel Ortiz The send ops for NFC builds the command header, updates the request id and then waits for an ACK. The recv ops check if it receives data or an ACK and in the latter case wakes the send ops up. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/nfc.c | 76 ++ drivers/misc/mei/nfc.h | 13 + 2 files changed, 89 insertions(+) diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index 372d336..14d6076 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -39,11 +40,15 @@ struct mei_nfc_dev { struct mei_cl *cl; struct mei_cl *cl_info; struct work_struct init_work; + wait_queue_head_t send_wq; u8 fw_ivn; u8 vendor_id; u8 radio_type; char *bus_name; + + u16 req_id; + u16 recv_req_id; }; static struct mei_nfc_dev nfc_dev; @@ -223,6 +228,74 @@ err: return ret; } +static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_nfc_dev *ndev; + struct mei_nfc_hci_hdr *hdr; + u8 *mei_buf; + int err; + + ndev = (struct mei_nfc_dev *) cldev->priv_data; + dev = ndev->cl->dev; + + mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); + if (!mei_buf) + return -ENOMEM; + + hdr = (struct mei_nfc_hci_hdr *) mei_buf; + hdr->cmd = MEI_NFC_CMD_HCI_SEND; + hdr->status = 0; + hdr->req_id = ndev->req_id; + hdr->reserved = 0; + hdr->data_size = length; + + memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); + + err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE); + + kfree(mei_buf); + + if (!wait_event_interruptible_timeout(ndev->send_wq, + ndev->recv_req_id == ndev->req_id, HZ)) { + dev_err(&dev->pdev->dev, "NFC MEI command timeout\n"); + err = -ETIMEDOUT; + } else { + ndev->req_id++; + } + + return err; +} + +static int mei_nfc_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) +{ + struct mei_nfc_dev *ndev; + struct mei_nfc_hci_hdr *hci_hdr; + int received_length; + + ndev = (struct mei_nfc_dev *)cldev->priv_data; + + received_length = __mei_cl_recv(ndev->cl, buf, length); + if (received_length < 0) + return received_length; + + hci_hdr = (struct mei_nfc_hci_hdr *) buf; + + if (hci_hdr->cmd == MEI_NFC_CMD_HCI_SEND) { + ndev->recv_req_id = hci_hdr->req_id; + wake_up(&ndev->send_wq); + + return 0; + } + + return received_length; +} + +static struct mei_cl_transport_ops nfc_ops = { + .send = mei_nfc_send, + .recv = mei_nfc_recv, +}; + static void mei_nfc_init(struct work_struct *work) { struct mei_device *dev; @@ -293,6 +366,7 @@ static void mei_nfc_init(struct work_struct *work) } cldev->priv_data = ndev; + cldev->ops = &nfc_ops; return; @@ -359,8 +433,10 @@ int mei_nfc_host_init(struct mei_device *dev) ndev->cl_info = cl_info; ndev->cl = cl; + ndev->req_id = 1; INIT_WORK(&ndev->init_work, mei_nfc_init); + init_waitqueue_head(&ndev->send_wq); schedule_work(&ndev->init_work); return 0; diff --git a/drivers/misc/mei/nfc.h b/drivers/misc/mei/nfc.h index 4440436..12e48d3 100644 --- a/drivers/misc/mei/nfc.h +++ b/drivers/misc/mei/nfc.h @@ -114,11 +114,24 @@ struct mei_nfc_connect_resp { uint16_t me_build; } __packed; +struct mei_nfc_hci_hdr { + u8 cmd; + u8 status; + u16 req_id; + u32 reserved; + u16 data_size; +} __packed; + #define MEI_NFC_CMD_MAINTENANCE 0x00 +#define MEI_NFC_CMD_HCI_SEND 0x01 +#define MEI_NFC_CMD_HCI_RECV 0x02 #define MEI_NFC_SUBCMD_CONNECT0x00 #define MEI_NFC_SUBCMD_IF_VERSION 0x01 +#define MEI_NFC_HEADER_SIZE 10 +#define MEI_NFC_MAX_HCI_PAYLOAD 300 + /* Vendors */ #define MEI_NFC_VENDOR_INSIDE 0x00 -- 1.8.1.3 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 04/11 V4] mei: bus: Add bus related structures to mei_cl
From: Samuel Ortiz We keep track of all MEI devices on the bus through a specific linked list. We also have a mei_device instance in the mei_cl structure. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 48 +++--- drivers/misc/mei/client.c | 1 + drivers/misc/mei/init.c| 1 + drivers/misc/mei/mei_dev.h | 8 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 43f77b8..5691f83 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -117,38 +117,54 @@ static struct device_type mei_cl_device_type = { .release= mei_cl_dev_release, }; -struct mei_cl_device *mei_cl_add_device(struct mei_device *mei_device, +static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev, + uuid_le uuid) +{ + struct mei_cl *cl, *next; + + list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { + if (!uuid_le_cmp(uuid, cl->device_uuid)) + return cl; + } + + return NULL; +} +struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, uuid_le uuid, char *name) { struct mei_cl_device *device; + struct mei_cl *cl; int status; + cl = mei_bus_find_mei_cl_by_uuid(dev, uuid); + if (cl == NULL) + return NULL; + device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); if (!device) return NULL; device->uuid = uuid; + device->cl = cl; - device->dev.parent = &mei_device->pdev->dev; + device->dev.parent = &dev->pdev->dev; device->dev.bus = &mei_cl_bus_type; device->dev.type = &mei_cl_device_type; dev_set_name(&device->dev, "%s", name); status = device_register(&device->dev); - if (status) - goto out_err; + if (status) { + dev_err(&dev->pdev->dev, "Failed to register MEI device\n"); + kfree(device); + return NULL; + } + + cl->device = device; dev_dbg(&device->dev, "client %s registered\n", name); return device; - -out_err: - dev_err(device->dev.parent, "Failed to register MEI client\n"); - - kfree(device); - - return NULL; } EXPORT_SYMBOL_GPL(mei_cl_add_device); @@ -346,9 +362,10 @@ out: int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) { - struct mei_cl *cl = NULL; + struct mei_cl *cl = device->cl; - /* TODO: hook between mei_bus_client and mei_cl */ + if (cl == NULL) + return -ENODEV; if (device->ops && device->ops->send) return device->ops->send(device, buf, length); @@ -359,9 +376,10 @@ EXPORT_SYMBOL_GPL(mei_cl_send); int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) { - struct mei_cl *cl = NULL; + struct mei_cl *cl = device->cl; - /* TODO: hook between mei_bus_client and mei_cl */ + if (cl == NULL) + return -ENODEV; if (device->ops && device->ops->recv) return device->ops->recv(device, buf, length); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 1569afe..e14397b 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -216,6 +216,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) init_waitqueue_head(&cl->rx_wait); init_waitqueue_head(&cl->tx_wait); INIT_LIST_HEAD(&cl->link); + INIT_LIST_HEAD(&cl->device_link); cl->reading_state = MEI_IDLE; cl->writing_state = MEI_IDLE; cl->dev = dev; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 09a9980..27df330 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -46,6 +46,7 @@ void mei_device_init(struct mei_device *dev) { /* setup our list array */ INIT_LIST_HEAD(&dev->file_list); + INIT_LIST_HEAD(&dev->device_list); mutex_init(&dev->device_lock); init_waitqueue_head(&dev->wait_hw_ready); init_waitqueue_head(&dev->wait_recvd_msg); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 800ac2b..d9da1fb 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -209,6 +209,11 @@ struct mei_cl { enum mei_file_transaction_states writing_state; int sm_state; struct mei_cl_cb *read_cb; + + /* MEI CL bus data */ + struct mei_cl_device *device; + struct list_head device
[char-misc-next 05/11 V4] mei: bus: Call bus routines from the core code
From: Samuel Ortiz Register the MEI bus type against the kernel core bus APIs and call the bus Rx handler from interrupt.c Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 22 ++ drivers/misc/mei/interrupt.c | 2 ++ drivers/misc/mei/mei_dev.h | 6 +- drivers/misc/mei/pci-me.c| 21 +++-- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 5691f83..d55f6d8 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -419,3 +419,25 @@ int mei_cl_register_event_cb(struct mei_cl_device *device, return 0; } EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); + +void mei_cl_bus_rx_event(struct mei_cl *cl) +{ + struct mei_cl_device *device = cl->device; + + if (!device || !device->event_cb) + return; + + set_bit(MEI_CL_EVENT_RX, &device->events); + + schedule_work(&device->event_work); +} + +int __init mei_cl_bus_init(struct pci_dev *pdev) +{ + return bus_register(&mei_cl_bus_type); +} + +void __exit mei_cl_bus_exit(void) +{ + bus_unregister(&mei_cl_bus_type); +} diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 73fbce3..c478332 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -49,6 +49,8 @@ static void mei_cl_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb) cl->reading_state = MEI_READ_COMPLETE; if (waitqueue_active(&cl->rx_wait)) wake_up_interruptible(&cl->rx_wait); + else + mei_cl_bus_rx_event(cl); } } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index d9da1fb..0391dc9 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -292,6 +292,11 @@ struct mei_cl_transport_ops { int (*recv)(struct mei_cl_device *device, u8 *buf, size_t length); }; +void mei_cl_bus_rx_event(struct mei_cl *cl); +int mei_cl_bus_init(struct pci_dev *pdev); +void mei_cl_bus_exit(void); + + /** * struct mei_cl_device - MEI device handle * An mei_cl_device pointer is returned from mei_add_device() @@ -452,7 +457,6 @@ static inline u32 mei_data2slots(size_t length) return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4); } - /* * mei init function prototypes */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index b8b5c9c..badc0bd 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -197,7 +197,6 @@ static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mei_pdev = pdev; pci_set_drvdata(pdev, dev); - schedule_delayed_work(&dev->timer_work, HZ); mutex_unlock(&mei_mutex); @@ -351,7 +350,25 @@ static struct pci_driver mei_driver = { .driver.pm = MEI_PM_OPS, }; -module_pci_driver(mei_driver); +static int __init mei_init(void) +{ + int err; + + err = mei_cl_bus_init(mei_pdev); + if (err) + return err; + + return pci_register_driver(&mei_driver); +} + +static void __exit mei_exit(void) +{ + pci_unregister_driver(&mei_driver); + mei_cl_bus_exit(); +} + +module_init(mei_init); +module_exit(mei_exit); MODULE_AUTHOR("Intel Corporation"); MODULE_DESCRIPTION("Intel(R) Management Engine Interface"); -- 1.8.1.3 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 10/11 V4] mei: nfc: Add NFC device to the MEI bus
From: Samuel Ortiz After building its bus name as a string based on its vendor id and radio type, we can add it to the bus. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/nfc.c | 55 ++ drivers/misc/mei/nfc.h | 6 ++ 2 files changed, 61 insertions(+) diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index b903d82..372d336 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -42,6 +42,8 @@ struct mei_nfc_dev { u8 fw_ivn; u8 vendor_id; u8 radio_type; + + char *bus_name; }; static struct mei_nfc_dev nfc_dev; @@ -70,6 +72,39 @@ static void mei_nfc_free(struct mei_nfc_dev *ndev) } } +static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev) +{ + struct mei_device *dev; + + if (!ndev->cl) + return -ENODEV; + + dev = ndev->cl->dev; + + switch (ndev->vendor_id) { + case MEI_NFC_VENDOR_INSIDE: + switch (ndev->radio_type) { + case MEI_NFC_VENDOR_INSIDE_UREAD: + ndev->bus_name = "microread"; + return 0; + + default: + dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n", + ndev->radio_type); + + return -EINVAL; + } + + default: + dev_err(&dev->pdev->dev, "Unknow vendor ID 0x%x\n", + ndev->vendor_id); + + return -EINVAL; + } + + return 0; +} + static int mei_nfc_connect(struct mei_nfc_dev *ndev) { struct mei_device *dev; @@ -191,6 +226,7 @@ err: static void mei_nfc_init(struct work_struct *work) { struct mei_device *dev; + struct mei_cl_device *cldev; struct mei_nfc_dev *ndev; struct mei_cl *cl_info, *cl; int ret; @@ -242,6 +278,22 @@ static void mei_nfc_init(struct work_struct *work) return; } + if (mei_nfc_build_bus_name(ndev) < 0) { + dev_err(&dev->pdev->dev, + "Could not build the bus ID name\n"); + return; + } + + cldev = mei_cl_add_device(dev, mei_nfc_guid, ndev->bus_name); + if (!cldev) { + dev_err(&dev->pdev->dev, + "Could not add the NFC device to the MEI bus\n"); + + goto err; + } + + cldev->priv_data = ndev; + return; err: @@ -323,5 +375,8 @@ void mei_nfc_host_exit(void) { struct mei_nfc_dev *ndev = &nfc_dev; + if (ndev->cl && ndev->cl->device) + mei_cl_remove_device(ndev->cl->device); + mei_nfc_free(ndev); } diff --git a/drivers/misc/mei/nfc.h b/drivers/misc/mei/nfc.h index 59e6703..4440436 100644 --- a/drivers/misc/mei/nfc.h +++ b/drivers/misc/mei/nfc.h @@ -119,4 +119,10 @@ struct mei_nfc_connect_resp { #define MEI_NFC_SUBCMD_CONNECT0x00 #define MEI_NFC_SUBCMD_IF_VERSION 0x01 +/* Vendors */ +#define MEI_NFC_VENDOR_INSIDE 0x00 + +/* Radio types */ +#define MEI_NFC_VENDOR_INSIDE_UREAD 0x00 + #endif /* _MEI_NFC_H */ -- 1.8.1.3 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 09/11 V4] mei: nfc: Connect also the regular ME client
From: Samuel Ortiz After receiving the NFC interface version, IVN and radio type, we can connect to the the actual nfc ME client and send the initialization (nfc connect) message. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/nfc.c | 128 +++-- 1 file changed, 123 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index 867cbe7..b903d82 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -28,6 +28,7 @@ /** mei_nfc_dev - NFC mei device * + * @cl: NFC host client * @cl_info: NFC info host client * @init_work: perform connection to the info client * @fw_ivn: NFC Intervace Version Number @@ -35,6 +36,7 @@ * @radio_type: NFC radio type */ struct mei_nfc_dev { + struct mei_cl *cl; struct mei_cl *cl_info; struct work_struct init_work; u8 fw_ivn; @@ -55,6 +57,12 @@ static const uuid_le mei_nfc_info_guid = UUID_LE(0xd2de1625, 0x382d, 0x417d, static void mei_nfc_free(struct mei_nfc_dev *ndev) { + if (ndev->cl) { + list_del(&ndev->cl->device_link); + mei_cl_unlink(ndev->cl); + kfree(ndev->cl); + } + if (ndev->cl_info) { list_del(&ndev->cl_info->device_link); mei_cl_unlink(ndev->cl_info); @@ -62,6 +70,73 @@ static void mei_nfc_free(struct mei_nfc_dev *ndev) } } +static int mei_nfc_connect(struct mei_nfc_dev *ndev) +{ + struct mei_device *dev; + struct mei_cl *cl; + struct mei_nfc_cmd *cmd, *reply; + struct mei_nfc_connect *connect; + struct mei_nfc_connect_resp *connect_resp; + size_t connect_length, connect_resp_length; + int bytes_recv, ret; + + cl = ndev->cl; + dev = cl->dev; + + connect_length = sizeof(struct mei_nfc_cmd) + + sizeof(struct mei_nfc_connect); + + connect_resp_length = sizeof(struct mei_nfc_cmd) + + sizeof(struct mei_nfc_connect_resp); + + cmd = kzalloc(connect_length, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + connect = (struct mei_nfc_connect *)cmd->data; + + reply = kzalloc(connect_resp_length, GFP_KERNEL); + if (!reply) { + kfree(cmd); + return -ENOMEM; + } + + connect_resp = (struct mei_nfc_connect_resp *)reply->data; + + cmd->command = MEI_NFC_CMD_MAINTENANCE; + cmd->data_size = 3; + cmd->sub_command = MEI_NFC_SUBCMD_CONNECT; + connect->fw_ivn = ndev->fw_ivn; + connect->vendor_id = ndev->vendor_id; + + ret = __mei_cl_send(cl, (u8 *)cmd, connect_length); + if (ret < 0) { + dev_err(&dev->pdev->dev, "Could not send connect cmd\n"); + goto err; + } + + bytes_recv = __mei_cl_recv(cl, (u8 *)reply, connect_resp_length); + if (bytes_recv < 0) { + dev_err(&dev->pdev->dev, "Could not read connect response\n"); + ret = bytes_recv; + goto err; + } + + dev_info(&dev->pdev->dev, "IVN 0x%x Vendor ID 0x%x\n", + connect_resp->fw_ivn, connect_resp->vendor_id); + + dev_info(&dev->pdev->dev, "ME FW %d.%d.%d.%d\n", + connect_resp->me_major, connect_resp->me_minor, + connect_resp->me_hotfix, connect_resp->me_build); + + ret = 0; + +err: + kfree(reply); + kfree(cmd); + + return ret; +} + static int mei_nfc_if_version(struct mei_nfc_dev *ndev) { struct mei_device *dev; @@ -117,12 +192,13 @@ static void mei_nfc_init(struct work_struct *work) { struct mei_device *dev; struct mei_nfc_dev *ndev; - struct mei_cl *cl_info; + struct mei_cl *cl_info, *cl; int ret; ndev = container_of(work, struct mei_nfc_dev, init_work); cl_info = ndev->cl_info; + cl = ndev->cl; dev = cl_info->dev; mutex_lock(&dev->device_lock); @@ -148,6 +224,24 @@ static void mei_nfc_init(struct work_struct *work) "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", ndev->fw_ivn, ndev->vendor_id, ndev->radio_type); + mutex_lock(&dev->device_lock); + + if (mei_cl_connect(cl, NULL) < 0) { + mutex_unlock(&dev->device_lock); + dev_err(&dev->pdev->dev, + "Could not connect to the NFC ME client"); + + goto err; + } + + mutex_unlock(&dev->device_lock); + + ret = mei_nfc_connect(ndev); + if (ret < 0) { + dev_err(&dev->pdev->dev, "Could not connect to NFC"); +
[char-misc-next 08/11 V4] mei: nfc: Initial nfc implementation
From: Samuel Ortiz NFC ME device is exported through the MEI bus to be consumed by the NFC subsystem. NFC is represented by two mei clients: An info one and the actual NFC one. In order to properly build the ME id we first need to retrieve the firmware information from the info client. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/Kconfig | 7 ++ drivers/misc/mei/Makefile | 1 + drivers/misc/mei/client.c | 3 + drivers/misc/mei/init.c| 2 + drivers/misc/mei/mei_dev.h | 28 ++ drivers/misc/mei/nfc.c | 209 + drivers/misc/mei/nfc.h | 122 ++ 7 files changed, 372 insertions(+) create mode 100644 drivers/misc/mei/nfc.c create mode 100644 drivers/misc/mei/nfc.h diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index d21b4d0..66e84ef 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -35,3 +35,10 @@ config INTEL_MEI_ME 82Q33 Express 82X38/X48 Express +config INTEL_MEI_BUS_NFC +bool "MEI bus NFC support" +depends on INTEL_MEI + help + When selecting this option the ME NFC device will be added to the + MEI bus. This is needed by the NFC kernel subsystem for sending and + receiving HCI frames to and from the NFC device. diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 5948621..644f92e 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -11,5 +11,6 @@ mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o mei-objs += bus.o +mei-$(CONFIG_INTEL_MEI_BUS_NFC) += nfc.o mei-$(CONFIG_INTEL_MEI_ME) += pci-me.o mei-$(CONFIG_INTEL_MEI_ME) += hw-me.o diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e14397b..eb27bf6 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -358,6 +358,9 @@ void mei_host_client_init(struct work_struct *work) mei_amthif_host_init(dev); else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) mei_wd_host_init(dev); + else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid)) + mei_nfc_host_init(dev); + } dev->dev_state = MEI_DEV_ENABLED; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 27df330..5de933d 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -206,6 +206,8 @@ void mei_stop(struct mei_device *dev) mei_wd_stop(dev); + mei_nfc_host_exit(); + dev->dev_state = MEI_DEV_POWER_DOWN; mei_reset(dev, 0); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 7199789..390d57e 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -499,6 +499,34 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, void mei_amthif_run_next_cmd(struct mei_device *dev); +#ifdef CONFIG_INTEL_MEI_BUS_NFC +/* + * NFC functions + */ +int mei_nfc_host_init(struct mei_device *dev); +void mei_nfc_host_exit(void); + +/* + * NFC Client UUID + */ +extern const uuid_le mei_nfc_guid; + +#else /* CONFIG_INTEL_MEI_BUS_NFC */ + +static inline int mei_nfc_host_init(struct mei_device *dev) +{ + return 0; +} + +static inline void mei_nfc_host_exit(void) +{ + return; +} + +static const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, + 0x94, 0xd4, 0x50, 0x26, + 0x67, 0x23, 0x77, 0x5c); +#endif /* CONFIG_INTEL_MEI_BUS_NFC */ int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c new file mode 100644 index 000..867cbe7 --- /dev/null +++ b/drivers/misc/mei/nfc.c @@ -0,0 +1,209 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mei_dev.h" +#include "client.h" +#include "nfc.h" + +/** mei_nfc_dev - NFC mei device + * + * @cl_info: NFC info host client + * @init_work: perform connection to the info client + * @fw_ivn: NFC Intervace Version Number + * @vendor_id: NFC manufacturer ID + * @radio_type: NFC
[char-misc-next 02/11 V4] mei: bus: Implement driver registration
From: Samuel Ortiz Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 26 ++ include/linux/mei_cl_bus.h | 7 +++ 2 files changed, 33 insertions(+) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 24a25db..9283dd6 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -149,3 +149,29 @@ void mei_cl_remove_device(struct mei_cl_device *device) device_unregister(&device->dev); } EXPORT_SYMBOL_GPL(mei_cl_remove_device); + +int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner) +{ + int err; + + driver->driver.name = driver->name; + driver->driver.owner = owner; + driver->driver.bus = &mei_cl_bus_type; + + err = driver_register(&driver->driver); + if (err) + return err; + + pr_debug("mei: driver [%s] registered\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL_GPL(__mei_cl_driver_register); + +void mei_cl_driver_unregister(struct mei_cl_driver *driver) +{ + driver_unregister(&driver->driver); + + pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); +} +EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h index ed4199b..92c30a8 100644 --- a/include/linux/mei_cl_bus.h +++ b/include/linux/mei_cl_bus.h @@ -89,4 +89,11 @@ struct mei_cl_driver { int (*remove)(struct mei_cl_device *dev); }; +int __mei_cl_driver_register(struct mei_cl_driver *driver, + struct module *owner); +#define mei_cl_driver_register(driver) \ + __mei_cl_driver_register(driver, THIS_MODULE) + +void mei_cl_driver_unregister(struct mei_cl_driver *driver); + #endif /* _LINUX_MEI_CL_BUS_H */ -- 1.8.1.3 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 07/11 V4] mei: bus: Implement bus driver data setter/getter
From: Samuel Ortiz MEI drivers should be able to carry their private data around. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 12 include/linux/mei_cl_bus.h | 3 +++ 2 files changed, 15 insertions(+) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index d542950..bb1cff4 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -439,6 +439,18 @@ int mei_cl_register_event_cb(struct mei_cl_device *device, } EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); +void *mei_cl_get_drvdata(const struct mei_cl_device *device) +{ + return dev_get_drvdata(&device->dev); +} +EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); + +void mei_cl_set_drvdata(struct mei_cl_device *device, void *data) +{ + dev_set_drvdata(&device->dev, data); +} +EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); + void mei_cl_bus_rx_event(struct mei_cl *cl) { struct mei_cl_device *device = cl->device; diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h index 054e7e3..e95399e 100644 --- a/include/linux/mei_cl_bus.h +++ b/include/linux/mei_cl_bus.h @@ -107,4 +107,7 @@ int mei_cl_register_event_cb(struct mei_cl_device *device, #define MEI_CL_EVENT_RX 0 #define MEI_CL_EVENT_TX 1 +void *mei_cl_get_drvdata(const struct mei_cl_device *device); +void mei_cl_set_drvdata(struct mei_cl_device *device, void *data); + #endif /* _LINUX_MEI_CL_BUS_H */ -- 1.8.1.3 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 03/11 V4] mei: bus: Initial implementation for I/O routines
From: Samuel Ortiz Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 226 + drivers/misc/mei/mei_dev.h | 30 ++ include/linux/mei_cl_bus.h | 11 +++ 3 files changed, 267 insertions(+) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 9283dd6..43f77b8 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,8 @@ #include #include "mei_dev.h" +#include "hw-me.h" +#include "client.h" #define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) #define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev) @@ -83,6 +86,11 @@ static int mei_cl_device_remove(struct device *dev) if (!device || !dev->driver) return 0; + if (device->event_cb) { + device->event_cb = NULL; + cancel_work_sync(&device->event_work); + } + driver = to_mei_cl_driver(dev->driver); if (!driver->remove) { dev->driver = NULL; @@ -175,3 +183,221 @@ void mei_cl_driver_unregister(struct mei_cl_driver *driver) pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); } EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); + +int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_msg_hdr mei_hdr; + struct mei_cl_cb *cb; + int me_cl_id, err; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + if (cl->state != MEI_FILE_CONNECTED) + return -ENODEV; + + cb = mei_io_cb_init(cl, NULL); + if (!cb) + return -ENOMEM; + + err = mei_io_cb_alloc_req_buf(cb, length); + if (err < 0) { + mei_io_cb_free(cb); + return err; + } + + memcpy(cb->request_buffer.data, buf, length); + cb->fop_type = MEI_FOP_WRITE; + + dev = cl->dev; + + mutex_lock(&dev->device_lock); + + /* Check if we have an ME client device */ + me_cl_id = mei_me_cl_by_id(dev, cl->me_client_id); + if (me_cl_id == dev->me_clients_num) { + err = -ENODEV; + goto out_err; + } + + if (length > dev->me_clients[me_cl_id].props.max_msg_length) { + err = -EINVAL; + goto out_err; + } + + err = mei_cl_flow_ctrl_creds(cl); + if (err < 0) + goto out_err; + + /* Host buffer is not ready, we queue the request */ + if (err == 0 || !dev->hbuf_is_ready) { + cb->buf_idx = 0; + mei_hdr.msg_complete = 0; + cl->writing_state = MEI_WRITING; + list_add_tail(&cb->list, &dev->write_list.list); + + mutex_unlock(&dev->device_lock); + + return length; + } + + dev->hbuf_is_ready = false; + + /* Check for a maximum length */ + if (length > mei_hbuf_max_len(dev)) { + mei_hdr.length = mei_hbuf_max_len(dev); + mei_hdr.msg_complete = 0; + } else { + mei_hdr.length = length; + mei_hdr.msg_complete = 1; + } + + mei_hdr.host_addr = cl->host_client_id; + mei_hdr.me_addr = cl->me_client_id; + mei_hdr.reserved = 0; + + if (mei_write_message(dev, &mei_hdr, buf)) { + err = -EIO; + goto out_err; + } + + cl->writing_state = MEI_WRITING; + cb->buf_idx = mei_hdr.length; + + if (!mei_hdr.msg_complete) { + list_add_tail(&cb->list, &dev->write_list.list); + } else { + if (mei_cl_flow_ctrl_reduce(cl)) { + err = -EIO; + goto out_err; + } + + list_add_tail(&cb->list, &dev->write_waiting_list.list); + } + + mutex_unlock(&dev->device_lock); + + return mei_hdr.length; + +out_err: + mutex_unlock(&dev->device_lock); + mei_io_cb_free(cb); + + return err; +} + +int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_cl_cb *cb; + size_t r_length; + int err; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + mutex_lock(&dev->device_lock); + + if (!cl->read_cb) { + err = mei_cl_read_start(cl); + if (err < 0) { + mutex_unlock(&dev->device_lock); + return err; + } + } + + if (cl->reading_state != MEI_READ_COMPLETE
[char-misc-next 0/6] MEI and ME separation
We are about to introduce new under laying hardware that supports MEI interface. For that purpose we split the driver into interface hand hardware specific layers Tomas Winkler (6): mei: sperate interface and pci code into two files mei: initial extract of ME hw specifics from mei_device mei: separate compilation of the ME hardware specifics mei: move interrupt handlers to be me hw specific mei: rename to mei_host_buffer_is_empty to hbuf_is_ready mei: move clients cleanup code from init.c to client.c drivers/misc/mei/Kconfig | 17 ++- drivers/misc/mei/Makefile|5 +- drivers/misc/mei/amthif.c| 12 +- drivers/misc/mei/client.c| 64 ++- drivers/misc/mei/client.h|5 + drivers/misc/mei/hw-me.c | 376 ++-- drivers/misc/mei/hw-me.h | 30 ++-- drivers/misc/mei/init.c | 58 +-- drivers/misc/mei/interrupt.c | 130 +- drivers/misc/mei/main.c | 363 ++ drivers/misc/mei/mei_dev.h | 168 +++--- drivers/misc/mei/pci-me.c| 396 ++ drivers/misc/mei/wd.c|9 +- 13 files changed, 960 insertions(+), 673 deletions(-) create mode 100644 drivers/misc/mei/pci-me.c -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 6/6] mei: move clients cleanup code from init.c to client.c
during reset we clean up client data structures we move that code into wrappers in client and call the wrappers Signed-off-by: Tomas Winkler --- drivers/misc/mei/client.c | 51 + drivers/misc/mei/client.h |5 drivers/misc/mei/init.c | 28 +--- 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e46663e..1569afe 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -676,3 +676,54 @@ err: return rets; } +/** + * mei_cl_all_disconnect - disconnect forcefully all connected clients + * + * @dev - mei device + */ + +void mei_cl_all_disconnect(struct mei_device *dev) +{ + struct mei_cl *cl, *next; + + list_for_each_entry_safe(cl, next, &dev->file_list, link) { + cl->state = MEI_FILE_DISCONNECTED; + cl->mei_flow_ctrl_creds = 0; + cl->read_cb = NULL; + cl->timer_count = 0; + } +} + + +/** + * mei_cl_all_read_wakeup - wake up all readings so they can be interrupted + * + * @dev - mei device + */ +void mei_cl_all_read_wakeup(struct mei_device *dev) +{ + struct mei_cl *cl, *next; + list_for_each_entry_safe(cl, next, &dev->file_list, link) { + if (waitqueue_active(&cl->rx_wait)) { + dev_dbg(&dev->pdev->dev, "Waking up client!\n"); + wake_up_interruptible(&cl->rx_wait); + } + } +} + +/** + * mei_cl_all_write_clear - clear all pending writes + + * @dev - mei device + */ +void mei_cl_all_write_clear(struct mei_device *dev) +{ + struct mei_cl_cb *cb, *next; + + list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { + list_del(&cb->list); + mei_io_cb_free(cb); + } +} + + diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 240a1f3..214b239 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -94,4 +94,9 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file); void mei_host_client_init(struct work_struct *work); +void mei_cl_all_disconnect(struct mei_device *dev); +void mei_cl_all_read_wakeup(struct mei_device *dev); +void mei_cl_all_write_clear(struct mei_device *dev); + + #endif /* _MEI_CLIENT_H_ */ diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 51a005e..6ec5301 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -135,10 +135,6 @@ err: */ void mei_reset(struct mei_device *dev, int interrupts_enabled) { - struct mei_cl *cl_pos = NULL; - struct mei_cl *cl_next = NULL; - struct mei_cl_cb *cb_pos = NULL; - struct mei_cl_cb *cb_next = NULL; bool unexpected; if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) @@ -157,13 +153,8 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev->dev_state != MEI_DEV_POWER_DOWN) dev->dev_state = MEI_DEV_RESETING; - list_for_each_entry_safe(cl_pos, - cl_next, &dev->file_list, link) { - cl_pos->state = MEI_FILE_DISCONNECTED; - cl_pos->mei_flow_ctrl_creds = 0; - cl_pos->read_cb = NULL; - cl_pos->timer_count = 0; - } + mei_cl_all_disconnect(dev); + /* remove entry if already in list */ dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); @@ -185,18 +176,11 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", mei_dev_state_str(dev->dev_state)); - /* Wake up all readings so they can be interrupted */ - list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { - if (waitqueue_active(&cl_pos->rx_wait)) { - dev_dbg(&dev->pdev->dev, "Waking up client!\n"); - wake_up_interruptible(&cl_pos->rx_wait); - } - } + /* wake up all readings so they can be interrupted */ + mei_cl_all_read_wakeup(dev); + /* remove all waiting requests */ - list_for_each_entry_safe(cb_pos, cb_next, &dev->write_list.list, list) { - list_del(&cb_pos->list); - mei_io_cb_free(cb_pos); - } + mei_cl_all_write_clear(dev); } -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 1/6] mei: sperate interface and pci code into two files
leave misc file operations in the main and move PCI related code into pci-me Signed-off-by: Tomas Winkler --- drivers/misc/mei/Makefile |1 + drivers/misc/mei/main.c| 354 ++-- drivers/misc/mei/mei_dev.h |3 + drivers/misc/mei/pci-me.c | 393 4 files changed, 409 insertions(+), 342 deletions(-) create mode 100644 drivers/misc/mei/pci-me.c diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 9f71933..068f553 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -11,3 +11,4 @@ mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o mei-objs += client.o +mei-objs += pci-me.o diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 123c663..018623c 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -43,54 +43,6 @@ #include "hw-me.h" #include "client.h" -/* AMT device is a singleton on the platform */ -static struct pci_dev *mei_pdev; - -/* mei_pci_tbl - PCI Device ID Table */ -static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)}, - - /* required last entry */ - {0, } -}; - -MODULE_DEVICE_TABLE(pci, mei_pci_tbl); - -static DEFINE_MUTEX(mei_mutex); - - /** * mei_open - the open function * @@ -101,15 +53,20 @@ static DEFINE_MUTEX(mei_mutex); */ static int mei_open(struct inode *inode, struct file *file) { + struct miscdevice *misc = file->private_data; + struct pci_dev *pdev; struct mei_cl *cl; struct mei_device *dev; + int err; err = -ENODEV; - if (!mei_pdev) + if (!misc->parent) goto out; - dev = pci_get_drvdata(mei_pdev); + pdev = container_of(misc->parent, struct pci_dev, dev); + + dev = pci_get_drvdata(pdev); if (!dev) goto out; @@ -787,7 +744,6 @@ static const struct file_operations mei_fops = { .llseek = no_llseek }; - /* * Misc Device Struct */ @@ -797,302 +753,16 @@ static struct miscdevice mei_misc_device = { .minor = MISC_DYNAMIC_MINOR, }; -/** - * mei_quirk_probe - probe for devices that doesn't valid ME interface - * @pdev: PCI device structure - * @ent: entry into pci_device_table - * - * returns true if ME Interface is valid, false otherwise - */ -static bool mei_quirk_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +int mei_register(struct device *dev) { - u32 reg; - if (ent->device == MEI_DEV_ID_PBG_1) { - pci_read_config_dword(pdev, 0x48, ®); - /* make sure that bit 9 is up and bit 10 is down */ - if ((reg & 0x600) == 0x200) { - dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n"); -
[char-misc-next 4/6] mei: move interrupt handlers to be me hw specific
interrupt handler are platform specifics so we move them to hw-mei.c. For sake of that we need to export write, read, and complete handlers from the interrupt.c Signed-off-by: Tomas Winkler --- drivers/misc/mei/hw-me.c | 164 +++-- drivers/misc/mei/hw-me.h |3 + drivers/misc/mei/interrupt.c | 126 ++-- drivers/misc/mei/mei_dev.h |9 ++- drivers/misc/mei/pci-me.c| 12 ++-- 5 files changed, 164 insertions(+), 150 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 6300943..3bebf8d 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -15,11 +15,16 @@ */ #include -#include + +#include +#include #include "mei_dev.h" #include "hw-me.h" +#include "hbm.h" + + /** * mei_reg_read - Reads 32bit data from the mei device * @@ -218,29 +223,6 @@ static bool mei_me_hw_is_ready(struct mei_device *dev) } /** - * mei_interrupt_quick_handler - The ISR of the MEI device - * - * @irq: The irq number - * @dev_id: pointer to the device structure - * - * returns irqreturn_t - */ -irqreturn_t mei_interrupt_quick_handler(int irq, void *dev_id) -{ - struct mei_device *dev = (struct mei_device *) dev_id; - struct mei_me_hw *hw = to_me_hw(dev); - u32 csr_reg = mei_hcsr_read(hw); - - if ((csr_reg & H_IS) != H_IS) - return IRQ_NONE; - - /* clear H_IS bit in H_CSR */ - mei_reg_write(hw, H_CSR, csr_reg); - - return IRQ_WAKE_THREAD; -} - -/** * mei_hbuf_filled_slots - gets number of device filled buffer slots * * @dev: the device structure @@ -403,6 +385,139 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, return 0; } +/** + * mei_me_irq_quick_handler - The ISR of the MEI device + * + * @irq: The irq number + * @dev_id: pointer to the device structure + * + * returns irqreturn_t + */ + +irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) +{ + struct mei_device *dev = (struct mei_device *) dev_id; + struct mei_me_hw *hw = to_me_hw(dev); + u32 csr_reg = mei_hcsr_read(hw); + + if ((csr_reg & H_IS) != H_IS) + return IRQ_NONE; + + /* clear H_IS bit in H_CSR */ + mei_reg_write(hw, H_CSR, csr_reg); + + return IRQ_WAKE_THREAD; +} + +/** + * mei_me_irq_thread_handler - function called after ISR to handle the interrupt + * processing. + * + * @irq: The irq number + * @dev_id: pointer to the device structure + * + * returns irqreturn_t + * + */ +irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) +{ + struct mei_device *dev = (struct mei_device *) dev_id; + struct mei_cl_cb complete_list; + struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; + struct mei_cl *cl; + s32 slots; + int rets; + bool bus_message_received; + + + dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n"); + /* initialize our complete list */ + mutex_lock(&dev->device_lock); + mei_io_list_init(&complete_list); + + /* Ack the interrupt here +* In case of MSI we don't go through the quick handler */ + if (pci_dev_msi_enabled(dev->pdev)) + mei_clear_interrupts(dev); + + /* check if ME wants a reset */ + if (!mei_hw_is_ready(dev) && + dev->dev_state != MEI_DEV_RESETING && + dev->dev_state != MEI_DEV_INITIALIZING) { + dev_dbg(&dev->pdev->dev, "FW not ready.\n"); + mei_reset(dev, 1); + mutex_unlock(&dev->device_lock); + return IRQ_HANDLED; + } + + /* check if we need to start the dev */ + if (!mei_host_is_ready(dev)) { + if (mei_hw_is_ready(dev)) { + dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); + + mei_host_set_ready(dev); + + dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); + /* link is established * start sending messages. */ + + dev->dev_state = MEI_DEV_INIT_CLIENTS; + + mei_hbm_start_req(dev); + mutex_unlock(&dev->device_lock); + return IRQ_HANDLED; + } else { + dev_dbg(&dev->pdev->dev, "FW not ready.\n"); + mutex_unlock(&dev->device_lock); + return IRQ_HANDLED; + } + } + /* check slots available for reading */ + slots = mei_count_full_read_slots(dev); + while (slots > 0) { + /* we have urgent data to send so break the read */ +
[char-misc-next 5/6] mei: rename to mei_host_buffer_is_empty to hbuf_is_ready
we rename the mei_host_buffer_is_empty to keep naming convention of hbuf and also make the query more generic to be correct also for other under laying hardware Signed-off-by: Tomas Winkler --- drivers/misc/mei/amthif.c |8 drivers/misc/mei/client.c | 13 ++--- drivers/misc/mei/hw-me.c |2 +- drivers/misc/mei/main.c|4 ++-- drivers/misc/mei/mei_dev.h | 14 ++ drivers/misc/mei/wd.c |9 - 6 files changed, 27 insertions(+), 23 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index a7c4838..c86d7e3 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -297,9 +297,9 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) if (ret < 0) return ret; - if (ret && dev->mei_host_buffer_is_empty) { + if (ret && dev->hbuf_is_ready) { ret = 0; - dev->mei_host_buffer_is_empty = false; + dev->hbuf_is_ready = false; if (cb->request_buffer.size > mei_hbuf_max_len(dev)) { mei_hdr.length = mei_hbuf_max_len(dev); mei_hdr.msg_complete = 0; @@ -330,7 +330,7 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) list_add_tail(&cb->list, &dev->write_list.list); } } else { - if (!(dev->mei_host_buffer_is_empty)) + if (!dev->hbuf_is_ready) dev_dbg(&dev->pdev->dev, "host buffer is not empty"); dev_dbg(&dev->pdev->dev, "No flow control credentials, so add iamthif cb to write list.\n"); @@ -583,7 +583,7 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) dev->iamthif_msg_buf_index = 0; dev->iamthif_msg_buf_size = 0; dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER; - dev->mei_host_buffer_is_empty = mei_hbuf_is_ready(dev); + dev->hbuf_is_ready = mei_hbuf_is_ready(dev); return 0; } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index a921001..e46663e 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -393,8 +393,8 @@ int mei_cl_disconnect(struct mei_cl *cl) return -ENOMEM; cb->fop_type = MEI_FOP_CLOSE; - if (dev->mei_host_buffer_is_empty) { - dev->mei_host_buffer_is_empty = false; + if (dev->hbuf_is_ready) { + dev->hbuf_is_ready = false; if (mei_hbm_cl_disconnect_req(dev, cl)) { rets = -ENODEV; dev_err(&dev->pdev->dev, "failed to disconnect.\n"); @@ -496,9 +496,8 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) cb->fop_type = MEI_FOP_IOCTL; - if (dev->mei_host_buffer_is_empty && - !mei_cl_is_other_connecting(cl)) { - dev->mei_host_buffer_is_empty = false; + if (dev->hbuf_is_ready && !mei_cl_is_other_connecting(cl)) { + dev->hbuf_is_ready = false; if (mei_hbm_cl_connect_req(dev, cl)) { rets = -ENODEV; @@ -661,8 +660,8 @@ int mei_cl_read_start(struct mei_cl *cl) cb->fop_type = MEI_FOP_READ; cl->read_cb = cb; - if (dev->mei_host_buffer_is_empty) { - dev->mei_host_buffer_is_empty = false; + if (dev->hbuf_is_ready) { + dev->hbuf_is_ready = false; if (mei_hbm_cl_flow_control_req(dev, cl)) { rets = -ENODEV; goto err; diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 3bebf8d..45ea718 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -486,7 +486,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) rets = mei_irq_write_handler(dev, &complete_list); end: dev_dbg(&dev->pdev->dev, "end of bottom half function.\n"); - dev->mei_host_buffer_is_empty = mei_hbuf_is_ready(dev); + dev->hbuf_is_ready = mei_hbuf_is_ready(dev); bus_message_received = false; if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) { diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 843ae2f..903f809 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -454,14 +454,14 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (rets < 0) goto err; - if (rets == 0 || dev->mei_host_buffer_is_empty == false) { + if (rets == 0 || !dev->hbuf_is_ready) { write_cb->buf_idx = 0; mei_hdr.msg_complete = 0;
[char-misc-next 2/6] mei: initial extract of ME hw specifics from mei_device
This is initial step of move the ME hw specifics out of mei_device structure into mei_me_hw Signed-off-by: Tomas Winkler --- drivers/misc/mei/hw-me.c | 131 +--- drivers/misc/mei/hw-me.h | 12 drivers/misc/mei/init.c| 28 +- drivers/misc/mei/mei_dev.h | 10 +-- drivers/misc/mei/pci-me.c | 21 --- 5 files changed, 114 insertions(+), 88 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 94b203e..3bdf228 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -28,10 +28,10 @@ * * returns register value (u32) */ -static inline u32 mei_reg_read(const struct mei_device *dev, +static inline u32 mei_reg_read(const struct mei_me_hw *hw, unsigned long offset) { - return ioread32(dev->mem_addr + offset); + return ioread32(hw->mem_addr + offset); } @@ -42,10 +42,10 @@ static inline u32 mei_reg_read(const struct mei_device *dev, * @offset: offset from which to write the data * @value: register value to write (u32) */ -static inline void mei_reg_write(const struct mei_device *dev, +static inline void mei_reg_write(const struct mei_me_hw *hw, unsigned long offset, u32 value) { - iowrite32(value, dev->mem_addr + offset); + iowrite32(value, hw->mem_addr + offset); } /** @@ -58,7 +58,7 @@ static inline void mei_reg_write(const struct mei_device *dev, */ u32 mei_mecbrw_read(const struct mei_device *dev) { - return mei_reg_read(dev, ME_CB_RW); + return mei_reg_read(to_me_hw(dev), ME_CB_RW); } /** * mei_mecsr_read - Reads 32bit data from the ME CSR @@ -67,9 +67,9 @@ u32 mei_mecbrw_read(const struct mei_device *dev) * * returns ME_CSR_HA register value (u32) */ -static inline u32 mei_mecsr_read(const struct mei_device *dev) +static inline u32 mei_mecsr_read(const struct mei_me_hw *hw) { - return mei_reg_read(dev, ME_CSR_HA); + return mei_reg_read(hw, ME_CSR_HA); } /** @@ -79,9 +79,9 @@ static inline u32 mei_mecsr_read(const struct mei_device *dev) * * returns H_CSR register value (u32) */ -static inline u32 mei_hcsr_read(const struct mei_device *dev) +static inline u32 mei_hcsr_read(const struct mei_me_hw *hw) { - return mei_reg_read(dev, H_CSR); + return mei_reg_read(hw, H_CSR); } /** @@ -90,10 +90,10 @@ static inline u32 mei_hcsr_read(const struct mei_device *dev) * * @dev: the device structure */ -static inline void mei_hcsr_set(struct mei_device *dev, u32 hcsr) +static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr) { hcsr &= ~H_IS; - mei_reg_write(dev, H_CSR, hcsr); + mei_reg_write(hw, H_CSR, hcsr); } @@ -104,7 +104,7 @@ static inline void mei_hcsr_set(struct mei_device *dev, u32 hcsr) */ void mei_hw_config(struct mei_device *dev) { - u32 hcsr = mei_hcsr_read(dev); + u32 hcsr = mei_hcsr_read(to_me_hw(dev)); /* Doesn't change in runtime */ dev->hbuf_depth = (hcsr & H_CBD) >> 24; } @@ -115,9 +115,10 @@ void mei_hw_config(struct mei_device *dev) */ void mei_clear_interrupts(struct mei_device *dev) { - u32 hcsr = mei_hcsr_read(dev); + struct mei_me_hw *hw = to_me_hw(dev); + u32 hcsr = mei_hcsr_read(hw); if ((hcsr & H_IS) == H_IS) - mei_reg_write(dev, H_CSR, hcsr); + mei_reg_write(hw, H_CSR, hcsr); } /** @@ -127,9 +128,10 @@ void mei_clear_interrupts(struct mei_device *dev) */ void mei_enable_interrupts(struct mei_device *dev) { - u32 hcsr = mei_hcsr_read(dev); + struct mei_me_hw *hw = to_me_hw(dev); + u32 hcsr = mei_hcsr_read(hw); hcsr |= H_IE; - mei_hcsr_set(dev, hcsr); + mei_hcsr_set(hw, hcsr); } /** @@ -139,9 +141,10 @@ void mei_enable_interrupts(struct mei_device *dev) */ void mei_disable_interrupts(struct mei_device *dev) { - u32 hcsr = mei_hcsr_read(dev); + struct mei_me_hw *hw = to_me_hw(dev); + u32 hcsr = mei_hcsr_read(hw); hcsr &= ~H_IE; - mei_hcsr_set(dev, hcsr); + mei_hcsr_set(hw, hcsr); } /** @@ -152,7 +155,8 @@ void mei_disable_interrupts(struct mei_device *dev) */ void mei_hw_reset(struct mei_device *dev, bool intr_enable) { - u32 hcsr = mei_hcsr_read(dev); + struct mei_me_hw *hw = to_me_hw(dev); + u32 hcsr = mei_hcsr_read(hw); dev_dbg(&dev->pdev->dev, "before reset HCSR = 0x%08x.\n", hcsr); @@ -163,14 +167,14 @@ void mei_hw_reset(struct mei_device *dev, bool intr_enable) else hcsr &= ~H_IE; - mei_hcsr_set(dev, hcsr); + mei_hcsr_set(hw, hcsr); - hcsr = mei_hcsr_read(dev) | H_IG; + hcsr = mei_hcsr_read(hw) | H_IG; hcsr &= ~H_RST; - mei_hcsr_set(dev, hcsr); + mei_hcsr_set(hw, hcsr); - hcsr = mei_hcsr_rea
[char-misc-next 3/6] mei: separate compilation of the ME hardware specifics
We add struct mei_hw_ops to virtualize access to hw specific configurations. This allows us to separate the compilation of the ME interface from the ME hardware specifics Signed-off-by: Tomas Winkler --- drivers/misc/mei/Kconfig | 17 - drivers/misc/mei/Makefile|6 +- drivers/misc/mei/amthif.c|6 +- drivers/misc/mei/hw-me.c | 87 +++- drivers/misc/mei/hw-me.h | 19 -- drivers/misc/mei/init.c |2 +- drivers/misc/mei/interrupt.c | 10 ++-- drivers/misc/mei/main.c |5 +- drivers/misc/mei/mei_dev.h | 132 ++--- 9 files changed, 210 insertions(+), 74 deletions(-) diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index 5a79ccd..fa5c249 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -1,11 +1,22 @@ config INTEL_MEI - tristate "Intel Management Engine Interface (Intel MEI)" - depends on X86 && PCI && WATCHDOG_CORE + tristate "Intel Management Engine Interface" + depends on X86 && PCI help The Intel Management Engine (Intel ME) provides Manageability, Security and Media services for system containing Intel chipsets. if selected /dev/mei misc device will be created. + For more information see + <http://software.intel.com/en-us/manageability/> + +config INTEL_MEI_ME + bool "ME Enabled Intel Chipsets" + depends on INTEL_MEI + depends on X86 && PCI && WATCHDOG_CORE + default y + help + MEI support for ME Enabled Intel chipsets. + Supported Chipsets are: 7 Series Chipset Family 6 Series Chipset Family @@ -24,5 +35,3 @@ config INTEL_MEI 82Q33 Express 82X38/X48 Express - For more information see - <http://software.intel.com/en-us/manageability/> diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 068f553..040af6c 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -6,9 +6,9 @@ obj-$(CONFIG_INTEL_MEI) += mei.o mei-objs := init.o mei-objs += hbm.o mei-objs += interrupt.o -mei-objs += hw-me.o +mei-objs += client.o mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o -mei-objs += client.o -mei-objs += pci-me.o +mei-$(CONFIG_INTEL_MEI_ME) += pci-me.o +mei-$(CONFIG_INTEL_MEI_ME) += hw-me.o diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 263ed4c..a7c4838 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -300,8 +300,8 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) if (ret && dev->mei_host_buffer_is_empty) { ret = 0; dev->mei_host_buffer_is_empty = false; - if (cb->request_buffer.size > mei_hbuf_max_data(dev)) { - mei_hdr.length = mei_hbuf_max_data(dev); + if (cb->request_buffer.size > mei_hbuf_max_len(dev)) { + mei_hdr.length = mei_hbuf_max_len(dev); mei_hdr.msg_complete = 0; } else { mei_hdr.length = cb->request_buffer.size; @@ -583,7 +583,7 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) dev->iamthif_msg_buf_index = 0; dev->iamthif_msg_buf_size = 0; dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER; - dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev); + dev->mei_host_buffer_is_empty = mei_hbuf_is_ready(dev); return 0; } diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 3bdf228..6300943 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -56,7 +56,7 @@ static inline void mei_reg_write(const struct mei_me_hw *hw, * * returns ME_CB_RW register value (u32) */ -u32 mei_mecbrw_read(const struct mei_device *dev) +static u32 mei_me_mecbrw_read(const struct mei_device *dev) { return mei_reg_read(to_me_hw(dev), ME_CB_RW); } @@ -102,7 +102,7 @@ static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr) * * @dev: mei device */ -void mei_hw_config(struct mei_device *dev) +static void mei_me_hw_config(struct mei_device *dev) { u32 hcsr = mei_hcsr_read(to_me_hw(dev)); /* Doesn't change in runtime */ @@ -113,20 +113,19 @@ void mei_hw_config(struct mei_device *dev) * * @dev: the device structure */ -void mei_clear_interrupts(struct mei_device *dev) +static void mei_me_intr_clear(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr = mei_hcsr_read(hw); if ((hcsr & H_IS) == H_IS) mei_reg_write(hw, H_CSR, hcsr); } - /** - * mei_enable_interrupts - enables mei device interrupts + * mei_me_intr_enable - enables mei device interrupts *
[char-misc-next] mei: fix undefined wd symbols when MEI_ME is not set
Currently watchdog client is compiled with MEI and not with MEI_ME Fixes error: When CONFIG_WATCHDOG is not enabled: ERROR: "watchdog_unregister_device" [drivers/misc/mei/mei.ko] undefined! ERROR: "watchdog_register_device" [drivers/misc/mei/mei.ko] undefined Reported-by: Randy Dunlap Signed-off-by: Tomas Winkler --- drivers/misc/mei/Kconfig |2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index fa5c249..d21b4d0 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -1,6 +1,6 @@ config INTEL_MEI tristate "Intel Management Engine Interface" - depends on X86 && PCI + depends on X86 && PCI && WATCHDOG_CORE help The Intel Management Engine (Intel ME) provides Manageability, Security and Media services for system containing Intel chipsets. -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 10/11] mei: nfc: Add NFC device to the MEI bus
From: Samuel Ortiz After building its bus name as a string based on its vendor id and radio type, we can add it to the bus. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/nfc.c | 56 drivers/misc/mei/nfc.h |6 + 2 files changed, 62 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index ab66944..c65bd44 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -43,6 +43,8 @@ struct mei_bus_dev_nfc { u8 fw_ivn; u8 vendor_id; u8 radio_type; + + char *bus_name; }; struct mei_bus_dev_nfc nfc_bdev; @@ -71,6 +73,39 @@ static void mei_nfc_free(struct mei_bus_dev_nfc *bdev) } } +static int mei_nfc_build_bus_name(struct mei_bus_dev_nfc *bdev) +{ + struct mei_device *dev; + + if (!bdev->cl) + return -ENODEV; + + dev = bdev->cl->dev; + + switch (bdev->vendor_id) { + case MEI_NFC_VENDOR_INSIDE: + switch (bdev->radio_type) { + case MEI_NFC_VENDOR_INSIDE_UREAD: + bdev->bus_name = "microread"; + return 0; + + default: + dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n", + bdev->radio_type); + + return -EINVAL; + } + + default: + dev_err(&dev->pdev->dev, "Unknow vendor ID 0x%x\n", + bdev->vendor_id); + + return -EINVAL; + } + + return 0; +} + static int mei_nfc_connect(struct mei_bus_dev_nfc *bdev) { struct mei_device *dev; @@ -192,6 +227,7 @@ err: static void mei_nfc_init(struct work_struct *work) { struct mei_device *dev; + struct mei_bus_client *bus_client; struct mei_bus_dev_nfc *bdev; struct mei_cl *cl_info, *cl; int ret; @@ -243,6 +279,23 @@ static void mei_nfc_init(struct work_struct *work) return; } + if (mei_nfc_build_bus_name(bdev) < 0) { + dev_err(&dev->pdev->dev, + "Could not build the bus ID name\n"); + return; + } + + bus_client = mei_add_device(dev, mei_nfc_guid, + bdev->bus_name); + if (!bus_client) { + dev_err(&dev->pdev->dev, + "Could not add the NFC device to the MEI bus\n"); + + goto err; + } + + bus_client->priv_data = bdev; + return; err: @@ -324,5 +377,8 @@ void mei_nfc_host_exit(void) { struct mei_bus_dev_nfc *bdev = &nfc_bdev; + if (bdev->cl && bdev->cl->client) + mei_remove_device(bdev->cl->client); + mei_nfc_free(bdev); } diff --git a/drivers/misc/mei/nfc.h b/drivers/misc/mei/nfc.h index 59e6703..4440436 100644 --- a/drivers/misc/mei/nfc.h +++ b/drivers/misc/mei/nfc.h @@ -119,4 +119,10 @@ struct mei_nfc_connect_resp { #define MEI_NFC_SUBCMD_CONNECT0x00 #define MEI_NFC_SUBCMD_IF_VERSION 0x01 +/* Vendors */ +#define MEI_NFC_VENDOR_INSIDE 0x00 + +/* Radio types */ +#define MEI_NFC_VENDOR_INSIDE_UREAD 0x00 + #endif /* _MEI_NFC_H */ -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 11/11] mei: nfc: Implement MEI bus IO ops
From: Samuel Ortiz The send ops for NFC builds the command header, updates the request id and then waits for an ACK. The recv ops check if it receives data or an ACK and in the latter case wakes the send ops up. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/nfc.c | 76 drivers/misc/mei/nfc.h | 13 2 files changed, 89 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index c65bd44..99246bc 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -40,11 +41,15 @@ struct mei_bus_dev_nfc { struct mei_cl *cl; struct mei_cl *cl_info; struct work_struct init_work; + wait_queue_head_t send_wq; u8 fw_ivn; u8 vendor_id; u8 radio_type; char *bus_name; + + u16 req_id; + u16 recv_req_id; }; struct mei_bus_dev_nfc nfc_bdev; @@ -224,6 +229,74 @@ err: return ret; } +static int mei_nfc_send(struct mei_bus_client *client, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_bus_dev_nfc *bdev; + struct mei_nfc_hci_hdr *hdr; + u8 *mei_buf; + int err; + + bdev = (struct mei_bus_dev_nfc *) client->priv_data; + dev = bdev->cl->dev; + + mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); + if (!mei_buf) + return -ENOMEM; + + hdr = (struct mei_nfc_hci_hdr *) mei_buf; + hdr->cmd = MEI_NFC_CMD_HCI_SEND; + hdr->status = 0; + hdr->req_id = bdev->req_id; + hdr->reserved = 0; + hdr->data_size = length; + + memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); + + err = mei_send(bdev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE); + + kfree(mei_buf); + + if (!wait_event_interruptible_timeout(bdev->send_wq, + bdev->recv_req_id == bdev->req_id, HZ)) { + dev_err(&dev->pdev->dev, "NFC MEI command timeout\n"); + err = -ETIMEDOUT; + } else { + bdev->req_id++; + } + + return err; +} + +static int mei_nfc_recv(struct mei_bus_client *client, u8 *buf, size_t length) +{ + struct mei_bus_dev_nfc *bdev; + struct mei_nfc_hci_hdr *hci_hdr; + int received_length; + + bdev = (struct mei_bus_dev_nfc *) client->priv_data; + + received_length = mei_recv(bdev->cl, buf, length); + if (received_length < 0) + return received_length; + + hci_hdr = (struct mei_nfc_hci_hdr *) buf; + + if (hci_hdr->cmd == MEI_NFC_CMD_HCI_SEND) { + bdev->recv_req_id = hci_hdr->req_id; + wake_up(&bdev->send_wq); + + return 0; + } + + return received_length; +} + +static struct mei_bus_ops nfc_ops = { + .send = mei_nfc_send, + .recv = mei_nfc_recv, +}; + static void mei_nfc_init(struct work_struct *work) { struct mei_device *dev; @@ -295,6 +368,7 @@ static void mei_nfc_init(struct work_struct *work) } bus_client->priv_data = bdev; + bus_client->ops = &nfc_ops; return; @@ -361,8 +435,10 @@ int mei_nfc_host_init(struct mei_device *dev) bdev->cl_info = cl_info; bdev->cl = cl; + bdev->req_id = 1; INIT_WORK(&bdev->init_work, mei_nfc_init); + init_waitqueue_head(&bdev->send_wq); schedule_work(&bdev->init_work); return 0; diff --git a/drivers/misc/mei/nfc.h b/drivers/misc/mei/nfc.h index 4440436..12e48d3 100644 --- a/drivers/misc/mei/nfc.h +++ b/drivers/misc/mei/nfc.h @@ -114,11 +114,24 @@ struct mei_nfc_connect_resp { uint16_t me_build; } __packed; +struct mei_nfc_hci_hdr { + u8 cmd; + u8 status; + u16 req_id; + u32 reserved; + u16 data_size; +} __packed; + #define MEI_NFC_CMD_MAINTENANCE 0x00 +#define MEI_NFC_CMD_HCI_SEND 0x01 +#define MEI_NFC_CMD_HCI_RECV 0x02 #define MEI_NFC_SUBCMD_CONNECT0x00 #define MEI_NFC_SUBCMD_IF_VERSION 0x01 +#define MEI_NFC_HEADER_SIZE 10 +#define MEI_NFC_MAX_HCI_PAYLOAD 300 + /* Vendors */ #define MEI_NFC_VENDOR_INSIDE 0x00 -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 04/11] mei: bus: Add bus related structures to mei_cl
From: Samuel Ortiz We keep track of all MEI bus clients through a specific linked list. We also have a mei_bus_client instance in the mei_cl structure. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 47 +++ drivers/misc/mei/client.c |1 + drivers/misc/mei/init.c|1 + drivers/misc/mei/mei_dev.h |8 +++ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 9689b95..97afec6 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -119,16 +119,37 @@ static struct device_type mei_client_type = { .release= mei_client_dev_release, }; +static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *mei_dev, + uuid_le uuid) +{ + struct mei_cl *cl, *next; + + list_for_each_entry_safe(cl, next, +&mei_dev->bus_client_list, bus_client_link) { + if (!uuid_le_cmp(uuid, cl->bus_client_uuid)) + return cl; + } + + return NULL; +} + struct mei_bus_client *mei_add_device(struct mei_device *mei_dev, uuid_le uuid, char *name) { struct mei_bus_client *client; + struct mei_cl *cl; int status; + cl = mei_bus_find_mei_cl_by_uuid(mei_dev, uuid); + if (cl == NULL) + return NULL; + client = kzalloc(sizeof(struct mei_bus_client), GFP_KERNEL); if (!client) return NULL; + client->cl = cl; + client->mei_dev = mei_dev; client->uuid = uuid; strlcpy(client->name, name, sizeof(client->name)); @@ -140,19 +161,17 @@ struct mei_bus_client *mei_add_device(struct mei_device *mei_dev, dev_set_name(&client->dev, "%s", client->name); status = device_register(&client->dev); - if (status) - goto out_err; + if (status) { + kfree(client); + dev_err(client->dev.parent, "Failed to register MEI client\n"); + return NULL; + } + + cl->client = client; dev_dbg(&client->dev, "client %s registered\n", client->name); return client; - -out_err: - dev_err(client->dev.parent, "Failed to register MEI client\n"); - - kfree(client); - - return NULL; } EXPORT_SYMBOL(mei_add_device); @@ -353,9 +372,10 @@ out: int mei_bus_send(struct mei_bus_client *client, u8 *buf, size_t length) { - struct mei_cl *cl = NULL; + struct mei_cl *cl = client->cl; - /* TODO: hook between mei_bus_client and mei_cl */ + if (cl == NULL) + return -ENODEV; if (client->ops && client->ops->send) return client->ops->send(client, buf, length); @@ -366,9 +386,10 @@ EXPORT_SYMBOL(mei_bus_send); int mei_bus_recv(struct mei_bus_client *client, u8 *buf, size_t length) { - struct mei_cl *cl = NULL; + struct mei_cl *cl = client->cl; - /* TODO: hook between mei_bus_client and mei_cl */ + if (cl == NULL) + return -ENODEV; if (client->ops && client->ops->recv) return client->ops->recv(client, buf, length); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 1569afe..5724499 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -216,6 +216,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) init_waitqueue_head(&cl->rx_wait); init_waitqueue_head(&cl->tx_wait); INIT_LIST_HEAD(&cl->link); + INIT_LIST_HEAD(&cl->bus_client_link); cl->reading_state = MEI_IDLE; cl->writing_state = MEI_IDLE; cl->dev = dev; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 6ec5301..b812d56 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -46,6 +46,7 @@ void mei_device_init(struct mei_device *dev) { /* setup our list array */ INIT_LIST_HEAD(&dev->file_list); + INIT_LIST_HEAD(&dev->bus_client_list); mutex_init(&dev->device_lock); init_waitqueue_head(&dev->wait_recvd_msg); init_waitqueue_head(&dev->wait_stop_wd); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 948f791..d9adad6 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -210,6 +210,11 @@ struct mei_cl { enum mei_file_transaction_states writing_state; int sm_state; struct mei_cl_cb *read_cb; + + /* MEI bus data */ + struct mei_bus_client *client; + struct list_head bus_client_link; + uuid_le b
[char-misc-next 09/11] mei: nfc: Connect also the regular ME client
From: Samuel Ortiz After receiving the NFC interface version, IVN and radio type, we can connect to the the actual nfc me client and send the initialization (nfc connect) message. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/nfc.c | 128 ++-- 1 files changed, 123 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index 240dd2b..ab66944 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -29,6 +29,7 @@ /** mei_nfc_bdev - nfc mei bus device * + * @cl: nfc info host client * @cl_info: nfc info host client * @init_work: perform connection to the info client * @fw_ivn: NFC Intervace Version Number @@ -36,6 +37,7 @@ * @radio_type: NFC radio type */ struct mei_bus_dev_nfc { + struct mei_cl *cl; struct mei_cl *cl_info; struct work_struct init_work; u8 fw_ivn; @@ -56,6 +58,12 @@ const uuid_le mei_nfc_info_guid = UUID_LE(0xd2de1625, 0x382d, 0x417d, static void mei_nfc_free(struct mei_bus_dev_nfc *bdev) { + if (bdev->cl) { + list_del(&bdev->cl->bus_client_link); + mei_cl_unlink(bdev->cl); + kfree(bdev->cl); + } + if (bdev->cl_info) { list_del(&bdev->cl_info->bus_client_link); mei_cl_unlink(bdev->cl_info); @@ -63,6 +71,73 @@ static void mei_nfc_free(struct mei_bus_dev_nfc *bdev) } } +static int mei_nfc_connect(struct mei_bus_dev_nfc *bdev) +{ + struct mei_device *dev; + struct mei_cl *cl; + struct mei_nfc_cmd *cmd, *reply; + struct mei_nfc_connect *connect; + struct mei_nfc_connect_resp *connect_resp; + size_t connect_length, connect_resp_length; + int bytes_recv, ret; + + cl = bdev->cl; + dev = cl->dev; + + connect_length = sizeof(struct mei_nfc_cmd) + + sizeof(struct mei_nfc_connect); + + connect_resp_length = sizeof(struct mei_nfc_cmd) + + sizeof(struct mei_nfc_connect_resp); + + cmd = kzalloc(connect_length, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + connect = (struct mei_nfc_connect *)cmd->data; + + reply = kzalloc(connect_resp_length, GFP_KERNEL); + if (!reply) { + kfree(cmd); + return -ENOMEM; + } + + connect_resp = (struct mei_nfc_connect_resp *)reply->data; + + cmd->command = MEI_NFC_CMD_MAINTENANCE; + cmd->data_size = 3; + cmd->sub_command = MEI_NFC_SUBCMD_CONNECT; + connect->fw_ivn = bdev->fw_ivn; + connect->vendor_id = bdev->vendor_id; + + ret = mei_send(cl, (u8 *)cmd, connect_length); + if (ret < 0) { + dev_err(&dev->pdev->dev, "Could not send connect cmd\n"); + goto err; + } + + bytes_recv = mei_recv(cl, (u8 *)reply, connect_resp_length); + if (bytes_recv < 0) { + dev_err(&dev->pdev->dev, "Could not read connect response\n"); + ret = bytes_recv; + goto err; + } + + dev_info(&dev->pdev->dev, "IVN 0x%x Vendor ID 0x%x\n", + connect_resp->fw_ivn, connect_resp->vendor_id); + + dev_info(&dev->pdev->dev, "ME FW %d.%d.%d.%d\n", + connect_resp->me_major, connect_resp->me_minor, + connect_resp->me_hotfix, connect_resp->me_build); + + ret = 0; + +err: + kfree(reply); + kfree(cmd); + + return ret; +} + static int mei_nfc_if_version(struct mei_bus_dev_nfc *bdev) { struct mei_device *dev; @@ -118,12 +193,13 @@ static void mei_nfc_init(struct work_struct *work) { struct mei_device *dev; struct mei_bus_dev_nfc *bdev; - struct mei_cl *cl_info; + struct mei_cl *cl_info, *cl; int ret; bdev = container_of(work, struct mei_bus_dev_nfc, init_work); cl_info = bdev->cl_info; + cl = bdev->cl; dev = cl_info->dev; mutex_lock(&dev->device_lock); @@ -149,6 +225,24 @@ static void mei_nfc_init(struct work_struct *work) "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", bdev->fw_ivn, bdev->vendor_id, bdev->radio_type); + mutex_lock(&dev->device_lock); + + if (mei_cl_connect(cl, NULL) < 0) { + mutex_unlock(&dev->device_lock); + dev_err(&dev->pdev->dev, + "Could not connect to the NFC ME client"); + + goto err; + } + + mutex_unlock(&dev->device_lock); + + ret = mei_nfc_connect(bdev); + if (ret < 0) { + dev_err(&dev->pdev->dev, "Could not c
[char-misc-next 02/11] mei: bus: Implement driver registration
From: Samuel Ortiz Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 29 + include/linux/mei_bus.h |3 +++ 2 files changed, 32 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index bb96423c..ea24e7c 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -153,3 +153,32 @@ void mei_remove_device(struct mei_bus_client *client) device_unregister(&client->dev); } EXPORT_SYMBOL(mei_remove_device); + +int mei_add_driver(struct mei_bus_driver *driver) +{ + int err; + + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!mei_bus_type.p))) + return -EAGAIN; + + driver->driver.owner = THIS_MODULE; + driver->driver.bus = &mei_bus_type; + + err = driver_register(&driver->driver); + if (err) + return err; + + pr_debug("mei: driver [%s] registered\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL(mei_add_driver); + +void mei_del_driver(struct mei_bus_driver *driver) +{ + driver_unregister(&driver->driver); + + pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); +} +EXPORT_SYMBOL(mei_del_driver); diff --git a/include/linux/mei_bus.h b/include/linux/mei_bus.h index 3a53f9e..395f573 100644 --- a/include/linux/mei_bus.h +++ b/include/linux/mei_bus.h @@ -88,4 +88,7 @@ struct mei_bus_driver { int (*remove)(struct mei_bus_client *client); }; +int mei_add_driver(struct mei_bus_driver *driver); +void mei_del_driver(struct mei_bus_driver *driver); + #endif /* _LINUX_MEI_BUS_H */ -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 08/11] mei: nfc: Initial nfc implementation
From: Samuel Ortiz NFC ME client is exported through mei bus to be consumed by the NFC subsystem. NFC is represented by two mei clients: An info one and the actual NFC one. In order for correct connection we first need to retrieve the firmware information from the info client. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/Kconfig |7 ++ drivers/misc/mei/Makefile |1 + drivers/misc/mei/client.c |3 + drivers/misc/mei/mei_dev.h | 28 ++ drivers/misc/mei/nfc.c | 210 drivers/misc/mei/nfc.h | 122 + drivers/misc/mei/pci-me.c |2 + 7 files changed, 373 insertions(+), 0 deletions(-) create mode 100644 drivers/misc/mei/nfc.c create mode 100644 drivers/misc/mei/nfc.h diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index d21b4d0..66e84ef 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -35,3 +35,10 @@ config INTEL_MEI_ME 82Q33 Express 82X38/X48 Express +config INTEL_MEI_BUS_NFC +bool "MEI bus NFC support" +depends on INTEL_MEI + help + When selecting this option the ME NFC device will be added to the + MEI bus. This is needed by the NFC kernel subsystem for sending and + receiving HCI frames to and from the NFC device. diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 5948621..644f92e 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -11,5 +11,6 @@ mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o mei-objs += bus.o +mei-$(CONFIG_INTEL_MEI_BUS_NFC) += nfc.o mei-$(CONFIG_INTEL_MEI_ME) += pci-me.o mei-$(CONFIG_INTEL_MEI_ME) += hw-me.o diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 5724499..e3ed1d4 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -358,6 +358,9 @@ void mei_host_client_init(struct work_struct *work) mei_amthif_host_init(dev); else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) mei_wd_host_init(dev); + else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid)) + mei_nfc_host_init(dev); + } dev->dev_state = MEI_DEV_ENABLED; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index cb1bac0..da0ae42 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -475,6 +475,34 @@ int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list, struct mei_device *dev, struct mei_msg_hdr *mei_hdr); int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); +#ifdef CONFIG_INTEL_MEI_BUS_NFC +/* + * NFC functions + */ +int mei_nfc_host_init(struct mei_device *dev); +void mei_nfc_host_exit(void); + +/* + * NFC Client UUID + */ +extern const uuid_le mei_nfc_guid; + +#else + +static inline int mei_nfc_host_init(struct mei_device *dev) +{ + return 0; +} + +static inline void mei_nfc_host_exit(void) +{ + return; +} + +static const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, + 0x94, 0xd4, 0x50, 0x26, + 0x67, 0x23, 0x77, 0x5c); +#endif int mei_wd_send(struct mei_device *dev); int mei_wd_stop(struct mei_device *dev); diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c new file mode 100644 index 000..240dd2b --- /dev/null +++ b/drivers/misc/mei/nfc.c @@ -0,0 +1,210 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mei_dev.h" +#include "client.h" +#include "bus.h" +#include "nfc.h" + +/** mei_nfc_bdev - nfc mei bus device + * + * @cl_info: nfc info host client + * @init_work: perform connection to the info client + * @fw_ivn: NFC Intervace Version Number + * @vendor_id: NFC manufacturer ID + * @radio_type: NFC radio type + */ +struct mei_bus_dev_nfc { + struct mei_cl *cl_info; + struct work_struct init_work; + u8 fw_ivn; + u8 vendor_id; + u8 radio_type; +}; + +struct mei_bus_dev_nfc nfc_bdev; + +/* UUIDs for NFC F/W clients */ +const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, +0x94, 0xd4,
[char-misc-next 07/11] mei: bus: Implement bus driver data setter/getter
From: Samuel Ortiz MEI bus drivers should be able to carry their private data around. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 12 include/linux/mei_bus.h |3 +++ 2 files changed, 15 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 3f10d51..58032cf 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -448,6 +448,18 @@ int mei_bus_register_event_cb(struct mei_bus_client *client, } EXPORT_SYMBOL(mei_bus_register_event_cb); +inline void *mei_bus_get_clientdata(const struct mei_bus_client *client) +{ + return dev_get_drvdata(&client->dev); +} +EXPORT_SYMBOL(mei_bus_get_clientdata); + +inline void mei_bus_set_clientdata(struct mei_bus_client *client, void *data) +{ + dev_set_drvdata(&client->dev, data); +} +EXPORT_SYMBOL(mei_bus_set_clientdata); + void mei_bus_rx_event(struct mei_cl *cl) { struct mei_bus_client *client = cl->client; diff --git a/include/linux/mei_bus.h b/include/linux/mei_bus.h index 241325f..0e8e4e9 100644 --- a/include/linux/mei_bus.h +++ b/include/linux/mei_bus.h @@ -102,4 +102,7 @@ typedef void (*mei_bus_event_cb_t)(struct mei_bus_client *client, int mei_bus_register_event_cb(struct mei_bus_client *client, mei_bus_event_cb_t read_cb, void *context); +void *mei_bus_get_clientdata(const struct mei_bus_client *client); +void mei_bus_set_clientdata(struct mei_bus_client *client, void *data); + #endif /* _LINUX_MEI_BUS_H */ -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 01/11] mei: bus: Initial MEI bus type implementation
From: Samuel Ortiz mei bus will present some of the me clients as devices for other standard subsystems Implement the probe, remove, match and the device addtion routines. A mei-bus.txt document describing the rationale and the API usage is also added. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- Documentation/misc-devices/mei/mei-bus.txt | 137 drivers/misc/mei/Makefile |1 + drivers/misc/mei/bus.c | 155 drivers/misc/mei/bus.h | 27 + drivers/misc/mei/mei_dev.h | 24 + include/linux/mei_bus.h| 91 6 files changed, 435 insertions(+), 0 deletions(-) create mode 100644 Documentation/misc-devices/mei/mei-bus.txt create mode 100644 drivers/misc/mei/bus.c create mode 100644 drivers/misc/mei/bus.h create mode 100644 include/linux/mei_bus.h diff --git a/Documentation/misc-devices/mei/mei-bus.txt b/Documentation/misc-devices/mei/mei-bus.txt new file mode 100644 index 000..dac6239 --- /dev/null +++ b/Documentation/misc-devices/mei/mei-bus.txt @@ -0,0 +1,137 @@ +Intel(R) Management Engine (ME) bus API +=== + + +Rationale += +While a misc character device is useful for applications to send and receive +data to the many IP blocks found in Intel's ME, kernel drivers rely on the +device model to be probed. +By adding a kernel virtual bus abstraction on top of the MEI driver we can +implement drivers for the various MEI features as standalone ones, found in +their respective subsystem. Existing drivers can even potentially be re-used +by adding an MEI bus layer to the existing code. + + +MEI bus API +=== +A driver implementation for an MEI IP block is very similar to existing bus +based device drivers. The driver registers itself as an MEI bus driver through +the mei_bus_driver structure: + +struct mei_bus_driver { + struct device_driver driver; + + struct mei_id id; + + int (*probe)(struct mei_bus_client *client); + int (*remove)(struct mei_bus_client *client); +}; + +struct mei_id { + char name[MEI_NAME_SIZE]; + uuid_le uuid; +}; + +The mei_id structure allows the driver to bind itself against an ME UUID and a +device name. There typically is one ME UUID per technology and the mei_id name +field matches a specific device name within that technology. As an example, +the ME supports several NFC devices: All of them have the same ME UUID but the +ME bus code will assign each of them a different name. + +To actually register a driver on the ME bus one must call the mei_add_driver() +API. This is typically called at module init time. + +Once registered on the ME bus, a driver will typically try to do some I/O on +this bus and this should be done through the mei_bus_send() and mei_bus_recv() +routines. The latter is synchronous (blocks and sleeps until data shows up). +In order for drivers to be notified of pending events waiting for them (e.g. +an Rx event) they can register an event handler through the +mei_bus_register_event_cb() routine. Currently only the MEI_BUS_EVENT_RX event +will trigger an event handler call and the driver implementation is supposed +to call mei_bus_recv() from the event handler in order to fetch the pending +received buffers. + + +Example +=== +As a theoretical example let's pretend the ME comes with a "contact" NFC IP. +The driver init and exit routines for this device would look like: + +#define CONTACT_DRIVER_NAME "contact" + +#define NFC_UUID UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, 0x94, \ + 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c) + +static struct mei_bus_driver contact_driver = { + .driver = { + .name = CONTAC_DRIVER_NAME, + }, + .id = { + .name = CONTACT_DRIVER_NAME, + .uuid = NFC_UUID, + }, + + .probe = contact_probe, + .remove = contact_remove, +}; + +static int contact_init(void) +{ + int r; + + pr_debug(DRIVER_DESC ": %s\n", __func__); + + r = mei_add_driver(&contact_driver); + if (r) { + pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n"); + return r; + } + + return 0; +} + +static void __exit contact_exit(void) +{ + mei_del_driver(&contact_driver); +} + +module_init(contact_init); +module_exit(contact_exit); + +And the driver's simplified probe routine would look like that: + +int contact_probe(struct mei_bus_client *client) +{ + struct contact_driver *contact; + + [...] + mei_bus_register_event_cb(client, contact_event_cb, contact); + + return 0; + } + +In the probe routine the driver basically registers an ME bus event handler +which is as close as it can get to registering a threaded IRQ handler. +
[char-misc-next 00/11] Add MEI BUS and NFC Device
This patch set adds implementation of MEI BUS abstraction over MEI device, this allows standard Linux device drivers to access functionality exposed by MEI device that was previously available only to the user space through /dev/mei The first exercises is to export the NFC radio More information can be found under Documentation/misc-devices/mei/mei-bus.txt Samuel Ortiz (11): mei: bus: Initial MEI bus type implementation mei: bus: Implement driver registration mei: bus: Initial implementation for I/O routines mei: bus: Add bus related structures to mei_cl mei: bus: Call bus routines from the core code mei: bus: Synchronous API for the data transmission mei: bus: Implement bus driver data setter/getter mei: nfc: Initial nfc implementation mei: nfc: Connect also the regular ME client mei: nfc: Add NFC device to the MEI bus mei: nfc: Implement MEI bus IO ops Documentation/misc-devices/mei/mei-bus.txt | 137 drivers/misc/mei/Kconfig |7 + drivers/misc/mei/Makefile |2 + drivers/misc/mei/bus.c | 483 drivers/misc/mei/bus.h | 32 ++ drivers/misc/mei/client.c |4 + drivers/misc/mei/init.c|1 + drivers/misc/mei/interrupt.c |2 + drivers/misc/mei/mei_dev.h | 78 + drivers/misc/mei/nfc.c | 460 ++ drivers/misc/mei/nfc.h | 141 drivers/misc/mei/pci-me.c |8 + include/linux/mei_bus.h| 108 ++ 13 files changed, 1463 insertions(+), 0 deletions(-) create mode 100644 Documentation/misc-devices/mei/mei-bus.txt create mode 100644 drivers/misc/mei/bus.c create mode 100644 drivers/misc/mei/bus.h create mode 100644 drivers/misc/mei/nfc.c create mode 100644 drivers/misc/mei/nfc.h create mode 100644 include/linux/mei_bus.h -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 03/11] mei: bus: Initial implementation for I/O routines
From: Samuel Ortiz Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 226 drivers/misc/mei/bus.h |3 + drivers/misc/mei/mei_dev.h | 14 +++ include/linux/mei_bus.h| 11 ++ 4 files changed, 254 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index ea24e7c..9689b95 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,8 @@ #include #include "mei_dev.h" +#include "hw-me.h" +#include "client.h" #include "bus.h" static int mei_device_match(struct device *dev, struct device_driver *drv) @@ -73,6 +76,11 @@ static int mei_device_remove(struct device *dev) if (!client || !dev->driver) return 0; + if (client->event_cb) { + client->event_cb = NULL; + cancel_work_sync(&client->event_work); + } + driver = to_mei_driver(dev->driver); if (!driver->remove) { dev->driver = NULL; @@ -182,3 +190,221 @@ void mei_del_driver(struct mei_bus_driver *driver) pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); } EXPORT_SYMBOL(mei_del_driver); + +int mei_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_msg_hdr mei_hdr; + struct mei_cl_cb *cb; + int me_cl_id, err; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + if (cl->state != MEI_FILE_CONNECTED) + return -ENODEV; + + cb = mei_io_cb_init(cl, NULL); + if (!cb) + return -ENOMEM; + + err = mei_io_cb_alloc_req_buf(cb, length); + if (err < 0) { + mei_io_cb_free(cb); + return err; + } + + memcpy(cb->request_buffer.data, buf, length); + cb->fop_type = MEI_FOP_WRITE; + + dev = cl->dev; + + mutex_lock(&dev->device_lock); + + /* Check if we have an ME client device */ + me_cl_id = mei_me_cl_by_id(dev, cl->me_client_id); + if (me_cl_id == dev->me_clients_num) { + err = -ENODEV; + goto out_err; + } + + if (length > dev->me_clients[me_cl_id].props.max_msg_length) { + err = -EINVAL; + goto out_err; + } + + err = mei_cl_flow_ctrl_creds(cl); + if (err < 0) + goto out_err; + + /* Host buffer is not ready, we queue the request */ + if (err == 0 || !dev->hbuf_is_ready) { + cb->buf_idx = 0; + mei_hdr.msg_complete = 0; + cl->writing_state = MEI_WRITING; + list_add_tail(&cb->list, &dev->write_list.list); + + mutex_unlock(&dev->device_lock); + + return length; + } + + dev->hbuf_is_ready = false; + + /* Check for a maximum length */ + if (length > mei_hbuf_max_len(dev)) { + mei_hdr.length = mei_hbuf_max_len(dev); + mei_hdr.msg_complete = 0; + } else { + mei_hdr.length = length; + mei_hdr.msg_complete = 1; + } + + mei_hdr.host_addr = cl->host_client_id; + mei_hdr.me_addr = cl->me_client_id; + mei_hdr.reserved = 0; + + if (mei_write_message(dev, &mei_hdr, buf)) { + err = -EIO; + goto out_err; + } + + cl->writing_state = MEI_WRITING; + cb->buf_idx = mei_hdr.length; + + if (!mei_hdr.msg_complete) { + list_add_tail(&cb->list, &dev->write_list.list); + } else { + if (mei_cl_flow_ctrl_reduce(cl)) { + err = -EIO; + goto out_err; + } + + list_add_tail(&cb->list, &dev->write_waiting_list.list); + } + + mutex_unlock(&dev->device_lock); + + return mei_hdr.length; + +out_err: + mutex_unlock(&dev->device_lock); + mei_io_cb_free(cb); + + return err; +} + +int mei_recv(struct mei_cl *cl, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_cl_cb *cb; + size_t r_length; + int err; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + mutex_lock(&dev->device_lock); + + if (!cl->read_cb) { + err = mei_cl_read_start(cl); + if (err < 0) { + mutex_unlock(&dev->device_lock); + return err; + } + } + + if (cl->reading_state != MEI_READ_COMPLETE && + !waitqueue_active(&
[char-misc-next 06/11] mei: bus: Synchronous API for the data transmission
From: Samuel Ortiz Define a truly synchronous API for the bus Tx path by putting all pending request to the write list and wait for the interrupt tx handler to wake us up. The __mei_send() out path is also slightly reworked to make it look more like main.c:mei_write(). Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 38 -- drivers/misc/mei/bus.h |2 ++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index e2e15d1..3f10d51 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -210,7 +210,7 @@ void mei_del_driver(struct mei_bus_driver *driver) } EXPORT_SYMBOL(mei_del_driver); -int mei_send(struct mei_cl *cl, u8 *buf, size_t length) +static int __mei_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking) { struct mei_device *dev; struct mei_msg_hdr mei_hdr; @@ -261,11 +261,8 @@ int mei_send(struct mei_cl *cl, u8 *buf, size_t length) cb->buf_idx = 0; mei_hdr.msg_complete = 0; cl->writing_state = MEI_WRITING; - list_add_tail(&cb->list, &dev->write_list.list); - - mutex_unlock(&dev->device_lock); - return length; + goto out; } dev->hbuf_is_ready = false; @@ -291,19 +288,30 @@ int mei_send(struct mei_cl *cl, u8 *buf, size_t length) cl->writing_state = MEI_WRITING; cb->buf_idx = mei_hdr.length; - if (!mei_hdr.msg_complete) { - list_add_tail(&cb->list, &dev->write_list.list); - } else { +out: + if (mei_hdr.msg_complete) { if (mei_cl_flow_ctrl_reduce(cl)) { - err = -EIO; + err = -ENODEV; goto out_err; } - list_add_tail(&cb->list, &dev->write_waiting_list.list); + } else { + list_add_tail(&cb->list, &dev->write_list.list); } mutex_unlock(&dev->device_lock); + if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { + if (wait_event_interruptible(cl->tx_wait, + cl->writing_state == MEI_WRITE_COMPLETE)) { + if (signal_pending(current)) + err = -EINTR; + err = -ERESTARTSYS; + mutex_lock(&dev->device_lock); + goto out_err; + } + } + return mei_hdr.length; out_err: @@ -370,6 +378,16 @@ out: return r_length; } +inline int mei_async_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + return __mei_send(cl, buf, length, 0); +} + +inline int mei_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + return __mei_send(cl, buf, length, 1); +} + int mei_bus_send(struct mei_bus_client *client, u8 *buf, size_t length) { struct mei_cl *cl = client->cl; diff --git a/drivers/misc/mei/bus.h b/drivers/misc/mei/bus.h index 81789f6..d7bf3b5 100644 --- a/drivers/misc/mei/bus.h +++ b/drivers/misc/mei/bus.h @@ -24,7 +24,9 @@ struct mei_bus_client *mei_add_device(struct mei_device *mei_dev, uuid_le uuid, char *name); void mei_remove_device(struct mei_bus_client *client); +int mei_async_send(struct mei_cl *cl, u8 *buf, size_t length); int mei_send(struct mei_cl *cl, u8 *buf, size_t length); int mei_recv(struct mei_cl *cl, u8 *buf, size_t length); + #endif /* _MEI_BUS_H_ */ -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 05/11] mei: bus: Call bus routines from the core code
From: Samuel Ortiz Register the MEI bus type against the kernel core bus APIs and call the bus Rx handler from interrupt.c Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 22 ++ drivers/misc/mei/interrupt.c |2 ++ drivers/misc/mei/mei_dev.h |4 drivers/misc/mei/pci-me.c|6 ++ 4 files changed, 34 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 97afec6..e2e15d1 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -429,3 +429,25 @@ int mei_bus_register_event_cb(struct mei_bus_client *client, return 0; } EXPORT_SYMBOL(mei_bus_register_event_cb); + +void mei_bus_rx_event(struct mei_cl *cl) +{ + struct mei_bus_client *client = cl->client; + + if (!client || !client->event_cb) + return; + + set_bit(MEI_BUS_EVENT_RX, &client->events); + + schedule_work(&client->event_work); +} + +int mei_bus_init(struct pci_dev *pdev) +{ + return bus_register(&mei_bus_type); +} + +void mei_bus_exit(void) +{ + bus_unregister(&mei_bus_type); +} diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 3535b26..d0dea50 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -49,6 +49,8 @@ void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos) cl->reading_state = MEI_READ_COMPLETE; if (waitqueue_active(&cl->rx_wait)) wake_up_interruptible(&cl->rx_wait); + else + mei_bus_rx_event(cl); } } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index d9adad6..cb1bac0 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -420,6 +420,10 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec) return msecs_to_jiffies(sec * MSEC_PER_SEC); } +void mei_bus_rx_event(struct mei_cl *cl); +int mei_bus_init(struct pci_dev *pdev); +void mei_bus_exit(void); + /* * mei init function prototypes diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index b40ec06..f736531 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -197,6 +197,9 @@ static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mei_pdev = pdev; pci_set_drvdata(pdev, dev); + err = mei_bus_init(mei_pdev); + if (err) + goto deregister_mei; schedule_delayed_work(&dev->timer_work, HZ); @@ -206,6 +209,8 @@ static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; +deregister_mei: + mei_deregister(); release_irq: mei_disable_interrupts(dev); flush_scheduled_work(); @@ -300,6 +305,7 @@ static void mei_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); + mei_bus_exit(); mei_deregister(); } -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 00/11 V2] Add MEI BUS and NFC Device
This is take 2 on the MEI bus + NFC Device patches addressing Arnd's comments This patch set adds implementation of MEI BUS abstraction over MEI device, this allows standard Linux device drivers to access functionality exposed by MEI device that was previously available only to the user space through /dev/mei The first exercises is to export the NFC radio More information can be found under Documentation/misc-devices/mei/mei-bus.txt V2: 1. Rename mei_add_driver to mei_driver_register and mei_del_driver to mei_driver_unregister. 2. Do not inline the exported bus driver data setter and getter functions. 3. Include the bus ops pointers directly into mei_bus_client. 4. Move the mei_bus_init/exit call from probe to module init time. Samuel Ortiz (11): mei: bus: Initial MEI bus type implementation mei: bus: Implement driver registration mei: bus: Initial implementation for I/O routines mei: bus: Add bus related structures to mei_cl mei: bus: Call bus routines from the core code mei: bus: Synchronous API for the data transmission mei: bus: Implement bus driver data setter/getter mei: nfc: Initial nfc implementation mei: nfc: Connect also the regular ME client mei: nfc: Add NFC device to the MEI bus mei: nfc: Implement MEI bus IO ops Documentation/misc-devices/mei/mei-bus.txt | 137 drivers/misc/mei/Kconfig |7 + drivers/misc/mei/Makefile |2 + drivers/misc/mei/bus.c | 483 drivers/misc/mei/bus.h | 32 ++ drivers/misc/mei/client.c |4 + drivers/misc/mei/init.c|1 + drivers/misc/mei/interrupt.c |2 + drivers/misc/mei/mei_dev.h | 79 + drivers/misc/mei/nfc.c | 456 ++ drivers/misc/mei/nfc.h | 141 drivers/misc/mei/pci-me.c | 23 ++- include/linux/mei_bus.h| 108 ++ 13 files changed, 1473 insertions(+), 2 deletions(-) create mode 100644 Documentation/misc-devices/mei/mei-bus.txt create mode 100644 drivers/misc/mei/bus.c create mode 100644 drivers/misc/mei/bus.h create mode 100644 drivers/misc/mei/nfc.c create mode 100644 drivers/misc/mei/nfc.h create mode 100644 include/linux/mei_bus.h -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 01/11 V2] mei: bus: Initial MEI bus type implementation
From: Samuel Ortiz mei bus will present some of the me clients as devices for other standard subsystems Implement the probe, remove, match and the device addtion routines. A mei-bus.txt document describing the rationale and the API usage is also added. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- Documentation/misc-devices/mei/mei-bus.txt | 137 drivers/misc/mei/Makefile |1 + drivers/misc/mei/bus.c | 155 drivers/misc/mei/bus.h | 27 + drivers/misc/mei/mei_dev.h | 24 + include/linux/mei_bus.h| 91 6 files changed, 435 insertions(+), 0 deletions(-) create mode 100644 Documentation/misc-devices/mei/mei-bus.txt create mode 100644 drivers/misc/mei/bus.c create mode 100644 drivers/misc/mei/bus.h create mode 100644 include/linux/mei_bus.h diff --git a/Documentation/misc-devices/mei/mei-bus.txt b/Documentation/misc-devices/mei/mei-bus.txt new file mode 100644 index 000..dac6239 --- /dev/null +++ b/Documentation/misc-devices/mei/mei-bus.txt @@ -0,0 +1,137 @@ +Intel(R) Management Engine (ME) bus API +=== + + +Rationale += +While a misc character device is useful for applications to send and receive +data to the many IP blocks found in Intel's ME, kernel drivers rely on the +device model to be probed. +By adding a kernel virtual bus abstraction on top of the MEI driver we can +implement drivers for the various MEI features as standalone ones, found in +their respective subsystem. Existing drivers can even potentially be re-used +by adding an MEI bus layer to the existing code. + + +MEI bus API +=== +A driver implementation for an MEI IP block is very similar to existing bus +based device drivers. The driver registers itself as an MEI bus driver through +the mei_bus_driver structure: + +struct mei_bus_driver { + struct device_driver driver; + + struct mei_id id; + + int (*probe)(struct mei_bus_client *client); + int (*remove)(struct mei_bus_client *client); +}; + +struct mei_id { + char name[MEI_NAME_SIZE]; + uuid_le uuid; +}; + +The mei_id structure allows the driver to bind itself against an ME UUID and a +device name. There typically is one ME UUID per technology and the mei_id name +field matches a specific device name within that technology. As an example, +the ME supports several NFC devices: All of them have the same ME UUID but the +ME bus code will assign each of them a different name. + +To actually register a driver on the ME bus one must call the mei_add_driver() +API. This is typically called at module init time. + +Once registered on the ME bus, a driver will typically try to do some I/O on +this bus and this should be done through the mei_bus_send() and mei_bus_recv() +routines. The latter is synchronous (blocks and sleeps until data shows up). +In order for drivers to be notified of pending events waiting for them (e.g. +an Rx event) they can register an event handler through the +mei_bus_register_event_cb() routine. Currently only the MEI_BUS_EVENT_RX event +will trigger an event handler call and the driver implementation is supposed +to call mei_bus_recv() from the event handler in order to fetch the pending +received buffers. + + +Example +=== +As a theoretical example let's pretend the ME comes with a "contact" NFC IP. +The driver init and exit routines for this device would look like: + +#define CONTACT_DRIVER_NAME "contact" + +#define NFC_UUID UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, 0x94, \ + 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c) + +static struct mei_bus_driver contact_driver = { + .driver = { + .name = CONTAC_DRIVER_NAME, + }, + .id = { + .name = CONTACT_DRIVER_NAME, + .uuid = NFC_UUID, + }, + + .probe = contact_probe, + .remove = contact_remove, +}; + +static int contact_init(void) +{ + int r; + + pr_debug(DRIVER_DESC ": %s\n", __func__); + + r = mei_add_driver(&contact_driver); + if (r) { + pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n"); + return r; + } + + return 0; +} + +static void __exit contact_exit(void) +{ + mei_del_driver(&contact_driver); +} + +module_init(contact_init); +module_exit(contact_exit); + +And the driver's simplified probe routine would look like that: + +int contact_probe(struct mei_bus_client *client) +{ + struct contact_driver *contact; + + [...] + mei_bus_register_event_cb(client, contact_event_cb, contact); + + return 0; + } + +In the probe routine the driver basically registers an ME bus event handler +which is as close as it can get to registering a threaded IRQ handler. +
[char-misc-next 06/11 V2] mei: bus: Synchronous API for the data transmission
From: Samuel Ortiz Define a truly synchronous API for the bus Tx path by putting all pending request to the write list and wait for the interrupt tx handler to wake us up. The __mei_send() out path is also slightly reworked to make it look more like main.c:mei_write(). Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 38 -- drivers/misc/mei/bus.h |2 ++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 1048cd4..5a700cd 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -210,7 +210,7 @@ void mei_driver_unregister(struct mei_bus_driver *driver) } EXPORT_SYMBOL(mei_driver_unregister); -int mei_send(struct mei_cl *cl, u8 *buf, size_t length) +static int __mei_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking) { struct mei_device *dev; struct mei_msg_hdr mei_hdr; @@ -261,11 +261,8 @@ int mei_send(struct mei_cl *cl, u8 *buf, size_t length) cb->buf_idx = 0; mei_hdr.msg_complete = 0; cl->writing_state = MEI_WRITING; - list_add_tail(&cb->list, &dev->write_list.list); - - mutex_unlock(&dev->device_lock); - return length; + goto out; } dev->hbuf_is_ready = false; @@ -291,19 +288,30 @@ int mei_send(struct mei_cl *cl, u8 *buf, size_t length) cl->writing_state = MEI_WRITING; cb->buf_idx = mei_hdr.length; - if (!mei_hdr.msg_complete) { - list_add_tail(&cb->list, &dev->write_list.list); - } else { +out: + if (mei_hdr.msg_complete) { if (mei_cl_flow_ctrl_reduce(cl)) { - err = -EIO; + err = -ENODEV; goto out_err; } - list_add_tail(&cb->list, &dev->write_waiting_list.list); + } else { + list_add_tail(&cb->list, &dev->write_list.list); } mutex_unlock(&dev->device_lock); + if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { + if (wait_event_interruptible(cl->tx_wait, + cl->writing_state == MEI_WRITE_COMPLETE)) { + if (signal_pending(current)) + err = -EINTR; + err = -ERESTARTSYS; + mutex_lock(&dev->device_lock); + goto out_err; + } + } + return mei_hdr.length; out_err: @@ -370,6 +378,16 @@ out: return r_length; } +inline int mei_async_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + return __mei_send(cl, buf, length, 0); +} + +inline int mei_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + return __mei_send(cl, buf, length, 1); +} + int mei_bus_send(struct mei_bus_client *client, u8 *buf, size_t length) { struct mei_cl *cl = client->cl; diff --git a/drivers/misc/mei/bus.h b/drivers/misc/mei/bus.h index 81789f6..d7bf3b5 100644 --- a/drivers/misc/mei/bus.h +++ b/drivers/misc/mei/bus.h @@ -24,7 +24,9 @@ struct mei_bus_client *mei_add_device(struct mei_device *mei_dev, uuid_le uuid, char *name); void mei_remove_device(struct mei_bus_client *client); +int mei_async_send(struct mei_cl *cl, u8 *buf, size_t length); int mei_send(struct mei_cl *cl, u8 *buf, size_t length); int mei_recv(struct mei_cl *cl, u8 *buf, size_t length); + #endif /* _MEI_BUS_H_ */ -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 05/11 V2] mei: bus: Call bus routines from the core code
From: Samuel Ortiz Register the MEI bus type against the kernel core bus APIs and call the bus Rx handler from interrupt.c Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 22 ++ drivers/misc/mei/interrupt.c |2 ++ drivers/misc/mei/mei_dev.h |4 drivers/misc/mei/pci-me.c| 21 +++-- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 2e12928..1048cd4 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -429,3 +429,25 @@ int mei_bus_register_event_cb(struct mei_bus_client *client, return 0; } EXPORT_SYMBOL(mei_bus_register_event_cb); + +void mei_bus_rx_event(struct mei_cl *cl) +{ + struct mei_bus_client *client = cl->client; + + if (!client || !client->event_cb) + return; + + set_bit(MEI_BUS_EVENT_RX, &client->events); + + schedule_work(&client->event_work); +} + +int __init mei_bus_init(struct pci_dev *pdev) +{ + return bus_register(&mei_bus_type); +} + +void __exit mei_bus_exit(void) +{ + bus_unregister(&mei_bus_type); +} diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 3535b26..d0dea50 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -49,6 +49,8 @@ void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos) cl->reading_state = MEI_READ_COMPLETE; if (waitqueue_active(&cl->rx_wait)) wake_up_interruptible(&cl->rx_wait); + else + mei_bus_rx_event(cl); } } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 4e1daf2..a9419a8 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -421,6 +421,10 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec) return msecs_to_jiffies(sec * MSEC_PER_SEC); } +void mei_bus_rx_event(struct mei_cl *cl); +int mei_bus_init(struct pci_dev *pdev); +void mei_bus_exit(void); + /* * mei init function prototypes diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index b40ec06..5daaa0c 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -197,7 +197,6 @@ static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mei_pdev = pdev; pci_set_drvdata(pdev, dev); - schedule_delayed_work(&dev->timer_work, HZ); mutex_unlock(&mei_mutex); @@ -389,7 +388,25 @@ static struct pci_driver mei_driver = { .driver.pm = MEI_PM_OPS, }; -module_pci_driver(mei_driver); +static int __init mei_init(void) +{ + int err; + + err = mei_bus_init(mei_pdev); + if (err) + return err; + + return pci_register_driver(&mei_driver); +} + +static void __exit mei_exit(void) +{ + pci_unregister_driver(&mei_driver); + mei_bus_exit(); +} + +module_init(mei_init); +module_exit(mei_exit); MODULE_AUTHOR("Intel Corporation"); MODULE_DESCRIPTION("Intel(R) Management Engine Interface"); -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 07/11 V2] mei: bus: Implement bus driver data setter/getter
From: Samuel Ortiz MEI bus drivers should be able to carry their private data around. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 12 include/linux/mei_bus.h |3 +++ 2 files changed, 15 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 5a700cd..a787e48 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -448,6 +448,18 @@ int mei_bus_register_event_cb(struct mei_bus_client *client, } EXPORT_SYMBOL(mei_bus_register_event_cb); +void *mei_bus_get_clientdata(const struct mei_bus_client *client) +{ + return dev_get_drvdata(&client->dev); +} +EXPORT_SYMBOL(mei_bus_get_clientdata); + +void mei_bus_set_clientdata(struct mei_bus_client *client, void *data) +{ + dev_set_drvdata(&client->dev, data); +} +EXPORT_SYMBOL(mei_bus_set_clientdata); + void mei_bus_rx_event(struct mei_cl *cl) { struct mei_bus_client *client = cl->client; diff --git a/include/linux/mei_bus.h b/include/linux/mei_bus.h index 738e073..912c84e 100644 --- a/include/linux/mei_bus.h +++ b/include/linux/mei_bus.h @@ -102,4 +102,7 @@ typedef void (*mei_bus_event_cb_t)(struct mei_bus_client *client, int mei_bus_register_event_cb(struct mei_bus_client *client, mei_bus_event_cb_t read_cb, void *context); +void *mei_bus_get_clientdata(const struct mei_bus_client *client); +void mei_bus_set_clientdata(struct mei_bus_client *client, void *data); + #endif /* _LINUX_MEI_BUS_H */ -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 11/11] mei: nfc: Implement MEI bus IO ops
From: Samuel Ortiz The send ops for NFC builds the command header, updates the request id and then waits for an ACK. The recv ops check if it receives data or an ACK and in the latter case wakes the send ops up. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/nfc.c | 72 drivers/misc/mei/nfc.h | 13 2 files changed, 85 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index c65bd44..86f7e47 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -40,11 +41,15 @@ struct mei_bus_dev_nfc { struct mei_cl *cl; struct mei_cl *cl_info; struct work_struct init_work; + wait_queue_head_t send_wq; u8 fw_ivn; u8 vendor_id; u8 radio_type; char *bus_name; + + u16 req_id; + u16 recv_req_id; }; struct mei_bus_dev_nfc nfc_bdev; @@ -224,6 +229,69 @@ err: return ret; } +static int mei_nfc_send(struct mei_bus_client *client, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_bus_dev_nfc *bdev; + struct mei_nfc_hci_hdr *hdr; + u8 *mei_buf; + int err; + + bdev = (struct mei_bus_dev_nfc *) client->priv_data; + dev = bdev->cl->dev; + + mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); + if (!mei_buf) + return -ENOMEM; + + hdr = (struct mei_nfc_hci_hdr *) mei_buf; + hdr->cmd = MEI_NFC_CMD_HCI_SEND; + hdr->status = 0; + hdr->req_id = bdev->req_id; + hdr->reserved = 0; + hdr->data_size = length; + + memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); + + err = mei_send(bdev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE); + + kfree(mei_buf); + + if (!wait_event_interruptible_timeout(bdev->send_wq, + bdev->recv_req_id == bdev->req_id, HZ)) { + dev_err(&dev->pdev->dev, "NFC MEI command timeout\n"); + err = -ETIMEDOUT; + } else { + bdev->req_id++; + } + + return err; +} + +static int mei_nfc_recv(struct mei_bus_client *client, u8 *buf, size_t length) +{ + struct mei_bus_dev_nfc *bdev; + struct mei_nfc_hci_hdr *hci_hdr; + int received_length; + + bdev = (struct mei_bus_dev_nfc *) client->priv_data; + + received_length = mei_recv(bdev->cl, buf, length); + if (received_length < 0) + return received_length; + + hci_hdr = (struct mei_nfc_hci_hdr *) buf; + + if (hci_hdr->cmd == MEI_NFC_CMD_HCI_SEND) { + bdev->recv_req_id = hci_hdr->req_id; + wake_up(&bdev->send_wq); + + return 0; + } + + return received_length; +} + static void mei_nfc_init(struct work_struct *work) { struct mei_device *dev; @@ -295,6 +363,8 @@ static void mei_nfc_init(struct work_struct *work) } bus_client->priv_data = bdev; + bus_client->send = mei_nfc_send; + bus_client->recv = mei_nfc_recv; return; @@ -361,8 +431,10 @@ int mei_nfc_host_init(struct mei_device *dev) bdev->cl_info = cl_info; bdev->cl = cl; + bdev->req_id = 1; INIT_WORK(&bdev->init_work, mei_nfc_init); + init_waitqueue_head(&bdev->send_wq); schedule_work(&bdev->init_work); return 0; diff --git a/drivers/misc/mei/nfc.h b/drivers/misc/mei/nfc.h index 4440436..12e48d3 100644 --- a/drivers/misc/mei/nfc.h +++ b/drivers/misc/mei/nfc.h @@ -114,11 +114,24 @@ struct mei_nfc_connect_resp { uint16_t me_build; } __packed; +struct mei_nfc_hci_hdr { + u8 cmd; + u8 status; + u16 req_id; + u32 reserved; + u16 data_size; +} __packed; + #define MEI_NFC_CMD_MAINTENANCE 0x00 +#define MEI_NFC_CMD_HCI_SEND 0x01 +#define MEI_NFC_CMD_HCI_RECV 0x02 #define MEI_NFC_SUBCMD_CONNECT0x00 #define MEI_NFC_SUBCMD_IF_VERSION 0x01 +#define MEI_NFC_HEADER_SIZE 10 +#define MEI_NFC_MAX_HCI_PAYLOAD 300 + /* Vendors */ #define MEI_NFC_VENDOR_INSIDE 0x00 -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 09/11 V2] mei: nfc: Connect also the regular ME client
From: Samuel Ortiz After receiving the NFC interface version, IVN and radio type, we can connect to the the actual nfc me client and send the initialization (nfc connect) message. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/nfc.c | 128 ++-- 1 files changed, 123 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index 240dd2b..ab66944 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -29,6 +29,7 @@ /** mei_nfc_bdev - nfc mei bus device * + * @cl: nfc info host client * @cl_info: nfc info host client * @init_work: perform connection to the info client * @fw_ivn: NFC Intervace Version Number @@ -36,6 +37,7 @@ * @radio_type: NFC radio type */ struct mei_bus_dev_nfc { + struct mei_cl *cl; struct mei_cl *cl_info; struct work_struct init_work; u8 fw_ivn; @@ -56,6 +58,12 @@ const uuid_le mei_nfc_info_guid = UUID_LE(0xd2de1625, 0x382d, 0x417d, static void mei_nfc_free(struct mei_bus_dev_nfc *bdev) { + if (bdev->cl) { + list_del(&bdev->cl->bus_client_link); + mei_cl_unlink(bdev->cl); + kfree(bdev->cl); + } + if (bdev->cl_info) { list_del(&bdev->cl_info->bus_client_link); mei_cl_unlink(bdev->cl_info); @@ -63,6 +71,73 @@ static void mei_nfc_free(struct mei_bus_dev_nfc *bdev) } } +static int mei_nfc_connect(struct mei_bus_dev_nfc *bdev) +{ + struct mei_device *dev; + struct mei_cl *cl; + struct mei_nfc_cmd *cmd, *reply; + struct mei_nfc_connect *connect; + struct mei_nfc_connect_resp *connect_resp; + size_t connect_length, connect_resp_length; + int bytes_recv, ret; + + cl = bdev->cl; + dev = cl->dev; + + connect_length = sizeof(struct mei_nfc_cmd) + + sizeof(struct mei_nfc_connect); + + connect_resp_length = sizeof(struct mei_nfc_cmd) + + sizeof(struct mei_nfc_connect_resp); + + cmd = kzalloc(connect_length, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + connect = (struct mei_nfc_connect *)cmd->data; + + reply = kzalloc(connect_resp_length, GFP_KERNEL); + if (!reply) { + kfree(cmd); + return -ENOMEM; + } + + connect_resp = (struct mei_nfc_connect_resp *)reply->data; + + cmd->command = MEI_NFC_CMD_MAINTENANCE; + cmd->data_size = 3; + cmd->sub_command = MEI_NFC_SUBCMD_CONNECT; + connect->fw_ivn = bdev->fw_ivn; + connect->vendor_id = bdev->vendor_id; + + ret = mei_send(cl, (u8 *)cmd, connect_length); + if (ret < 0) { + dev_err(&dev->pdev->dev, "Could not send connect cmd\n"); + goto err; + } + + bytes_recv = mei_recv(cl, (u8 *)reply, connect_resp_length); + if (bytes_recv < 0) { + dev_err(&dev->pdev->dev, "Could not read connect response\n"); + ret = bytes_recv; + goto err; + } + + dev_info(&dev->pdev->dev, "IVN 0x%x Vendor ID 0x%x\n", + connect_resp->fw_ivn, connect_resp->vendor_id); + + dev_info(&dev->pdev->dev, "ME FW %d.%d.%d.%d\n", + connect_resp->me_major, connect_resp->me_minor, + connect_resp->me_hotfix, connect_resp->me_build); + + ret = 0; + +err: + kfree(reply); + kfree(cmd); + + return ret; +} + static int mei_nfc_if_version(struct mei_bus_dev_nfc *bdev) { struct mei_device *dev; @@ -118,12 +193,13 @@ static void mei_nfc_init(struct work_struct *work) { struct mei_device *dev; struct mei_bus_dev_nfc *bdev; - struct mei_cl *cl_info; + struct mei_cl *cl_info, *cl; int ret; bdev = container_of(work, struct mei_bus_dev_nfc, init_work); cl_info = bdev->cl_info; + cl = bdev->cl; dev = cl_info->dev; mutex_lock(&dev->device_lock); @@ -149,6 +225,24 @@ static void mei_nfc_init(struct work_struct *work) "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", bdev->fw_ivn, bdev->vendor_id, bdev->radio_type); + mutex_lock(&dev->device_lock); + + if (mei_cl_connect(cl, NULL) < 0) { + mutex_unlock(&dev->device_lock); + dev_err(&dev->pdev->dev, + "Could not connect to the NFC ME client"); + + goto err; + } + + mutex_unlock(&dev->device_lock); + + ret = mei_nfc_connect(bdev); + if (ret < 0) { + dev_err(&dev->pdev->dev, "Could not c
[char-misc-next 03/11 V2] mei: bus: Initial implementation for I/O routines
From: Samuel Ortiz Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 226 drivers/misc/mei/bus.h |3 + drivers/misc/mei/mei_dev.h | 15 +++ include/linux/mei_bus.h| 11 ++ 4 files changed, 255 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 0a5e624..97e1988 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,8 @@ #include #include "mei_dev.h" +#include "hw-me.h" +#include "client.h" #include "bus.h" static int mei_device_match(struct device *dev, struct device_driver *drv) @@ -73,6 +76,11 @@ static int mei_device_remove(struct device *dev) if (!client || !dev->driver) return 0; + if (client->event_cb) { + client->event_cb = NULL; + cancel_work_sync(&client->event_work); + } + driver = to_mei_driver(dev->driver); if (!driver->remove) { dev->driver = NULL; @@ -182,3 +190,221 @@ void mei_driver_unregister(struct mei_bus_driver *driver) pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); } EXPORT_SYMBOL(mei_driver_unregister); + +int mei_send(struct mei_cl *cl, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_msg_hdr mei_hdr; + struct mei_cl_cb *cb; + int me_cl_id, err; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + if (cl->state != MEI_FILE_CONNECTED) + return -ENODEV; + + cb = mei_io_cb_init(cl, NULL); + if (!cb) + return -ENOMEM; + + err = mei_io_cb_alloc_req_buf(cb, length); + if (err < 0) { + mei_io_cb_free(cb); + return err; + } + + memcpy(cb->request_buffer.data, buf, length); + cb->fop_type = MEI_FOP_WRITE; + + dev = cl->dev; + + mutex_lock(&dev->device_lock); + + /* Check if we have an ME client device */ + me_cl_id = mei_me_cl_by_id(dev, cl->me_client_id); + if (me_cl_id == dev->me_clients_num) { + err = -ENODEV; + goto out_err; + } + + if (length > dev->me_clients[me_cl_id].props.max_msg_length) { + err = -EINVAL; + goto out_err; + } + + err = mei_cl_flow_ctrl_creds(cl); + if (err < 0) + goto out_err; + + /* Host buffer is not ready, we queue the request */ + if (err == 0 || !dev->hbuf_is_ready) { + cb->buf_idx = 0; + mei_hdr.msg_complete = 0; + cl->writing_state = MEI_WRITING; + list_add_tail(&cb->list, &dev->write_list.list); + + mutex_unlock(&dev->device_lock); + + return length; + } + + dev->hbuf_is_ready = false; + + /* Check for a maximum length */ + if (length > mei_hbuf_max_len(dev)) { + mei_hdr.length = mei_hbuf_max_len(dev); + mei_hdr.msg_complete = 0; + } else { + mei_hdr.length = length; + mei_hdr.msg_complete = 1; + } + + mei_hdr.host_addr = cl->host_client_id; + mei_hdr.me_addr = cl->me_client_id; + mei_hdr.reserved = 0; + + if (mei_write_message(dev, &mei_hdr, buf)) { + err = -EIO; + goto out_err; + } + + cl->writing_state = MEI_WRITING; + cb->buf_idx = mei_hdr.length; + + if (!mei_hdr.msg_complete) { + list_add_tail(&cb->list, &dev->write_list.list); + } else { + if (mei_cl_flow_ctrl_reduce(cl)) { + err = -EIO; + goto out_err; + } + + list_add_tail(&cb->list, &dev->write_waiting_list.list); + } + + mutex_unlock(&dev->device_lock); + + return mei_hdr.length; + +out_err: + mutex_unlock(&dev->device_lock); + mei_io_cb_free(cb); + + return err; +} + +int mei_recv(struct mei_cl *cl, u8 *buf, size_t length) +{ + struct mei_device *dev; + struct mei_cl_cb *cb; + size_t r_length; + int err; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + mutex_lock(&dev->device_lock); + + if (!cl->read_cb) { + err = mei_cl_read_start(cl); + if (err < 0) { + mutex_unlock(&dev->device_lock); + return err; + } + } + + if (cl->reading_state != MEI_READ_COMPLETE && +
[char-misc-next 10/11] mei: nfc: Add NFC device to the MEI bus
From: Samuel Ortiz After building its bus name as a string based on its vendor id and radio type, we can add it to the bus. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/nfc.c | 56 drivers/misc/mei/nfc.h |6 + 2 files changed, 62 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index ab66944..c65bd44 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -43,6 +43,8 @@ struct mei_bus_dev_nfc { u8 fw_ivn; u8 vendor_id; u8 radio_type; + + char *bus_name; }; struct mei_bus_dev_nfc nfc_bdev; @@ -71,6 +73,39 @@ static void mei_nfc_free(struct mei_bus_dev_nfc *bdev) } } +static int mei_nfc_build_bus_name(struct mei_bus_dev_nfc *bdev) +{ + struct mei_device *dev; + + if (!bdev->cl) + return -ENODEV; + + dev = bdev->cl->dev; + + switch (bdev->vendor_id) { + case MEI_NFC_VENDOR_INSIDE: + switch (bdev->radio_type) { + case MEI_NFC_VENDOR_INSIDE_UREAD: + bdev->bus_name = "microread"; + return 0; + + default: + dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n", + bdev->radio_type); + + return -EINVAL; + } + + default: + dev_err(&dev->pdev->dev, "Unknow vendor ID 0x%x\n", + bdev->vendor_id); + + return -EINVAL; + } + + return 0; +} + static int mei_nfc_connect(struct mei_bus_dev_nfc *bdev) { struct mei_device *dev; @@ -192,6 +227,7 @@ err: static void mei_nfc_init(struct work_struct *work) { struct mei_device *dev; + struct mei_bus_client *bus_client; struct mei_bus_dev_nfc *bdev; struct mei_cl *cl_info, *cl; int ret; @@ -243,6 +279,23 @@ static void mei_nfc_init(struct work_struct *work) return; } + if (mei_nfc_build_bus_name(bdev) < 0) { + dev_err(&dev->pdev->dev, + "Could not build the bus ID name\n"); + return; + } + + bus_client = mei_add_device(dev, mei_nfc_guid, + bdev->bus_name); + if (!bus_client) { + dev_err(&dev->pdev->dev, + "Could not add the NFC device to the MEI bus\n"); + + goto err; + } + + bus_client->priv_data = bdev; + return; err: @@ -324,5 +377,8 @@ void mei_nfc_host_exit(void) { struct mei_bus_dev_nfc *bdev = &nfc_bdev; + if (bdev->cl && bdev->cl->client) + mei_remove_device(bdev->cl->client); + mei_nfc_free(bdev); } diff --git a/drivers/misc/mei/nfc.h b/drivers/misc/mei/nfc.h index 59e6703..4440436 100644 --- a/drivers/misc/mei/nfc.h +++ b/drivers/misc/mei/nfc.h @@ -119,4 +119,10 @@ struct mei_nfc_connect_resp { #define MEI_NFC_SUBCMD_CONNECT0x00 #define MEI_NFC_SUBCMD_IF_VERSION 0x01 +/* Vendors */ +#define MEI_NFC_VENDOR_INSIDE 0x00 + +/* Radio types */ +#define MEI_NFC_VENDOR_INSIDE_UREAD 0x00 + #endif /* _MEI_NFC_H */ -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 08/11 V2] mei: nfc: Initial nfc implementation
From: Samuel Ortiz NFC ME client is exported through mei bus to be consumed by the NFC subsystem. NFC is represented by two mei clients: An info one and the actual NFC one. In order for correct connection we first need to retrieve the firmware information from the info client. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/Kconfig |7 ++ drivers/misc/mei/Makefile |1 + drivers/misc/mei/client.c |3 + drivers/misc/mei/mei_dev.h | 28 ++ drivers/misc/mei/nfc.c | 210 drivers/misc/mei/nfc.h | 122 + drivers/misc/mei/pci-me.c |2 + 7 files changed, 373 insertions(+), 0 deletions(-) create mode 100644 drivers/misc/mei/nfc.c create mode 100644 drivers/misc/mei/nfc.h diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index d21b4d0..66e84ef 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -35,3 +35,10 @@ config INTEL_MEI_ME 82Q33 Express 82X38/X48 Express +config INTEL_MEI_BUS_NFC +bool "MEI bus NFC support" +depends on INTEL_MEI + help + When selecting this option the ME NFC device will be added to the + MEI bus. This is needed by the NFC kernel subsystem for sending and + receiving HCI frames to and from the NFC device. diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 5948621..644f92e 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -11,5 +11,6 @@ mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o mei-objs += bus.o +mei-$(CONFIG_INTEL_MEI_BUS_NFC) += nfc.o mei-$(CONFIG_INTEL_MEI_ME) += pci-me.o mei-$(CONFIG_INTEL_MEI_ME) += hw-me.o diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 5724499..e3ed1d4 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -358,6 +358,9 @@ void mei_host_client_init(struct work_struct *work) mei_amthif_host_init(dev); else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) mei_wd_host_init(dev); + else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid)) + mei_nfc_host_init(dev); + } dev->dev_state = MEI_DEV_ENABLED; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index a9419a8..39ddfe9 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -476,6 +476,34 @@ int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list, struct mei_device *dev, struct mei_msg_hdr *mei_hdr); int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); +#ifdef CONFIG_INTEL_MEI_BUS_NFC +/* + * NFC functions + */ +int mei_nfc_host_init(struct mei_device *dev); +void mei_nfc_host_exit(void); + +/* + * NFC Client UUID + */ +extern const uuid_le mei_nfc_guid; + +#else + +static inline int mei_nfc_host_init(struct mei_device *dev) +{ + return 0; +} + +static inline void mei_nfc_host_exit(void) +{ + return; +} + +static const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, + 0x94, 0xd4, 0x50, 0x26, + 0x67, 0x23, 0x77, 0x5c); +#endif int mei_wd_send(struct mei_device *dev); int mei_wd_stop(struct mei_device *dev); diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c new file mode 100644 index 000..240dd2b --- /dev/null +++ b/drivers/misc/mei/nfc.c @@ -0,0 +1,210 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mei_dev.h" +#include "client.h" +#include "bus.h" +#include "nfc.h" + +/** mei_nfc_bdev - nfc mei bus device + * + * @cl_info: nfc info host client + * @init_work: perform connection to the info client + * @fw_ivn: NFC Intervace Version Number + * @vendor_id: NFC manufacturer ID + * @radio_type: NFC radio type + */ +struct mei_bus_dev_nfc { + struct mei_cl *cl_info; + struct work_struct init_work; + u8 fw_ivn; + u8 vendor_id; + u8 radio_type; +}; + +struct mei_bus_dev_nfc nfc_bdev; + +/* UUIDs for NFC F/W clients */ +const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, +0x94, 0xd4,
[char-misc-next 04/11 V2] mei: bus: Add bus related structures to mei_cl
From: Samuel Ortiz We keep track of all MEI bus clients through a specific linked list. We also have a mei_bus_client instance in the mei_cl structure. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 47 +++ drivers/misc/mei/client.c |1 + drivers/misc/mei/init.c|1 + drivers/misc/mei/mei_dev.h |8 +++ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 97e1988..2e12928 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -119,16 +119,37 @@ static struct device_type mei_client_type = { .release= mei_client_dev_release, }; +static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *mei_dev, + uuid_le uuid) +{ + struct mei_cl *cl, *next; + + list_for_each_entry_safe(cl, next, +&mei_dev->bus_client_list, bus_client_link) { + if (!uuid_le_cmp(uuid, cl->bus_client_uuid)) + return cl; + } + + return NULL; +} + struct mei_bus_client *mei_add_device(struct mei_device *mei_dev, uuid_le uuid, char *name) { struct mei_bus_client *client; + struct mei_cl *cl; int status; + cl = mei_bus_find_mei_cl_by_uuid(mei_dev, uuid); + if (cl == NULL) + return NULL; + client = kzalloc(sizeof(struct mei_bus_client), GFP_KERNEL); if (!client) return NULL; + client->cl = cl; + client->mei_dev = mei_dev; client->uuid = uuid; strlcpy(client->name, name, sizeof(client->name)); @@ -140,19 +161,17 @@ struct mei_bus_client *mei_add_device(struct mei_device *mei_dev, dev_set_name(&client->dev, "%s", client->name); status = device_register(&client->dev); - if (status) - goto out_err; + if (status) { + kfree(client); + dev_err(client->dev.parent, "Failed to register MEI client\n"); + return NULL; + } + + cl->client = client; dev_dbg(&client->dev, "client %s registered\n", client->name); return client; - -out_err: - dev_err(client->dev.parent, "Failed to register MEI client\n"); - - kfree(client); - - return NULL; } EXPORT_SYMBOL(mei_add_device); @@ -353,9 +372,10 @@ out: int mei_bus_send(struct mei_bus_client *client, u8 *buf, size_t length) { - struct mei_cl *cl = NULL; + struct mei_cl *cl = client->cl; - /* TODO: hook between mei_bus_client and mei_cl */ + if (cl == NULL) + return -ENODEV; if (client->send) return client->send(client, buf, length); @@ -366,9 +386,10 @@ EXPORT_SYMBOL(mei_bus_send); int mei_bus_recv(struct mei_bus_client *client, u8 *buf, size_t length) { - struct mei_cl *cl = NULL; + struct mei_cl *cl = client->cl; - /* TODO: hook between mei_bus_client and mei_cl */ + if (cl == NULL) + return -ENODEV; if (client->recv) return client->recv(client, buf, length); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 1569afe..5724499 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -216,6 +216,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) init_waitqueue_head(&cl->rx_wait); init_waitqueue_head(&cl->tx_wait); INIT_LIST_HEAD(&cl->link); + INIT_LIST_HEAD(&cl->bus_client_link); cl->reading_state = MEI_IDLE; cl->writing_state = MEI_IDLE; cl->dev = dev; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 6ec5301..b812d56 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -46,6 +46,7 @@ void mei_device_init(struct mei_device *dev) { /* setup our list array */ INIT_LIST_HEAD(&dev->file_list); + INIT_LIST_HEAD(&dev->bus_client_list); mutex_init(&dev->device_lock); init_waitqueue_head(&dev->wait_recvd_msg); init_waitqueue_head(&dev->wait_stop_wd); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index e95d6e1..4e1daf2 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -210,6 +210,11 @@ struct mei_cl { enum mei_file_transaction_states writing_state; int sm_state; struct mei_cl_cb *read_cb; + + /* MEI bus data */ + struct mei_bus_client *client; + struct list_head bus_client_link; + uuid_le bus_client_uuid; }; /** struct mei_hw_ops @@ -404,6 +409,9 @@ struct mei_device {
[char-misc-next 02/11 V2] mei: bus: Implement driver registration
From: Samuel Ortiz Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 29 + include/linux/mei_bus.h |3 +++ 2 files changed, 32 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index bb96423c..0a5e624 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -153,3 +153,32 @@ void mei_remove_device(struct mei_bus_client *client) device_unregister(&client->dev); } EXPORT_SYMBOL(mei_remove_device); + +int mei_driver_register(struct mei_bus_driver *driver) +{ + int err; + + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!mei_bus_type.p))) + return -EAGAIN; + + driver->driver.owner = THIS_MODULE; + driver->driver.bus = &mei_bus_type; + + err = driver_register(&driver->driver); + if (err) + return err; + + pr_debug("mei: driver [%s] registered\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL(mei_driver_register); + +void mei_driver_unregister(struct mei_bus_driver *driver) +{ + driver_unregister(&driver->driver); + + pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); +} +EXPORT_SYMBOL(mei_driver_unregister); diff --git a/include/linux/mei_bus.h b/include/linux/mei_bus.h index 3a53f9e..0bd1189c 100644 --- a/include/linux/mei_bus.h +++ b/include/linux/mei_bus.h @@ -88,4 +88,7 @@ struct mei_bus_driver { int (*remove)(struct mei_bus_client *client); }; +int mei_driver_register(struct mei_bus_driver *driver); +void mei_driver_unregister(struct mei_bus_driver *driver); + #endif /* _LINUX_MEI_BUS_H */ -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next] mei: add debugfs hooks
debugfs exposes device state and list of me clients and their properties Signed-off-by: Tomas Winkler --- drivers/misc/mei/Makefile | 1 + drivers/misc/mei/debugfs.c | 150 + drivers/misc/mei/main.c| 18 -- drivers/misc/mei/mei_dev.h | 20 +- drivers/misc/mei/pci-me.c | 5 +- 5 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 drivers/misc/mei/debugfs.c diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 1b29f7c..ee85456 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -11,6 +11,7 @@ mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o mei-objs += bus.o +mei-objs += debugfs.o obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o mei-me-objs := pci-me.o diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c new file mode 100644 index 000..f1caa51 --- /dev/null +++ b/drivers/misc/mei/debugfs.c @@ -0,0 +1,150 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2012-2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#include +#include +#include +#include +#include + +#include + +#include "mei_dev.h" +#include "hw.h" + + + +#define DEBUGFS_ADD_FILE(_mei, name, parent, mode) \ + debugfs_create_file(#name, mode, parent,\ + _mei, &mei_dbgfs_fops_##name) + +static int mei_dbgfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_device *dev = fp->private_data; + struct mei_me_client *cl; + const size_t bufsz = 1024; + char *buf = kzalloc(bufsz, GFP_KERNEL); + int i; + int pos = 0; + int ret; + + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, + " |id|addr| UUID |con|msg len|\n"); + + mutex_lock(&dev->device_lock); + + /* if the driver is not enabled the list won't b consitent */ + if (dev->dev_state != MEI_DEV_ENABLED) + goto out; + + for (i = 0; i < dev->me_clients_num; i++) { + cl = &dev->me_clients[i]; + + /* skip me clients that cannot be connected */ + if (cl->props.max_number_of_connections == 0) + continue; + + pos += scnprintf(buf + pos, bufsz - pos, + "%2d|%2d|%4d|%pUl|%3d|%7d|\n", + i, cl->client_id, + cl->props.fixed_address, + &cl->props.protocol_name, + cl->props.max_number_of_connections, + cl->props.max_msg_length); + } +out: + mutex_unlock(&dev->device_lock); + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} + +static const struct file_operations mei_dbgfs_fops_meclients = { + .open = mei_dbgfs_open, + .read = mei_dbgfs_read_meclients, + .llseek = generic_file_llseek, +}; + +static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_device *dev = fp->private_data; + const size_t bufsz = 1024; + char *buf = kzalloc(bufsz, GFP_KERNEL); + int pos = 0; + int ret; + + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "%s\n", + mei_dev_state_str(dev->dev_state)); + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} +static const struct file_operations mei_dbgfs_fops_devstate = { + .open = mei_dbgfs_open, + .read = mei_dbgfs_read_devstate, + .llseek = generic_file_llseek, +}; + +/** + * mei_dbgfs_deregister - Remove the debugfs files and directories + * @mei - pointer to mei device private dat + */ +void mei_dbgfs_deregister(struct mei_device *dev) +{ + if (!dev->dbgfs_dir) + return; + debugfs_remove_recursive(dev->dbgfs_dir); + dev->dbgfs_dir = NULL; +} + +/** + * Add the debugfs files + * + */ +int
[char-misc-next] mei: add debugfs hooks
debugfs exposes device state and list of me clients and their properties Signed-off-by: Tomas Winkler --- V2: 1. don't use DEBUGFS_ADD_FILE macro 2. compile debugfs.c only if CONFIG_DEBUG_FS is set drivers/misc/mei/Makefile | 1 + drivers/misc/mei/debugfs.c | 155 + drivers/misc/mei/main.c| 18 -- drivers/misc/mei/mei_dev.h | 20 +- drivers/misc/mei/pci-me.c | 5 +- 5 files changed, 191 insertions(+), 8 deletions(-) create mode 100644 drivers/misc/mei/debugfs.c diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 1b29f7c..3612d57 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -11,6 +11,7 @@ mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o mei-objs += bus.o +mei-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o mei-me-objs := pci-me.o diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c new file mode 100644 index 000..7164ffa --- /dev/null +++ b/drivers/misc/mei/debugfs.c @@ -0,0 +1,155 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2012-2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#include +#include +#include +#include +#include + +#include + +#include "mei_dev.h" +#include "hw.h" + + + +#define DEBUGFS_ADD_FILE(_mei, name, parent, mode) \ + debugfs_create_file(#name, mode, parent,\ + _mei, &mei_dbgfs_fops_##name) + +static int mei_dbgfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_device *dev = fp->private_data; + struct mei_me_client *cl; + const size_t bufsz = 1024; + char *buf = kzalloc(bufsz, GFP_KERNEL); + int i; + int pos = 0; + int ret; + + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, + " |id|addr| UUID |con|msg len|\n"); + + mutex_lock(&dev->device_lock); + + /* if the driver is not enabled the list won't b consitent */ + if (dev->dev_state != MEI_DEV_ENABLED) + goto out; + + for (i = 0; i < dev->me_clients_num; i++) { + cl = &dev->me_clients[i]; + + /* skip me clients that cannot be connected */ + if (cl->props.max_number_of_connections == 0) + continue; + + pos += scnprintf(buf + pos, bufsz - pos, + "%2d|%2d|%4d|%pUl|%3d|%7d|\n", + i, cl->client_id, + cl->props.fixed_address, + &cl->props.protocol_name, + cl->props.max_number_of_connections, + cl->props.max_msg_length); + } +out: + mutex_unlock(&dev->device_lock); + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} + +static const struct file_operations mei_dbgfs_fops_meclients = { + .open = mei_dbgfs_open, + .read = mei_dbgfs_read_meclients, + .llseek = generic_file_llseek, +}; + +static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_device *dev = fp->private_data; + const size_t bufsz = 1024; + char *buf = kzalloc(bufsz, GFP_KERNEL); + int pos = 0; + int ret; + + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "%s\n", + mei_dev_state_str(dev->dev_state)); + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} +static const struct file_operations mei_dbgfs_fops_devstate = { + .open = mei_dbgfs_open, + .read = mei_dbgfs_read_devstate, + .llseek = generic_file_llseek, +}; + +/** + * mei_dbgfs_deregister - Remove the debugfs files and directories + * @mei - pointer to mei device private dat + */ +void mei_dbgfs_deregister(struct mei_device *dev) +{ + if (!dev->dbgfs_dir) + return; + debugfs_remove_recursive(
[char-misc-next V3] mei: add debugfs hooks
debugfs exposes device state and list of me clients and their properties Signed-off-by: Tomas Winkler --- V3: 1. remove DEBUGFS_ADD_FILE macro V2: 1. don't use DEBUGFS_ADD_FILE macro 2. compile debugfs.c only if CONFIG_DEBUG_FS is set drivers/misc/mei/Makefile | 1 + drivers/misc/mei/debugfs.c | 149 + drivers/misc/mei/main.c| 18 -- drivers/misc/mei/mei_dev.h | 20 +- drivers/misc/mei/pci-me.c | 5 +- 5 files changed, 185 insertions(+), 8 deletions(-) create mode 100644 drivers/misc/mei/debugfs.c diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 1b29f7c..3612d57 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -11,6 +11,7 @@ mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o mei-objs += bus.o +mei-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o mei-me-objs := pci-me.o diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c new file mode 100644 index 000..7135a718 --- /dev/null +++ b/drivers/misc/mei/debugfs.c @@ -0,0 +1,149 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2012-2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#include +#include +#include +#include +#include + +#include + +#include "mei_dev.h" +#include "hw.h" + +static int mei_dbgfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_device *dev = fp->private_data; + struct mei_me_client *cl; + const size_t bufsz = 1024; + char *buf = kzalloc(bufsz, GFP_KERNEL); + int i; + int pos = 0; + int ret; + + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, + " |id|addr| UUID |con|msg len|\n"); + + mutex_lock(&dev->device_lock); + + /* if the driver is not enabled the list won't b consitent */ + if (dev->dev_state != MEI_DEV_ENABLED) + goto out; + + for (i = 0; i < dev->me_clients_num; i++) { + cl = &dev->me_clients[i]; + + /* skip me clients that cannot be connected */ + if (cl->props.max_number_of_connections == 0) + continue; + + pos += scnprintf(buf + pos, bufsz - pos, + "%2d|%2d|%4d|%pUl|%3d|%7d|\n", + i, cl->client_id, + cl->props.fixed_address, + &cl->props.protocol_name, + cl->props.max_number_of_connections, + cl->props.max_msg_length); + } +out: + mutex_unlock(&dev->device_lock); + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} + +static const struct file_operations mei_dbgfs_fops_meclients = { + .open = mei_dbgfs_open, + .read = mei_dbgfs_read_meclients, + .llseek = generic_file_llseek, +}; + +static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_device *dev = fp->private_data; + const size_t bufsz = 1024; + char *buf = kzalloc(bufsz, GFP_KERNEL); + int pos = 0; + int ret; + + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "%s\n", + mei_dev_state_str(dev->dev_state)); + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} +static const struct file_operations mei_dbgfs_fops_devstate = { + .open = mei_dbgfs_open, + .read = mei_dbgfs_read_devstate, + .llseek = generic_file_llseek, +}; + +/** + * mei_dbgfs_deregister - Remove the debugfs files and directories + * @mei - pointer to mei device private dat + */ +void mei_dbgfs_deregister(struct mei_device *dev) +{ + if (!dev->dbgfs_dir) + return; + debugfs_remove_recursive(dev->dbgfs_dir); + dev->dbgfs_dir = NULL; +} + +/** + * Add the debugfs files + * + */ +int mei_dbgfs_register(str
Re: [char-misc-next 01/12 v3] mei: Rename mei_device to mei_host
On Wed, Feb 13, 2013 at 11:39 AM, Samuel Ortiz wrote: > > On Tue, Feb 12, 2013 at 11:09:00PM +, Arnd Bergmann wrote: > > On Tuesday 12 February 2013, gre...@linuxfoundation.org wrote: > > > > > > > > > Please let's find something that makes both hw and Linux happy > > > > I still believe it makes sense to use mei_device for what we add to the > > > > MEI > > > > bus. I'd be fine with mei_bus_device as well, but that would somehow > > > > look > > > > a bit awkward. Greg, Arnd, any preference ? > > > > > > "mei_device" works the best for me. Tomas, what you think of as a "MEI > > > Device" really is a "MEI Controller", it bridges the difference between > > > the PCI bus and your new MEI bus, so you will need to start thinking of > > > these things a bit differently now that you have created your own little > > > virtual bus. > > > > Yes, I agree. mei_bus_device would also work as the name for the controller, > > but not for the devices attached to it IMO. > Tomas, I propose to switch to mei_controller instead of mei_host and keep the > mei_device name for the devices we attach to the MEI bus. > Does that work for you ? > The issue is that when we added our virtual bus we haven't gave up on /dev/mei backed by mei_device This is the interface, defined in linux/mei.h which user space applications use to connect to ME Clients within ME device. Any ME client can be connected through this interface and we have few legacy applications running for few years that use this interface so we are not going to break them. What we've done now is we added a virtual bus so also in-kernel applications/subsystems can more naturally connect to the ME Clients, this connection is client specific. So the device that connect to the bus is not an mei device but mei client device hence the name I've proposed mei_cl_device. Does it make sense? Thanks > > Cheers, > Samuel. > > -- > Intel Open Source Technology Centre > http://oss.intel.com/ > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 1/2] mei: add mei_irq_compl_handler function
similar to read/write add also irq completion handler that is called for the irq thread rename missnamed mei_irq_complete_handler to mei_cl_complete_handler as it operates on a single client Signed-off-by: Tomas Winkler --- drivers/misc/mei/hw-me.c | 18 +- drivers/misc/mei/interrupt.c | 39 --- drivers/misc/mei/mei_dev.h | 3 +-- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 0db071f..7c2b14d 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -466,8 +466,6 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; struct mei_cl_cb complete_list; - struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; - struct mei_cl *cl; s32 slots; int rets; bool bus_message_received; @@ -538,23 +536,9 @@ end: wake_up_interruptible(&dev->wait_recvd_msg); bus_message_received = false; } - if (list_empty(&complete_list.list)) - return IRQ_HANDLED; + mei_irq_compl_handler(dev, &complete_list); - list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) { - cl = cb_pos->cl; - list_del(&cb_pos->list); - if (cl) { - if (cl != &dev->iamthif_cl) { - dev_dbg(&dev->pdev->dev, "completing call back.\n"); - mei_irq_complete_handler(cl, cb_pos); - cb_pos = NULL; - } else if (cl == &dev->iamthif_cl) { - mei_amthif_complete(dev, cb_pos); - } - } - } return IRQ_HANDLED; } static const struct mei_hw_ops mei_me_hw_ops = { diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 14c70b8..73fbce3 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -30,21 +30,21 @@ /** - * mei_complete_handler - processes completed operation. + * mei_cl_complete_handler - processes completed operation for a client * * @cl: private data of the file object. - * @cb_pos: callback block. + * @cb: callback block. */ -void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos) +static void mei_cl_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb) { - if (cb_pos->fop_type == MEI_FOP_WRITE) { - mei_io_cb_free(cb_pos); - cb_pos = NULL; + if (cb->fop_type == MEI_FOP_WRITE) { + mei_io_cb_free(cb); + cb = NULL; cl->writing_state = MEI_WRITE_COMPLETE; if (waitqueue_active(&cl->tx_wait)) wake_up_interruptible(&cl->tx_wait); - } else if (cb_pos->fop_type == MEI_FOP_READ && + } else if (cb->fop_type == MEI_FOP_READ && MEI_READING == cl->reading_state) { cl->reading_state = MEI_READ_COMPLETE; if (waitqueue_active(&cl->rx_wait)) @@ -54,6 +54,31 @@ void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos) } /** + * mei_irq_compl_handler - dispatch complete handelers + * for the completed callbacks + * + * @dev - mei device + * @compl_list - list of completed cbs + */ +void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) +{ + struct mei_cl_cb *cb, *next; + struct mei_cl *cl; + + list_for_each_entry_safe(cb, next, &compl_list->list, list) { + cl = cb->cl; + list_del(&cb->list); + if (!cl) + continue; + + dev_dbg(&dev->pdev->dev, "completing call back.\n"); + if (cl == &dev->iamthif_cl) + mei_amthif_complete(dev, cb); + else + mei_cl_complete_handler(cl, cb); + } +} +/** * _mei_irq_thread_state_ok - checks if mei header matches file private data * * @cl: private data of the file object diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index c17bced..d6fd3d6 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -406,8 +406,7 @@ int mei_irq_read_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list, s32 *slots); int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); - -void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos); +void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); /* * AMTHIF - AMT Host Interface Functions -- 1.8.1.3 -- To unsubscribe from this list: sen
[char-misc-next 2/2] mei: drop RECOVERING_FROM_RESET device state
ECOVERING_FROM_RESET device state is never set we can remove it Signed-off-by: Tomas Winkler --- drivers/misc/mei/init.c| 4 drivers/misc/mei/mei_dev.h | 1 - 2 files changed, 5 deletions(-) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 1ab1fb1..09a9980 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -34,7 +34,6 @@ const char *mei_dev_state_str(int state) MEI_DEV_STATE(ENABLED); MEI_DEV_STATE(RESETING); MEI_DEV_STATE(DISABLED); - MEI_DEV_STATE(RECOVERING_FROM_RESET); MEI_DEV_STATE(POWER_DOWN); MEI_DEV_STATE(POWER_UP); default: @@ -139,9 +138,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) { bool unexpected; - if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) - return; - unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN && diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index d6fd3d6..091f50af 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -97,7 +97,6 @@ enum mei_dev_state { MEI_DEV_ENABLED, MEI_DEV_RESETING, MEI_DEV_DISABLED, - MEI_DEV_RECOVERING_FROM_RESET, MEI_DEV_POWER_DOWN, MEI_DEV_POWER_UP }; -- 1.8.1.3 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next] mei: add lynx point pci device ids
Signed-off-by: Tomas Winkler --- drivers/misc/mei/hw.h |2 ++ drivers/misc/mei/main.c |2 ++ 2 files changed, 4 insertions(+), 0 deletions(-) diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index b3b4c6d..9700532 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -80,6 +80,8 @@ #define MEI_DEV_ID_PPT_2 0x1CBA /* Panther Point */ #define MEI_DEV_ID_PPT_3 0x1DBA /* Panther Point */ +#define MEI_DEV_ID_LPT0x8C3A /* Lynx Point */ +#define MEI_DEV_ID_LPT_LP 0x9C3A /* Lynx Point LP */ /* * MEI HW Section */ diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index d6fe278..ae2cd0d 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -77,6 +77,8 @@ static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)}, /* required last entry */ {0, } -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH] [wireless/iwlwifi/iwl-4965.c] add parentheses
On Feb 2, 2008 9:48 PM, Roel Kluin <[EMAIL PROTECTED]> wrote: > '!' has a higher priority than '&': bitanding has no effect. > > Signed-off-by: Roel Kluin <[EMAIL PROTECTED]> > --- > diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c > b/drivers/net/wireless/iwlwifi/iwl-4965.c > index 569347f..2439868 100644 > --- a/drivers/net/wireless/iwlwifi/iwl-4965.c > +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c > @@ -4589,7 +4589,7 @@ static u8 iwl4965_is_fat_tx_allowed(struct iwl4965_priv > *priv, > > if (sta_ht_inf) { > if ((!sta_ht_inf->ht_supported) || > - (!sta_ht_inf->cap & IEEE80211_HT_CAP_SUP_WIDTH)) > + (!(sta_ht_inf->cap & IEEE80211_HT_CAP_SUP_WIDTH))) > return 0; > } > > - > To unsubscribe from this list: send the line "unsubscribe linux-wireless" in > the body of a message to [EMAIL PROTECTED] > More majordomo info at http://vger.kernel.org/majordomo-info.html > ACK -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 2/6] mei: normalize timeouts definitions
1. The hardware book defines timeouts in seconds so we stick to this and define the wrapper function mei_secs_to_jiffies around msecs_to_jiffies to use be used instead multiplying by HZ 2. We add name space prefix MEI_ to all timer defines Signed-off-by: Tomas Winkler --- drivers/misc/mei/hw.h| 14 +++--- drivers/misc/mei/init.c | 18 +- drivers/misc/mei/interrupt.c |4 ++-- drivers/misc/mei/iorw.c |8 drivers/misc/mei/main.c |2 +- drivers/misc/mei/mei_dev.h |5 + drivers/misc/mei/wd.c|2 +- 7 files changed, 29 insertions(+), 24 deletions(-) diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 9700532..f21721a 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -20,16 +20,16 @@ #include /* - * Timeouts + * Timeouts in Seconds */ -#define MEI_INTEROP_TIMEOUT(HZ * 7) -#define MEI_CONNECT_TIMEOUT3 /* at least 2 seconds */ +#define MEI_INTEROP_TIMEOUT 7 /* Timeout on ready message */ +#define MEI_CONNECT_TIMEOUT 3 /* HPS: at least 2 seconds */ -#define CONNECT_TIMEOUT15 /* HPS definition */ -#define INIT_CLIENTS_TIMEOUT 15 /* HPS definition */ +#define MEI_CL_CONNECT_TIMEOUT 15 /* HPS: Client Connect Timeout */ +#define MEI_CLIENTS_INIT_TIMEOUT 15 /* HPS: Clients Enumeration Timeout */ -#define IAMTHIF_STALL_TIMER12 /* seconds */ -#define IAMTHIF_READ_TIMER 1 /* ms */ +#define MEI_IAMTHIF_STALL_TIMER12 /* HPS */ +#define MEI_IAMTHIF_READ_TIMER 10 /* HPS */ /* * Internal Clients Number diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 1f13eb9..e6951ec 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -184,7 +184,8 @@ int mei_hw_init(struct mei_device *dev) if (!dev->recvd_msg) { mutex_unlock(&dev->device_lock); err = wait_event_interruptible_timeout(dev->wait_recvd_msg, - dev->recvd_msg, MEI_INTEROP_TIMEOUT); + dev->recvd_msg, + mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); mutex_lock(&dev->device_lock); } @@ -381,7 +382,7 @@ void mei_host_start_message(struct mei_device *dev) mei_reset(dev, 1); } dev->init_clients_state = MEI_START_MESSAGE; - dev->init_clients_timer = INIT_CLIENTS_TIMEOUT; + dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; return ; } @@ -414,7 +415,7 @@ void mei_host_enum_clients_message(struct mei_device *dev) mei_reset(dev, 1); } dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE; - dev->init_clients_timer = INIT_CLIENTS_TIMEOUT; + dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; return; } @@ -502,7 +503,7 @@ int mei_host_client_properties(struct mei_device *dev) return -EIO; } - dev->init_clients_timer = INIT_CLIENTS_TIMEOUT; + dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; dev->me_client_index = b; return 1; } @@ -621,7 +622,7 @@ void mei_host_init_iamthif(struct mei_device *dev) dev->iamthif_cl.state = MEI_FILE_DISCONNECTED; dev->iamthif_cl.host_client_id = 0; } else { - dev->iamthif_cl.timer_count = CONNECT_TIMEOUT; + dev->iamthif_cl.timer_count = MEI_CONNECT_TIMEOUT; } } @@ -658,9 +659,8 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev) */ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) { - int rets, err; - long timeout = 15; /* 15 seconds */ struct mei_cl_cb *cb; + int rets, err; if (!dev || !cl) return -ENODEV; @@ -690,8 +690,8 @@ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) mutex_unlock(&dev->device_lock); err = wait_event_timeout(dev->wait_recvd_msg, -(MEI_FILE_DISCONNECTED == cl->state), -timeout * HZ); + MEI_FILE_DISCONNECTED == cl->state, + mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); mutex_lock(&dev->device_lock); if (MEI_FILE_DISCONNECTED == cl->state) { diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 5c65bac..248f581 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -269,7 +269,7 @@ static int _mei_irq_thread_iamthif_read(struct mei_device *dev, s32 *slots) dev->iamthif_flow_control_pending = false; dev->iamthif_msg_buf_index = 0; dev->iamthif_msg_buf_size = 0; - dev->iamthif_stall_timer = IAMTHIF_STALL_TIMER; + dev-&g
[char-misc-next 6/6] mei: move amthif specific code from mei_write to mei_amthif_write
For sake of amthif consolidation move amthif specific code from mei_write to mei_amthif_write The original mei_amthif_write to mei_amthif_send_cmd as this function deals with sending single command while mei_amthif_write is interface function called from the main driver which in turns calls mei_amthif_send_cmd Signed-off-by: Tomas Winkler --- drivers/misc/mei/amthif.c | 39 --- drivers/misc/mei/main.c | 25 + 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 392203d..96db3ad 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -255,14 +255,15 @@ out: } /** - * mei_amthif_write - write amthif data to amthif client + * mei_amthif_send_cmd - send amthif command to the ME * * @dev: the device structure * @cb: mei call back struct * * returns 0 on success, <0 on failure. + * */ -int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) +static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) { struct mei_msg_hdr mei_hdr; int ret; @@ -329,6 +330,38 @@ int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) } /** + * mei_amthif_write - write amthif data to amthif client + * + * @dev: the device structure + * @cb: mei call back struct + * + * returns 0 on success, <0 on failure. + * + */ +int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) +{ + int ret; + + if (!dev || !cb) + return -ENODEV; + + ret = mei_io_cb_alloc_resp_buf(cb, dev->iamthif_mtu); + if (ret) + return ret; + + cb->major_file_operations = MEI_IOCTL; + + if (!list_empty(&dev->amthi_cmd_list.list) || + dev->iamthif_state != MEI_IAMTHIF_IDLE) { + dev_dbg(&dev->pdev->dev, + "amthif state = %d\n", dev->iamthif_state); + dev_dbg(&dev->pdev->dev, "AMTHIF: add cb to the wait list\n"); + list_add_tail(&cb->list, &dev->amthi_cmd_list.list); + return 0; + } + return mei_amthif_send_cmd(dev, cb); +} +/** * mei_amthif_run_next_cmd * * @dev: the device structure @@ -360,7 +393,7 @@ void mei_amthif_run_next_cmd(struct mei_device *dev) cl_tmp = (struct mei_cl *)pos->file_private; if (cl_tmp && cl_tmp == &dev->iamthif_cl) { - status = mei_amthif_write(dev, pos); + status = mei_amthif_send_cmd(dev, pos); if (status) { dev_dbg(&dev->pdev->dev, "amthi write failed status = %d\n", diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index d8221a5..ff50cc1 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -620,27 +620,12 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, cl->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT; if (cl == &dev->iamthif_cl) { - rets = mei_io_cb_alloc_resp_buf(write_cb, dev->iamthif_mtu); - if (rets) - goto err; + rets = mei_amthif_write(dev, write_cb); - write_cb->major_file_operations = MEI_IOCTL; - - if (!list_empty(&dev->amthi_cmd_list.list) || - dev->iamthif_state != MEI_IAMTHIF_IDLE) { - dev_dbg(&dev->pdev->dev, "amthi_state = %d\n", - (int) dev->iamthif_state); - dev_dbg(&dev->pdev->dev, "add amthi cb to amthi cmd waiting list\n"); - list_add_tail(&write_cb->list, &dev->amthi_cmd_list.list); - } else { - dev_dbg(&dev->pdev->dev, "call amthi write\n"); - rets = mei_amthif_write(dev, write_cb); - - if (rets) { - dev_err(&dev->pdev->dev, "amthi write failed with status = %d\n", - rets); - goto err; - } + if (rets) { + dev_err(&dev->pdev->dev, + "amthi write failed with status = %d\n", rets); + goto err; } mutex_unlock(&dev->device_lock); return length; -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 5/6] mei: use internal watchdog device registration tracking
remove bool wd_interface_reg as watchdog device already keeps track of its registration Signed-off-by: Tomas Winkler --- drivers/misc/mei/init.c|2 -- drivers/misc/mei/mei_dev.h |1 - drivers/misc/mei/wd.c |5 + 3 files changed, 1 insertions(+), 7 deletions(-) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 4a8eb92..8c3c268 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -111,8 +111,6 @@ struct mei_device *mei_device_init(struct pci_dev *pdev) init_waitqueue_head(&dev->wait_stop_wd); dev->dev_state = MEI_DEV_INITIALIZING; dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->wd_interface_reg = false; - mei_io_list_init(&dev->read_list); mei_io_list_init(&dev->write_list); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 57a5a4e..8b96d99 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -249,7 +249,6 @@ struct mei_device { struct mei_cl wd_cl; enum mei_wd_states wd_state; - bool wd_interface_reg; bool wd_pending; u16 wd_timeout; unsigned char wd_data[MEI_WD_START_MSG_SIZE]; diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index 8edb054..4fc2b3d 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -360,23 +360,20 @@ void mei_watchdog_register(struct mei_device *dev) if (watchdog_register_device(&amt_wd_dev)) { dev_err(&dev->pdev->dev, "wd: unable to register watchdog device.\n"); - dev->wd_interface_reg = false; return; } dev_dbg(&dev->pdev->dev, "wd: successfully register watchdog interface.\n"); - dev->wd_interface_reg = true; watchdog_set_drvdata(&amt_wd_dev, dev); } void mei_watchdog_unregister(struct mei_device *dev) { - if (!dev->wd_interface_reg) + if (test_bit(WDOG_UNREGISTERED, &amt_wd_dev.status)) return; watchdog_set_drvdata(&amt_wd_dev, NULL); watchdog_unregister_device(&amt_wd_dev); - dev->wd_interface_reg = false; } -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 4/6] mei: mei_clear_list: kill file_temp
file_temp is used only once, so there is no any benefit of creating a temporary variable Signed-off-by: Tomas Winkler --- drivers/misc/mei/main.c |6 ++ 1 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index f69e085..d8221a5 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -103,18 +103,16 @@ static DEFINE_MUTEX(mei_mutex); * returns true if callback removed from the list, false otherwise */ static bool mei_clear_list(struct mei_device *dev, - struct file *file, struct list_head *mei_cb_list) + const struct file *file, struct list_head *mei_cb_list) { struct mei_cl_cb *cb_pos = NULL; struct mei_cl_cb *cb_next = NULL; - struct file *file_temp; bool removed = false; /* list all list member */ list_for_each_entry_safe(cb_pos, cb_next, mei_cb_list, list) { - file_temp = (struct file *)cb_pos->file_object; /* check if list member associated with a file */ - if (file_temp == file) { + if (file == cb_pos->file_object) { /* remove member from the list */ list_del(&cb_pos->list); /* check if cb equal to current iamthif cb */ -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 1/6] mei: mei_write: revamp function flow
Use goto statement for error handling instead of deeper if constructs and rename label 'unlock_dev' to more appropriate 'err' Signed-off-by: Tomas Winkler --- drivers/misc/mei/main.c | 88 +++--- 1 files changed, 44 insertions(+), 44 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index ed4943f..7a03d77 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -543,24 +543,24 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (dev->dev_state != MEI_DEV_ENABLED) { rets = -ENODEV; - goto unlock_dev; + goto err; } i = mei_me_cl_by_id(dev, cl->me_client_id); if (i < 0) { rets = -ENODEV; - goto unlock_dev; + goto err; } if (length > dev->me_clients[i].props.max_msg_length || length <= 0) { rets = -EMSGSIZE; - goto unlock_dev; + goto err; } if (cl->state != MEI_FILE_CONNECTED) { rets = -ENODEV; dev_err(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d", cl->host_client_id, cl->me_client_id); - goto unlock_dev; + goto err; } if (cl == &dev->iamthif_cl) { write_cb = find_amthi_read_list_entry(dev, file); @@ -599,17 +599,17 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (!write_cb) { dev_err(&dev->pdev->dev, "write cb allocation failed\n"); rets = -ENOMEM; - goto unlock_dev; + goto err; } rets = mei_io_cb_alloc_req_buf(write_cb, length); if (rets) - goto unlock_dev; + goto err; dev_dbg(&dev->pdev->dev, "cb request size = %zd\n", length); rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); if (rets) - goto unlock_dev; + goto err; cl->sm_state = 0; if (length == 4 && @@ -624,7 +624,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (cl == &dev->iamthif_cl) { rets = mei_io_cb_alloc_resp_buf(write_cb, dev->iamthif_mtu); if (rets) - goto unlock_dev; + goto err; write_cb->major_file_operations = MEI_IOCTL; @@ -641,7 +641,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (rets) { dev_err(&dev->pdev->dev, "amthi write failed with status = %d\n", rets); - goto unlock_dev; + goto err; } } mutex_unlock(&dev->device_lock); @@ -654,51 +654,51 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, cl->host_client_id, cl->me_client_id); rets = mei_flow_ctrl_creds(dev, cl); if (rets < 0) - goto unlock_dev; + goto err; - if (rets && dev->mei_host_buffer_is_empty) { - rets = 0; - dev->mei_host_buffer_is_empty = false; - if (length > mei_hbuf_max_data(dev)) { - mei_hdr.length = mei_hbuf_max_data(dev); - mei_hdr.msg_complete = 0; - } else { - mei_hdr.length = length; - mei_hdr.msg_complete = 1; - } - mei_hdr.host_addr = cl->host_client_id; - mei_hdr.me_addr = cl->me_client_id; - mei_hdr.reserved = 0; - dev_dbg(&dev->pdev->dev, "call mei_write_message header=%08x.\n", - *((u32 *) &mei_hdr)); - if (mei_write_message(dev, &mei_hdr, - (unsigned char *) (write_cb->request_buffer.data), - mei_hdr.length)) { - rets = -ENODEV; - goto unlock_dev; - } + if (rets == 0 || dev->mei_host_buffer_is_empty == false) { + write_cb->buf_idx = 0; + mei_hdr.msg_complete = 0; cl->writing_state = MEI_WRITING; - write_cb->buf_idx = mei_hdr.length; - if (mei_hdr.msg_complete) { - if (mei_flow_ctrl_reduce(dev, cl)) { - rets = -ENODEV; - goto unlock_dev; - } -
[char-misc-next 3/6] mei: extract AMTHI functions into the amthif.c file
Move AMT Host Interface functions into the new amthif.c file. All functions has now common prefix: mei_amthif_ Signed-off-by: Tomas Winkler --- drivers/misc/mei/Makefile|1 + drivers/misc/mei/amthif.c| 572 ++ drivers/misc/mei/init.c | 73 +-- drivers/misc/mei/interrupt.c | 218 +--- drivers/misc/mei/iorw.c | 266 drivers/misc/mei/main.c | 10 +- drivers/misc/mei/mei_dev.h | 29 ++- 7 files changed, 613 insertions(+), 556 deletions(-) create mode 100644 drivers/misc/mei/amthif.c diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 57168db..0017842 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -8,4 +8,5 @@ mei-objs += interrupt.o mei-objs += interface.o mei-objs += iorw.o mei-objs += main.o +mei-objs += amthif.o mei-objs += wd.o diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c new file mode 100644 index 000..392203d --- /dev/null +++ b/drivers/misc/mei/amthif.c @@ -0,0 +1,572 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "mei_dev.h" +#include "hw.h" +#include +#include "interface.h" + +const uuid_le mei_amthi_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac, + 0xa8, 0x46, 0xe0, 0xff, 0x65, + 0x81, 0x4c); + +/** + * mei_amthif_reset_params - initializes mei device iamthif + * + * @dev: the device structure + */ +void mei_amthif_reset_params(struct mei_device *dev) +{ + /* reset iamthif parameters. */ + dev->iamthif_current_cb = NULL; + dev->iamthif_msg_buf_size = 0; + dev->iamthif_msg_buf_index = 0; + dev->iamthif_canceled = false; + dev->iamthif_ioctl = false; + dev->iamthif_state = MEI_IAMTHIF_IDLE; + dev->iamthif_timer = 0; +} + +/** + * mei_amthif_host_init_ - mei initialization amthif client. + * + * @dev: the device structure + * + */ +void mei_amthif_host_init(struct mei_device *dev) +{ + int i; + unsigned char *msg_buf; + + mei_cl_init(&dev->iamthif_cl, dev); + dev->iamthif_cl.state = MEI_FILE_DISCONNECTED; + + /* find ME amthi client */ + i = mei_me_cl_update_filext(dev, &dev->iamthif_cl, + &mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID); + if (i < 0) { + dev_dbg(&dev->pdev->dev, "failed to find iamthif client.\n"); + return; + } + + /* Assign iamthif_mtu to the value received from ME */ + + dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length; + dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n", + dev->me_clients[i].props.max_msg_length); + + kfree(dev->iamthif_msg_buf); + dev->iamthif_msg_buf = NULL; + + /* allocate storage for ME message buffer */ + msg_buf = kcalloc(dev->iamthif_mtu, + sizeof(unsigned char), GFP_KERNEL); + if (!msg_buf) { + dev_dbg(&dev->pdev->dev, "memory allocation for ME message buffer failed.\n"); + return; + } + + dev->iamthif_msg_buf = msg_buf; + + if (mei_connect(dev, &dev->iamthif_cl)) { + dev_dbg(&dev->pdev->dev, "Failed to connect to AMTHI client\n"); + dev->iamthif_cl.state = MEI_FILE_DISCONNECTED; + dev->iamthif_cl.host_client_id = 0; + } else { + dev->iamthif_cl.timer_count = MEI_CONNECT_TIMEOUT; + } +} + +/** + * mei_amthif_find_read_list_entry - finds a amthilist entry for current file + * + * @dev: the device structure + * @file: pointer to file object + * + * returns returned a list entry on success, NULL on failure. + */ +struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, + struct file *file) +{ + struct mei_cl *cl_temp; + struct mei_cl_cb *pos = NULL; + struct mei_cl_cb *next = NULL; + + list_for_each_entry_s
[3.10][PATCH 3/4] mei: me: fix waiting for hw ready
stable: 3.10 commit dab9bf41b23fe700c4a74133e41eb6a21706031e upstream 1. MEI_INTEROP_TIMEOUT is in seconds not in jiffies so we use mei_secs_to_jiffies macro While cold boot is fast this is relevant in resume 2. wait_event_interruptible_timeout can return with -ERESTARTSYS so do not override it with -ETIMEDOUT 3.Adjust error message Cc: sta...@vger.kernel.org Tested-by: Shuah Khan Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 10 +++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 0310859..700fe55 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -238,14 +238,18 @@ static int mei_me_hw_ready_wait(struct mei_device *dev) if (mei_me_hw_is_ready(dev)) return 0; + dev->recvd_hw_ready = false; mutex_unlock(&dev->device_lock); err = wait_event_interruptible_timeout(dev->wait_hw_ready, - dev->recvd_hw_ready, MEI_INTEROP_TIMEOUT); + dev->recvd_hw_ready, + mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); mutex_lock(&dev->device_lock); if (!err && !dev->recvd_hw_ready) { + if (!err) + err = -ETIMEDOUT; dev_err(&dev->pdev->dev, - "wait hw ready failed. status = 0x%x\n", err); - return -ETIMEDOUT; + "wait hw ready failed. status = %d\n", err); + return err; } dev->recvd_hw_ready = false; -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[3.10][PATCH 4/4] mei: me: fix hardware reset flow
stable: 3.10 commit ff96066e3171acdea356b331163495957cb833d0 char-misc Both H_IS and H_IE needs to be set to receive H_RDY interrupt 1. Assert H_IS to clear the interrupts during hw reset and use mei_me_reg_write instead of mei_hcsr_set as the later strips down the H_IS 2. fix interrupt disablement embarrassing typo hcsr |= ~H_IE -> hcsr &= ~H_IE; this will remove the unwanted interrupt on power down 3. remove useless debug print outs Cc: sta...@vger.kernel.org Cc: Shuah Khan Cc: Konstantin Khlebnikov Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman Conflicts: drivers/misc/mei/hw-me.c --- drivers/misc/mei/hw-me.c | 8 +++- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 700fe55..1bf3f8b 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -176,16 +176,14 @@ static void mei_me_hw_reset(struct mei_device *dev, bool intr_enable) struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr = mei_hcsr_read(hw); - dev_dbg(&dev->pdev->dev, "before reset HCSR = 0x%08x.\n", hcsr); - - hcsr |= (H_RST | H_IG); + hcsr |= H_RST | H_IG | H_IS; if (intr_enable) hcsr |= H_IE; else - hcsr |= ~H_IE; + hcsr &= ~H_IE; - mei_hcsr_set(hw, hcsr); + mei_me_reg_write(hw, H_CSR, hcsr); if (dev->dev_state == MEI_DEV_POWER_DOWN) mei_me_hw_reset_release(dev); -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[3.10][PATCH 1/4] mei: me: fix reset state machine
stable: 3.10 commit 315a383ad7dbd484fafb93ef08038e3dbafbb7a8 upstream ME HW ready bit is down after hw reset was asserted or on error. Only on error we need to enter the reset flow, additional reset need to be prevented when reset was triggered during initialization , power up/down or a reset is already in progress Cc: sta...@vger.kernel.org Tested-by: Shuah Khan Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 822170f..0310859 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -482,7 +482,9 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) /* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING && - dev->dev_state != MEI_DEV_INITIALIZING) { + dev->dev_state != MEI_DEV_INITIALIZING && + dev->dev_state != MEI_DEV_POWER_DOWN && + dev->dev_state != MEI_DEV_POWER_UP) { dev_dbg(&dev->pdev->dev, "FW not ready.\n"); mei_reset(dev, 1); mutex_unlock(&dev->device_lock); -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[3.10][PATCH 2/4] mei: don't have to clean the state on power up
stable: 3.10 commit 99f22c4ef24cf87b0dae6aabe6b5e620b62961d9 upstream When powering up, we don't have to clean up the device state nothing is connected. Cc: sta...@vger.kernel.org Tested-by: Shuah Khan Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index f580d30..6eec689 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -143,7 +143,8 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev->hbm_state = MEI_HBM_IDLE; - if (dev->dev_state != MEI_DEV_INITIALIZING) { + if (dev->dev_state != MEI_DEV_INITIALIZING && + dev->dev_state != MEI_DEV_POWER_UP) { if (dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN) dev->dev_state = MEI_DEV_RESETTING; -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[3.10][PATCH 0/4] resume reset fixes for stable 3.10
Tomas Winkler (4): mei: me: fix reset state machine mei: don't have to clean the state on power up mei: me: fix waiting for hw ready mei: me: fix hardware reset flow drivers/misc/mei/hw-me.c | 22 +- drivers/misc/mei/init.c | 3 ++- 2 files changed, 15 insertions(+), 10 deletions(-) -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 1/2] mei: mei_release: drop redundant check if cb is NULL
mei_io_cb_free follows kfree design and check for NULL internally Signed-off-by: Tomas Winkler --- drivers/misc/mei/main.c | 5 + 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index b8f8604..c71420e 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -165,10 +165,7 @@ static int mei_release(struct inode *inode, struct file *file) file->private_data = NULL; - if (cb) { - mei_io_cb_free(cb); - cb = NULL; - } + mei_io_cb_free(cb); kfree(cl); out: -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 2/2] mei: prefix client log messages with client me and host ids
From: Alexander Usyskin define cl_dbg and cl_err macros that add me and host id prefix for debug and error log messages so we can track for the client context of the flow. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler --- drivers/misc/mei/client.c| 43 ++- drivers/misc/mei/client.h| 9 + drivers/misc/mei/interrupt.c | 18 +++--- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e0684b4..77b8d9b 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -187,10 +187,14 @@ int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) */ int mei_cl_flush_queues(struct mei_cl *cl) { + struct mei_device *dev; + if (WARN_ON(!cl || !cl->dev)) return -EINVAL; - dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n"); + dev = cl->dev; + + cl_dbg(dev, cl, "remove list entry belonging to cl\n"); mei_io_list_flush(&cl->dev->read_list, cl); mei_io_list_flush(&cl->dev->write_list, cl); mei_io_list_flush(&cl->dev->write_waiting_list, cl); @@ -296,7 +300,7 @@ int mei_cl_link(struct mei_cl *cl, int id) cl->state = MEI_FILE_INITIALIZING; - dev_dbg(&dev->pdev->dev, "link cl host id = %d\n", cl->host_client_id); + cl_dbg(dev, cl, "link cl\n"); return 0; } @@ -322,7 +326,7 @@ int mei_cl_unlink(struct mei_cl *cl) list_for_each_entry_safe(pos, next, &dev->file_list, link) { if (cl->host_client_id == pos->host_client_id) { - dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n", + cl_dbg(dev, cl, "remove host client = %d, ME client = %d\n", pos->host_client_id, pos->me_client_id); list_del_init(&pos->link); break; @@ -390,6 +394,8 @@ int mei_cl_disconnect(struct mei_cl *cl) dev = cl->dev; + cl_dbg(dev, cl, "disconnecting"); + if (cl->state != MEI_FILE_DISCONNECTING) return 0; @@ -402,13 +408,13 @@ int mei_cl_disconnect(struct mei_cl *cl) dev->hbuf_is_ready = false; if (mei_hbm_cl_disconnect_req(dev, cl)) { rets = -ENODEV; - dev_err(&dev->pdev->dev, "failed to disconnect.\n"); + cl_err(dev, cl, "failed to disconnect.\n"); goto free; } mdelay(10); /* Wait for hardware disconnection ready */ list_add_tail(&cb->list, &dev->ctrl_rd_list.list); } else { - dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n"); + cl_dbg(dev, cl, "add disconnect cb to control write list\n"); list_add_tail(&cb->list, &dev->ctrl_wr_list.list); } @@ -421,18 +427,17 @@ int mei_cl_disconnect(struct mei_cl *cl) mutex_lock(&dev->device_lock); if (MEI_FILE_DISCONNECTED == cl->state) { rets = 0; - dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n"); + cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); } else { rets = -ENODEV; if (MEI_FILE_DISCONNECTED != cl->state) - dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n"); + cl_err(dev, cl, "wrong status client disconnect.\n"); if (err) - dev_dbg(&dev->pdev->dev, - "wait failed disconnect err=%08x\n", + cl_dbg(dev, cl, "wait failed disconnect err=%08x\n", err); - dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n"); + cl_err(dev, cl, "failed to disconnect from FW client.\n"); } mei_io_list_flush(&dev->ctrl_rd_list, cl); @@ -639,13 +644,12 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) return -ENODEV; if (cl->read_cb) { - dev_dbg(&dev->pdev->dev, "read is pending.\n"); + cl_dbg(dev, cl, "read is pending.\n"); return -EBUSY; } i = mei_me_cl_by_id(dev, cl->me_client_id); if (i < 0) { - dev_err(&dev->pdev->dev, &
[char-misc-next 6/7] mei: revamp open handler counts
Make open counter to be incremented and decremented from mei_cl_link and mei_cl_unlik function respectively Nfc was assuming symmetric linking API and thus open handler count was never decreased. This patch fixes that. We need to add separate open hander count for amthif which is handled out of link/unlink functions and doesn't break the symmetric API. Last we do not waste clients slots if amthif or wd are not present in the device. we don't need to allocates slots ahead it is all covered by link/unlink before the devices is responding to user space connection and thus not racing on allocation Signed-off-by: Tomas Winkler --- drivers/misc/mei/amthif.c | 5 +++-- drivers/misc/mei/client.c | 19 +++ drivers/misc/mei/init.c| 5 - drivers/misc/mei/main.c| 6 +- drivers/misc/mei/mei_dev.h | 1 + 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 226c3f3..4f259d4 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -58,6 +58,7 @@ void mei_amthif_reset_params(struct mei_device *dev) dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; dev->iamthif_stall_timer = 0; + dev->iamthif_open_count = 0; } /** @@ -731,8 +732,8 @@ static bool mei_clear_lists(struct mei_device *dev, struct file *file) */ int mei_amthif_release(struct mei_device *dev, struct file *file) { - if (dev->open_handle_count > 0) - dev->open_handle_count--; + if (dev->iamthif_open_count > 0) + dev->iamthif_open_count--; if (dev->iamthif_file_object == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index f62bf76..37ed142 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -275,6 +275,7 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) int mei_cl_link(struct mei_cl *cl, int id) { struct mei_device *dev; + long open_handle_count; if (WARN_ON(!cl || !cl->dev)) return -EINVAL; @@ -291,7 +292,8 @@ int mei_cl_link(struct mei_cl *cl, int id) return -EMFILE; } - if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { + open_handle_count = dev->open_handle_count + dev->iamthif_open_count; + if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { dev_err(&dev->pdev->dev, "open_handle_count exceded %d", MEI_MAX_OPEN_HANDLE_COUNT); return -EMFILE; @@ -331,6 +333,17 @@ int mei_cl_unlink(struct mei_cl *cl) cl_dbg(dev, cl, "unlink client"); + if (dev->open_handle_count > 0) + dev->open_handle_count--; + + /* never clear the 0 bit */ + if (cl->host_client_id) + clear_bit(cl->host_client_id, dev->host_clients_map); + + list_del_init(&cl->link); + + cl->state = MEI_FILE_INITIALIZING; + list_del_init(&cl->link); return 0; @@ -352,10 +365,8 @@ void mei_host_client_init(struct work_struct *work) /* * Reserving the first three client IDs * 0: Reserved for MEI Bus Message communications -* 1: Reserved for Watchdog -* 2: Reserved for AMTHI */ - bitmap_set(dev->host_clients_map, 0, 3); + bitmap_set(dev->host_clients_map, 0, 1); for (i = 0; i < dev->me_clients_num; i++) { client_props = &dev->me_clients[i].props; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index eca24df..bcb4a6b 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -166,12 +166,7 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) /* remove entry if already in list */ dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); - if (dev->open_handle_count > 0) - dev->open_handle_count--; mei_cl_unlink(&dev->iamthif_cl); - if (dev->open_handle_count > 0) - dev->open_handle_count--; - mei_amthif_reset_params(dev); memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 87ab5ca..9661a81 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -141,10 +141,6 @@ static int mei_release(struct inode *inode, struct file *file) cl->host_client_id, cl->me_client_id); - if (dev->open_handle_count > 0) { - clear_bit(cl->host_
[char-misc-next 7/7] mei: amthif: mei_amthif_host_init: propagate errors from called functions
propagate error codes from called functions, they are correct. Signed-off-by: Tomas Winkler --- drivers/misc/mei/amthif.c | 11 +++ 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 4f259d4..d22c686 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -79,8 +79,10 @@ int mei_amthif_host_init(struct mei_device *dev) i = mei_me_cl_by_uuid(dev, &mei_amthif_guid); if (i < 0) { - dev_info(&dev->pdev->dev, "amthif: failed to find the client\n"); - return -ENOENT; + ret = i; + dev_info(&dev->pdev->dev, + "amthif: failed to find the client %d\n", ret); + return ret; } cl->me_client_id = dev->me_clients[i].client_id; @@ -107,8 +109,9 @@ int mei_amthif_host_init(struct mei_device *dev) ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID); if (ret < 0) { - dev_err(&dev->pdev->dev, "amthif: failed link client\n"); - return -ENOENT; + dev_err(&dev->pdev->dev, + "amthif: failed link client %d\n", ret); + return ret; } cl->state = MEI_FILE_CONNECTING; -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 0/7] mei minor fixes and cleanups
Alexander Usyskin (1): mei: fix function names in debug prints Tomas Winkler (6): mei: propagate error from write routines instead of ENODEV mei: push credentials inside the irq write handler mei: mei_cl_unlink: no need to loop over dev list mei: simplify mei_open error handling mei: revamp open handler counts mei: amthif: mei_amthif_host_init: propagate errors from called functions drivers/misc/mei/amthif.c| 49 +-- drivers/misc/mei/client.c| 81 +++- drivers/misc/mei/hbm.c | 2 +- drivers/misc/mei/init.c | 5 --- drivers/misc/mei/interrupt.c | 31 + drivers/misc/mei/main.c | 37 drivers/misc/mei/mei_dev.h | 1 + 7 files changed, 122 insertions(+), 84 deletions(-) -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 2/7] mei: propagate error from write routines instead of ENODEV
ENODEV will cause application to try to reconnect since it assumes that device went through the reset write errors are not always fatal it can happen due to resource contention Signed-off-by: Tomas Winkler --- drivers/misc/mei/amthif.c| 24 +--- drivers/misc/mei/client.c| 17 + drivers/misc/mei/hbm.c | 2 +- drivers/misc/mei/interrupt.c | 20 +--- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index f6ff711..e4a4e2e 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -313,13 +313,13 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) mei_hdr.me_addr = dev->iamthif_cl.me_client_id; mei_hdr.reserved = 0; dev->iamthif_msg_buf_index += mei_hdr.length; - if (mei_write_message(dev, &mei_hdr, - (unsigned char *)dev->iamthif_msg_buf)) - return -ENODEV; + ret = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf); + if (ret) + return ret; if (mei_hdr.msg_complete) { if (mei_cl_flow_ctrl_reduce(&dev->iamthif_cl)) - return -ENODEV; + return -EIO; dev->iamthif_flow_control_pending = true; dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; dev_dbg(&dev->pdev->dev, "add amthif cb to write waiting list\n"); @@ -459,6 +459,7 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_msg_hdr mei_hdr; size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; u32 msg_slots = mei_data2slots(len); + int rets; mei_hdr.host_addr = cl->host_client_id; mei_hdr.me_addr = cl->me_client_id; @@ -481,16 +482,17 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); *slots -= msg_slots; - if (mei_write_message(dev, &mei_hdr, - dev->iamthif_msg_buf + dev->iamthif_msg_buf_index)) { - dev->iamthif_state = MEI_IAMTHIF_IDLE; - cl->status = -ENODEV; - list_del(&cb->list); - return -ENODEV; + rets = mei_write_message(dev, &mei_hdr, + dev->iamthif_msg_buf + dev->iamthif_msg_buf_index); + if (rets) { + dev->iamthif_state = MEI_IAMTHIF_IDLE; + cl->status = rets; + list_del(&cb->list); + return rets; } if (mei_cl_flow_ctrl_reduce(cl)) - return -ENODEV; + return -EIO; dev->iamthif_msg_buf_index += mei_hdr.length; cl->status = 0; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 77b8d9b..c59c8a2 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -700,6 +700,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_msg_hdr mei_hdr; size_t len = cb->request_buffer.size - cb->buf_idx; u32 msg_slots = mei_data2slots(len); + int rets; mei_hdr.host_addr = cl->host_client_id; mei_hdr.me_addr = cl->me_client_id; @@ -723,11 +724,12 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, cb->request_buffer.size, cb->buf_idx); *slots -= msg_slots; - if (mei_write_message(dev, &mei_hdr, - cb->request_buffer.data + cb->buf_idx)) { - cl->status = -ENODEV; + rets = mei_write_message(dev, &mei_hdr, + cb->request_buffer.data + cb->buf_idx); + if (rets) { + cl->status = rets; list_move_tail(&cb->list, &cmpl_list->list); - return -ENODEV; + return rets; } cl->status = 0; @@ -736,7 +738,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, if (mei_hdr.msg_complete) { if (mei_cl_flow_ctrl_reduce(cl)) - return -ENODEV; + return -EIO; list_move_tail(&cb->list, &dev->write_waiting_list.list); } @@ -805,10 +807,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) mei_hdr.reserved = 0; - if (mei_write_message(dev, &mei_hdr, buf->data)) { - rets = -EIO; + rets = mei_write_message(dev
[char-misc-next 1/7] mei: fix function names in debug prints
From: Alexander Usyskin Fix calling function names in debug prints. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler --- drivers/misc/mei/interrupt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index d27804e..be42c70 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -343,14 +343,14 @@ int mei_irq_read_handler(struct mei_device *dev, /* decide where to read the message too */ if (!mei_hdr->host_addr) { - dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n"); + dev_dbg(&dev->pdev->dev, "call mei_hbm_dispatch.\n"); mei_hbm_dispatch(dev, mei_hdr); - dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n"); + dev_dbg(&dev->pdev->dev, "end mei_hbm_dispatch.\n"); } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id && (MEI_FILE_CONNECTED == dev->iamthif_cl.state) && (dev->iamthif_state == MEI_IAMTHIF_READING)) { - dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n"); + dev_dbg(&dev->pdev->dev, "call mei_amthif_irq_read_msg.\n"); dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list); -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 5/7] mei: simplify mei_open error handling
1. Perform simple checks first and only then attempt to allocate cl structure. 2. Remove open_handler_count test, this is already checked in mei_cl_link function 3. return -EMFILE instead of -ENOENT as expected by user space Signed-off-by: Tomas Winkler --- drivers/misc/mei/client.c | 8 +++- drivers/misc/mei/main.c | 31 ++- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 653b318..f62bf76 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -288,7 +288,13 @@ int mei_cl_link(struct mei_cl *cl, int id) if (id >= MEI_CLIENTS_MAX) { dev_err(&dev->pdev->dev, "id exceded %d", MEI_CLIENTS_MAX) ; - return -ENOENT; + return -EMFILE; + } + + if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { + dev_err(&dev->pdev->dev, "open_handle_count exceded %d", + MEI_MAX_OPEN_HANDLE_COUNT); + return -EMFILE; } dev->open_handle_count++; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index c71420e..87ab5ca 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -60,48 +60,45 @@ static int mei_open(struct inode *inode, struct file *file) int err; - err = -ENODEV; if (!misc->parent) - goto out; + return -ENODEV; pdev = container_of(misc->parent, struct pci_dev, dev); dev = pci_get_drvdata(pdev); if (!dev) - goto out; + return -ENODEV; mutex_lock(&dev->device_lock); - err = -ENOMEM; - cl = mei_cl_allocate(dev); - if (!cl) - goto out_unlock; + + cl = NULL; err = -ENODEV; if (dev->dev_state != MEI_DEV_ENABLED) { dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); - goto out_unlock; - } - err = -EMFILE; - if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { - dev_err(&dev->pdev->dev, "open_handle_count exceded %d", - MEI_MAX_OPEN_HANDLE_COUNT); - goto out_unlock; + goto err_unlock; } + err = -ENOMEM; + cl = mei_cl_allocate(dev); + if (!cl) + goto err_unlock; + + /* open_handle_count check is handled in the mei_cl_link */ err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); if (err) - goto out_unlock; + goto err_unlock; file->private_data = cl; + mutex_unlock(&dev->device_lock); return nonseekable_open(inode, file); -out_unlock: +err_unlock: mutex_unlock(&dev->device_lock); kfree(cl); -out: return err; } -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 3/7] mei: push credentials inside the irq write handler
this eventually allows as use a single write queue both for control and data messages and removing possible race Signed-off-by: Tomas Winkler --- drivers/misc/mei/amthif.c| 9 + drivers/misc/mei/client.c| 30 +- drivers/misc/mei/interrupt.c | 5 - 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index e4a4e2e..226c3f3 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -461,6 +461,15 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, u32 msg_slots = mei_data2slots(len); int rets; + rets = mei_cl_flow_ctrl_creds(cl); + if (rets < 0) + return rets; + + if (rets == 0) { + cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); + return 0; + } + mei_hdr.host_addr = cl->host_client_id; mei_hdr.me_addr = cl->me_client_id; mei_hdr.reserved = 0; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index c59c8a2..88bcde4 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -696,12 +696,33 @@ err: int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, s32 *slots, struct mei_cl_cb *cmpl_list) { - struct mei_device *dev = cl->dev; + struct mei_device *dev; + struct mei_msg_data *buf; struct mei_msg_hdr mei_hdr; - size_t len = cb->request_buffer.size - cb->buf_idx; - u32 msg_slots = mei_data2slots(len); + size_t len; + u32 msg_slots; int rets; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + buf = &cb->request_buffer; + + rets = mei_cl_flow_ctrl_creds(cl); + if (rets < 0) + return rets; + + if (rets == 0) { + cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); + return 0; + } + + len = buf->size - cb->buf_idx; + msg_slots = mei_data2slots(len); + mei_hdr.host_addr = cl->host_client_id; mei_hdr.me_addr = cl->me_client_id; mei_hdr.reserved = 0; @@ -724,8 +745,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, cb->request_buffer.size, cb->buf_idx); *slots -= msg_slots; - rets = mei_write_message(dev, &mei_hdr, - cb->request_buffer.data + cb->buf_idx); + rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); if (rets) { cl->status = rets; list_move_tail(&cb->list, &cmpl_list->list); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index e4bb9ae..7a95c07 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -513,11 +513,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) cl = cb->cl; if (cl == NULL) continue; - if (mei_cl_flow_ctrl_creds(cl) <= 0) { - cl_dbg(dev, cl, "No flow control credentials, not sending.\n"); - continue; - } - if (cl == &dev->iamthif_cl) ret = mei_amthif_irq_write_complete(cl, cb, &slots, cmpl_list); -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 4/7] mei: mei_cl_unlink: no need to loop over dev list
we can call list_del_init regardless the client is linked or not it is always properly initialized Signed-off-by: Tomas Winkler --- drivers/misc/mei/client.c | 13 - 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 88bcde4..653b318 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -312,7 +312,6 @@ int mei_cl_link(struct mei_cl *cl, int id) int mei_cl_unlink(struct mei_cl *cl) { struct mei_device *dev; - struct mei_cl *pos, *next; /* don't shout on error exit path */ if (!cl) @@ -324,14 +323,10 @@ int mei_cl_unlink(struct mei_cl *cl) dev = cl->dev; - list_for_each_entry_safe(pos, next, &dev->file_list, link) { - if (cl->host_client_id == pos->host_client_id) { - cl_dbg(dev, cl, "remove host client = %d, ME client = %d\n", - pos->host_client_id, pos->me_client_id); - list_del_init(&pos->link); - break; - } - } + cl_dbg(dev, cl, "unlink client"); + + list_del_init(&cl->link); + return 0; } -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next] hpet: remove useless check if fixmem32 is NULL
fixmem32 is assigned to address of res->data member so the address is always valid Actually since we are not checking for res != NULL static analyzing is complaining about referencing the pointer and consequent check for null. The code snippet looks confusing also for human eyes. Cc: Randy Dunlap Signed-off-by: Tomas Winkler --- drivers/char/hpet.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 448ce5e..c8f4329 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -971,8 +971,6 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) struct acpi_resource_fixed_memory32 *fixmem32; fixmem32 = &res->data.fixed_memory32; - if (!fixmem32) - return AE_NO_MEMORY; hdp->hd_phys_address = fixmem32->address; hdp->hd_address = ioremap(fixmem32->address, -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 1/5] mei: mei_cl_link protect open_handle_count from overflow
mei_cl_link is called both from mei_open and also from in-kernel drivers so we need to protect open_handle_count from overflow Signed-off-by: Tomas Winkler --- drivers/misc/mei/client.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e0684b4..a82b443 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -287,6 +287,12 @@ int mei_cl_link(struct mei_cl *cl, int id) return -ENOENT; } + if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { + dev_err(&dev->pdev->dev, "open_handle_count exceded %d", + MEI_MAX_OPEN_HANDLE_COUNT); + return -ENOENT; + } + dev->open_handle_count++; cl->host_client_id = id; -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 4/5] mei: fix format compilation warrning on 32 bit architecture
hbm.c: In function mei_hbm_me_cl_allocate: hbm.c:52:212: warning: format %zd expects argument of type signed size_t but argument 4 has type long unsigned Signed-off-by: Tomas Winkler --- drivers/misc/mei/hbm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 95d4dab..f706fe8 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -45,7 +45,7 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev) kfree(dev->me_clients); dev->me_clients = NULL; - dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n", + dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n", dev->me_clients_num * sizeof(struct mei_me_client)); /* allocate storage for ME clients representation */ clients = kcalloc(dev->me_clients_num, -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 3/5] mei: mei_write correct checks for copy_from_user
From: Alexander Usyskin 1. return -EFUALT when copy_from_user fails 2. display error message on failure in error level Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler --- drivers/misc/mei/main.c | 7 +-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 173ff09..5ff810b 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -404,8 +404,11 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); - if (rets) + if (rets) { + dev_err(&dev->pdev->dev, "failed to copy data from userland\n"); + rets = -EFAULT; goto out; + } if (cl == &dev->iamthif_cl) { rets = mei_amthif_write(dev, write_cb); @@ -567,7 +570,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) dev_dbg(&dev->pdev->dev, "copy connect data from user\n"); if (copy_from_user(connect_data, (char __user *)data, sizeof(struct mei_connect_client_data))) { - dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n"); + dev_err(&dev->pdev->dev, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 5/5] mei: revamp read and write length checks
1. Return zero on zero length read and writes 2. For a too large write return -EFBIG as defined in man write(2) EFBIG An attempt was made to write a file that exceeds the implementation-defined maximum file size or the process's file size limit, or to write at a position past the maximum allowed offset. Signed-off-by: Tomas Winkler --- drivers/misc/mei/main.c | 16 ++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 5ff810b..7404584 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -203,12 +203,18 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, dev = cl->dev; + mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { rets = -ENODEV; goto out; } + if (length == 0) { + rets = 0; + goto out; + } + if (cl == &dev->iamthif_cl) { rets = mei_amthif_read(dev, file, ubuf, length, offset); goto out; @@ -350,8 +356,14 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = -ENODEV; goto out; } - if (length > dev->me_clients[id].props.max_msg_length || length <= 0) { - rets = -EMSGSIZE; + + if (length == 0) { + rets = 0; + goto out; + } + + if (length > dev->me_clients[id].props.max_msg_length) { + rets = -EFBIG; goto out; } -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 2/5] mei: make sure that me_clients_map big enough before copying
To make static analyzers happy validated that sizeof me_clients_map is larger than sizeof valid_addresses from the enumeration response before memcpy We can use BUILD_ON macro as both arrays are defined statically Signed-off-by: Tomas Winkler --- drivers/misc/mei/hbm.c | 5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 6127ab6..95d4dab 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -673,7 +673,10 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) case HOST_ENUM_RES_CMD: enum_res = (struct hbm_host_enum_response *) mei_msg; - memcpy(dev->me_clients_map, enum_res->valid_addresses, 32); + BUILD_BUG_ON(sizeof(dev->me_clients_map) + < sizeof(enum_res->valid_addresses)); + memcpy(dev->me_clients_map, enum_res->valid_addresses, + sizeof(enum_res->valid_addresses)); if (dev->dev_state == MEI_DEV_INIT_CLIENTS && dev->hbm_state == MEI_HBM_ENUM_CLIENTS) { dev->init_clients_timer = 0; -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 0/5] mei driver small fixes and cleanups
Alexander Usyskin (1): mei: mei_write correct checks for copy_from_user Tomas Winkler (4): mei: mei_cl_link protect open_handle_count from overflow mei: make sure that me_clients_map big enough before copying mei: fix format compilation warrning on 32 bit architecture mei: revamp read and write length checks drivers/misc/mei/client.c | 6 ++ drivers/misc/mei/hbm.c| 7 +-- drivers/misc/mei/main.c | 23 +++ 3 files changed, 30 insertions(+), 6 deletions(-) -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 0/3] mei: few more reset recursion fixes
*** BLURB HERE *** Alexander Usyskin (1): mei: cancel stall timers in mei_reset Tomas Winkler (2): mei: make me client counters less error prone mei: bus: stop wait for read during cl state transition drivers/misc/mei/amthif.c | 1 + drivers/misc/mei/bus.c | 5 - drivers/misc/mei/client.h | 6 ++ drivers/misc/mei/hbm.c | 10 ++ drivers/misc/mei/init.c| 3 +++ drivers/misc/mei/main.c| 11 --- drivers/misc/mei/mei_dev.h | 6 +++--- 7 files changed, 27 insertions(+), 15 deletions(-) -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 3/3] mei: cancel stall timers in mei_reset
From: Alexander Usyskin Unset init_clients_timer and amthif_stall_timers in mei_reset in order to cancel timer ticking and hence avoid recursive reset calls. Cc: # 3.9+ Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler --- drivers/misc/mei/amthif.c | 1 + drivers/misc/mei/init.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index d0fdc13..f6ff711 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -57,6 +57,7 @@ void mei_amthif_reset_params(struct mei_device *dev) dev->iamthif_ioctl = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; + dev->iamthif_stall_timer = 0; } /** diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 92c7311..6197018 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -175,6 +175,9 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); } + /* we're already in reset, cancel the init timer */ + dev->init_clients_timer = 0; + dev->me_clients_num = 0; dev->rd_msg_hdr = 0; dev->wd_pending = false; -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 1/3] mei: make me client counters less error prone
1. u8 counters are prone to hard to detect overflow: make them unsigned long to match bit_ functions argument type 2. don't check me_clients_num for negativity, it is unsigned. 3. init all the me client counters from one place Cc: # 3.9+ Signed-off-by: Tomas Winkler --- drivers/misc/mei/hbm.c | 10 ++ drivers/misc/mei/mei_dev.h | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index f706fe8..52afe1e 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -35,11 +35,15 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev) struct mei_me_client *clients; int b; + dev->me_clients_num = 0; + dev->me_client_presentation_num = 0; + dev->me_client_index = 0; + /* count how many ME clients we have */ for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX) dev->me_clients_num++; - if (dev->me_clients_num <= 0) + if (dev->me_clients_num == 0) return; kfree(dev->me_clients); @@ -221,7 +225,7 @@ static int mei_hbm_prop_req(struct mei_device *dev) struct hbm_props_request *prop_req; const size_t len = sizeof(struct hbm_props_request); unsigned long next_client_index; - u8 client_num; + unsigned long client_num; client_num = dev->me_client_presentation_num; @@ -680,8 +684,6 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) if (dev->dev_state == MEI_DEV_INIT_CLIENTS && dev->hbm_state == MEI_HBM_ENUM_CLIENTS) { dev->init_clients_timer = 0; - dev->me_client_presentation_num = 0; - dev->me_client_index = 0; mei_hbm_me_cl_allocate(dev); dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 7b918b2..456b322 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -396,9 +396,9 @@ struct mei_device { struct mei_me_client *me_clients; /* Note: memory has to be allocated */ DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX); DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX); - u8 me_clients_num; - u8 me_client_presentation_num; - u8 me_client_index; + unsigned long me_clients_num; + unsigned long me_client_presentation_num; + unsigned long me_client_index; struct mei_cl wd_cl; enum mei_wd_states wd_state; -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 2/3] mei: bus: stop wait for read during cl state transition
Bus layer omitted check for client state transition while waiting for read completion The client state transition may occur for example as result of firmware initiated reset Add mei_cl_is_transitioning wrapper to reduce the code repetition.: Cc: # 3.9+ Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c| 5 - drivers/misc/mei/client.h | 6 ++ drivers/misc/mei/main.c | 11 --- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index a150a42..f5dabb3 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -295,10 +295,13 @@ int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) if (cl->reading_state != MEI_READ_COMPLETE && !waitqueue_active(&cl->rx_wait)) { + mutex_unlock(&dev->device_lock); if (wait_event_interruptible(cl->rx_wait, - (MEI_READ_COMPLETE == cl->reading_state))) { + cl->reading_state == MEI_READ_COMPLETE || + mei_cl_is_transitioning(cl))) { + if (signal_pending(current)) return -EINTR; return -ERESTARTSYS; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 9eb031e..892cc42 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -90,6 +90,12 @@ static inline bool mei_cl_is_connected(struct mei_cl *cl) cl->dev->dev_state == MEI_DEV_ENABLED && cl->state == MEI_FILE_CONNECTED); } +static inline bool mei_cl_is_transitioning(struct mei_cl *cl) +{ + return (MEI_FILE_INITIALIZING == cl->state || + MEI_FILE_DISCONNECTED == cl->state || + MEI_FILE_DISCONNECTING == cl->state); +} bool mei_cl_is_other_connecting(struct mei_cl *cl); int mei_cl_disconnect(struct mei_cl *cl); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 7404584..b8f8604 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -255,19 +255,16 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, mutex_unlock(&dev->device_lock); if (wait_event_interruptible(cl->rx_wait, - (MEI_READ_COMPLETE == cl->reading_state || -MEI_FILE_INITIALIZING == cl->state || -MEI_FILE_DISCONNECTED == cl->state || -MEI_FILE_DISCONNECTING == cl->state))) { + MEI_READ_COMPLETE == cl->reading_state || + mei_cl_is_transitioning(cl))) { + if (signal_pending(current)) return -EINTR; return -ERESTARTSYS; } mutex_lock(&dev->device_lock); - if (MEI_FILE_INITIALIZING == cl->state || - MEI_FILE_DISCONNECTED == cl->state || - MEI_FILE_DISCONNECTING == cl->state) { + if (mei_cl_is_transitioning(cl)) { rets = -EBUSY; goto out; } -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
drivers/char/hpet.c check
In hpet_resources function Can anybody explain why we check fixmem32 for NULL, looks strange as it derreferences a member address I'm asking since a static analyzer is complaining about it. } else if (res->id == ACPI_RSTYPE_FIXED_MEM32) { struct acpi_resource_fixed_mem32 *fixmem32; fixmem32 = &res->data.fixed_memory32; if (!fixmem32) ^^ return -EINVAL; Thanks Tomas -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
drivers/char/hpet.c ACPI parsing
In hpet_resources function Can anybody explain why we check fixmem32 for NULL, looks strange as it derreferences a member address I'm asking since a static analyzer is complaining about it. } else if (res->id == ACPI_RSTYPE_FIXED_MEM32) { struct acpi_resource_fixed_mem32 *fixmem32; fixmem32 = &res->data.fixed_memory32; if (!fixmem32) ^^ return -EINVAL; Thanks Tomas -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v2] mei: me: downgrade two errors to debug level
On Thu, Sep 19, 2013 at 1:17 PM, Paul Bolle wrote: > The mei_me driver prints "suspend" at error level at each suspend. It > also prints "stop" at error level at driver unload. Downgrade these > uninteresting messages to debug level. > > Signed-off-by: Paul Bolle > --- > 0) v1 was called "mei: me: downgrade "suspend" message to debug level". > v2 also downgrades the "stop" message. > > 1) Added Adam and Joe to Cc:. They were involved in discussing a patch > by Adam, which was similar to my v1, about a week ago. > > 2) Still lightly tested only, but now on top of v3.11.1. > > drivers/misc/mei/pci-me.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c > index 1b3844e..897ee6b 100644 > --- a/drivers/misc/mei/pci-me.c > +++ b/drivers/misc/mei/pci-me.c > @@ -231,7 +231,7 @@ static void mei_me_remove(struct pci_dev *pdev) > hw = to_me_hw(dev); > > > - dev_err(&pdev->dev, "stop\n"); > + dev_dbg(&pdev->dev, "stop\n"); > mei_stop(dev); > > /* disable interrupts */ > @@ -262,7 +262,7 @@ static int mei_me_pci_suspend(struct device *device) > if (!dev) > return -ENODEV; > > - dev_err(&pdev->dev, "suspend\n"); > + dev_dbg(&pdev->dev, "suspend\n"); > > mei_stop(dev); > > -- > 1.8.1.4 Ack -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 1/3] mei: add error messages for open count errors
Signed-off-by: Tomas Winkler --- drivers/misc/mei/main.c | 10 -- 1 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index ae2cd0d..e8b0858 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -239,12 +239,18 @@ static int mei_open(struct inode *inode, struct file *file) goto out_unlock; } err = -EMFILE; - if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) + if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { + dev_err(&dev->pdev->dev, "open_handle_count exceded %d", + MEI_MAX_OPEN_HANDLE_COUNT); goto out_unlock; + } cl_id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX); - if (cl_id >= MEI_CLIENTS_MAX) + if (cl_id >= MEI_CLIENTS_MAX) { + dev_err(&dev->pdev->dev, "client_id exceded %d", + MEI_CLIENTS_MAX) ; goto out_unlock; + } cl->host_client_id = cl_id; -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 2/3] mei: struct mei_message_data doesn't have to be packed
Signed-off-by: Tomas Winkler --- drivers/misc/mei/mei_dev.h |2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 96d3e79..adb35fb 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -140,7 +140,7 @@ enum mei_cb_major_types { struct mei_message_data { u32 size; unsigned char *data; -} __packed; +}; struct mei_cl_cb { -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 3/3] mei: don't print buffer as a string
non readable junk was printed to the logs we will add proper buffer dumping mechanism later if needed Signed-off-by: Tomas Winkler --- drivers/misc/mei/interrupt.c |9 + 1 files changed, 1 insertions(+), 8 deletions(-) diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 0900a71..3533edd 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -221,17 +221,10 @@ static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list, cl->status = 0; list_del(&cb_pos->cb_list); dev_dbg(&dev->pdev->dev, - "completed read host client = %d," - "ME client = %d, " - "data length = %lu\n", + "completed read H cl = %d, ME cl = %d, length = %lu\n", cl->host_client_id, cl->me_client_id, cb_pos->information); - - *(cb_pos->response_buffer.data + - cb_pos->information) = '\0'; - dev_dbg(&dev->pdev->dev, "cb_pos->res_buffer - %s\n", - cb_pos->response_buffer.data); list_add_tail(&cb_pos->cb_list, &complete_list->mei_cb.cb_list); } -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 1/6] mei: rename mei_cl_cb.information to mei_cl_cb.buf_idx
rename 'information' member of the struct mei_cl_cb to more self-descriptive 'buf_idx' Signed-off-by: Tomas Winkler --- drivers/misc/mei/interrupt.c | 43 - drivers/misc/mei/iorw.c | 17 +++ drivers/misc/mei/main.c | 33 ++- drivers/misc/mei/mei_dev.h |2 +- 4 files changed, 45 insertions(+), 50 deletions(-) diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 3533edd..320ebd2 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -149,7 +149,7 @@ static int mei_irq_thread_read_amthi_message(struct mei_io_list *complete_list, return -ENODEV; dev->iamthif_stall_timer = 0; - cb->information = dev->iamthif_msg_buf_index; + cb->buf_idx = dev->iamthif_msg_buf_index; cb->read_time = jiffies; if (dev->iamthif_ioctl && cl == &dev->iamthif_cl) { /* found the iamthif cb */ @@ -205,10 +205,10 @@ static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list, cl = (struct mei_cl *)cb_pos->file_private; if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) { cl->reading_state = MEI_READING; - buffer = cb_pos->response_buffer.data + cb_pos->information; + buffer = cb_pos->response_buffer.data + cb_pos->buf_idx; if (cb_pos->response_buffer.size < - mei_hdr->length + cb_pos->information) { + mei_hdr->length + cb_pos->buf_idx) { dev_dbg(&dev->pdev->dev, "message overflow.\n"); list_del(&cb_pos->cb_list); return -ENOMEM; @@ -216,7 +216,7 @@ static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list, if (buffer) mei_read_slots(dev, buffer, mei_hdr->length); - cb_pos->information += mei_hdr->length; + cb_pos->buf_idx += mei_hdr->length; if (mei_hdr->msg_complete) { cl->status = 0; list_del(&cb_pos->cb_list); @@ -224,7 +224,8 @@ static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list, "completed read H cl = %d, ME cl = %d, length = %lu\n", cl->host_client_id, cl->me_client_id, - cb_pos->information); + cb_pos->buf_idx); + list_add_tail(&cb_pos->cb_list, &complete_list->mei_cb.cb_list); } @@ -300,14 +301,14 @@ static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots, if (mei_disconnect(dev, cl)) { cl->status = 0; - cb_pos->information = 0; + cb_pos->buf_idx = 0; list_move_tail(&cb_pos->cb_list, &cmpl_list->mei_cb.cb_list); return -EMSGSIZE; } else { cl->state = MEI_FILE_DISCONNECTING; cl->status = 0; - cb_pos->information = 0; + cb_pos->buf_idx = 0; list_move_tail(&cb_pos->cb_list, &dev->ctrl_rd_list.mei_cb.cb_list); cl->timer_count = MEI_CONNECT_TIMEOUT; @@ -834,7 +835,7 @@ static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots, if (mei_send_flow_control(dev, cl)) { cl->status = -ENODEV; - cb_pos->information = 0; + cb_pos->buf_idx = 0; list_move_tail(&cb_pos->cb_list, &cmpl_list->mei_cb.cb_list); return -ENODEV; } @@ -871,7 +872,7 @@ static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots, *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); if (mei_connect(dev, cl)) { cl->status = -ENODEV; - cb_pos->information = 0; + cb_pos->buf_idx = 0; list_del(&cb_pos->cb_list); return -ENODEV; } else { @@ -901,28 +902,26 @@ static int _mei_irq_thread_cmpl(struct mei_device *dev, s32 *slots, struct mei_msg_hdr *mei_hdr; if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + -
[char-misc-next 3/6] mei: mei_write: revamp error path handling
1. unify common amt and regular error path and use it early in the function 2. fix indentation 3. propagate error code directly from copy_from_user 4. print out errors using dev_err instead of dev_dbg Signed-off-by: Tomas Winkler --- drivers/misc/mei/main.c | 75 +-- 1 files changed, 27 insertions(+), 48 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 15de4b1..8dcf59d 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -621,10 +621,26 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { - mutex_unlock(&dev->device_lock); - return -ENODEV; + rets = -ENODEV; + goto unlock_dev; } + i = mei_me_cl_by_id(dev, cl->me_client_id); + if (i < 0) { + rets = -ENODEV; + goto unlock_dev; + } + if (length > dev->me_clients[i].props.max_msg_length || length <= 0) { + rets = -EMSGSIZE; + goto unlock_dev; + } + + if (cl->state != MEI_FILE_CONNECTED) { + rets = -ENODEV; + dev_err(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d", + cl->host_client_id, cl->me_client_id); + goto unlock_dev; + } if (cl == &dev->iamthif_cl) { write_cb = find_amthi_read_list_entry(dev, file); @@ -633,11 +649,11 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, msecs_to_jiffies(IAMTHIF_READ_TIMER); if (time_after(jiffies, timeout) || -cl->reading_state == MEI_READ_COMPLETE) { - *offset = 0; - list_del(&write_cb->cb_list); - mei_free_cb_private(write_cb); - write_cb = NULL; + cl->reading_state == MEI_READ_COMPLETE) { + *offset = 0; + list_del(&write_cb->cb_list); + mei_free_cb_private(write_cb); + write_cb = NULL; } } } @@ -670,8 +686,8 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, dev_dbg(&dev->pdev->dev, "cb request size = %zd\n", length); - rets = -EFAULT; - if (copy_from_user(write_cb->request_buffer.data, ubuf, length)) + rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); + if (rets) goto unlock_dev; cl->sm_state = 0; @@ -685,29 +701,11 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, cl->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT; if (cl == &dev->iamthif_cl) { - if (dev->dev_state != MEI_DEV_ENABLED) { - rets = -ENODEV; - goto unlock_dev; - } - i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id); - if (i < 0) { - rets = -ENODEV; - goto unlock_dev; - } - if (length > dev->me_clients[i].props.max_msg_length || - length <= 0) { - rets = -EMSGSIZE; - goto unlock_dev; - } rets = mei_io_cb_alloc_resp_buf(write_cb, dev->iamthif_mtu); if (rets) goto unlock_dev; write_cb->major_file_operations = MEI_IOCTL; - if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) { - rets = -ENODEV; - goto unlock_dev; - } if (!list_empty(&dev->amthi_cmd_list.mei_cb.cb_list) || dev->iamthif_state != MEI_IAMTHIF_IDLE) { @@ -716,43 +714,24 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, dev_dbg(&dev->pdev->dev, "add amthi cb to amthi cmd waiting list\n"); list_add_tail(&write_cb->cb_list, &dev->amthi_cmd_list.mei_cb.cb_list); - rets = length; } else { dev_dbg(&dev->pdev->dev, "call amthi write\n"); rets = amthi_write(dev, write_cb); if (rets) { -
[char-misc-next 4/6] mei: kill usless struct mei_io_list
kill useless mei_io_list list wrapper and use directly struct mei_cl_cb mei_cb which was its only member for managing io queues Signed-off-by: Tomas Winkler --- drivers/misc/mei/init.c | 29 ++--- drivers/misc/mei/interrupt.c | 134 ++ drivers/misc/mei/iorw.c | 34 +-- drivers/misc/mei/main.c | 43 ++ drivers/misc/mei/mei_dev.h | 32 ++- 5 files changed, 116 insertions(+), 156 deletions(-) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 98f1430..dd02e87 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -48,34 +48,22 @@ const uuid_le mei_amthi_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac, 0x81, 0x4c); /** - * mei_io_list_init - Sets up a queue list. - * - * @list: An instance io list structure - * @dev: the device structure - */ -void mei_io_list_init(struct mei_io_list *list) -{ - /* initialize our queue list */ - INIT_LIST_HEAD(&list->mei_cb.cb_list); -} - -/** * mei_io_list_flush - removes list entry belonging to cl. * * @list: An instance of our list structure * @cl: private data of the file object */ -void mei_io_list_flush(struct mei_io_list *list, struct mei_cl *cl) +void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) { struct mei_cl_cb *pos; struct mei_cl_cb *next; - list_for_each_entry_safe(pos, next, &list->mei_cb.cb_list, cb_list) { + list_for_each_entry_safe(pos, next, &list->list, list) { if (pos->file_private) { struct mei_cl *cl_tmp; cl_tmp = (struct mei_cl *)pos->file_private; if (mei_cl_cmp_id(cl, cl_tmp)) - list_del(&pos->cb_list); + list_del(&pos->list); } } } @@ -351,9 +339,8 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) } } /* remove all waiting requests */ - list_for_each_entry_safe(cb_pos, cb_next, - &dev->write_list.mei_cb.cb_list, cb_list) { - list_del(&cb_pos->cb_list); + list_for_each_entry_safe(cb_pos, cb_next, &dev->write_list.list, list) { + list_del(&cb_pos->list); mei_free_cb_private(cb_pos); } } @@ -685,7 +672,7 @@ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) if (!cb) return -ENOMEM; - INIT_LIST_HEAD(&cb->cb_list); + mei_io_list_init(cb); cb->file_private = cl; cb->major_file_operations = MEI_CLOSE; if (dev->mei_host_buffer_is_empty) { @@ -696,11 +683,9 @@ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) goto free; } mdelay(10); /* Wait for hardware disconnection ready */ - list_add_tail(&cb->cb_list, &dev->ctrl_rd_list.mei_cb.cb_list); + list_add_tail(&cb->list, &dev->ctrl_rd_list.list); } else { dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n"); - list_add_tail(&cb->cb_list, - &dev->ctrl_wr_list.mei_cb.cb_list); } mutex_unlock(&dev->device_lock); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 320ebd2..54d6f1a 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -87,9 +87,8 @@ static void _mei_cmpl_iamthif(struct mei_device *dev, struct mei_cl_cb *cb_pos) memcpy(cb_pos->response_buffer.data, dev->iamthif_msg_buf, dev->iamthif_msg_buf_index); - list_add_tail(&cb_pos->cb_list, - &dev->amthi_read_complete_list.mei_cb.cb_list); - dev_dbg(&dev->pdev->dev, "amthi read completed.\n"); + list_add_tail(&cb_pos->list, &dev->amthi_read_complete_list.list); + dev_dbg(&dev->pdev->dev, "amthi read completed\n"); dev->iamthif_timer = jiffies; dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", dev->iamthif_timer); @@ -112,7 +111,7 @@ static void _mei_cmpl_iamthif(struct mei_device *dev, struct mei_cl_cb *cb_pos) * * returns 0 on success, <0 on failure. */ -static int mei_irq_thread_read_amthi_message(struct mei_io_list *complete_list, +static int mei_irq_thread_read_amthi_message(struct mei_cl_cb *complete_list, struct mei_device *dev,
[char-misc-next 6/6] mei: use mei_io_cb_ warppers also for control flows
move the mei_io_cb_ wrappers to to iorw.c for global use and use them also for handling control flows Signed-off-by: Tomas Winkler --- drivers/misc/mei/init.c|4 +- drivers/misc/mei/iorw.c| 120 +-- drivers/misc/mei/main.c| 78 drivers/misc/mei/mei_dev.h |4 ++ 4 files changed, 97 insertions(+), 109 deletions(-) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 2dcdedf..e1205b3 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -668,12 +668,10 @@ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) if (cl->state != MEI_FILE_DISCONNECTING) return 0; - cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); + cb = mei_io_cb_init(cl, NULL); if (!cb) return -ENOMEM; - mei_io_list_init(cb); - cb->file_private = cl; cb->major_file_operations = MEI_CLOSE; if (dev->mei_host_buffer_is_empty) { dev->mei_host_buffer_is_empty = false; diff --git a/drivers/misc/mei/iorw.c b/drivers/misc/mei/iorw.c index 2891bc4..4c91908 100644 --- a/drivers/misc/mei/iorw.c +++ b/drivers/misc/mei/iorw.c @@ -52,6 +52,83 @@ void mei_io_cb_free(struct mei_cl_cb *cb) kfree(cb->response_buffer.data); kfree(cb); } +/** + * mei_io_cb_init - allocate and initialize io callback + * + * @cl - mei client + * @file: pointer to file structure + * + * returns mei_cl_cb pointer or NULL; + */ +struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) +{ + struct mei_cl_cb *cb; + struct mei_device *dev; + + dev = cl->dev; + + cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); + if (!cb) + return NULL; + + mei_io_list_init(cb); + + cb->file_object = fp; + cb->file_private = cl; + cb->buf_idx = 0; + return cb; +} + + +/** + * mei_io_cb_alloc_req_buf - allocate request buffer + * + * @cb - io callback structure + * @size: size of the buffer + * + * returns 0 on success + * -EINVAL if cb is NULL + * -ENOMEM if allocation failed + */ +int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) +{ + if (!cb) + return -EINVAL; + + if (length == 0) + return 0; + + cb->request_buffer.data = kmalloc(length, GFP_KERNEL); + if (!cb->request_buffer.data) + return -ENOMEM; + cb->request_buffer.size = length; + return 0; +} +/** + * mei_io_cb_alloc_req_buf - allocate respose buffer + * + * @cb - io callback structure + * @size: size of the buffer + * + * returns 0 on success + * -EINVAL if cb is NULL + * -ENOMEM if allocation failed + */ +int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) +{ + if (!cb) + return -EINVAL; + + if (length == 0) + return 0; + + cb->response_buffer.data = kmalloc(length, GFP_KERNEL); + if (!cb->response_buffer.data) + return -ENOMEM; + cb->response_buffer.size = length; + return 0; +} + /** * mei_me_cl_by_id return index to me_clients for client_id @@ -112,14 +189,12 @@ int mei_ioctl_connect_client(struct file *file, dev_dbg(&dev->pdev->dev, "mei_ioctl_connect_client() Entry\n"); - /* buffered ioctl cb */ - cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); + cb = mei_io_cb_init(cl, file); if (!cb) { rets = -ENOMEM; goto end; } - mei_io_list_init(cb); cb->major_file_operations = MEI_IOCTL; @@ -207,14 +282,12 @@ int mei_ioctl_connect_client(struct file *file, } else { dev_dbg(&dev->pdev->dev, "Sending connect message - succeeded\n"); cl->timer_count = MEI_CONNECT_TIMEOUT; - cb->file_private = cl; list_add_tail(&cb->list, &dev->ctrl_rd_list.list); } } else { dev_dbg(&dev->pdev->dev, "Queuing the connect request due to device busy\n"); - cb->file_private = cl; dev_dbg(&dev->pdev->dev, "add connect cb to control write list.\n"); list_add_tail(&cb->list, &dev->ctrl_wr_list.list); } @@ -407,7 +480,7 @@ out: int mei_start_read(struct mei_device *dev, struct mei_cl *cl) { struct mei_cl_cb *cb; - int rets = 0; + int rets; int i; if (cl->state != MEI_FILE_CONNECTED) @@ -416,49 +489,40 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl) if (dev->dev_state != MEI_DEV_ENABLED) return -ENODEV; - dev_dbg(&dev->pd
[char-misc-next 5/6] mei: rename mei_free_cb_private to mei_io_cb_free
1. cb_private was an old name that we depriacated in earlier cleanups 2. we also group the funcion declaration with other _io_ functions 3. Don't check cb for NULL as mei_io_cb_free is NULL safe Signed-off-by: Tomas Winkler --- drivers/misc/mei/init.c |4 ++-- drivers/misc/mei/interrupt.c | 12 +--- drivers/misc/mei/iorw.c | 35 ++- drivers/misc/mei/main.c | 14 +++--- drivers/misc/mei/mei_dev.h |5 +++-- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index dd02e87..2dcdedf 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -341,7 +341,7 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) /* remove all waiting requests */ list_for_each_entry_safe(cb_pos, cb_next, &dev->write_list.list, list) { list_del(&cb_pos->list); - mei_free_cb_private(cb_pos); + mei_io_cb_free(cb_pos); } } @@ -713,7 +713,7 @@ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) mei_io_list_flush(&dev->ctrl_rd_list, cl); mei_io_list_flush(&dev->ctrl_wr_list, cl); free: - mei_free_cb_private(cb); + mei_io_cb_free(cb); return rets; } diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 54d6f1a..5c65bac 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -58,7 +58,7 @@ irqreturn_t mei_interrupt_quick_handler(int irq, void *dev_id) static void _mei_cmpl(struct mei_cl *cl, struct mei_cl_cb *cb_pos) { if (cb_pos->major_file_operations == MEI_WRITE) { - mei_free_cb_private(cb_pos); + mei_io_cb_free(cb_pos); cb_pos = NULL; cl->writing_state = MEI_WRITE_COMPLETE; if (waitqueue_active(&cl->tx_wait)) @@ -1368,11 +1368,10 @@ void mei_timer(struct work_struct *work) dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; - if (dev->iamthif_current_cb) - mei_free_cb_private(dev->iamthif_current_cb); + mei_io_cb_free(dev->iamthif_current_cb); + dev->iamthif_current_cb = NULL; dev->iamthif_file_object = NULL; - dev->iamthif_current_cb = NULL; mei_run_next_iamthif_cmd(dev); } } @@ -1404,12 +1403,11 @@ void mei_timer(struct work_struct *work) if (cl_pos == &dev->iamthif_cl) list_del(&cb_pos->list); } - if (dev->iamthif_current_cb) - mei_free_cb_private(dev->iamthif_current_cb); + mei_io_cb_free(dev->iamthif_current_cb); + dev->iamthif_current_cb = NULL; dev->iamthif_file_object->private_data = NULL; dev->iamthif_file_object = NULL; - dev->iamthif_current_cb = NULL; dev->iamthif_timer = 0; mei_run_next_iamthif_cmd(dev); diff --git a/drivers/misc/mei/iorw.c b/drivers/misc/mei/iorw.c index 4ff9eaf..2891bc4 100644 --- a/drivers/misc/mei/iorw.c +++ b/drivers/misc/mei/iorw.c @@ -39,6 +39,21 @@ #include "interface.h" /** + * mei_io_cb_free - free mei_cb_private related memory + * + * @cb: mei callback struct + */ +void mei_io_cb_free(struct mei_cl_cb *cb) +{ + if (cb == NULL) + return; + + kfree(cb->request_buffer.data); + kfree(cb->response_buffer.data); + kfree(cb); +} + +/** * mei_me_cl_by_id return index to me_clients for client_id * * @dev: the device structure @@ -231,7 +246,7 @@ int mei_ioctl_connect_client(struct file *file, rets = 0; end: dev_dbg(&dev->pdev->dev, "free connect cb memory."); - kfree(cb); + mei_io_cb_free(cb); return rets; } @@ -375,7 +390,7 @@ int amthi_read(struct mei_device *dev, struct file *file, free: dev_dbg(&dev->pdev->dev, "free amthi cb memory.\n"); *offset = 0; - mei_free_cb_private(cb); + mei_io_cb_free(cb); out: return rets; } @@ -444,7 +459,7 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl) } return rets; unlock: - mei_free_cb_private(cb); + mei_io_cb_free(cb); return rets; } @@ -568,17 +583,3 @@ void mei_run_next_iamthif_cmd(struct mei_device *dev) } } -/** - * mei_free_cb_private - free mei_cb_private related memory - * - * @cb: mei callback st
[char-misc-next 2/6] mei: add allocation and initialization wrappers for io callback
mei_io_cb_init - allocat and initializate mei_cl_cb mei_io_cb_alloc_req/resp_buf are separate function as buffers are not always needed Signed-off-by: Tomas Winkler --- drivers/misc/mei/main.c | 111 -- 1 files changed, 87 insertions(+), 24 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 3d4f6d1..15de4b1 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -516,6 +516,83 @@ out: } /** + * mei_io_cb_init - allocate and initialize io callback + * + * @cl - mei client + * @file: pointer to file structure + * + * returns mei_cl_cb pointer or NULL; + */ +static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) +{ + struct mei_cl_cb *cb; + struct mei_device *dev; + + dev = cl->dev; + + cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); + if (!cb) + return NULL; + + INIT_LIST_HEAD(&cb->cb_list); + + cb->file_object = fp; + cb->file_private = cl; + cb->buf_idx = 0; + return cb; +} + + +/** + * mei_io_cb_alloc_req_buf - allocate request buffer + * + * @cb - io callback structure + * @size: size of the buffer + * + * returns 0 on success + * -EINVAL if cb is NULL + * -ENOMEM if allocation failed + */ +static int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) +{ + if (!cb) + return -EINVAL; + + if (length == 0) + return 0; + + cb->request_buffer.data = kmalloc(length, GFP_KERNEL); + if (!cb->request_buffer.data) + return -ENOMEM; + cb->request_buffer.size = length; + return 0; +} +/** + * mei_io_cb_alloc_req_buf - allocate respose buffer + * + * @cb - io callback structure + * @size: size of the buffer + * + * returns 0 on success + * -EINVAL if cb is NULL + * -ENOMEM if allocation failed + */ +static int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) +{ + if (!cb) + return -EINVAL; + + if (length == 0) + return 0; + + cb->response_buffer.data = kmalloc(length, GFP_KERNEL); + if (!cb->response_buffer.data) + return -ENOMEM; + cb->response_buffer.size = length; + return 0; +} + +/** * mei_write - the write function. * * @file: pointer to file structure @@ -581,20 +658,17 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, *offset = 0; - write_cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); + write_cb = mei_io_cb_init(cl, file); if (!write_cb) { - mutex_unlock(&dev->device_lock); - return -ENOMEM; + dev_err(&dev->pdev->dev, "write cb allocation failed\n"); + rets = -ENOMEM; + goto unlock_dev; } - - write_cb->file_object = file; - write_cb->file_private = cl; - write_cb->request_buffer.data = kmalloc(length, GFP_KERNEL); - rets = -ENOMEM; - if (!write_cb->request_buffer.data) + rets = mei_io_cb_alloc_req_buf(write_cb, length); + if (rets) goto unlock_dev; - dev_dbg(&dev->pdev->dev, "length =%d\n", (int) length); + dev_dbg(&dev->pdev->dev, "cb request size = %zd\n", length); rets = -EFAULT; if (copy_from_user(write_cb->request_buffer.data, ubuf, length)) @@ -610,14 +684,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, write_cb->request_buffer.data, 4) == 0))) cl->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT; - INIT_LIST_HEAD(&write_cb->cb_list); if (cl == &dev->iamthif_cl) { - write_cb->response_buffer.data = - kmalloc(dev->iamthif_mtu, GFP_KERNEL); - if (!write_cb->response_buffer.data) { - rets = -ENOMEM; - goto unlock_dev; - } if (dev->dev_state != MEI_DEV_ENABLED) { rets = -ENODEV; goto unlock_dev; @@ -632,11 +699,11 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = -EMSGSIZE; goto unlock_dev; } + rets = mei_io_cb_alloc_resp_buf(write_cb, dev->iamthif_mtu); + if (rets) + goto unlock_dev; - write_cb->response_buffer.size = dev->iamthif_mtu; write_cb->major_file_operations = MEI_IOCTL; - write_cb->buf_idx = 0; - write_cb->request_buffer.size = length; if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) { ret
[char-misc-next 01/15] mei: drop redundant length paramter from mei_write_message function
The length is already part of the message header and it is validated before the function call Signed-off-by: Tomas Winkler --- drivers/misc/mei/amthif.c|6 ++ drivers/misc/mei/init.c |7 +++ drivers/misc/mei/interface.c | 24 +++- drivers/misc/mei/interface.h |5 ++--- drivers/misc/mei/interrupt.c | 17 - drivers/misc/mei/main.c |3 +-- drivers/misc/mei/wd.c| 18 +- 7 files changed, 36 insertions(+), 44 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 18794ae..8a9313a 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -300,8 +300,7 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) mei_hdr.reserved = 0; dev->iamthif_msg_buf_index += mei_hdr.length; if (mei_write_message(dev, &mei_hdr, - (unsigned char *)(dev->iamthif_msg_buf), - mei_hdr.length)) + (unsigned char *)dev->iamthif_msg_buf)) return -ENODEV; if (mei_hdr.msg_complete) { @@ -463,8 +462,7 @@ int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, *slots -= msg_slots; if (mei_write_message(dev, mei_hdr, - dev->iamthif_msg_buf + dev->iamthif_msg_buf_index, - mei_hdr->length)) { + dev->iamthif_msg_buf + dev->iamthif_msg_buf_index)) { dev->iamthif_state = MEI_IAMTHIF_IDLE; cl->status = -ENODEV; list_del(&cb->list); diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index a54cd55..c0c0b3e 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -345,7 +345,7 @@ void mei_host_start_message(struct mei_device *dev) start_req->host_version.minor_version = HBM_MINOR_VERSION; dev->recvd_msg = false; - if (mei_write_message(dev, mei_hdr, (unsigned char *)start_req, len)) { + if (mei_write_message(dev, mei_hdr, (unsigned char *)start_req)) { dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n"); dev->dev_state = MEI_DEV_RESETING; mei_reset(dev, 1); @@ -374,7 +374,7 @@ void mei_host_enum_clients_message(struct mei_device *dev) memset(enum_req, 0, sizeof(struct hbm_host_enum_request)); enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; - if (mei_write_message(dev, mei_hdr, (unsigned char *)enum_req, len)) { + if (mei_write_message(dev, mei_hdr, (unsigned char *)enum_req)) { dev->dev_state = MEI_DEV_RESETING; dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n"); mei_reset(dev, 1); @@ -492,8 +492,7 @@ int mei_host_client_enumerate(struct mei_device *dev) prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; prop_req->address = next_client_index; - if (mei_write_message(dev, mei_hdr, (unsigned char *) prop_req, - mei_hdr->length)) { + if (mei_write_message(dev, mei_hdr, (unsigned char *) prop_req)) { dev->dev_state = MEI_DEV_RESETING; dev_err(&dev->pdev->dev, "Properties request command failed\n"); mei_reset(dev, 1); diff --git a/drivers/misc/mei/interface.c b/drivers/misc/mei/interface.c index 8de8547..21ccbe6 100644 --- a/drivers/misc/mei/interface.c +++ b/drivers/misc/mei/interface.c @@ -113,21 +113,20 @@ int mei_hbuf_empty_slots(struct mei_device *dev) * mei_write_message - writes a message to mei device. * * @dev: the device structure - * @header: header of message - * @write_buffer: message buffer will be written - * @write_length: message size will be written + * @hader: mei HECI header of message + * @buf: message payload will be written * * This function returns -EIO if write has failed */ int mei_write_message(struct mei_device *dev, struct mei_msg_hdr *header, - unsigned char *buf, unsigned long length) + unsigned char *buf) { unsigned long rem, dw_cnt; + unsigned long length = header->length; u32 *reg_buf = (u32 *)buf; int i; int empty_slots; - dev_dbg(&dev->pdev->dev, "mei_write_message header=%08x.\n", *((u32 *) header)); @@ -307,8 +306,7 @@ int mei_send_flow_control(struct mei_device *dev, struct mei_cl *cl) dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n", cl->host_client_id, cl->me_client_id); -
[char-misc-next 04/15] mei: extarct device dependent constants into hw-mei.h
Signed-off-by: Tomas Winkler --- drivers/misc/mei/hw-mei.h | 118 drivers/misc/mei/hw.h | 98 drivers/misc/mei/mei_dev.h |2 + 3 files changed, 120 insertions(+), 98 deletions(-) create mode 100644 drivers/misc/mei/hw-mei.h diff --git a/drivers/misc/mei/hw-mei.h b/drivers/misc/mei/hw-mei.h new file mode 100644 index 000..ecc58f7 --- /dev/null +++ b/drivers/misc/mei/hw-mei.h @@ -0,0 +1,118 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _MEI_HW_MEI_H_ +#define _MEI_HW_MEI_H_ + +/* + * MEI device IDs + */ +#define MEI_DEV_ID_82946GZ0x2974 /* 82946GZ/GL */ +#define MEI_DEV_ID_82G35 0x2984 /* 82G35 Express */ +#define MEI_DEV_ID_82Q965 0x2994 /* 82Q963/Q965 */ +#define MEI_DEV_ID_82G965 0x29A4 /* 82P965/G965 */ + +#define MEI_DEV_ID_82GM9650x2A04 /* Mobile PM965/GM965 */ +#define MEI_DEV_ID_82GME965 0x2A14 /* Mobile GME965/GLE960 */ + +#define MEI_DEV_ID_ICH9_82Q35 0x29B4 /* 82Q35 Express */ +#define MEI_DEV_ID_ICH9_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */ +#define MEI_DEV_ID_ICH9_82Q33 0x29D4 /* 82Q33 Express */ +#define MEI_DEV_ID_ICH9_82X38 0x29E4 /* 82X38/X48 Express */ +#define MEI_DEV_ID_ICH9_3200 0x29F4 /* 3200/3210 Server */ + +#define MEI_DEV_ID_ICH9_6 0x28B4 /* Bearlake */ +#define MEI_DEV_ID_ICH9_7 0x28C4 /* Bearlake */ +#define MEI_DEV_ID_ICH9_8 0x28D4 /* Bearlake */ +#define MEI_DEV_ID_ICH9_9 0x28E4 /* Bearlake */ +#define MEI_DEV_ID_ICH9_100x28F4 /* Bearlake */ + +#define MEI_DEV_ID_ICH9M_10x2A44 /* Cantiga */ +#define MEI_DEV_ID_ICH9M_20x2A54 /* Cantiga */ +#define MEI_DEV_ID_ICH9M_30x2A64 /* Cantiga */ +#define MEI_DEV_ID_ICH9M_40x2A74 /* Cantiga */ + +#define MEI_DEV_ID_ICH10_10x2E04 /* Eaglelake */ +#define MEI_DEV_ID_ICH10_20x2E14 /* Eaglelake */ +#define MEI_DEV_ID_ICH10_30x2E24 /* Eaglelake */ +#define MEI_DEV_ID_ICH10_40x2E34 /* Eaglelake */ + +#define MEI_DEV_ID_IBXPK_10x3B64 /* Calpella */ +#define MEI_DEV_ID_IBXPK_20x3B65 /* Calpella */ + +#define MEI_DEV_ID_CPT_1 0x1C3A /* Couger Point */ +#define MEI_DEV_ID_PBG_1 0x1D3A /* C600/X79 Patsburg */ + +#define MEI_DEV_ID_PPT_1 0x1E3A /* Panther Point */ +#define MEI_DEV_ID_PPT_2 0x1CBA /* Panther Point */ +#define MEI_DEV_ID_PPT_3 0x1DBA /* Panther Point */ + +#define MEI_DEV_ID_LPT0x8C3A /* Lynx Point */ +#define MEI_DEV_ID_LPT_LP 0x9C3A /* Lynx Point LP */ +/* + * MEI HW Section + */ + +/* MEI registers */ +/* H_CB_WW - Host Circular Buffer (CB) Write Window register */ +#define H_CB_WW0 +/* H_CSR - Host Control Status register */ +#define H_CSR 4 +/* ME_CB_RW - ME Circular Buffer Read Window register (read only) */ +#define ME_CB_RW 8 +/* ME_CSR_HA - ME Control Status Host Access register (read only) */ +#define ME_CSR_HA 0xC + + +/* register bits of H_CSR (Host Control Status register) */ +/* Host Circular Buffer Depth - maximum number of 32-bit entries in CB */ +#define H_CBD 0xFF00 +/* Host Circular Buffer Write Pointer */ +#define H_CBWP0x00FF +/* Host Circular Buffer Read Pointer */ +#define H_CBRP0xFF00 +/* Host Reset */ +#define H_RST 0x0010 +/* Host Ready */ +#define H_RDY 0x0008 +/* Host Interrupt Generate */ +#define H_IG 0x0004 +/* Host Interrupt Status */ +#define H_IS 0x0002 +/* Host Interrupt Enable */ +#define H_IE 0x0001 + + +/* register bits of ME_CSR_HA (ME Control Status Host Access register) */ +/* ME CB (Circular Buffer) Depth HRA (Host Read Access) - host read only +access to ME_CBD */ +#define ME_CBD_HRA0xFF00 +/* ME CB Write Pointer HRA - host read only access to ME_CBWP */ +#define ME_CBWP_HRA 0x00FF +/* ME CB Read Pointer HRA - host read only access to ME_CBRP */ +#define ME_CBRP_HRA 0xFF00 +/* ME Reset HRA - host read only access to ME_RST */ +#define ME_RST_HRA0x0010 +/* ME Ready HRA - host read only access to ME_RDY */ +#define ME_RDY_HRA0x0008 +/* ME Interrupt Generate HRA - host read only access to ME_IG */ +#define ME_IG_HRA 0x0004 +/* ME Interrupt Status HRA - host read only access to ME_IS */ +#define ME_IS_HRA 0x0002 +/* ME Interrupt Enable HRA - host read only access to ME_IE
[char-misc-next 08/15] mei: mei_me_client is not hw API move to mei_dev.h
Remove struct mei_me_client from hw.h as it is not part of of the hardware API. Also it doesn't have to be packed Add kdoc for this structure Signed-off-by: Tomas Winkler --- drivers/misc/mei/hw.h |6 -- drivers/misc/mei/mei_dev.h | 13 + 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index ba7d06c..6ebb369 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -230,11 +230,5 @@ struct hbm_flow_control { u8 reserved[MEI_FC_MESSAGE_RESERVED_LENGTH]; } __packed; -struct mei_me_client { - struct mei_client_properties props; - u8 client_id; - u8 mei_flow_ctrl_creds; -} __packed; - #endif diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 37eb041..ab085d1 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -158,6 +158,19 @@ struct mei_message_data { unsigned char *data; }; +/** + * struct mei_me_client - representation of me (fw) client + * + * @props - client properties + * @client_id - me client id + * @mei_flow_ctrl_creds - flow control credits + */ +struct mei_me_client { + struct mei_client_properties props; + u8 client_id; + u8 mei_flow_ctrl_creds; +}; + struct mei_cl; -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[char-misc-next 10/15] mei: move host bus message handling to hbm.c
for sake of more layered desing we move to the new hbm.c file Signed-off-by: Tomas Winkler --- drivers/misc/mei/Makefile|1 + drivers/misc/mei/hbm.c | 440 ++ drivers/misc/mei/init.c | 111 --- drivers/misc/mei/interface.c | 78 drivers/misc/mei/interrupt.c | 235 +-- drivers/misc/mei/mei_dev.h | 10 + 6 files changed, 456 insertions(+), 419 deletions(-) create mode 100644 drivers/misc/mei/hbm.c diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 0017842..1f382a5 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_INTEL_MEI) += mei.o mei-objs := init.o +mei-objs += hbm.o mei-objs += interrupt.o mei-objs += interface.o mei-objs += iorw.o diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c new file mode 100644 index 000..2c4c1bb --- /dev/null +++ b/drivers/misc/mei/hbm.c @@ -0,0 +1,440 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include + +#include "mei_dev.h" +#include "interface.h" + +/** + * host_start_message - mei host sends start message. + * + * @dev: the device structure + * + * returns none. + */ +void mei_host_start_message(struct mei_device *dev) +{ + struct mei_msg_hdr *mei_hdr; + struct hbm_host_version_request *start_req; + const size_t len = sizeof(struct hbm_host_version_request); + + mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len); + + /* host start message */ + start_req = (struct hbm_host_version_request *)&dev->wr_msg_buf[1]; + memset(start_req, 0, len); + start_req->hbm_cmd = HOST_START_REQ_CMD; + start_req->host_version.major_version = HBM_MAJOR_VERSION; + start_req->host_version.minor_version = HBM_MINOR_VERSION; + + dev->recvd_msg = false; + if (mei_write_message(dev, mei_hdr, (unsigned char *)start_req)) { + dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n"); + dev->dev_state = MEI_DEV_RESETING; + mei_reset(dev, 1); + } + dev->init_clients_state = MEI_START_MESSAGE; + dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; + return ; +} + +/** + * host_enum_clients_message - host sends enumeration client request message. + * + * @dev: the device structure + * + * returns none. + */ +void mei_host_enum_clients_message(struct mei_device *dev) +{ + struct mei_msg_hdr *mei_hdr; + struct hbm_host_enum_request *enum_req; + const size_t len = sizeof(struct hbm_host_enum_request); + /* enumerate clients */ + mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len); + + enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1]; + memset(enum_req, 0, sizeof(struct hbm_host_enum_request)); + enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; + + if (mei_write_message(dev, mei_hdr, (unsigned char *)enum_req)) { + dev->dev_state = MEI_DEV_RESETING; + dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n"); + mei_reset(dev, 1); + } + dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE; + dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; + return; +} + + +int mei_host_client_enumerate(struct mei_device *dev) +{ + + struct mei_msg_hdr *mei_hdr; + struct hbm_props_request *prop_req; + const size_t len = sizeof(struct hbm_props_request); + unsigned long next_client_index; + u8 client_num; + + + client_num = dev->me_client_presentation_num; + + next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, + dev->me_client_index); + + /* We got all client properties */ + if (next_client_index == MEI_CLIENTS_MAX) { + schedule_work(&dev->init_work); + + return 0; + } + + dev->me_clients[client_num].client_id = next_client_index; + dev->me_clients[client_num].mei_flow_ctrl_creds = 0; + + mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len); + prop_req = (struct hbm_props_request *)&dev->wr_msg_buf[1]; + + memset(prop_req, 0, s