1 --- a/lib/python/Components/Harddisk.py
2 +++ b/lib/python/Components/Harddisk.py
3 @@ -11,17 +11,17 @@ def readFile(filename):
14 def __init__(self, device):
17 if access("/dev/.udev", 0):
18 - self.type = self.DEVTYPE_UDEV
19 + self.type = DEVTYPE_UDEV
20 elif access("/dev/.devfsd", 0):
21 - self.type = self.DEVTYPE_DEVFS
22 + self.type = DEVTYPE_DEVFS
24 print "Unable to determine structure of /dev"
26 @@ -33,11 +33,11 @@ class Harddisk:
28 self.phys_path = path.realpath(self.sysfsPath('device'))
30 - if self.type == self.DEVTYPE_UDEV:
31 + if self.type == DEVTYPE_UDEV:
32 self.dev_path = '/dev/' + self.device
33 self.disk_path = self.dev_path
35 - elif self.type == self.DEVTYPE_DEVFS:
36 + elif self.type == DEVTYPE_DEVFS:
37 tmp = readFile(self.sysfsPath('dev')).split(':')
40 @@ -60,9 +60,9 @@ class Harddisk:
41 return self.device < ob.device
43 def partitionPath(self, n):
44 - if self.type == self.DEVTYPE_UDEV:
45 + if self.type == DEVTYPE_UDEV:
46 return self.dev_path + n
47 - elif self.type == self.DEVTYPE_DEVFS:
48 + elif self.type == DEVTYPE_DEVFS:
49 return self.dev_path + '/part' + n
51 def sysfsPath(self, filename):
52 @@ -75,9 +75,9 @@ class Harddisk:
56 - if self.type == self.DEVTYPE_UDEV:
57 + if self.type == DEVTYPE_UDEV:
58 ide_cf = False # FIXME
59 - elif self.type == self.DEVTYPE_DEVFS:
60 + elif self.type == DEVTYPE_DEVFS:
61 ide_cf = self.device[:2] == "hd" and "host0" not in self.dev_path
63 internal = "pci" in self.phys_path
64 @@ -136,7 +136,7 @@ class Harddisk:
66 def numPartitions(self):
68 - if self.type == self.DEVTYPE_UDEV:
69 + if self.type == DEVTYPE_UDEV:
71 devdir = listdir('/dev')
73 @@ -145,7 +145,7 @@ class Harddisk:
74 if filename.startswith(self.device):
77 - elif self.type == self.DEVTYPE_DEVFS:
78 + elif self.type == DEVTYPE_DEVFS:
80 idedir = listdir(self.dev_path)
82 @@ -394,24 +394,38 @@ class Partition:
91 - "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0": "Front USB Slot",
92 - "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.2/1-1.2:1.0": "Back, upper USB Slot",
93 - "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.3/1-1.3:1.0": "Back, lower USB Slot",
94 - "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/host1/target1:0:0/1:0:0:0": "DVD Drive",
95 + "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("DVD Drive"),
96 + "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("DVD Drive"),
97 + "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/host3/target3:0:0/3:0:0:0": _("DVD Drive"),
110 + "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0": _("Front USB Slot"),
111 + "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.2/1-1.2:1.0": _("Back, upper USB Slot"),
112 + "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.3/1-1.3:1.0": _("Back, lower USB Slot"),
113 + "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0": _("Front USB Slot"),
114 + "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/": _("Internal USB Slot"),
115 + "/devices/platform/brcm-ohci-1.1/usb4/4-1/4-1:1.0/": _("Internal USB Slot"),
120 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2:1.0": "Upper USB Slot",
121 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1:1.0": "Lower USB Slot",
126 "/devices/pci0000:00/0000:00:14.1/ide1/1.0": "CF Card Slot", #hdc
127 "/devices/pci0000:00/0000:00:14.1/ide0/0.0": "Internal Harddisk"
129 @@ -422,6 +436,7 @@ class HarddiskManager:
132 self.partitions = [ ]
133 + self.devices_scanned_on_init = [ ]
135 self.on_partition_list_change = CList()
137 @@ -489,24 +504,23 @@ class HarddiskManager:
138 def enumerateBlockDevices(self):
139 print "enumerating block devices..."
140 for blockdev in listdir("/sys/block"):
141 - error, blacklisted, removable, is_cdrom, partitions, medium_found = self.getBlockDevInfo(blockdev)
142 - print "found block device '%s':" % blockdev,
144 - print "error querying properties"
146 - print "blacklisted"
147 - elif not medium_found:
150 - print "ok, removable=%s, cdrom=%s, partitions=%s, device=%s" % (removable, is_cdrom, partitions, blockdev)
152 - self.addHotplugPartition(blockdev)
153 - for part in partitions:
154 - self.addHotplugPartition(part)
155 + error, blacklisted, removable, is_cdrom, partitions, medium_found = self.addHotplugPartition(blockdev)
156 + if not error and not blacklisted:
158 + for part in partitions:
159 + self.addHotplugPartition(part)
160 + self.devices_scanned_on_init.append((blockdev, removable, is_cdrom, medium_found))
162 def getAutofsMountpoint(self, device):
163 return "/autofs/%s/" % (device)
165 + def is_hard_mounted(self, device):
166 + mounts = file('/proc/mounts').read().split('\n')
168 + if x.find('/autofs') == -1 and x.find(device) != -1:
172 def addHotplugPartition(self, device, physdev = None):
174 dev, part = self.splitDeviceName(device)
175 @@ -516,22 +530,36 @@ class HarddiskManager:
177 print "couldn't determine blockdev physdev for device", device
179 - # device is the device name, without /dev
180 - # physdev is the physical device path, which we (might) use to determine the userfriendly name
181 - description = self.getUserfriendlyDeviceName(device, physdev)
182 + error, blacklisted, removable, is_cdrom, partitions, medium_found = self.getBlockDevInfo(device)
183 + print "found block device '%s':" % device,
185 - p = Partition(mountpoint = self.getAutofsMountpoint(device), description = description, force_mounted = True, device = device)
186 - self.partitions.append(p)
187 - self.on_partition_list_change("add", p)
189 + print "blacklisted"
192 + print "error querying properties"
193 + elif not medium_found:
196 + print "ok, removable=%s, cdrom=%s, partitions=%s" % (removable, is_cdrom, partitions)
200 + # see if this is a harddrive
201 + if not device[l-1].isdigit() and not removable and not is_cdrom:
202 + self.hdd.append(Harddisk(device))
204 + SystemInfo["Harddisk"] = len(self.hdd) > 0
206 + if (not removable or medium_found) and not self.is_hard_mounted(device):
207 + # device is the device name, without /dev
208 + # physdev is the physical device path, which we (might) use to determine the userfriendly name
209 + description = self.getUserfriendlyDeviceName(device, physdev)
210 + p = Partition(mountpoint = self.getAutofsMountpoint(device), description = description, force_mounted = True, device = device)
211 + self.partitions.append(p)
212 + self.on_partition_list_change("add", p)
214 - # see if this is a harddrive
216 - if l and not device[l-1].isdigit():
217 - error, blacklisted, removable, is_cdrom, partitions, medium_found = self.getBlockDevInfo(device)
218 - if not blacklisted and not removable and not is_cdrom and medium_found:
219 - self.hdd.append(Harddisk(device))
221 - SystemInfo["Harddisk"] = len(self.hdd) > 0
222 + return error, blacklisted, removable, is_cdrom, partitions, medium_found
224 def removeHotplugPartition(self, device):
225 mountpoint = self.getAutofsMountpoint(device)
226 @@ -589,15 +617,23 @@ class HarddiskManager:
227 def getUserfriendlyDeviceName(self, dev, phys):
228 dev, part = self.splitDeviceName(dev)
229 description = "External Storage %s" % dev
230 + have_model_descr = False
232 description = readFile("/sys" + phys + "/model")
233 + have_model_descr = True
235 print "couldn't read model: ", s
236 from Tools.HardwareInfo import HardwareInfo
237 - for physdevprefix, pdescription in DEVICEDB.get(HardwareInfo().device_name,{}).items():
238 + if dev.find('sr') == 0 and dev[2].isdigit():
239 + devicedb = DEVICEDB_SR
241 + devicedb = DEVICEDB
242 + for physdevprefix, pdescription in devicedb.get(HardwareInfo().device_name,{}).items():
243 if phys.startswith(physdevprefix):
244 - description = pdescription
246 + if have_model_descr:
247 + description = pdescription + ' - ' + description
249 + description = pdescription
250 # not wholedisk and not partition 1
251 if part and part != 1:
252 description += " (Partition %d)" % part
253 --- a/lib/python/Plugins/SystemPlugins/Hotplug/plugin.py
254 +++ b/lib/python/Plugins/SystemPlugins/Hotplug/plugin.py
256 from Plugins.Plugin import PluginDescriptor
257 -from twisted.internet.protocol import Protocol, Factory
258 -from twisted.internet import reactor
259 from Components.Harddisk import harddiskmanager
260 +from Tools.Directories import fileExists
262 hotplugNotifier = [ ]
265 -class Hotplug(Protocol):
266 - def connectionMade(self):
268 +def processHotplugData(self, v):
269 + print "hotplug:", v
270 + action = v.get("ACTION")
271 + device = v.get("DEVPATH")
272 + physdevpath = v.get("PHYSDEVPATH")
273 + media_state = v.get("X_E2_MEDIA_STATUS")
275 - def dataReceived(self, data):
276 - self.received += data
277 + dev = device.split('/')[-1]
279 - def connectionLost(self, reason):
280 - data = self.received.split('\0')[:-1]
281 + if action is not None and action == "add":
282 + error, blacklisted, removable, is_cdrom, partitions, medium_found = harddiskmanager.addHotplugPartition(dev, physdevpath)
283 + if bdpoll and removable or is_cdrom:
284 + bdpoll.addDevice(dev, is_cdrom, medium_found)
285 + elif action is not None and action == "remove":
287 + bdpoll.removeDevice(dev)
288 + harddiskmanager.removeHotplugPartition(dev)
289 + elif media_state is not None:
290 + if media_state == '1':
291 + harddiskmanager.removeHotplugPartition(dev)
292 + harddiskmanager.addHotplugPartition(dev, physdevpath)
293 + elif media_state == '0':
294 + harddiskmanager.removeHotplugPartition(dev)
297 + for callback in hotplugNotifier:
299 + callback(dev, action or media_state)
300 + except AttributeError:
301 + hotplugNotifier.remove(callback)
305 - var, val = x[:i], x[i+1:]
307 +CDROM_DRIVE_STATUS = 0x5326
308 +CDROM_MEDIA_CHANGED = 0x5325
309 +CDSL_CURRENT = ((int)(~0>>1))
313 +CDS_DRIVE_NOT_READY = 3
318 +IOC_TYPESHIFT = (IOC_NRSHIFT+IOC_NRBITS)
319 +BLKRRPART = ((0x12<<IOC_TYPESHIFT) | (95<<IOC_NRSHIFT))
321 - print "hotplug:", v
322 +def autostart(reason, **kwargs):
324 + print "starting hotplug handler"
326 - action = v.get("ACTION")
327 - device = v.get("DEVPATH")
328 - physdevpath = v.get("PHYSDEVPATH")
329 - media_state = v.get("X_E2_MEDIA_STATUS")
330 + if fileExists('/dev/.udev'):
333 + from enigma import eSocketNotifier, eTimer, ePythonMessagePump
335 + from select import POLLIN, POLLPRI
337 - dev = device.split('/')[-1]
339 + def __init__(self):
340 + self.netlink = socket.socket(socket.AF_NETLINK, socket.SOCK_DGRAM, 15)
341 + self.netlink.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
342 + self.netlink.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
343 + self.netlink.bind((0, 1))
344 + self.sn = eSocketNotifier(self.netlink.fileno(), POLLIN|POLLPRI)
345 + self.sn.callback.append(self.dataAvail)
347 - if action is not None and action == "add":
348 - harddiskmanager.addHotplugPartition(dev, physdevpath)
349 - elif action is not None and action == "remove":
350 - harddiskmanager.removeHotplugPartition(dev)
351 - elif media_state is not None:
352 - if media_state == '1':
353 - harddiskmanager.removeHotplugPartition(dev)
354 - harddiskmanager.addHotplugPartition(dev, physdevpath)
355 - elif media_state == '0':
356 - harddiskmanager.removeHotplugPartition(dev)
358 - for callback in hotplugNotifier:
360 - callback(dev, action or media_state)
361 - except AttributeError:
362 - hotplugNotifier.remove(callback)
363 + def dataAvail(self, what):
364 + received = self.netlink.recvfrom(16384)
365 +# print "HOTPLUG(%d):" %(what), received
367 -def autostart(reason, **kwargs):
369 - print "starting hotplug handler"
370 - factory = Factory()
371 - factory.protocol = Hotplug
372 + data = received[0].split('\0')[:-1]
377 + var, val = x[:i], x[i+1:]
380 + if v['SUBSYSTEM'] == 'block' and v['ACTION'] in ('add', 'remove'):
381 + processHotplugData(self, v)
383 + from threading import Thread, Semaphore, Lock
386 + def __init__(self):
388 + self.__lock = Lock()
390 + def push(self, val):
408 - os.remove("/tmp/hotplug.socket")
414 + class BDPoll(Thread):
415 + CHECK_INTERVAL = 2000
416 + MSG_MEDIUM_REMOVED = 1
417 + MSG_MEDIUM_INSERTED = 2
418 + MSG_POLL_FINISHED = 4
419 + def __init__(self):
420 + Thread.__init__(self)
421 + self.__sema = Semaphore(0)
422 + self.__lock = Lock()
423 + self.running = False
424 + self.devices_to_poll = { }
425 + self.messages = ThreadQueue()
426 + self.checkTimer = eTimer()
427 + self.checkTimer.callback.append(self.timeout)
428 + self.checkTimer.start(BDPoll.CHECK_INTERVAL, True)
429 + self.mp = ePythonMessagePump()
430 + self.mp.recv_msg.get().append(self.gotThreadMsg)
433 + def gotThreadMsg(self, msg):
434 + msg = self.messages.pop()
435 + if msg[0] == BDPoll.MSG_MEDIUM_REMOVED:
436 + print "MSG_MEDIUM_REMOVED"
437 + harddiskmanager.removeHotplugPartition(msg[1])
438 + elif msg[0] == BDPoll.MSG_MEDIUM_INSERTED:
439 + print "MSG_MEDIUM_INSERTED"
440 + harddiskmanager.addHotplugPartition(msg[1])
441 + elif msg[0] == BDPoll.MSG_POLL_FINISHED:
442 + self.checkTimer.start(BDPoll.CHECK_INTERVAL, True)
445 + self.__sema.release() # start bdpoll loop in thread
447 + def is_mounted(self, dev):
448 + mounts = file('/proc/mounts').read()
449 + return mounts.find(dev) != -1
454 + messages = self.messages
456 + self.running = True
457 + while self.running:
459 + self.__lock.acquire()
460 + devices_to_poll = self.devices_to_poll.items()
461 + self.__lock.release()
462 + devices_to_poll_processed = [ ]
463 + for device, state in devices_to_poll:
465 + is_cdrom, prev_media_state = state
468 + fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK | os.O_EXCL)
469 + except OSError, err:
470 + if err.errno == errno.EBUSY:
471 + print "open cdrom exclusive failed:",
472 + if not self.is_mounted(device):
473 + print "not mounted"
476 + print "mounted... try non exclusive"
477 + fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK)
478 + except OSError, err:
479 + print "open cdrom not exclusive failed", os.strerror(err.errno)
481 + #here the fs must be valid!
483 + ret = fcntl.ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)
484 + except IOError, err:
485 + print "ioctl CDROM_DRIVE_STATUS failed", os.strerror(err.errno)
487 + if ret in (CDS_NO_INFO, CDS_NO_DISC, CDS_TRAY_OPEN, CDS_DRIVE_NOT_READY):
489 + elif ret == CDS_DISC_OK:
490 + #todo new kernels support events to userspace event on media change
491 + #but not 2.6.18.... see hotplug-ng bdpoll.c
496 + fd = os.open("/dev/" + device, os.O_RDONLY)
497 + except OSError, err:
498 + if err.errno == ENOMEDIUM:
501 + print "open non cdrom failed", os.strerror(err.errno)
506 + if prev_media_state:
508 + print "media removal detected on", device
510 + fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK)
511 + except OSError, err:
512 + print "open device for blkrrpart ioctl failed", os.strerror(err.errno)
515 + fcntl.ioctl(fd, BLKRRPART)
516 + except IOError, err:
517 + print "ioctl BLKRRPART failed", os.strerror(err.errno)
521 + print "media insertion detected on", device
522 + devices_to_poll_processed.append((device, is_cdrom, got_media))
523 + self.__lock.acquire()
524 + for device, is_cdrom, state in devices_to_poll_processed:
525 + old_state = self.devices_to_poll.get(device)
526 + if old_state is not None and old_state[1] != state:
527 + msg = state and BDPoll.MSG_MEDIUM_INSERTED or BDPoll.MSG_MEDIUM_REMOVED
528 + self.devices_to_poll[device] = (is_cdrom, state)
529 + messages.push((msg, device))
532 + self.__lock.release()
533 + messages.push((self.MSG_POLL_FINISHED,))
536 + def addDevice(self, device, is_cdrom, inserted):
537 + self.__lock.acquire()
538 + if device in self.devices_to_poll:
539 + print "device", device, "already in bdpoll"
541 + print "add device", device, "to bdpoll current state:",
543 + print "medium inserted"
545 + print "medium removed"
546 + self.devices_to_poll[device] = (is_cdrom, inserted)
547 + self.__lock.release()
549 + def removeDevice(self, device):
550 + self.__lock.acquire()
551 + if device in self.devices_to_poll:
552 + print "device", device, "removed from bdpoll"
553 + del self.devices_to_poll[device]
555 + print "try to del not exist device", device, "from bdpoll"
556 + self.__lock.release()
558 + netlink = Netlink()
560 + for blockdev, removable, is_cdrom, medium_found in harddiskmanager.devices_scanned_on_init:
561 + if removable or is_cdrom:
562 + bdpoll.addDevice(blockdev, is_cdrom, medium_found)
564 + from twisted.internet.protocol import Protocol, Factory
565 + from twisted.internet import reactor
569 + os.remove("/tmp/hotplug.socket")
573 + class Hotplug(Protocol):
574 + def connectionMade(self):
575 + print "HOTPLUG connection!"
578 + def dataReceived(self, data):
579 + print "hotplug:", data
580 + self.received += data
581 + print "complete", self.received
583 + def connectionLost(self, reason):
584 + print "HOTPLUG connection lost!"
585 + data = self.received.split('\0')[:-1]
590 + var, val = x[:i], x[i+1:]
593 + processHotplugData(self, v)
595 - reactor.listenUNIX("/tmp/hotplug.socket", factory)
596 + factory = Factory()
597 + factory.protocol = Hotplug
598 + reactor.listenUNIX("/tmp/hotplug.socket", factory)
600 def Plugins(**kwargs):
601 return PluginDescriptor(name = "Hotplug", description = "listens to hotplug events", where = PluginDescriptor.WHERE_AUTOSTART, fnc = autostart)